pax_global_header00006660000000000000000000000064132726401700014514gustar00rootroot0000000000000052 comment=39c67e2640fc34346dffe3cff9f9f8e905fe5ad9 osmo-sgsn-1.3.0/000077500000000000000000000000001327264017000134425ustar00rootroot00000000000000osmo-sgsn-1.3.0/.gitignore000066400000000000000000000011771327264017000154400ustar00rootroot00000000000000debian/*.log *.o *.lo *.a .deps Makefile Makefile.in bscconfig.h bscconfig.h.in *.*~ *.sw? .libs *.pyc *.gcda *.gcno #configure aclocal.m4 autom4te.cache/ m4/*.m4 !m4/ax_*.m4 config.log config.status config.guess config.sub configure compile depcomp install-sh missing stamp-h1 libtool ltmain.sh # git-version-gen magic .tarball-version .version # apps and app data src/gprs/osmo-sgsn src/gprs/osmo-gbproxy src/gprs/osmo-gtphub src/libcommon/gsup_test_client #tests tests/testsuite.dir tests/*/*_test tests/atconfig tests/atlocal tests/package.m4 tests/testsuite tests/testsuite.log gsn_restart writtenconfig/ gtphub_restart_count osmo-sgsn-1.3.0/.gitreview000066400000000000000000000000631327264017000154470ustar00rootroot00000000000000[gerrit] host=gerrit.osmocom.org project=osmo-sgsn osmo-sgsn-1.3.0/.mailmap000066400000000000000000000012741327264017000150670ustar00rootroot00000000000000Harald 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-sgsn-1.3.0/AUTHORS000066400000000000000000000005371327264017000145170ustar00rootroot00000000000000Harald Welte Holger Freyther Jan Luebbe Stefan Schmidt Daniel Willmann Andreas Eversberg Sylvain Munaut <246tnt@gmail.com> Jacob Erlbeck Neels Hofmeyr osmo-sgsn-1.3.0/COPYING000066400000000000000000001033301327264017000144750ustar00rootroot00000000000000 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-sgsn-1.3.0/Makefile.am000066400000000000000000000012411327264017000154740ustar00rootroot00000000000000AUTOMAKE_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) pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = osmo-sgsn.pc 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-sgsn-1.3.0/README000066400000000000000000000011431327264017000143210ustar00rootroot00000000000000About OsmoSGSN ============== OsmoSGSN originated from the OpenBSC project, as a separate program within openbsc.git. In 2017, OpenBSC was split in separate repositories, and hence OsmoSGSN was given its own separate git repository. OsmoSGSN exposes - GSUP towards OsmoHLR (or a MAP proxy); - GTP towards a GGSN (e.g. OsmoGGSN); - Gb towards a BSS (e.g. OsmoPCU); - IuPS towards an RNC or HNB-GW (e.g. OsmoHNBGW) for 3G data; - The Osmocom typical telnet VTY and CTRL interfaces. Find OsmoSGSN issue tracker and wiki online at https://osmocom.org/projects/osmosgsn https://osmocom.org/projects/osmosgsn/wiki osmo-sgsn-1.3.0/README.vty-tests000066400000000000000000000004421327264017000163030ustar00rootroot00000000000000To 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-sgsn-1.3.0/configure.ac000066400000000000000000000151321327264017000157320ustar00rootroot00000000000000dnl Process this file with autoconf to produce a configure script AC_INIT([osmo-sgsn], m4_esyscmd([./git-version-gen .tarball-version]), [osmocom-net-gprs@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.11.0) PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.11.0) PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 0.11.0) PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.11.0) PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 0.5.0) PKG_CHECK_MODULES(LIBOSMOGB, libosmogb >= 0.11.0) PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.2.0) # Enable/disable 3G aka IuPS + IuCS support? AC_ARG_ENABLE([iu], [AS_HELP_STRING([--enable-iu], [Build 3G support, aka IuPS and IuCS interfaces])], [osmo_ac_iu="$enableval"],[osmo_ac_iu="no"]) if test "x$osmo_ac_iu" = "xyes" ; then PKG_CHECK_MODULES(LIBOSMOSIGTRAN, libosmo-sigtran >= 0.9.0) PKG_CHECK_MODULES(LIBASN1C, libasn1c >= 0.9.30) PKG_CHECK_MODULES(LIBOSMORANAP, libosmo-ranap >= 0.3.0) AC_DEFINE(BUILD_IU, 1, [Define if we want to build IuPS and IuCS interfaces support]) fi AM_CONDITIONAL(BUILD_IU, test "x$osmo_ac_iu" = "xyes") AC_SUBST(osmo_ac_iu) PKG_CHECK_MODULES(LIBGTP, libgtp >= 1.2.0) PKG_CHECK_MODULES(LIBCARES, libcares) dnl checks for header files AC_HEADER_STDC 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) CPPFLAGS="$CPPFLAGS -Wall" CFLAGS="$CFLAGS -Wall" 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_DEFUN([CHECK_TM_INCLUDES_TM_GMTOFF], [ AC_CACHE_CHECK( [whether struct tm has tm_gmtoff member], osmo_cv_tm_includes_tm_gmtoff, [AC_LINK_IFELSE([ AC_LANG_PROGRAM([ #include ], [ time_t t = time(NULL); struct tm* lt = localtime(&t); int off = lt->tm_gmtoff; ]) ], osmo_cv_tm_includes_tm_gmtoff=yes, osmo_cv_tm_includes_tm_gmtoff=no )] ) if test "x$osmo_cv_tm_includes_tm_gmtoff" = xyes; then AC_DEFINE(HAVE_TM_GMTOFF_IN_TM, 1, [Define if struct tm has tm_gmtoff member.]) fi ]) CHECK_TM_INCLUDES_TM_GMTOFF 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( osmo-sgsn.pc include/Makefile include/osmocom/Makefile include/osmocom/sgsn/Makefile src/Makefile src/gprs/Makefile tests/Makefile tests/atlocal tests/gprs/Makefile tests/gbproxy/Makefile tests/sgsn/Makefile tests/oap/Makefile tests/gtphub/Makefile tests/xid/Makefile tests/sndcp_xid/Makefile tests/slhc/Makefile tests/v42bis/Makefile doc/Makefile doc/examples/Makefile Makefile) osmo-sgsn-1.3.0/contrib/000077500000000000000000000000001327264017000151025ustar00rootroot00000000000000osmo-sgsn-1.3.0/contrib/gprs/000077500000000000000000000000001327264017000160555ustar00rootroot00000000000000osmo-sgsn-1.3.0/contrib/gprs/gb-proxy-unblock-bug.py000077500000000000000000000026201327264017000224070ustar00rootroot00000000000000#!/usr/bin/env python """ demonstrate a unblock bug on the GB Proxy.. """ bts_ns_reset = "\x02\x00\x81\x01\x01\x82\x1f\xe7\x04\x82\x1f\xe7" ns_reset_ack = "\x03\x01\x82\x1f\xe7\x04\x82\x1f\xe7" bts_ns_unblock = "\x06" ns_unblock_ack = "\x07" bts_bvc_reset_0 = "\x00\x00\x00\x00\x22\x04\x82\x00\x00\x07\x81\x03\x3b\x81\x02" ns_bvc_reset_0_ack = "\x00\x00\x00\x00\x23\x04\x82\x00\x00" bts_bvc_reset_8167 = "\x00\x00\x00\x00\x22\x04\x82\x1f\xe7\x07\x81\x08\x08\x88\x72\xf4\x80\x10\x1c\x00\x9c\x40" import socket socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) socket.bind(("0.0.0.0", 0)) socket.setblocking(1) import sys port = int(sys.argv[1]) print "Sending data to port: %d" % port def send_and_receive(packet): socket.sendto(packet, ("127.0.0.1", port)) try: data, addr = socket.recvfrom(4096) except socket.error, e: print "ERROR", e import sys sys.exit(0) return data #send stuff once to_send = [ (bts_ns_reset, ns_reset_ack, "reset ack"), (bts_ns_unblock, ns_unblock_ack, "unblock ack"), (bts_bvc_reset_0, ns_bvc_reset_0_ack, "BVCI=0 reset ack"), ] for (out, inp, type) in to_send: res = send_and_receive(out) if res != inp: print "Failed to get the %s" % type sys.exit(-1) import time time.sleep(3) res = send_and_receive(bts_bvc_reset_8167) print "Sent all messages... check wireshark for the last response" osmo-sgsn-1.3.0/contrib/gprs/gprs-bssgp-histogram.lua000066400000000000000000000036241327264017000226470ustar00rootroot00000000000000-- Simple LUA script to print the size of BSSGP messages over their type... do local ip_bucket = {} local pdu_types = {} pdu_types[ 6] = "PAGING" pdu_types[11] = "SUSPEND" pdu_types[12] = "SUSPEND-ACK" pdu_types[32] = "BVC-BLOCK" pdu_types[33] = "BVC-BLOCK-ACK" pdu_types[34] = "BVC-RESET" pdu_types[35] = "BVC-RESET-ACK" pdu_types[36] = "UNBLOCK" pdu_types[37] = "UNBLOCK-ACK" pdu_types[38] = "FLOW-CONTROL-BVC" pdu_types[39] = "FLOW-CONTROL-BVC-ACK" pdu_types[40] = "FLOW-CONTROL-MS" pdu_types[41] = "FLOW-CONTROL-MS-ACK" pdu_types[44] = "LLC-DISCARDED" local function init_listener() -- handle the port as NS over IP local udp_port_table = DissectorTable.get("udp.port") local gprs_ns_dis = Dissector.get("gprs_ns") udp_port_table:add(23000,gprs_ns_dis) -- bssgp filters local bssgp_pdu_get = Field.new("bssgp.pdu_type") local udp_length_get = Field.new("udp.length") local tap = Listener.new("ip", "udp.port == 23000") function tap.packet(pinfo,tvb,ip) local pdu = bssgp_pdu_get() local len = udp_length_get() -- only handle bssgp, but we also want the IP frame if not pdu then return end pdu = tostring(pdu) if tonumber(pdu) == 0 or tonumber(pdu) == 1 then return end local ip_src = tostring(ip.ip_src) local bssgp_histo = ip_bucket[ip_src] if not bssgp_histo then bssgp_histo = {} ip_bucket[ip_src] = bssgp_histo end local key = pdu local bucket = bssgp_histo[key] if not bucket then bucket = {} bssgp_histo[key] = bucket end table.insert(bucket, tostring(len)) print("IP: " .. ip_src .. " PDU: " .. pdu_types[tonumber(pdu)] .. " Length: " .. tostring(len)) end function tap.draw() -- well... this will not be called... -- for ip,bssgp_histo in pairs(dumpers) do -- print("IP " .. ip) -- end end function tap.reset() -- well... this will not be called... end end init_listener() end osmo-sgsn-1.3.0/contrib/gprs/gprs-buffer-count.lua000066400000000000000000000037421327264017000221360ustar00rootroot00000000000000-- I count the buffer space needed for LLC PDUs in the worse case and print it do local function init_listener() -- handle the port as NS over IP local udp_port_table = DissectorTable.get("udp.port") local gprs_ns_dis = Dissector.get("gprs_ns") udp_port_table:add(23000,gprs_ns_dis) -- bssgp filters local bssgp_pdu_get = Field.new("bssgp.pdu_type") local bssgp_delay_get = Field.new("bssgp.delay_val") local llcgprs_get = Field.new("llcgprs") local pdus = nil print("START...") local tap = Listener.new("ip", "udp.port == 23000 && bssgp.pdu_type == 0") function tap.packet(pinfo,tvb,ip) local pdu = bssgp_pdu_get() local len = llcgprs_get().len local delay = bssgp_delay_get() -- only handle bssgp, but we also want the IP frame if not pdu then return end if tonumber(tostring(delay)) == 65535 then pdus = { next = pdus, len = len, expires = -1 } else local off = tonumber(tostring(delay)) / 100.0 pdus = { next = pdus, len = len, expires = pinfo.rel_ts + off } end local now_time = tonumber(tostring(pinfo.rel_ts)) local now_size = 0 local l = pdus local prev = nil local count = 0 while l do if now_time < l.expires or l.expires == -1 then now_size = now_size + l.len prev = l l = l.next count = count + 1 else -- delete things if prev == nil then pdus = nil l = nil else prev.next = l.next l = l.next end end end -- print("TOTAL: " .. now_time .. " PDU_SIZE: " .. now_size) print(now_time .. " " .. now_size / 1024.0 .. " " .. count) -- print("NOW: " .. tostring(pinfo.rel_ts) .. " Delay: " .. tostring(delay) .. " Length: " .. tostring(len)) end function tap.draw() -- well... this will not be called... -- for ip,bssgp_histo in pairs(dumpers) do -- print("IP " .. ip) -- end print("END") end function tap.reset() -- well... this will not be called... end end init_listener() end osmo-sgsn-1.3.0/contrib/gprs/gprs-split-trace-by-tlli.lua000066400000000000000000000025071327264017000233360ustar00rootroot00000000000000-- Create a file named by_ip/''ip_addess''.cap with all ip traffic of each ip host. (works for tshark only) -- Dump files are created for both source and destination hosts do local dir = "by_tlli" local dumpers = {} local function init_listener() local udp_port_table = DissectorTable.get("udp.port") local gprs_ns_dis = Dissector.get("gprs_ns") udp_port_table:add(23000,gprs_ns_dis) local field_tlli = Field.new("bssgp.tlli") local tap = Listener.new("ip", "udp.port == 23000") -- we will be called once for every IP Header. -- If there's more than one IP header in a given packet we'll dump the packet once per every header function tap.packet(pinfo,tvb,ip) local tlli = field_tlli() if not tlli then return end local tlli_str = tostring(tlli) tlli_dmp = dumpers[tlli_str] if not tlli_dmp then local tlli_hex = string.format("0x%x", tonumber(tlli_str)) print("Creating dump for TLLI " .. tlli_hex) tlli_dmp = Dumper.new_for_current(dir .. "/" .. tlli_hex .. ".pcap") dumpers[tlli_str] = tlli_dmp end tlli_dmp:dump_current() tlli_dmp:flush() end function tap.draw() for tlli,dumper in pairs(dumpers) do dumper:flush() end end function tap.reset() for tlli,dumper in pairs(dumpers) do dumper:close() end dumpers = {} end end init_listener() end osmo-sgsn-1.3.0/contrib/gprs/gprs-verify-nu.lua000066400000000000000000000030501327264017000214530ustar00rootroot00000000000000-- This script verifies that the N(U) is increasing... -- do local nu_state_src = {} local function init_listener() -- handle the port as NS over IP local udp_port_table = DissectorTable.get("udp.port") local gprs_ns_dis = Dissector.get("gprs_ns") udp_port_table:add(23000,gprs_ns_dis) -- we want to look here... local llc_sapi_get = Field.new("llcgprs.sapib") local llc_nu_get = Field.new("llcgprs.nu") local bssgp_tlli_get = Field.new("bssgp.tlli") local tap = Listener.new("ip", "udp.port == 23000") function tap.packet(pinfo,tvb,ip) local llc_sapi = llc_sapi_get() local llc_nu = llc_nu_get() local bssgp_tlli = bssgp_tlli_get() if not llc_sapi or not llc_nu or not bssgp_tlli then return end local ip_src = tostring(ip.ip_src) local bssgp_tlli = tostring(bssgp_tlli) local llc_nu = tostring(llc_nu) local llc_sapi = tostring(llc_sapi) local src_key = ip_src .. "-" .. bssgp_tlli .. "-" .. llc_sapi local last_nu = nu_state_src[src_key] if not last_nu then -- print("Establishing mapping for " .. src_key) nu_state_src[src_key] = llc_nu return end local function tohex(number) return string.format("0x%x", tonumber(number)) end nu_state_src[src_key] = llc_nu if tonumber(last_nu) + 1 ~= tonumber(llc_nu) then print("JUMP in N(U) on TLLI " .. tohex(bssgp_tlli) .. " and SAPI: " .. llc_sapi .. " src: " .. ip_src) print("\t last: " .. last_nu .. " now: " .. llc_nu) end end function tap.draw() end function tap.reset() end end init_listener() end osmo-sgsn-1.3.0/contrib/jenkins.sh000077500000000000000000000026411327264017000171050ustar00rootroot00000000000000#!/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 "" ac_cv_path_DOXYGEN=false 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 osmo-ggsn if [ "x$IU" = "x--enable-iu" ]; then osmo-build-dep.sh libosmo-sccp osmo-build-dep.sh libasn1c #osmo-build-dep.sh asn1c aper-prefix # only needed for make regen in osmo-iuh osmo-build-dep.sh osmo-iuh fi set +x echo echo echo echo " =============================== osmo-sgsn ===============================" echo set -x cd "$base" autoreconf --install --force ./configure --enable-sanitize $SMPP $MGCP $IU --enable-external-tests $MAKE $PARALLEL_MAKE LD_LIBRARY_PATH="$inst/lib" $MAKE check \ || cat-testlogs.sh LD_LIBRARY_PATH="$inst/lib" \ DISTCHECK_CONFIGURE_FLAGS="--enable-osmo-bsc --enable-nat $SMPP $MGCP $IU --enable-external-tests" \ $MAKE distcheck \ || cat-testlogs.sh osmo-clean-workspace.sh osmo-sgsn-1.3.0/contrib/systemd/000077500000000000000000000000001327264017000165725ustar00rootroot00000000000000osmo-sgsn-1.3.0/contrib/systemd/osmo-gbproxy.service000066400000000000000000000003311327264017000226160ustar00rootroot00000000000000[Unit] Description=Osmocom Gb proxy [Service] Type=simple ExecStart=/usr/bin/osmo-gbproxy -c /etc/osmocom/osmo-gbproxy.cfg Restart=always RestartSec=2 RestartPreventExitStatus=1 [Install] WantedBy=multi-user.target osmo-sgsn-1.3.0/contrib/systemd/osmo-sgsn.service000066400000000000000000000004271327264017000221040ustar00rootroot00000000000000[Unit] Description=Osmocom SGSN (Serving GPRS Support Node) Wants=osmo-hlr.service After=osmo-hlr.service After=osmo-hnbgw.service [Service] Type=simple Restart=always ExecStart=/usr/bin/osmo-sgsn -c /etc/osmocom/osmo-sgsn.cfg RestartSec=2 [Install] WantedBy=multi-user.target osmo-sgsn-1.3.0/debian/000077500000000000000000000000001327264017000146645ustar00rootroot00000000000000osmo-sgsn-1.3.0/debian/changelog000066400000000000000000000116301327264017000165370ustar00rootroot00000000000000osmo-sgsn (1.3.0) unstable; urgency=medium [ Neels Hofmeyr ] * drop osmo_sgsn.cfg from src/gprs dir * change default config filename to osmo-sgsn.cfg, not osmo_sgsn.cfg * vty: skip installing cmds now always installed by default * add --enable-sanitize config option * use default point-code as listed on Point_Codes wiki page * gprs_gmm: segfault: gracefully handle failure to alloc context * gsm48_rx_gmm_att_req: fix error handling: don't clean up NULL llme * gprs_llc: tx dl ud: make mismatching LLE not crash osmo-sgsn * fix build: missing LIBGTP_CFLAGS in sgsn_test * sgsn_test: guard against struct gprs_ra_id changing * vty: absorb command explanations from osmo-gsm-manuals * configure: add --enable-werror * implement support for 3-digit MNC with leading zeros * osmo-gbproxy: use 'osmo-gbproxy.cfg' as default config name * compiler warnings: use enum ranap_nsap_addr_enc, constify local var * use osmo_init_logging2(), fix regression test memleaks * auth+ciph: log is_r99 and auth types * log two RA Update Request failure causes * GERAN: allow GSM SRES on UMTS AKA challenge [ Alexander Couzens ] * .gitignore: remove unneeded ignores of bsc/msc/nitb files * tests/ctrl_test_runner.py: remove BSC/NAT TestRunner * debian: remove doublicated project name in example files * .gitignore: remove non-existent /src/utils exludes * configure.ac: remove pcap check * configure.ac: remove unused libcdk check * .service: remove OpenBSC from service desription * mandatory depend on libc-ares and libgtp * GMM: dont reply to GMM_DETACH_REQ with POWER OFF when MS is unknown [ Harald Welte ] * Replace '.' in counter names with ':' * Add talloc context introspection via VTY [ Pau Espin Pedrol ] * Replace '.' in counter names with ':' * tests: Fix selection of python version * sgsn_cdr: Split log formatting into a snprintf function * Add vty cmd 'cdr trap' to send CDR through CTRL iface * tests: sgsn_test: Define wrap APIs with correct parameters * cosmetic: tests: sgsn_test: Use proper formatting and remove uneeded semicolons * gprs_gmm: Remove unused variable * cosmetic: gprs_gmm: Remove trailing whitespace * gprs_gmm: Convert warning message to pragma message * configure.ac: Enable Wall in CFLAGS * .gitignore: Add m4 files * sgsn_libgtp.c: Fix typos and whitespace [ Max ] * Fix display of GTP addresses * Show GTP version for PDP context in vty * Remove unneeded .py scripts * Replace '.' in counter names with ':' * Remove dead code * Enable sanitize for CI tests * cosmetic: use macro for gtphub plane iteration * Use connection id when allocating rate counters * Migrate from OpenSSL to osmo_get_rand_id() * Remove obsolete ./configure option * Fix RAI construction * gtphub: make rate_ctr unique * Remove unused bsc_version.c * Use gsm48_encode_ra() for RAI encoding * gtphub: check for gsn_addr_from_sockaddr() errors * gtphub: check for gsn_addr_from_sockaddr() error -- Pau Espin Pedrol Thu, 03 May 2018 19:01:44 +0200 osmo-sgsn (1.2.0) unstable; urgency=medium [ Neels Hofmeyr ] * jenkins: fix build: remove unused dependencies * debian: fix osmo-sgsn.install, tweak VCS link and descriptions * drop files unrelated to osmo-sgsn * rewrite README * configure.ac: set name to osmo-sgsn, fix ML addr * move openbsc.pc to osmo-sgsn.pc * move include/openbsc to include/osmocom/sgsn * jenkins.sh: fix echo string to say osmo-sgsn, not msc * jenkins: use osmo-clean-workspace.sh before and after build [ Alexander Couzens ] * debian: fix paths of examples * debian/rules: show testsuite.log when tests are failing [ Max ] * Remove rest_octets.h * gbproxy: ensure peer allocation result * jenkins: use osmo-ggsn for tests * Cleanup configure checks * Use extended logging for PDP contexts * deb: fix copyright file issues * Move P-TMSI alloc/update into separate function * Check for correct P-TMSI allocation * Use new FSF address in license header * SGSN: uncomment BSSGP vty tests * SGSN: print additional GTP-related info * SGSN: check that GSN is created in proper mode * Fix APN printing * Fix build after recent rate_ctr patches * gbproxy: don't link unnecessary * Fix libosmo-sigtran dependency * jenkins: check for IU properly * Log GTP-U endpoints update * Log address on GTP creation [ Pau Espin Pedrol ] * Remove unneeded dep libdbi [ Philipp Maier ] * log: fix default loglevels * non-iu-build: guard vty libosmo-sigtran function calls. * configure: fix libosmo-sigtran dependency [ Harald Welte ] * Debian: Add systemd service files for osmo-sgsn and osmo-gbproxy * Debian: fix dh_strip rules for creating one -dbg per program -- Harald Welte Sat, 28 Oct 2017 19:07:48 +0200 osmo-sgsn (0.1.0) unstable; urgency=low * Initial release. -- Alexander Couzens Tue, 08 Aug 2017 01:13:05 +0000 osmo-sgsn-1.3.0/debian/compat000066400000000000000000000000021327264017000160620ustar00rootroot000000000000009 osmo-sgsn-1.3.0/debian/control000066400000000000000000000037131327264017000162730ustar00rootroot00000000000000Source: osmo-sgsn Section: net Priority: extra Maintainer: Alexander Couzens Build-Depends: debhelper (>=9), dh-autoreconf, autotools-dev, autoconf, automake, libtool, pkg-config, libtalloc-dev, libc-ares-dev, libgtp-dev, libosmocore-dev, libosmo-abis-dev, libosmo-ranap-dev, libosmo-sccp-dev, libosmo-netif-dev Standards-Version: 3.9.8 Vcs-Git: git://git.osmocom.org/osmo-sgsn.git Vcs-Browser: https://git.osmocom.org/osmo-sgsn Homepage: https://projects.osmocom.org/projects/osmo-sgsn Package: osmo-sgsn Architecture: any Multi-Arch: foreign Depends: ${misc:Depends}, ${shlibs:Depends} Description: OsmoSGSN: Osmocom's Serving GPRS Support Node for 2G and 3G packet-switched mobile networks Package: osmo-sgsn-dbg Section: debug Architecture: any Multi-Arch: same Depends: osmo-sgsn (= ${binary:Version}), ${misc:Depends} Description: OsmoSGSN: Osmocom's Serving GPRS Support Node for 2G and 3G packet-switched mobile networks Package: osmo-gtphub Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: Osmocom GTP Hub: Proxy for GTP traffic between multiple SGSNs and GGSNs Package: osmo-gtphub-dbg Architecture: any Section: debug Priority: extra Depends: osmo-gtphub (= ${binary:Version}), ${misc:Depends} Description: Debug symbols for Osmocom GTP Hub Package: osmo-gbproxy Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Recommends: osmo-sgsn Description: Osmocom GPRS Gb Interface Proxy The purpose of the Gb proxy is to aggregate the Gb links of multiple BSS's and present them in one Gb link to the SGSN. Package: osmo-gbproxy-dbg Architecture: any Section: debug Priority: extra Depends: osmo-gbproxy (= ${binary:Version}), ${misc:Depends} Description: Debug symbols for Osmocom GPRS Gb Interface Proxy osmo-sgsn-1.3.0/debian/copyright000066400000000000000000000370631327264017000166300ustar00rootroot00000000000000Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: osmo-sgsn Source: git://git.osmocom.org/osmo-sgsn Files: .gitignore .gitreview .mailmap AUTHORS Makefile.am README README.vty-tests configure.ac contrib/Makefile.am contrib/gprs/gb-proxy-unblock-bug.py contrib/gprs/gprs-bssgp-histogram.lua contrib/gprs/gprs-buffer-count.lua contrib/gprs/gprs-split-trace-by-tlli.lua contrib/gprs/gprs-verify-nu.lua contrib/ipa.py contrib/jenkins.sh contrib/soap.py contrib/systemd/osmo-gbproxy.service contrib/systemd/osmo-sgsn.service contrib/twisted_ipa.py doc/Makefile.am doc/examples/Makefile.am doc/examples/osmo-gbproxy/osmo-gbproxy-legacy.cfg doc/examples/osmo-gbproxy/osmo-gbproxy.cfg doc/examples/osmo-gtphub/gtphub-example.txt doc/examples/osmo-gtphub/osmo-gtphub-1iface.cfg doc/examples/osmo-gtphub/osmo-gtphub.cfg doc/examples/osmo-sgsn/osmo-sgsn.cfg include/Makefile.am include/osmocom/sgsn/Makefile.am include/osmocom/sgsn/common.h include/osmocom/sgsn/crc24.h include/osmocom/sgsn/debug.h include/osmocom/sgsn/gb_proxy.h include/osmocom/sgsn/gprs_gb_parse.h include/osmocom/sgsn/gprs_gmm.h include/osmocom/sgsn/gprs_llc.h include/osmocom/sgsn/gprs_sgsn.h include/osmocom/sgsn/gprs_sndcp.h include/osmocom/sgsn/gprs_subscriber.h include/osmocom/sgsn/sgsn.h include/osmocom/sgsn/vty.h m4/README osmo-sgsn.pc.in src/Makefile.am src/gprs/.gitignore src/gprs/Makefile.am src/gprs/osmo_sgsn.cfg src/libcommon/Makefile.am src/libcommon/gsup_test_client.c tests/Makefile.am tests/atlocal.in tests/gbproxy/Makefile.am tests/gbproxy/gbproxy_test.ok tests/gprs/Makefile.am tests/gprs/gprs_test.c tests/gprs/gprs_test.ok tests/gtphub/Makefile.am tests/gtphub/gtphub_test.ok tests/oap/Makefile.am tests/oap/oap_client_test.err tests/oap/oap_client_test.ok tests/sgsn/Makefile.am tests/sgsn/sgsn_test.ok tests/slhc/Makefile.am tests/slhc/slhc_test.ok tests/sndcp_xid/Makefile.am tests/sndcp_xid/sndcp_xid_test.ok tests/testsuite.at tests/v42bis/Makefile.am tests/v42bis/v42bis_test.ok tests/xid/Makefile.am tests/xid/xid_test.ok Copyright: __NO_COPYRIGHT_NOR_LICENSE__ License: __NO_COPYRIGHT_NOR_LICENSE__ Files: include/osmocom/sgsn/a_reset.h include/osmocom/sgsn/gprs_llc_xid.h include/osmocom/sgsn/gprs_sndcp_comp.h include/osmocom/sgsn/gprs_sndcp_dcomp.h include/osmocom/sgsn/gprs_sndcp_pcomp.h include/osmocom/sgsn/gprs_sndcp_xid.h include/osmocom/sgsn/gprs_utils.h include/osmocom/sgsn/gsup_client.h include/osmocom/sgsn/gtphub.h include/osmocom/sgsn/oap_client.h include/osmocom/sgsn/signal.h src/gprs/crc24.c src/gprs/gb_proxy.c src/gprs/gb_proxy_main.c src/gprs/gb_proxy_patch.c src/gprs/gb_proxy_peer.c src/gprs/gb_proxy_tlli.c src/gprs/gb_proxy_vty.c src/gprs/gprs_gb_parse.c src/gprs/gprs_gmm.c src/gprs/gprs_llc.c src/gprs/gprs_llc_parse.c src/gprs/gprs_llc_vty.c src/gprs/gprs_llc_xid.c src/gprs/gprs_sgsn.c src/gprs/gprs_sndcp.c src/gprs/gprs_sndcp_comp.c src/gprs/gprs_sndcp_dcomp.c src/gprs/gprs_sndcp_pcomp.c src/gprs/gprs_sndcp_vty.c src/gprs/gprs_sndcp_xid.c src/gprs/gprs_subscriber.c src/gprs/gprs_utils.c src/gprs/gsup_client.c src/gprs/gtphub.c src/gprs/gtphub_main.c src/gprs/gtphub_vty.c src/gprs/oap_client.c src/gprs/sgsn_ares.c src/gprs/sgsn_auth.c src/gprs/sgsn_cdr.c src/gprs/sgsn_ctrl.c src/gprs/sgsn_libgtp.c src/gprs/sgsn_main.c src/gprs/sgsn_vty.c src/libcommon/common_vty.c src/libcommon/debug.c src/libcommon/gsm_data.c src/libcommon/gsm_data_shared.c src/libcommon/gsm_subscriber_base.c src/libcommon/gsup_client.c src/libcommon/oap_client.c src/libcommon/socket.c src/libcommon/talloc_ctx.c tests/gtphub/gtphub_test.c tests/oap/oap_client_test.c tests/sgsn/sgsn_test.c tests/slhc/slhc_test.c tests/sndcp_xid/sndcp_xid_test.c tests/v42bis/v42bis_test.c tests/xid/xid_test.c Copyright: 2008-2015 Holger Hans Peter Freyther 2008-2016 Harald Welte 2009-2015 Holger Hans Peter Freyther 2010-2014 On-Waves 2011-2016 sysmocom s.f.m.c. GmbH 2014-2016 Sysmocom s.f.m.c. GmbH 2014-2017 sysmocom s.f.m.c. GmbH 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/gprs/gtphub_ares.c src/gprs/gtphub_sock.c tests/gbproxy/gbproxy_test.c Copyright: 2013 Jacob Erlbeck 2013 sysmocom s.f.m.c. GmbH 2014 Holger Hans Peter Freyther 2015 sysmocom s.f.m.c. GmbH License: __NO_LICENSE__ Files: include/osmocom/sgsn/v42bis.h include/osmocom/sgsn/v42bis_private.h Copyright: 2005-2011 Steve Underwood License: LGPL-2.1 This program 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 program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. . You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. . The FSF address in the above text is the old one. . 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: osmoappdesc.py tests/ctrl_test_runner.py tests/vty_test_runner.py Copyright: 2013 Katerina Barone-Adesi 2013 Jacob Erlbeck 2013-2014 Holger Hans Peter Freyther License: GPL-3+ Files: git-version-gen Copyright: 2007-2010 Free Software Foundation, Inc. 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 . . 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). . 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: src/gprs/v42bis.c Copyright: 2005-2011 Steve Underwood License: LGPL-2.1 This program 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 program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. . You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. . THIS IS A WORK IN PROGRESS. IT IS NOT FINISHED. Currently it performs the core compression and decompression functions OK. However, a number of the bells and whistles in V.42bis are incomplete. . ! \file . The FSF address in the above text is the old one. . 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/osmocom/sgsn/slhc.h Copyright: 1989 Regents of the University of California. License: __UNKNOWN__ Redistribution and use in source and binary forms are permitted provided that the above copyright notice and this paragraph are duplicated in all such forms and that any documentation, advertising materials, and other materials related to such distribution and use acknowledge that the software was developed by the University of California, Berkeley. The name of the University may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. . Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: - Initial distribution. . modified for KA9Q Internet Software Package by Katie Stevens (dkstevens@ucdavis.edu) University of California, Davis Computing Services - 01-31-90 initial adaptation Files: src/gprs/slhc.c Copyright: 1989 Regents of the University of California. License: __UNKNOWN__ Redistribution and use in source and binary forms are permitted provided that the above copyright notice and this paragraph are duplicated in all such forms and that any documentation, advertising materials, and other materials related to such distribution and use acknowledge that the software was developed by the University of California, Berkeley. The name of the University may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. . Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: - Initial distribution. . modified for KA9Q Internet Software Package by Katie Stevens (dkstevens@ucdavis.edu) University of California, Davis Computing Services - 01-31-90 initial adaptation (from 1.19) PPP.05 02-15-90 [ks] PPP.08 05-02-90 [ks] use PPP protocol field to signal compression PPP.15 09-90 [ks] improve mbuf handling PPP.16 11-02 [karn] substantially rewritten to use NOS facilities Files: m4/ax_check_compile_flag.m4 Copyright: 2008 Guido U. Draheim 2011 Maarten Bosmans License: GNU-All-Permissive-License 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. . On Debian systems, the complete text of the GNU General Public License Version 3 can be found in `/usr/share/common-licenses/GPL-3'. osmo-sgsn-1.3.0/debian/osmo-gbproxy.init000077500000000000000000000100301327264017000202130ustar00rootroot00000000000000#!/bin/sh ### BEGIN INIT INFO # Provides: osmo-gbproxy # Required-Start: $network $local_fs # Required-Stop: # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Osmocom GBproxy # Description: A tool to proxy the GPRS Gb interface. ### END INIT INFO # Author: Harald Welte # PATH should only include /usr/* if it runs after the mountnfs.sh script PATH=/sbin:/usr/sbin:/bin:/usr/bin NAME=osmo-gbproxy # Introduce the short server's name here DESC="Osmocom GBProxy" # Introduce a short description here DAEMON=/usr/bin/osmo-gbproxy # Introduce the server's location here SCRIPTNAME=/etc/init.d/osmocom-gbproxy CONFIG_FILE=/etc/osmocom/osmocom-gbproxy.cfg # Exit if the package is not installed [ -x $DAEMON ] || exit 0 # Read configuration variable file if it is present [ -r /etc/default/osmocom-gbproxy ] && . /etc/default/osmocom-gbproxy # Load the VERBOSE setting and other rcS variables . /lib/init/vars.sh # Define LSB log_* functions. # Depend on lsb-base (>= 3.0-6) to ensure that this file is present. . /lib/lsb/init-functions DAEMON_ARGS="-D -c $CONFIG_FILE" # # Function that starts the daemon/service # do_start() { # Return # 0 if daemon has been started # 1 if daemon was already running # 2 if daemon could not be started start-stop-daemon --start --quiet --exec $DAEMON --test > /dev/null \ || return 1 start-stop-daemon --start --quiet --exec $DAEMON -- \ $DAEMON_ARGS \ || return 2 # Add code here, if necessary, that waits for the process to be ready # to handle requests from services started subsequently which depend # on this one. As a last resort, sleep for some time. } # # Function that stops the daemon/service # do_stop() { # Return # 0 if daemon has been stopped # 1 if daemon was already stopped # 2 if daemon could not be stopped # other if a failure occurred start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --name $NAME RETVAL="$?" [ "$RETVAL" = 2 ] && return 2 # Wait for children to finish too if this is a daemon that forks # and if the daemon is only ever run from this initscript. # If the above conditions are not satisfied then add some other code # that waits for the process to drop all resources that could be # needed by services started subsequently. A last resort is to # sleep for some time. start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON [ "$?" = 2 ] && return 2 return "$RETVAL" } # # Function that sends a SIGHUP to the daemon/service # do_reload() { # # If the daemon can reload its configuration without # restarting (for example, when it is sent a SIGHUP), # then implement that here. # start-stop-daemon --stop --signal 1 --quiet $PIDFILE --name $NAME return 0 } case "$1" in start) [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC " "$NAME" do_start case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; stop) [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" do_stop case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; status) status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? ;; #reload|force-reload) # # If do_reload() is not implemented then leave this commented out # and leave 'force-reload' as an alias for 'restart'. # #log_daemon_msg "Reloading $DESC" "$NAME" #do_reload #log_end_msg $? #;; restart|force-reload) # # If the "reload" option is implemented then remove the # 'force-reload' alias # log_daemon_msg "Restarting $DESC" "$NAME" do_stop case "$?" in 0|1) do_start case "$?" in 0) log_end_msg 0 ;; 1) log_end_msg 1 ;; # Old process is still running *) log_end_msg 1 ;; # Failed to start esac ;; *) # Failed to stop log_end_msg 1 ;; esac ;; *) #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 exit 3 ;; esac : osmo-sgsn-1.3.0/debian/osmo-gbproxy.install000066400000000000000000000003421327264017000207200ustar00rootroot00000000000000usr/bin/osmo-gbproxy usr/share/doc/osmo-sgsn/examples/osmo-gbproxy/osmo-gbproxy-legacy.cfg usr/share/doc/osmo-gbproxy/examples usr/share/doc/osmo-sgsn/examples/osmo-gbproxy/osmo-gbproxy.cfg usr/share/doc/osmo-gbproxy/examples osmo-sgsn-1.3.0/debian/osmo-gbproxy.service000077700000000000000000000000001327264017000303002../contrib/systemd/osmo-gbproxy.serviceustar00rootroot00000000000000osmo-sgsn-1.3.0/debian/osmo-gtphub.default000066400000000000000000000000541327264017000204750ustar00rootroot00000000000000CONFIG_FILE="/etc/osmocom/osmo-gtphub.cfg" osmo-sgsn-1.3.0/debian/osmo-gtphub.init000077500000000000000000000077511327264017000200320ustar00rootroot00000000000000#!/bin/sh ### BEGIN INIT INFO # Provides: osmo-gtphub # Required-Start: $network $local_fs # Required-Stop: # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Osmocom GTP hub # Description: Osmocom GTP hub ### END INIT INFO # Author: Neels Hofmeyr # PATH should only include /usr/* if it runs after the mountnfs.sh script PATH=/sbin:/usr/sbin:/bin:/usr/bin NAME=osmo-gtphub # Introduce the short server's name here DESC="Osmocom GTP hub" # Introduce a short description here DAEMON=/usr/bin/osmo-gtphub # Introduce the server's location here SCRIPTNAME=/etc/init.d/osmo-gtphub # Exit if the package is not installed [ -x $DAEMON ] || exit 0 # Read configuration variable file if it is present [ -r /etc/default/osmo-gtphub ] && . /etc/default/osmo-gtphub # Load the VERBOSE setting and other rcS variables . /lib/init/vars.sh # Define LSB log_* functions. # Depend on lsb-base (>= 3.0-6) to ensure that this file is present. . /lib/lsb/init-functions DAEMON_ARGS="$DAEMON_ARGS -D -c $CONFIG_FILE" # # Function that starts the daemon/service # do_start() { # Return # 0 if daemon has been started # 1 if daemon was already running # 2 if daemon could not be started start-stop-daemon --start --quiet --exec $DAEMON --test > /dev/null \ || return 1 start-stop-daemon --start --quiet --exec $DAEMON -- \ $DAEMON_ARGS \ || return 2 # Add code here, if necessary, that waits for the process to be ready # to handle requests from services started subsequently which depend # on this one. As a last resort, sleep for some time. } # # Function that stops the daemon/service # do_stop() { # Return # 0 if daemon has been stopped # 1 if daemon was already stopped # 2 if daemon could not be stopped # other if a failure occurred start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --name $NAME RETVAL="$?" [ "$RETVAL" = 2 ] && return 2 # Wait for children to finish too if this is a daemon that forks # and if the daemon is only ever run from this initscript. # If the above conditions are not satisfied then add some other code # that waits for the process to drop all resources that could be # needed by services started subsequently. A last resort is to # sleep for some time. start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON [ "$?" = 2 ] && return 2 return "$RETVAL" } # # Function that sends a SIGHUP to the daemon/service # do_reload() { # # If the daemon can reload its configuration without # restarting (for example, when it is sent a SIGHUP), # then implement that here. # start-stop-daemon --stop --signal 1 --quiet $PIDFILE --name $NAME return 0 } case "$1" in start) [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC " "$NAME" do_start case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; stop) [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" do_stop case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; status) status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? ;; #reload|force-reload) # # If do_reload() is not implemented then leave this commented out # and leave 'force-reload' as an alias for 'restart'. # #log_daemon_msg "Reloading $DESC" "$NAME" #do_reload #log_end_msg $? #;; restart|force-reload) # # If the "reload" option is implemented then remove the # 'force-reload' alias # log_daemon_msg "Restarting $DESC" "$NAME" do_stop case "$?" in 0|1) do_start case "$?" in 0) log_end_msg 0 ;; 1) log_end_msg 1 ;; # Old process is still running *) log_end_msg 1 ;; # Failed to start esac ;; *) # Failed to stop log_end_msg 1 ;; esac ;; *) #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 exit 3 ;; esac : osmo-sgsn-1.3.0/debian/osmo-gtphub.install000066400000000000000000000003331327264017000205170ustar00rootroot00000000000000usr/bin/osmo-gtphub usr/share/doc/osmo-sgsn/examples/osmo-gtphub/osmo-gtphub-1iface.cfg usr/share/doc/osmo-gtphub/examples usr/share/doc/osmo-sgsn/examples/osmo-gtphub/osmo-gtphub.cfg usr/share/doc/osmo-gtphub/examples osmo-sgsn-1.3.0/debian/osmo-sgsn.install000066400000000000000000000003211327264017000201750ustar00rootroot00000000000000usr/bin/osmo-sgsn usr/share/doc/osmo-sgsn/examples/osmo-sgsn/osmo-sgsn.cfg usr/share/doc/osmo-sgsn/examples usr/share/doc/osmo-sgsn/examples/osmo-sgsn/osmo-sgsn-accept-all.cfg usr/share/doc/osmo-sgsn/examples osmo-sgsn-1.3.0/debian/osmo-sgsn.service000077700000000000000000000000001327264017000270402../contrib/systemd/osmo-sgsn.serviceustar00rootroot00000000000000osmo-sgsn-1.3.0/debian/rules000077500000000000000000000043521327264017000157500ustar00rootroot00000000000000#!/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 # debmake generated override targets # Set options for ./configure CONFIGURE_FLAGS += --enable-iu override_dh_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-sgsn --dbg-package=osmo-sgsn-dbg dh_strip -posmo-gtphub --dbg-package=osmo-gtphub-dbg dh_strip -posmo-gbproxy --dbg-package=osmo-gbproxy-dbg # Print test results in case of a failure override_dh_auto_test: dh_auto_test || (find . -name testsuite.log -exec cat {} \; ; false) osmo-sgsn-1.3.0/debian/source/000077500000000000000000000000001327264017000161645ustar00rootroot00000000000000osmo-sgsn-1.3.0/debian/source/format000066400000000000000000000000151327264017000173730ustar00rootroot000000000000003.0 (native) osmo-sgsn-1.3.0/doc/000077500000000000000000000000001327264017000142075ustar00rootroot00000000000000osmo-sgsn-1.3.0/doc/Makefile.am000066400000000000000000000000411327264017000162360ustar00rootroot00000000000000SUBDIRS = \ examples \ $(NULL) osmo-sgsn-1.3.0/doc/examples/000077500000000000000000000000001327264017000160255ustar00rootroot00000000000000osmo-sgsn-1.3.0/doc/examples/Makefile.am000066400000000000000000000010201327264017000200520ustar00rootroot00000000000000CFG_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-sgsn-1.3.0/doc/examples/osmo-gbproxy/000077500000000000000000000000001327264017000204725ustar00rootroot00000000000000osmo-sgsn-1.3.0/doc/examples/osmo-gbproxy/osmo-gbproxy-legacy.cfg000066400000000000000000000017471327264017000250730ustar00rootroot00000000000000! ! OsmoGbProxy (UNKNOWN) configuration saved from vty !! ! log stderr logging filter all 1 logging color 1 logging timestamp 0 logging level all debug logging level gprs debug logging level ns info logging level bssgp debug logging level lglobal notice logging level llapd notice logging level linp notice logging level lmux notice logging level lmi notice logging level lmib notice logging level lsms notice ! line vty no login ! ns nse 666 nsvci 666 nse 666 remote-role sgsn ! nse 666 encapsulation framerelay-gre ! nse 666 remote-ip 172.16.1.70 ! nse 666 fr-dlci 666 timer tns-block 3 timer tns-block-retries 3 timer tns-reset 3 timer tns-reset-retries 3 timer tns-test 30 timer tns-alive 3 timer tns-alive-retries 10 encapsulation udp local-port 23000 ! encapsulation framerelay-gre enabled 1 gbproxy sgsn nsei 666 core-mobile-country-code 666 core-mobile-network-code 6 core-access-point-name none match-imsi ^666066|^66607 tlli-list max-length 200 osmo-sgsn-1.3.0/doc/examples/osmo-gbproxy/osmo-gbproxy.cfg000066400000000000000000000010251327264017000236160ustar00rootroot00000000000000! ! Osmocom Gb Proxy (0.9.0.404-6463) configuration saved from vty !! ! line vty no login ! gbproxy sgsn nsei 101 ns nse 101 nsvci 101 nse 101 remote-role sgsn nse 101 encapsulation udp nse 101 remote-ip 192.168.100.239 nse 101 remote-port 7777 timer tns-block 3 timer tns-block-retries 3 timer tns-reset 3 timer tns-reset-retries 3 timer tns-test 30 timer tns-alive 3 timer tns-alive-retries 10 encapsulation framerelay-gre enabled 0 encapsulation framerelay-gre local-ip 0.0.0.0 encapsulation udp local-port 23000 osmo-sgsn-1.3.0/doc/examples/osmo-gtphub/000077500000000000000000000000001327264017000202715ustar00rootroot00000000000000osmo-sgsn-1.3.0/doc/examples/osmo-gtphub/gtphub-example.txt000066400000000000000000000052061327264017000237570ustar00rootroot00000000000000Here is a simple setup to test GTPHub operations. The IP addresses picked will work well only on a system that creates local addresses (127.0.0.123) on the fly (like linux) -- you may pick of course different IP addresses. Overview of the example setup: sgsnemu gtphub ggsn 127.0.0.1 <--> 127.0.0.3 127.0.0.4 <--> 127.0.0.2 Prerequisites: openggsn. Have a local directory where you store config files and from which you launch the GSNs and the hub (they will store restart counter files in that dir). In it, have these config files: ggsn.conf: # GGSN local address listen 127.0.0.2 # End User Addresses are picked from this range net 10.23.42.0/24 pcodns1 8.8.8.8 logfile /tmp/foo gtphub.conf: gtphub bind-to-sgsns 127.0.0.3 bind-to-ggsns 127.0.0.4 ggsn-proxy 127.0.0.2 end ( You may omit the ggsn-proxy if GRX ares is working, or if you add the GRX address and GGSN IP address to /etc/hosts something like: 127.0.0.2 internet.mnc070.mcc901.gprs ) Once the config files are in place, start the programs, in separate terminals. GGSN and SGSN need to be started with root priviliges to be able to create tun interfaces. GTPHub may run as unprivileged user. The LD_LIBRARY_PATH below may be needed if OpenGGSN installed to /usr/local. 1. GGSN: sudo -s cd LD_LIBRARY_PATH=/usr/local/lib /usr/local/bin/ggsn -f -c ./ggsn.conf 2. GTPHub: cd path/to/openbsc/openbsc/src/gprs/osmo-gtphub -c gtphub.conf #-e 1 #for DEBUG level 3. SGSN tests: sudo -s cd /usr/local/bin/sgsnemu --createif -l 127.0.0.1 -r 127.0.0.3 --imsi 420001214365100 --contexts=3 Add more SGSNs using different IMSIs and local ports (if the same IMSI is used, the GGSN will reuse TEIs and tunnels will be discarded automatically): /usr/local/bin/sgsnemu --createif -l 127.0.0.11 -r 127.0.0.3 --imsi 420001214365300 --contexts=3 This shows the basic setup of GTPHub. Testing internet traffic via sgsnemu still needs some effort to announce a mobile subscriber or the like (I have used a real BTS, osmo-sgsn and a testing SIM in a web phone, instead). The core capability of GTPHub is to manage more than two GSNs, e.g. an SGSN contacting various GGSNs over the single GTPHub link. You would configure the SGSN to use one fixed GGSN (sending to gtphub) and gtphub will resolve the GGSNs once it has received the messages. So the SGSN may be behind NAT (add "sgsn-use-sender" to gtphub.conf) and communicate to various GGSNs over a single link to gtphub. I hope this helps to get you going. Any suggestions/patches are welcome! ~Neels osmo-sgsn-1.3.0/doc/examples/osmo-gtphub/osmo-gtphub-1iface.cfg000066400000000000000000000012341327264017000243440ustar00rootroot00000000000000! ! Osmocom gtphub configuration ! ! This file is used for VTY tests, referenced by openbsc/osmoappdesc.py ! For the test, try to use most config commands. ! line vty no login gtphub ! Local addresses to listen on and send from, both on one interface. ! The side towards SGSN uses nonstandard ports. bind-to-sgsns ctrl 127.0.0.1 12123 user 127.0.0.1 12153 ! The GGSN side with standard ports. bind-to-ggsns 127.0.0.1 ! Proxy: unconditionally direct all traffic to... sgsn-proxy 127.0.0.4 ! Proxy with nonstandard ports or separate IPs: ggsn-proxy ctrl 127.0.0.3 2123 user 127.0.0.5 2152 ! Add a name server for GGSN resolution grx-dns-add 192.168.0.1 osmo-sgsn-1.3.0/doc/examples/osmo-gtphub/osmo-gtphub.cfg000066400000000000000000000011411327264017000232130ustar00rootroot00000000000000! ! Osmocom gtphub configuration ! line vty no login gtphub ! Local addresses to listen on and send from, each on standard ports ! 2123 and 2152. Setting these addresses is mandatory. bind-to-sgsns 127.0.0.1 bind-to-ggsns 127.0.0.2 ! Local nonstandard ports or separate IPs: !bind-to-sgsns ctrl 127.0.0.1 2342 user 127.0.0.1 4223 ! Proxy: unconditionally direct all traffic to... !ggsn-proxy 127.0.0.3 !sgsn-proxy 127.0.0.4 ! Proxy with nonstandard ports or separate IPs: !ggsn-proxy ctrl 127.0.0.3 2123 user 127.0.0.5 2152 ! Add a name server for GGSN resolution !grx-dns-add 192.168.0.1 osmo-sgsn-1.3.0/doc/examples/osmo-sgsn/000077500000000000000000000000001327264017000177525ustar00rootroot00000000000000osmo-sgsn-1.3.0/doc/examples/osmo-sgsn/osmo-sgsn-accept-all.cfg000066400000000000000000000006761327264017000243740ustar00rootroot00000000000000! ! Osmocom SGSN configuration ! ! line vty no login ! sgsn gtp local-ip 127.0.0.1 ggsn 0 remote-ip 127.0.0.2 ggsn 0 gtp-version 1 auth-policy accept-all ! ns timer tns-block 3 timer tns-block-retries 3 timer tns-reset 3 timer tns-reset-retries 3 timer tns-test 30 timer tns-alive 3 timer tns-alive-retries 10 encapsulation udp local-ip 127.0.0.1 encapsulation udp local-port 23000 encapsulation framerelay-gre enabled 0 ! bssgp ! osmo-sgsn-1.3.0/doc/examples/osmo-sgsn/osmo-sgsn.cfg000066400000000000000000000007531327264017000223650ustar00rootroot00000000000000! ! Osmocom SGSN configuration ! ! line vty no login ! sgsn gtp local-ip 127.0.0.1 ggsn 0 remote-ip 127.0.0.2 ggsn 0 gtp-version 1 auth-policy remote gsup remote-ip 127.0.0.1 gsup remote-port 4222 ! ns timer tns-block 3 timer tns-block-retries 3 timer tns-reset 3 timer tns-reset-retries 3 timer tns-test 30 timer tns-alive 3 timer tns-alive-retries 10 encapsulation udp local-ip 127.0.0.1 encapsulation udp local-port 23000 encapsulation framerelay-gre enabled 0 ! bssgp ! osmo-sgsn-1.3.0/git-version-gen000077500000000000000000000125211327264017000164060ustar00rootroot00000000000000#!/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 test -d ./.git \ && 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-sgsn-1.3.0/include/000077500000000000000000000000001327264017000150655ustar00rootroot00000000000000osmo-sgsn-1.3.0/include/Makefile.am000066400000000000000000000000401327264017000171130ustar00rootroot00000000000000SUBDIRS = \ osmocom \ $(NULL) osmo-sgsn-1.3.0/include/osmocom/000077500000000000000000000000001327264017000165415ustar00rootroot00000000000000osmo-sgsn-1.3.0/include/osmocom/Makefile.am000066400000000000000000000000351327264017000205730ustar00rootroot00000000000000SUBDIRS = \ sgsn \ $(NULL) osmo-sgsn-1.3.0/include/osmocom/sgsn/000077500000000000000000000000001327264017000175135ustar00rootroot00000000000000osmo-sgsn-1.3.0/include/osmocom/sgsn/Makefile.am000066400000000000000000000006341327264017000215520ustar00rootroot00000000000000noinst_HEADERS = \ common.h \ crc24.h \ debug.h \ gb_proxy.h \ gprs_gb_parse.h \ gprs_gmm.h \ gprs_llc.h \ gprs_llc_xid.h \ gprs_sgsn.h \ gprs_sndcp_comp.h \ gprs_sndcp_dcomp.h \ gprs_sndcp.h \ gprs_sndcp_pcomp.h \ gprs_sndcp_xid.h \ gprs_subscriber.h \ gprs_utils.h \ gsup_client.h \ gtphub.h \ oap_client.h \ sgsn.h \ signal.h \ slhc.h \ v42bis.h \ v42bis_private.h \ vty.h \ $(NULL) osmo-sgsn-1.3.0/include/osmocom/sgsn/common.h000066400000000000000000000001211327264017000211460ustar00rootroot00000000000000#pragma once enum nsap_addr_enc { NSAP_ADDR_ENC_X213, NSAP_ADDR_ENC_V4RAW, }; osmo-sgsn-1.3.0/include/osmocom/sgsn/crc24.h000066400000000000000000000002371327264017000206030ustar00rootroot00000000000000#ifndef _CRC24_H #define _CRC24_H #include #define INIT_CRC24 0xffffff uint32_t crc24_calc(uint32_t fcs, uint8_t *cp, unsigned int len); #endif osmo-sgsn-1.3.0/include/osmocom/sgsn/debug.h000066400000000000000000000007251327264017000207560ustar00rootroot00000000000000#pragma once #include #include #define DEBUG #include /* Debug Areas of the code */ enum { DRLL, DCC, DMM, DRR, DRSL, DNM, DMNCC, DPAG, DMEAS, DSCCP, DMSC, DMGCP, DHO, DDB, DREF, DGPRS, DNS, DBSSGP, DLLC, DSNDCP, DSLHC, DNAT, DCTRL, DSMPP, DFILTER, DGTPHUB, DRANAP, DSUA, DV42BIS, DPCU, DVLR, DIUCS, DSIGTRAN, Debug_LastEntry, }; extern const struct log_info log_info; osmo-sgsn-1.3.0/include/osmocom/sgsn/gb_proxy.h000066400000000000000000000177101327264017000215230ustar00rootroot00000000000000#ifndef _GB_PROXY_H #define _GB_PROXY_H #include #include #include #include #include #include #define GBPROXY_INIT_VU_GEN_TX 256 struct rate_ctr_group; struct gprs_gb_parse_context; struct tlv_parsed; enum gbproxy_global_ctr { GBPROX_GLOB_CTR_INV_BVCI, GBPROX_GLOB_CTR_INV_LAI, GBPROX_GLOB_CTR_INV_RAI, GBPROX_GLOB_CTR_INV_NSEI, GBPROX_GLOB_CTR_PROTO_ERR_BSS, GBPROX_GLOB_CTR_PROTO_ERR_SGSN, GBPROX_GLOB_CTR_NOT_SUPPORTED_BSS, GBPROX_GLOB_CTR_NOT_SUPPORTED_SGSN, GBPROX_GLOB_CTR_RESTART_RESET_SGSN, GBPROX_GLOB_CTR_TX_ERR_SGSN, GBPROX_GLOB_CTR_OTHER_ERR, GBPROX_GLOB_CTR_PATCH_PEER_ERR, }; enum gbproxy_peer_ctr { GBPROX_PEER_CTR_BLOCKED, GBPROX_PEER_CTR_UNBLOCKED, GBPROX_PEER_CTR_DROPPED, GBPROX_PEER_CTR_INV_NSEI, GBPROX_PEER_CTR_TX_ERR, GBPROX_PEER_CTR_RAID_PATCHED_BSS, GBPROX_PEER_CTR_RAID_PATCHED_SGSN, GBPROX_PEER_CTR_APN_PATCHED, GBPROX_PEER_CTR_TLLI_PATCHED_BSS, GBPROX_PEER_CTR_TLLI_PATCHED_SGSN, GBPROX_PEER_CTR_PTMSI_PATCHED_BSS, GBPROX_PEER_CTR_PTMSI_PATCHED_SGSN, GBPROX_PEER_CTR_PATCH_CRYPT_ERR, GBPROX_PEER_CTR_PATCH_ERR, GBPROX_PEER_CTR_ATTACH_REQS, GBPROX_PEER_CTR_ATTACH_REJS, GBPROX_PEER_CTR_ATTACH_ACKS, GBPROX_PEER_CTR_ATTACH_COMPLS, GBPROX_PEER_CTR_RA_UPD_REQS, GBPROX_PEER_CTR_RA_UPD_REJS, GBPROX_PEER_CTR_RA_UPD_ACKS, GBPROX_PEER_CTR_RA_UPD_COMPLS, GBPROX_PEER_CTR_GMM_STATUS_BSS, GBPROX_PEER_CTR_GMM_STATUS_SGSN, GBPROX_PEER_CTR_DETACH_REQS, GBPROX_PEER_CTR_DETACH_ACKS, GBPROX_PEER_CTR_PDP_ACT_REQS, GBPROX_PEER_CTR_PDP_ACT_REJS, GBPROX_PEER_CTR_PDP_ACT_ACKS, GBPROX_PEER_CTR_PDP_DEACT_REQS, GBPROX_PEER_CTR_PDP_DEACT_ACKS, GBPROX_PEER_CTR_TLLI_UNKNOWN, GBPROX_PEER_CTR_TLLI_CACHE_SIZE, GBPROX_PEER_CTR_LAST, }; enum gbproxy_keep_mode { GBPROX_KEEP_NEVER, GBPROX_KEEP_REATTACH, GBPROX_KEEP_IDENTIFIED, GBPROX_KEEP_ALWAYS, }; enum gbproxy_match_id { GBPROX_MATCH_PATCHING, GBPROX_MATCH_ROUTING, GBPROX_MATCH_LAST }; struct gbproxy_match { int enable; char *re_str; regex_t re_comp; }; struct gbproxy_config { /* parsed from config file */ uint16_t nsip_sgsn_nsei; /* misc */ struct gprs_ns_inst *nsi; /* Linked list of all Gb peers (except SGSN) */ struct llist_head bts_peers; /* Counter */ struct rate_ctr_group *ctrg; /* force mcc/mnc */ struct osmo_plmn_id core_plmn; uint8_t* core_apn; size_t core_apn_size; int tlli_max_age; int tlli_max_len; /* Experimental config */ int patch_ptmsi; int acquire_imsi; int route_to_sgsn2; uint16_t nsip_sgsn2_nsei; enum gbproxy_keep_mode keep_link_infos; /* IMSI checking/matching */ struct gbproxy_match matches[GBPROX_MATCH_LAST]; }; struct gbproxy_patch_state { struct osmo_plmn_id local_plmn; /* List of TLLIs for which patching is enabled */ struct llist_head logical_links; int logical_link_count; }; struct gbproxy_peer { struct llist_head list; /* point back to the config */ struct gbproxy_config *cfg; /* NSEI of the peer entity */ uint16_t nsei; /* BVCI used for Point-to-Point to this peer */ uint16_t bvci; int blocked; /* Routeing Area that this peer is part of (raw 04.08 encoding) */ uint8_t ra[6]; /* Counter */ struct rate_ctr_group *ctrg; struct gbproxy_patch_state patch_state; }; struct gbproxy_tlli_state { uint32_t current; uint32_t assigned; int bss_validated; int net_validated; uint32_t ptmsi; }; struct gbproxy_link_info { struct llist_head list; struct gbproxy_tlli_state tlli; struct gbproxy_tlli_state sgsn_tlli; uint32_t sgsn_nsei; time_t timestamp; uint8_t *imsi; size_t imsi_len; int imsi_acq_pending; struct llist_head stored_msgs; unsigned vu_gen_tx_bss; int is_deregistered; int is_matching[GBPROX_MATCH_LAST]; }; /* gb_proxy_vty .c */ int gbproxy_vty_init(void); int gbproxy_parse_config(const char *config_file, struct gbproxy_config *cfg); /* gb_proxy.c */ int gbproxy_init_config(struct gbproxy_config *cfg); /* Main input function for Gb proxy */ int gbprox_rcvmsg(struct gbproxy_config *cfg, struct msgb *msg, uint16_t nsei, uint16_t ns_bvci, uint16_t nsvci); int gbprox_signal(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data); /* Reset all persistent NS-VC's */ int gbprox_reset_persistent_nsvcs(struct gprs_ns_inst *nsi); void gbprox_reset(struct gbproxy_config *cfg); /* TLLI info handling */ void gbproxy_delete_link_infos(struct gbproxy_peer *peer); struct gbproxy_link_info *gbproxy_update_link_state_ul( struct gbproxy_peer *peer, time_t now, struct gprs_gb_parse_context *parse_ctx); struct gbproxy_link_info *gbproxy_update_link_state_dl( struct gbproxy_peer *peer, time_t now, struct gprs_gb_parse_context *parse_ctx); int gbproxy_update_link_state_after( struct gbproxy_peer *peer, struct gbproxy_link_info *link_info, time_t now, struct gprs_gb_parse_context *parse_ctx); int gbproxy_remove_stale_link_infos(struct gbproxy_peer *peer, time_t now); void gbproxy_delete_link_info(struct gbproxy_peer *peer, struct gbproxy_link_info *link_info); void gbproxy_link_info_discard_messages(struct gbproxy_link_info *link_info); void gbproxy_attach_link_info(struct gbproxy_peer *peer, time_t now, struct gbproxy_link_info *link_info); void gbproxy_update_link_info(struct gbproxy_link_info *link_info, const uint8_t *imsi, size_t imsi_len); void gbproxy_detach_link_info(struct gbproxy_peer *peer, struct gbproxy_link_info *link_info); struct gbproxy_link_info *gbproxy_link_info_alloc( struct gbproxy_peer *peer); struct gbproxy_link_info *gbproxy_link_info_by_tlli( struct gbproxy_peer *peer, uint32_t tlli); struct gbproxy_link_info *gbproxy_link_info_by_imsi( struct gbproxy_peer *peer, const uint8_t *imsi, size_t imsi_len); struct gbproxy_link_info *gbproxy_link_info_by_any_sgsn_tlli( struct gbproxy_peer *peer, uint32_t tlli); struct gbproxy_link_info *gbproxy_link_info_by_sgsn_tlli( struct gbproxy_peer *peer, uint32_t tlli, uint32_t sgsn_nsei); struct gbproxy_link_info *gbproxy_link_info_by_ptmsi( struct gbproxy_peer *peer, uint32_t ptmsi); int gbproxy_imsi_matches( struct gbproxy_config *cfg, enum gbproxy_match_id match_id, struct gbproxy_link_info *link_info); uint32_t gbproxy_map_tlli( uint32_t other_tlli, struct gbproxy_link_info *link_info, int to_bss); /* needed by gb_proxy_tlli.h */ uint32_t gbproxy_make_bss_ptmsi(struct gbproxy_peer *peer, uint32_t sgsn_ptmsi); uint32_t gbproxy_make_sgsn_tlli( struct gbproxy_peer *peer, struct gbproxy_link_info *link_info, uint32_t bss_tlli); void gbproxy_reset_link(struct gbproxy_link_info *link_info); int gbproxy_check_imsi( struct gbproxy_match *match, const uint8_t *imsi, size_t imsi_len); /* Message patching */ void gbproxy_patch_bssgp( struct msgb *msg, uint8_t *bssgp, size_t bssgp_len, struct gbproxy_peer *peer, struct gbproxy_link_info *link_info, int *len_change, struct gprs_gb_parse_context *parse_ctx); int gbproxy_patch_llc( struct msgb *msg, uint8_t *llc, size_t llc_len, struct gbproxy_peer *peer, struct gbproxy_link_info *link_info, int *len_change, struct gprs_gb_parse_context *parse_ctx); int gbproxy_set_patch_filter( struct gbproxy_match *match, const char *filter, const char **err_msg); void gbproxy_clear_patch_filter(struct gbproxy_match *match); /* Peer handling */ struct gbproxy_peer *gbproxy_peer_by_bvci( struct gbproxy_config *cfg, uint16_t bvci); struct gbproxy_peer *gbproxy_peer_by_nsei( struct gbproxy_config *cfg, uint16_t nsei); struct gbproxy_peer *gbproxy_peer_by_rai( struct gbproxy_config *cfg, const uint8_t *ra); struct gbproxy_peer *gbproxy_peer_by_lai( struct gbproxy_config *cfg, const uint8_t *la); struct gbproxy_peer *gbproxy_peer_by_lac( struct gbproxy_config *cfg, const uint8_t *la); struct gbproxy_peer *gbproxy_peer_by_bssgp_tlv( struct gbproxy_config *cfg, struct tlv_parsed *tp); struct gbproxy_peer *gbproxy_peer_alloc(struct gbproxy_config *cfg, uint16_t bvci); void gbproxy_peer_free(struct gbproxy_peer *peer); int gbproxy_cleanup_peers(struct gbproxy_config *cfg, uint16_t nsei, uint16_t bvci); #endif osmo-sgsn-1.3.0/include/osmocom/sgsn/gprs_gb_parse.h000066400000000000000000000026551327264017000225110ustar00rootroot00000000000000#pragma once #include #include struct gprs_gb_parse_context { /* Pointer to protocol specific parts */ struct gsm48_hdr *g48_hdr; struct bssgp_normal_hdr *bgp_hdr; struct bssgp_ud_hdr *bud_hdr; uint8_t *bssgp_data; size_t bssgp_data_len; uint8_t *llc; size_t llc_len; /* Extracted information */ struct gprs_llc_hdr_parsed llc_hdr_parsed; struct tlv_parsed bssgp_tp; int to_bss; uint8_t *tlli_enc; uint8_t *old_tlli_enc; uint8_t *imsi; size_t imsi_len; uint8_t *apn_ie; size_t apn_ie_len; uint8_t *ptmsi_enc; uint8_t *new_ptmsi_enc; uint8_t *raid_enc; uint8_t *old_raid_enc; uint8_t *bssgp_raid_enc; uint8_t *bssgp_ptmsi_enc; /* General info */ const char *llc_msg_name; int invalidate_tlli; int await_reattach; int need_decryption; uint32_t tlli; int pdu_type; int old_raid_is_foreign; int peer_nsei; }; int gprs_gb_parse_dtap(uint8_t *data, size_t data_len, struct gprs_gb_parse_context *parse_ctx); int gprs_gb_parse_llc(uint8_t *llc, size_t llc_len, struct gprs_gb_parse_context *parse_ctx); int gprs_gb_parse_bssgp(uint8_t *bssgp, size_t bssgp_len, struct gprs_gb_parse_context *parse_ctx); const char *gprs_gb_message_name(const struct gprs_gb_parse_context *parse_ctx, const char *default_msg_name); void gprs_gb_log_parse_context(int log_level, struct gprs_gb_parse_context *parse_ctx, const char *default_msg_name); osmo-sgsn-1.3.0/include/osmocom/sgsn/gprs_gmm.h000066400000000000000000000025131327264017000215000ustar00rootroot00000000000000#ifndef _GPRS_GMM_H #define _GPRS_GMM_H #include #include #include int gsm48_tx_gsm_deact_pdp_req(struct sgsn_pdp_ctx *pdp, uint8_t sm_cause); int gsm48_tx_gsm_act_pdp_rej(struct sgsn_mm_ctx *mm, uint8_t tid, uint8_t cause, uint8_t pco_len, uint8_t *pco_v); int gsm48_tx_gsm_act_pdp_acc(struct sgsn_pdp_ctx *pdp); int gsm48_tx_gsm_deact_pdp_acc(struct sgsn_pdp_ctx *pdp); int gsm0408_gprs_rcvmsg_gb(struct msgb *msg, struct gprs_llc_llme *llme, bool drop_cipherable); int gsm0408_gprs_rcvmsg_iu(struct msgb *msg, struct gprs_ra_id *ra_id, uint16_t *sai); int gsm0408_gprs_force_reattach(struct sgsn_mm_ctx *mmctx); int gsm0408_gprs_force_reattach_oldmsg(struct msgb *msg, struct gprs_llc_llme *llme); void gsm0408_gprs_access_granted(struct sgsn_mm_ctx *mmctx); void gsm0408_gprs_access_denied(struct sgsn_mm_ctx *mmctx, int gmm_cause); void gsm0408_gprs_access_cancelled(struct sgsn_mm_ctx *mmctx, int gmm_cause); void gsm0408_gprs_authenticate(struct sgsn_mm_ctx *mmctx); int gprs_gmm_rx_suspend(struct gprs_ra_id *raid, uint32_t tlli); int gprs_gmm_rx_resume(struct gprs_ra_id *raid, uint32_t tlli, uint8_t suspend_ref); time_t gprs_max_time_to_idle(void); int iu_rab_act_ps(uint8_t rab_id, struct sgsn_pdp_ctx *pdp); #endif /* _GPRS_GMM_H */ osmo-sgsn-1.3.0/include/osmocom/sgsn/gprs_llc.h000066400000000000000000000166231327264017000215010ustar00rootroot00000000000000#ifndef _GPRS_LLC_H #define _GPRS_LLC_H #include #include #include #include /* Section 4.7 LLC Layer Structure */ enum gprs_llc_sapi { GPRS_SAPI_GMM = 1, GPRS_SAPI_TOM2 = 2, GPRS_SAPI_SNDCP3 = 3, GPRS_SAPI_SNDCP5 = 5, GPRS_SAPI_SMS = 7, GPRS_SAPI_TOM8 = 8, GPRS_SAPI_SNDCP9 = 9, GPRS_SAPI_SNDCP11 = 11, }; /* Section 6.4 Commands and Responses */ enum gprs_llc_u_cmd { GPRS_LLC_U_DM_RESP = 0x01, GPRS_LLC_U_DISC_CMD = 0x04, GPRS_LLC_U_UA_RESP = 0x06, GPRS_LLC_U_SABM_CMD = 0x07, GPRS_LLC_U_FRMR_RESP = 0x08, GPRS_LLC_U_XID = 0x0b, GPRS_LLC_U_NULL_CMD = 0x00, }; /* Section 6.4.1.6 / Table 6 */ enum gprs_llc_xid_type { GPRS_LLC_XID_T_VERSION = 0, GPRS_LLC_XID_T_IOV_UI = 1, GPRS_LLC_XID_T_IOV_I = 2, GPRS_LLC_XID_T_T200 = 3, GPRS_LLC_XID_T_N200 = 4, GPRS_LLC_XID_T_N201_U = 5, GPRS_LLC_XID_T_N201_I = 6, GPRS_LLC_XID_T_mD = 7, GPRS_LLC_XID_T_mU = 8, GPRS_LLC_XID_T_kD = 9, GPRS_LLC_XID_T_kU = 10, GPRS_LLC_XID_T_L3_PAR = 11, GPRS_LLC_XID_T_RESET = 12, }; extern const struct value_string gprs_llc_xid_type_names[]; /* TS 04.64 Section 7.1.2 Table 7: LLC layer primitives (GMM/SNDCP/SMS/TOM) */ /* TS 04.65 Section 5.1.2 Table 2: Service primitives used by SNDCP */ enum gprs_llc_primitive { /* GMM <-> LLME */ LLGMM_ASSIGN_REQ, /* GMM tells us new TLLI: TLLI old, TLLI new, Kc, CiphAlg */ LLGMM_RESET_REQ, /* GMM tells us to perform XID negotiation: TLLI */ LLGMM_RESET_CNF, /* LLC informs GMM that XID has completed: TLLI */ LLGMM_SUSPEND_REQ, /* GMM tells us MS has suspended: TLLI, Page */ LLGMM_RESUME_REQ, /* GMM tells us MS has resumed: TLLI */ LLGMM_PAGE_IND, /* LLC asks GMM to page MS: TLLI */ LLGMM_IOV_REQ, /* GMM tells us to perform XID: TLLI */ LLGMM_STATUS_IND, /* LLC informs GMM about error: TLLI, Cause */ /* LLE <-> (GMM/SNDCP/SMS/TOM) */ LL_RESET_IND, /* TLLI */ LL_ESTABLISH_REQ, /* TLLI, XID Req */ LL_ESTABLISH_IND, /* TLLI, XID Req, N201-I, N201-U */ LL_ESTABLISH_RESP, /* TLLI, XID Negotiated */ LL_ESTABLISH_CONF, /* TLLI, XID Neg, N201-i, N201-U */ LL_RELEASE_REQ, /* TLLI, Local */ LL_RELEASE_IND, /* TLLI, Cause */ LL_RELEASE_CONF, /* TLLI */ LL_XID_REQ, /* TLLI, XID Requested */ LL_XID_IND, /* TLLI, XID Req, N201-I, N201-U */ LL_XID_RESP, /* TLLI, XID Negotiated */ LL_XID_CONF, /* TLLI, XID Neg, N201-I, N201-U */ LL_DATA_REQ, /* TLLI, SN-PDU, Ref, QoS, Radio Prio, Ciph */ LL_DATA_IND, /* TLLI, SN-PDU */ LL_DATA_CONF, /* TLLI, Ref */ LL_UNITDATA_REQ, /* TLLI, SN-PDU, Ref, QoS, Radio Prio, Ciph */ LL_UNITDATA_IND, /* TLLI, SN-PDU */ LL_STATUS_IND, /* TLLI, Cause */ }; /* Section 4.5.2 Logical Link States + Annex C.2 */ enum gprs_llc_lle_state { GPRS_LLES_UNASSIGNED = 1, /* No TLLI yet */ GPRS_LLES_ASSIGNED_ADM = 2, /* TLLI assigned */ GPRS_LLES_LOCAL_EST = 3, /* Local Establishment */ GPRS_LLES_REMOTE_EST = 4, /* Remote Establishment */ GPRS_LLES_ABM = 5, GPRS_LLES_LOCAL_REL = 6, /* Local Release */ GPRS_LLES_TIMER_REC = 7, /* Timer Recovery */ }; enum gprs_llc_llme_state { GPRS_LLMS_UNASSIGNED = 1, /* No TLLI yet */ GPRS_LLMS_ASSIGNED = 2, /* TLLI assigned */ }; /* Section 8.9.9 LLC layer parameter default values */ struct gprs_llc_params { uint16_t iov_i_exp; uint16_t t200_201; uint16_t n200; uint16_t n201_u; uint16_t n201_i; uint16_t mD; uint16_t mU; uint16_t kD; uint16_t kU; }; /* Section 4.7.1: Logical Link Entity: One per DLCI (TLLI + SAPI) */ struct gprs_llc_lle { struct llist_head list; uint32_t sapi; struct gprs_llc_llme *llme; enum gprs_llc_lle_state state; struct osmo_timer_list t200; struct osmo_timer_list t201; /* wait for acknowledgement */ uint16_t v_sent; uint16_t v_ack; uint16_t v_recv; uint16_t vu_send; uint16_t vu_recv; /* non-standard LLC state */ uint16_t vu_recv_last; uint16_t vu_recv_duplicates; /* Overflow Counter for ABM */ uint32_t oc_i_send; uint32_t oc_i_recv; /* Overflow Counter for unconfirmed transfer */ uint32_t oc_ui_send; uint32_t oc_ui_recv; unsigned int retrans_ctr; struct gprs_llc_params params; }; #define NUM_SAPIS 16 struct gprs_llc_llme { struct llist_head list; enum gprs_llc_llme_state state; uint32_t tlli; uint32_t old_tlli; /* Crypto parameters */ enum gprs_ciph_algo algo; uint8_t kc[16]; uint8_t cksn; /* 3GPP TS 44.064 § 8.9.2: */ uint32_t iov_ui; /* over which BSSGP BTS ctx do we need to transmit */ uint16_t bvci; uint16_t nsei; struct gprs_llc_lle lle[NUM_SAPIS]; /* Copy of the XID fields we have sent with the last * network originated XID-Request. Since the phone * may strip the optional fields in the confirmation * we need to remeber those fields in order to be * able to create the compression entity. */ struct llist_head *xid; /* Compression entities */ struct { /* In these two list_heads we will store the * data and protocol compression entities, * together with their compression states */ struct llist_head *proto; struct llist_head *data; } comp; /* Internal management */ uint32_t age_timestamp; }; #define GPRS_LLME_RESET_AGE (0) extern struct llist_head gprs_llc_llmes; /* LLC low level types */ enum gprs_llc_cmd { GPRS_LLC_NULL, GPRS_LLC_RR, GPRS_LLC_ACK, GPRS_LLC_RNR, GPRS_LLC_SACK, GPRS_LLC_DM, GPRS_LLC_DISC, GPRS_LLC_UA, GPRS_LLC_SABM, GPRS_LLC_FRMR, GPRS_LLC_XID, GPRS_LLC_UI, }; struct gprs_llc_hdr_parsed { uint8_t sapi; uint8_t is_cmd:1, ack_req:1, is_encrypted:1; uint32_t seq_rx; uint32_t seq_tx; uint32_t fcs; uint32_t fcs_calc; uint8_t *data; uint16_t data_len; uint16_t crc_length; enum gprs_llc_cmd cmd; }; /* BSSGP-UL-UNITDATA.ind */ int gprs_llc_rcvmsg(struct msgb *msg, struct tlv_parsed *tv); /* LL-UNITDATA.req */ int gprs_llc_tx_ui(struct msgb *msg, uint8_t sapi, int command, struct sgsn_mm_ctx *mmctx, bool encryptable); /* Chapter 7.2.1.2 LLGMM-RESET.req */ int gprs_llgmm_reset(struct gprs_llc_llme *llme); int gprs_llgmm_reset_oldmsg(struct msgb* oldmsg, uint8_t sapi, struct gprs_llc_llme *llme); /* Set of LL-XID negotiation (See also: TS 101 351, Section 7.2.2.4) */ int gprs_ll_xid_req(struct gprs_llc_lle *lle, struct gprs_llc_xid_field *l3_xid_field); /* 04.64 Chapter 7.2.1.1 LLGMM-ASSIGN */ int gprs_llgmm_assign(struct gprs_llc_llme *llme, uint32_t old_tlli, uint32_t new_tlli); int gprs_llgmm_unassign(struct gprs_llc_llme *llme); int gprs_llc_init(const char *cipher_plugin_path); int gprs_llc_vty_init(void); /** * \short Check if N(U) should be considered a retransmit * * Implements the range check as of GSM 04.64 8.4.2 * Receipt of unacknowledged information. * * @returns Returns 1 if (V(UR)-32) <= N(U) < V(UR) * @param nu N(U) unconfirmed sequence number of the UI frame * @param vur V(UR) unconfirmend received state variable */ static inline int gprs_llc_is_retransmit(uint16_t nu, uint16_t vur) { int delta = (vur - nu) & 0x1ff; return 0 < delta && delta < 32; } /* LLC low level functions */ void gprs_llme_copy_key(struct sgsn_mm_ctx *mm, struct gprs_llc_llme *llme); /* parse a GPRS LLC header, also check for invalid frames */ int gprs_llc_hdr_parse(struct gprs_llc_hdr_parsed *ghp, uint8_t *llc_hdr, int len); void gprs_llc_hdr_dump(struct gprs_llc_hdr_parsed *gph, struct gprs_llc_lle *lle); int gprs_llc_fcs(uint8_t *data, unsigned int len); /* LLME handling routines */ struct llist_head *gprs_llme_list(void); struct gprs_llc_lle *gprs_lle_get_or_create(const uint32_t tlli, uint8_t sapi); #endif osmo-sgsn-1.3.0/include/osmocom/sgsn/gprs_llc_xid.h000066400000000000000000000037751327264017000223510ustar00rootroot00000000000000/* GPRS LLC XID field encoding/decoding as per 3GPP TS 44.064 */ /* (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 /* 3GPP TS 44.064 6.4.1.6 Exchange Identification (XID) command/response parameter field */ struct gprs_llc_xid_field { struct llist_head list; uint8_t type; /* See also Table 6: LLC layer parameter negotiation */ uint8_t *data; /* Payload data (memory is owned by the * creator of the struct) */ unsigned int data_len; /* Payload length */ }; /* Transform a list with XID fields into a XID message (dst) */ int gprs_llc_compile_xid(uint8_t *dst, int dst_maxlen, const struct llist_head *xid_fields); /* Transform a XID message (dst) into a list of XID fields */ struct llist_head *gprs_llc_parse_xid(const void *ctx, const uint8_t *src, int src_len); /* Create a duplicate of an XID-Field */ struct gprs_llc_xid_field *gprs_llc_dup_xid_field(const void *ctx, const struct gprs_llc_xid_field *xid_field); /* Copy an llist with xid fields */ struct llist_head *gprs_llc_copy_xid(const void *ctx, const struct llist_head *xid_fields); /* Dump a list with XID fields (Debug) */ void gprs_llc_dump_xid_fields(const struct llist_head *xid_fields, unsigned int logl); osmo-sgsn-1.3.0/include/osmocom/sgsn/gprs_sgsn.h000066400000000000000000000323731327264017000217010ustar00rootroot00000000000000#ifndef _GPRS_SGSN_H #define _GPRS_SGSN_H #include #include #include #include #include #include #include #define GSM_EXTENSION_LENGTH 15 #define GSM_APN_LENGTH 102 struct gprs_llc_lle; struct ctrl_handle; struct gprs_subscr; enum gsm48_gsm_cause; /* TS 04.08 4.1.3.3 GMM mobility management states on the network side */ enum gprs_gmm_state { GMM_DEREGISTERED, /* 4.1.3.3.1.1 */ GMM_COMMON_PROC_INIT, /* 4.1.3.3.1.2 */ GMM_REGISTERED_NORMAL, /* 4.1.3.3.2.1 */ GMM_REGISTERED_SUSPENDED, /* 4.1.3.3.2.2 */ GMM_DEREGISTERED_INIT, /* 4.1.3.3.1.4 */ }; /* TS 23.060 6.1.1 and 6.1.2 Mobility management states A/Gb and Iu mode */ enum gprs_pmm_state { PMM_DETACHED, PMM_CONNECTED, PMM_IDLE, MM_IDLE, MM_READY, MM_STANDBY, }; enum gprs_mm_ctr { GMM_CTR_PKTS_SIG_IN, GMM_CTR_PKTS_SIG_OUT, GMM_CTR_PKTS_UDATA_IN, GMM_CTR_PKTS_UDATA_OUT, GMM_CTR_BYTES_UDATA_IN, GMM_CTR_BYTES_UDATA_OUT, GMM_CTR_PDP_CTX_ACT, GMM_CTR_SUSPEND, GMM_CTR_PAGING_PS, GMM_CTR_PAGING_CS, GMM_CTR_RA_UPDATE, }; enum gprs_pdp_ctx { PDP_CTR_PKTS_UDATA_IN, PDP_CTR_PKTS_UDATA_OUT, PDP_CTR_BYTES_UDATA_IN, PDP_CTR_BYTES_UDATA_OUT, }; enum gprs_t3350_mode { GMM_T3350_MODE_NONE, GMM_T3350_MODE_ATT, GMM_T3350_MODE_RAU, GMM_T3350_MODE_PTMSI_REALL, }; /* Authorization/ACL handling */ enum sgsn_auth_state { SGSN_AUTH_UNKNOWN, SGSN_AUTH_AUTHENTICATE, SGSN_AUTH_UMTS_RESYNC, SGSN_AUTH_ACCEPTED, SGSN_AUTH_REJECTED }; #define MS_RADIO_ACCESS_CAPA enum sgsn_ggsn_lookup_state { SGSN_GGSN_2DIGIT, SGSN_GGSN_3DIGIT, }; struct sgsn_ggsn_lookup { int state; struct sgsn_mm_ctx *mmctx; /* APN string */ char apn_str[GSM_APN_LENGTH]; /* the original data */ struct msgb *orig_msg; struct tlv_parsed tp; /* for dealing with re-transmissions */ uint8_t nsapi; uint8_t sapi; uint8_t ti; }; enum sgsn_ran_type { /* GPRS/EDGE via Gb */ MM_CTX_T_GERAN_Gb, /* UMTS via Iu */ MM_CTX_T_UTRAN_Iu, /* GPRS/EDGE via Iu */ MM_CTX_T_GERAN_Iu, }; struct service_info { uint8_t type; uint16_t pdp_status; }; struct ranap_ue_conn_ctx; struct gsm_auth_tuple { int use_count; int key_seq; struct osmo_auth_vector vec; }; #define GSM_KEY_SEQ_INVAL 7 /* GSM 04.08 - 10.5.1.2 */ /* According to TS 03.60, Table 5: SGSN MM and PDP Contexts */ /* Extended by 3GPP TS 23.060, Table 6: SGSN MM and PDP Contexts */ struct sgsn_mm_ctx { struct llist_head list; enum sgsn_ran_type ran_type; char imsi[GSM23003_IMSI_MAX_DIGITS+1]; enum gprs_gmm_state gmm_state; enum gprs_pmm_state pmm_state; /* Iu: page when in PMM-IDLE mode */ uint32_t p_tmsi; uint32_t p_tmsi_old; /* old P-TMSI before new is confirmed */ uint32_t p_tmsi_sig; char imei[GSM23003_IMEISV_NUM_DIGITS+1]; /* Opt: Software Version Numbber / TS 23.195 */ char msisdn[GSM_EXTENSION_LENGTH]; struct gprs_ra_id ra; struct { uint16_t cell_id; /* Gb only */ uint32_t cell_id_age; /* Gb only */ uint8_t radio_prio_sms; /* Additional bits not present in the GSM TS */ uint16_t nsei; uint16_t bvci; struct gprs_llc_llme *llme; uint32_t tlli; uint32_t tlli_new; } gb; struct { int new_key; uint16_t sac; /* Iu: Service Area Code */ uint32_t sac_age; /* Iu: Service Area Code age */ /* CSG ID */ /* CSG Membership */ /* Access Mode */ /* Seelected CN Operator ID (TS 23.251) */ /* CSG Subscription Data */ /* LIPA Allowed */ /* Voice Support Match Indicator */ struct ranap_ue_conn_ctx *ue_ctx; struct service_info service; } iu; /* VLR number */ uint32_t new_sgsn_addr; /* Authentication Triplet */ struct gsm_auth_tuple auth_triplet; /* Kc */ /* Iu: CK, IK, KSI */ /* CKSN */ enum gprs_ciph_algo ciph_algo; /* Auth & Ciphering Request reference from 3GPP TS 24.008 § 10.5.5.19: */ uint8_t ac_ref_nr_used; struct { uint8_t len; uint8_t buf[50]; /* GSM 04.08 10.5.5.12a, extended in TS 24.008 */ } ms_radio_access_capa; /* Supported Codecs (SRVCC) */ struct { uint8_t len; uint8_t buf[8]; /* GSM 04.08 10.5.5.12, extended in TS 24.008 */ } ms_network_capa; /* UE Netowrk Capability (E-UTRAN) */ uint16_t drx_parms; /* Active Time value for PSM */ int mnrg; /* MS reported to HLR? */ int ngaf; /* MS reported to MSC/VLR? */ int ppf; /* paging for GPRS + non-GPRS? */ /* Subscribed Charging Characteristics */ /* Trace Reference */ /* Trace Type */ /* Trigger ID */ /* OMC Identity */ /* SMS Parameters */ int recovery; /* Access Restriction */ /* GPRS CSI (CAMEL) */ /* MG-CSI (CAMEL) */ /* Subscribed UE-AMBR */ /* UE-AMBR */ /* APN Subscribed */ struct llist_head pdp_list; struct rate_ctr_group *ctrg; struct osmo_timer_list timer; unsigned int T; /* Txxxx number */ unsigned int num_T_exp; /* number of consecutive T expirations */ enum gprs_t3350_mode t3350_mode; uint8_t t3370_id_type; uint8_t pending_req; /* the request's message type */ /* TODO: There isn't much semantic difference between t3350_mode * (refers to the timer) and pending_req (refers to the procedure), * where mm->T == 3350 => mm->t3350_mode == f(mm->pending_req). Check * whether one of them can be dropped. */ enum sgsn_auth_state auth_state; enum osmo_sub_auth_type sec_ctx; /* the string representation of the current hlr */ char hlr[GSM_EXTENSION_LENGTH]; /* the current GGSN look-up operation */ struct sgsn_ggsn_lookup *ggsn_lookup; struct gprs_subscr *subscr; }; static inline bool sgsn_mm_ctx_is_authenticated(struct sgsn_mm_ctx *ctx) { switch (ctx->sec_ctx) { case OSMO_AUTH_TYPE_GSM: case OSMO_AUTH_TYPE_UMTS: return true; default: return false; } } #define LOGMMCTXP(level, mm, fmt, args...) \ LOGP(DMM, level, "MM(%s/%08x) " fmt, (mm) ? (mm)->imsi : "---", \ (mm) ? (mm)->p_tmsi : GSM_RESERVED_TMSI, ## args) /* look-up a SGSN MM context based on TLLI + RAI */ struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli(uint32_t tlli, const struct gprs_ra_id *raid); struct sgsn_mm_ctx *sgsn_mm_ctx_by_ptmsi(uint32_t tmsi); struct sgsn_mm_ctx *sgsn_mm_ctx_by_imsi(const char *imsi); struct sgsn_mm_ctx *sgsn_mm_ctx_by_ue_ctx(const void *uectx); /* look-up by matching TLLI and P-TMSI (think twice before using this) */ struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli_and_ptmsi(uint32_t tlli, const struct gprs_ra_id *raid); /* Allocate a new SGSN MM context */ struct sgsn_mm_ctx *sgsn_mm_ctx_alloc_gb(uint32_t tlli, const struct gprs_ra_id *raid); struct sgsn_mm_ctx *sgsn_mm_ctx_alloc_iu(void *uectx); void sgsn_mm_ctx_cleanup_free(struct sgsn_mm_ctx *ctx); struct sgsn_ggsn_ctx *sgsn_mm_ctx_find_ggsn_ctx(struct sgsn_mm_ctx *mmctx, struct tlv_parsed *tp, enum gsm48_gsm_cause *gsm_cause, char *apn_str); enum pdp_ctx_state { PDP_STATE_NONE, PDP_STATE_CR_REQ, PDP_STATE_CR_CONF, /* 04.08 / Figure 6.2 / 6.1.2.2 */ PDP_STATE_INACT_PEND, PDP_STATE_INACTIVE = PDP_STATE_NONE, }; enum pdp_type { PDP_TYPE_NONE, PDP_TYPE_ETSI_PPP, PDP_TYPE_IANA_IPv4, PDP_TYPE_IANA_IPv6, }; struct sgsn_pdp_ctx { struct llist_head list; /* list_head for mmctx->pdp_list */ struct llist_head g_list; /* list_head for global list */ struct sgsn_mm_ctx *mm; /* back pointer to MM CTX */ int destroy_ggsn; /* destroy it on destruction */ struct sgsn_ggsn_ctx *ggsn; /* which GGSN serves this PDP */ struct rate_ctr_group *ctrg; //unsigned int id; struct pdp_t *lib; /* pointer to libgtp PDP ctx */ enum pdp_ctx_state state; enum pdp_type type; uint32_t address; char *apn_subscribed; //char *apn_used; uint16_t nsapi; /* SNDCP */ uint16_t sapi; /* LLC */ uint8_t ti; /* transaction identifier */ int vplmn_allowed; uint32_t qos_profile_subscr; //uint32_t qos_profile_req; //uint32_t qos_profile_neg; uint8_t radio_prio; //uint32_t charging_id; struct osmo_timer_list timer; unsigned int T; /* Txxxx number */ unsigned int num_T_exp; /* number of consecutive T expirations */ struct osmo_timer_list cdr_timer; /* CDR record wird timer */ struct timespec cdr_start; /* The start of the CDR */ uint64_t cdr_bytes_in; uint64_t cdr_bytes_out; uint32_t cdr_charging_id; }; #define LOGPDPCTXP(level, pdp, fmt, args...) \ LOGP(DGPRS, level, "PDP(%s/%u) " \ fmt, (pdp)->mm ? (pdp)->mm->imsi : "---", (pdp)->ti, ## args) /* look up PDP context by MM context and NSAPI */ struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_nsapi(const struct sgsn_mm_ctx *mm, uint8_t nsapi); /* look up PDP context by MM context and transaction ID */ struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_tid(const struct sgsn_mm_ctx *mm, uint8_t tid); struct sgsn_pdp_ctx *sgsn_pdp_ctx_alloc(struct sgsn_mm_ctx *mm, uint8_t nsapi); void sgsn_pdp_ctx_terminate(struct sgsn_pdp_ctx *pdp); void sgsn_pdp_ctx_free(struct sgsn_pdp_ctx *pdp); struct sgsn_ggsn_ctx { struct llist_head list; uint32_t id; unsigned int gtp_version; struct in_addr remote_addr; int remote_restart_ctr; struct gsn_t *gsn; }; struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_alloc(uint32_t id); void sgsn_ggsn_ctx_free(struct sgsn_ggsn_ctx *ggc); struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_id(uint32_t id); struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_addr(struct in_addr *addr); struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_find_alloc(uint32_t id); struct apn_ctx { struct llist_head list; struct sgsn_ggsn_ctx *ggsn; char *name; char *imsi_prefix; char *description; }; struct apn_ctx *sgsn_apn_ctx_find_alloc(const char *name, const char *imsi_prefix); void sgsn_apn_ctx_free(struct apn_ctx *actx); struct apn_ctx *sgsn_apn_ctx_by_name(const char *name, const char *imsi_prefix); struct apn_ctx *sgsn_apn_ctx_match(const char *name, const char *imsi_prefix); extern struct llist_head sgsn_mm_ctxts; extern struct llist_head sgsn_ggsn_ctxts; extern struct llist_head sgsn_apn_ctxts; extern struct llist_head sgsn_pdp_ctxts; uint32_t sgsn_alloc_ptmsi(void); void sgsn_inst_init(void); /* High-level function to be called in case a GGSN has disappeared or * ottherwise lost state (recovery procedure) */ int drop_all_pdp_for_ggsn(struct sgsn_ggsn_ctx *ggsn); char *gprs_pdpaddr2str(uint8_t *pdpa, uint8_t len); /* * ctrl interface related work */ struct gsm_network; struct ctrl_handle *sgsn_controlif_setup(struct gsm_network *, const char *bind_addr, uint16_t port); int sgsn_ctrl_cmds_install(void); /* * Authorization/ACL handling */ struct imsi_acl_entry { struct llist_head list; char imsi[16+1]; }; /* see GSM 09.02, 17.7.1, PDP-Context and GPRSSubscriptionData */ /* see GSM 09.02, B.1, gprsSubscriptionData */ struct sgsn_subscriber_pdp_data { struct llist_head list; unsigned int context_id; uint16_t pdp_type; char apn_str[GSM_APN_LENGTH]; uint8_t qos_subscribed[20]; size_t qos_subscribed_len; uint8_t pdp_charg[2]; bool has_pdp_charg; }; struct sgsn_subscriber_data { struct sgsn_mm_ctx *mm; struct gsm_auth_tuple auth_triplets[5]; int auth_triplets_updated; struct llist_head pdp_list; int error_cause; uint8_t msisdn[9]; size_t msisdn_len; uint8_t hlr[9]; size_t hlr_len; uint8_t pdp_charg[2]; bool has_pdp_charg; }; #define SGSN_ERROR_CAUSE_NONE (-1) #define LOGGSUBSCRP(level, subscr, fmt, args...) \ LOGP(DGPRS, level, "SUBSCR(%s) " fmt, \ (subscr) ? (subscr)->imsi : "---", \ ## args) struct sgsn_config; struct sgsn_instance; extern const struct value_string *sgsn_auth_state_names; void sgsn_auth_init(void); struct imsi_acl_entry *sgsn_acl_lookup(const char *imsi, struct sgsn_config *cfg); int sgsn_acl_add(const char *imsi, struct sgsn_config *cfg); int sgsn_acl_del(const char *imsi, struct sgsn_config *cfg); /* Request authorization */ int sgsn_auth_request(struct sgsn_mm_ctx *mm); enum sgsn_auth_state sgsn_auth_state(struct sgsn_mm_ctx *mm); void sgsn_auth_update(struct sgsn_mm_ctx *mm); struct gsm_auth_tuple *sgsn_auth_get_tuple(struct sgsn_mm_ctx *mmctx, unsigned key_seq); /* * GPRS subscriber data */ #define GPRS_SUBSCRIBER_FIRST_CONTACT 0x00000001 #define GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING (1 << 16) #define GPRS_SUBSCRIBER_UPDATE_LOCATION_PENDING (1 << 17) #define GPRS_SUBSCRIBER_CANCELLED (1 << 18) #define GPRS_SUBSCRIBER_ENABLE_PURGE (1 << 19) #define GPRS_SUBSCRIBER_UPDATE_PENDING_MASK ( \ GPRS_SUBSCRIBER_UPDATE_LOCATION_PENDING | \ GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING \ ) int gprs_subscr_init(struct sgsn_instance *sgi); int gprs_subscr_request_update_location(struct sgsn_mm_ctx *mmctx); int gprs_subscr_request_auth_info(struct sgsn_mm_ctx *mmctx, const uint8_t *auts, const uint8_t *auts_rand); int gprs_subscr_auth_sync(struct gprs_subscr *subscr, const uint8_t *auts, const uint8_t *auts_rand); void gprs_subscr_cleanup(struct gprs_subscr *subscr); struct gprs_subscr *gprs_subscr_get_or_create(const char *imsi); struct gprs_subscr *gprs_subscr_get_or_create_by_mmctx( struct sgsn_mm_ctx *mmctx); struct gprs_subscr *gprs_subscr_get_by_imsi(const char *imsi); void gprs_subscr_cancel(struct gprs_subscr *subscr); void gprs_subscr_update(struct gprs_subscr *subscr); void gprs_subscr_update_auth_info(struct gprs_subscr *subscr); int gprs_subscr_rx_gsup_message(struct msgb *msg); /* Called on subscriber data updates */ void sgsn_update_subscriber_data(struct sgsn_mm_ctx *mmctx); int gprs_sndcp_vty_init(void); struct sgsn_instance; int sgsn_gtp_init(struct sgsn_instance *sgi); void sgsn_rate_ctr_init(); #endif /* _GPRS_SGSN_H */ osmo-sgsn-1.3.0/include/osmocom/sgsn/gprs_sndcp.h000066400000000000000000000044231327264017000220310ustar00rootroot00000000000000#ifndef _INT_SNDCP_H #define _INT_SNDCP_H #include #include /* A fragment queue header, maintaining list of fragments for one N-PDU */ struct defrag_state { /* PDU number for which the defragmentation state applies */ uint16_t npdu; /* highest segment number we have received so far */ uint8_t highest_seg; /* bitmask of the segments we already have */ uint32_t seg_have; /* do we still expect more segments? */ unsigned int no_more; /* total length of all segments together */ unsigned int tot_len; /* linked list of defrag_queue_entry: one for each fragment */ struct llist_head frag_list; struct osmo_timer_list timer; /* Holds state to know which compression mode is used * when the packet is re-assembled */ uint8_t pcomp; uint8_t dcomp; /* Holds the pointers to the compression entity list * that is used when the re-assembled packet is decompressed */ struct llist_head *proto; struct llist_head *data; }; /* See 6.7.1.2 Reassembly */ enum sndcp_rx_state { SNDCP_RX_S_FIRST, SNDCP_RX_S_SUBSEQ, SNDCP_RX_S_DISCARD, }; struct gprs_sndcp_entity { struct llist_head list; /* FIXME: move this RA_ID up to the LLME or even higher */ struct gprs_ra_id ra_id; /* reference to the LLC Entity below this SNDCP entity */ struct gprs_llc_lle *lle; /* The NSAPI we shall use on top of LLC */ uint8_t nsapi; /* NPDU number for the GTP->SNDCP side */ uint16_t tx_npdu_nr; /* SNDCP eeceiver state */ enum sndcp_rx_state rx_state; /* The defragmentation queue */ struct defrag_state defrag; }; extern struct llist_head gprs_sndcp_entities; /* Set of SNDCP-XID negotiation (See also: TS 144 065, * Section 6.8 XID parameter negotiation) */ int sndcp_sn_xid_req(struct gprs_llc_lle *lle, uint8_t nsapi); /* Process SNDCP-XID indication (See also: TS 144 065, * Section 6.8 XID parameter negotiation) */ int sndcp_sn_xid_ind(struct gprs_llc_xid_field *xid_field_indication, struct gprs_llc_xid_field *xid_field_response, struct gprs_llc_lle *lle); /* Process SNDCP-XID indication * (See also: TS 144 065, Section 6.8 XID parameter negotiation) */ int sndcp_sn_xid_conf(struct gprs_llc_xid_field *xid_field_conf, struct gprs_llc_xid_field *xid_field_request, struct gprs_llc_lle *lle); #endif /* INT_SNDCP_H */ osmo-sgsn-1.3.0/include/osmocom/sgsn/gprs_sndcp_comp.h000066400000000000000000000060761327264017000230550ustar00rootroot00000000000000/* GPRS SNDCP header compression entity management tools */ /* (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 #include /* Header / Data compression entity */ struct gprs_sndcp_comp { struct llist_head list; /* Serves as an ID in case we want to delete this entity later */ unsigned int entity; /* see also: 6.5.1.1.3 and 6.6.1.1.3 */ /* Specifies to which NSAPIs the compression entity is assigned */ uint8_t nsapi_len; /* Number of applicable NSAPIs (default 0) */ uint8_t nsapi[MAX_NSAPI]; /* Applicable NSAPIs (default 0) */ /* Assigned pcomp values */ uint8_t comp_len; /* Number of contained PCOMP / DCOMP values */ uint8_t comp[MAX_COMP]; /* see also: 6.5.1.1.5 and 6.6.1.1.5 */ /* Algorithm parameters */ int algo; /* Algorithm type (see gprs_sndcp_xid.h) */ int compclass; /* See gprs_sndcp_xid.h/c */ void *state; /* Algorithm status and parameters */ }; #define MAX_COMP 16 /* Maximum number of possible pcomp/dcomp values */ #define MAX_NSAPI 11 /* Maximum number usable NSAPIs */ /* Allocate a compression enitiy list */ struct llist_head *gprs_sndcp_comp_alloc(const void *ctx); /* Free a compression entitiy list */ void gprs_sndcp_comp_free(struct llist_head *comp_entities); /* Delete a compression entity */ void gprs_sndcp_comp_delete(struct llist_head *comp_entities, unsigned int entity); /* Create and Add a new compression entity * (returns a pointer to the compression entity that has just been created) */ struct gprs_sndcp_comp *gprs_sndcp_comp_add(const void *ctx, struct llist_head *comp_entities, const struct gprs_sndcp_comp_field *comp_field); /* Find which compression entity handles the specified pcomp/dcomp */ struct gprs_sndcp_comp *gprs_sndcp_comp_by_comp(const struct llist_head *comp_entities, uint8_t comp); /* Find which compression entity handles the specified nsapi */ struct gprs_sndcp_comp *gprs_sndcp_comp_by_nsapi(const struct llist_head *comp_entities, uint8_t nsapi); /* Find a comp_index for a given pcomp/dcomp value */ uint8_t gprs_sndcp_comp_get_idx(const struct gprs_sndcp_comp *comp_entity, uint8_t comp); /* Find a pcomp/dcomp value for a given comp_index */ uint8_t gprs_sndcp_comp_get_comp(const struct gprs_sndcp_comp *comp_entity, uint8_t comp_index); osmo-sgsn-1.3.0/include/osmocom/sgsn/gprs_sndcp_dcomp.h000066400000000000000000000037641327264017000232220ustar00rootroot00000000000000/* GPRS SNDCP data compression handler */ /* (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 #include /* Note: The decompressed packet may have a maximum size of: * Return value * MAX_DATADECOMPR_FAC */ #define MAX_DATADECOMPR_FAC 10 /* Note: In unacknowledged mode (SN_UNITDATA), the comression state is reset * for every NPDU. The compressor needs a reasonably large payload to operate * effectively (yield positive compression gain). For packets shorter than 100 * byte, no positive compression gain can be expected so we will skip the * compression for short packets. */ #define MIN_COMPR_PAYLOAD 100 /* Initalize data compression */ int gprs_sndcp_dcomp_init(const void *ctx, struct gprs_sndcp_comp *comp_entity, const struct gprs_sndcp_comp_field *comp_field); /* Terminate data compression */ void gprs_sndcp_dcomp_term(struct gprs_sndcp_comp *comp_entity); /* Expand packet */ int gprs_sndcp_dcomp_expand(uint8_t *data, unsigned int len, uint8_t pcomp, const struct llist_head *comp_entities); /* Compress packet */ int gprs_sndcp_dcomp_compress(uint8_t *data, unsigned int len, uint8_t *pcomp, const struct llist_head *comp_entities, uint8_t nsapi); osmo-sgsn-1.3.0/include/osmocom/sgsn/gprs_sndcp_pcomp.h000066400000000000000000000032151327264017000232250ustar00rootroot00000000000000/* GPRS SNDCP header compression handler */ /* (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 #include /* Note: The decompressed packet may have a maximum size of: * Return value + MAX_DECOMPR_INCR */ #define MAX_HDRDECOMPR_INCR 64 /* Initalize header compression */ int gprs_sndcp_pcomp_init(const void *ctx, struct gprs_sndcp_comp *comp_entity, const struct gprs_sndcp_comp_field *comp_field); /* Terminate header compression */ void gprs_sndcp_pcomp_term(struct gprs_sndcp_comp *comp_entity); /* Expand packet header */ int gprs_sndcp_pcomp_expand(uint8_t *data, unsigned int len, uint8_t pcomp, const struct llist_head *comp_entities); /* Compress packet header */ int gprs_sndcp_pcomp_compress(uint8_t *data, unsigned int len, uint8_t *pcomp, const struct llist_head *comp_entities, uint8_t nsapi); osmo-sgsn-1.3.0/include/osmocom/sgsn/gprs_sndcp_xid.h000066400000000000000000000201551327264017000226750ustar00rootroot00000000000000/* GPRS SNDCP XID field encoding/decoding as per 3GPP TS 44.065 */ /* (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 #define DEFAULT_SNDCP_VERSION 0 /* See 3GPP TS 44.065, clause 8 */ #define MAX_ENTITIES 32 /* 3GPP TS 44.065 reserves 5 bit * for compression enitity number */ #define MAX_COMP 16 /* Maximum number of possible pcomp/dcomp values */ #define MAX_NSAPI 11 /* Maximum number usable NSAPIs */ #define MAX_ROHC 16 /* Maximum number of ROHC compression profiles */ /* According to: 3GPP TS 44.065, 6.5.1.1 Format of the protocol control * information compression field (Figure 7) and 3GPP TS 44.065, * 6.6.1.1 Format of the data compression field (Figure 9) */ struct gprs_sndcp_comp_field { struct llist_head list; /* Propose bit (P), see also: 6.5.1.1.2 and 6.6.1.1.2 */ unsigned int p; /* Entity number, see also: 6.5.1.1.3 and 6.6.1.1.3 */ unsigned int entity; /* Algorithm identifier, see also: 6.5.1.1.4 and 6.6.1.1.4 */ int algo; /* Number of contained PCOMP / DCOMP values */ uint8_t comp_len; /* PCOMP / DCOMP values, see also: 6.5.1.1.5 and 6.6.1.1.5 */ uint8_t comp[MAX_COMP]; /* Note: Only one of the following struct pointers may, be used. Unused pointers must be set to NULL! */ struct gprs_sndcp_pcomp_rfc1144_params *rfc1144_params; struct gprs_sndcp_pcomp_rfc2507_params *rfc2507_params; struct gprs_sndcp_pcomp_rohc_params *rohc_params; struct gprs_sndcp_dcomp_v42bis_params *v42bis_params; struct gprs_sndcp_dcomp_v44_params *v44_params; }; /* According to: 3GPP TS 44.065, 6.5.1.1.4 Algorithm identifier */ enum gprs_sndcp_hdr_comp_algo { RFC_1144, /* TCP/IP header compression, see also 6.5.2 */ RFC_2507, /* TCP/UDP/IP header compression, see also: 6.5.3 */ ROHC /* Robust Header Compression, see also 6.5.4 */ }; /* According to: 3GPP TS 44.065, 6.5.1.1.4 Algorithm identifier */ enum gprs_sndcp_data_comp_algo { V42BIS, /* V.42bis data compression, see also 6.6.2 */ V44 /* V44 data compression, see also: 6.6.3 */ }; /* According to: 3GPP TS 44.065, 8 SNDCP XID parameters */ enum gprs_sndcp_xid_param_types { SNDCP_XID_VERSION_NUMBER, SNDCP_XID_DATA_COMPRESSION, /* See also: subclause 6.6.1 */ SNDCP_XID_PROTOCOL_COMPRESSION, /* See also: subclause 6.5.1 */ }; /* According to: 3GPP TS 44.065, 6.5.2.1 Parameters (Table 5) */ struct gprs_sndcp_pcomp_rfc1144_params { uint8_t nsapi_len; /* Number of applicable NSAPIs * (default 0) */ uint8_t nsapi[MAX_NSAPI]; /* Applicable NSAPIs (default 0) */ int s01; /* (default 15) */ }; /* According to: 3GPP TS 44.065, 6.5.2.2 Assignment of PCOMP values */ enum gprs_sndcp_pcomp_rfc1144_pcomp { RFC1144_PCOMP1, /* Uncompressed TCP */ RFC1144_PCOMP2, /* Compressed TCP */ RFC1144_PCOMP_NUM /* Number of pcomp values */ }; /* According to: 3GPP TS 44.065, 6.5.3.1 Parameters (Table 6) */ struct gprs_sndcp_pcomp_rfc2507_params { uint8_t nsapi_len; /* Number of applicable NSAPIs * (default 0) */ uint8_t nsapi[MAX_NSAPI]; /* Applicable NSAPIs (default 0) */ int f_max_period; /* (default 256) */ int f_max_time; /* (default 5) */ int max_header; /* (default 168) */ int tcp_space; /* (default 15) */ int non_tcp_space; /* (default 15) */ }; /* According to: 3GPP TS 44.065, 6.5.3.2 Assignment of PCOMP values for RFC2507 */ enum gprs_sndcp_pcomp_rfc2507_pcomp { RFC2507_PCOMP1, /* Full Header */ RFC2507_PCOMP2, /* Compressed TCP */ RFC2507_PCOMP3, /* Compressed TCP non delta */ RFC2507_PCOMP4, /* Compressed non TCP */ RFC2507_PCOMP5, /* Context state */ RFC2507_PCOMP_NUM /* Number of pcomp values */ }; /* According to: 3GPP TS 44.065, 6.5.4.1 Parameter (Table 10) */ struct gprs_sndcp_pcomp_rohc_params { uint8_t nsapi_len; /* Number of applicable NSAPIs * (default 0) */ uint8_t nsapi[MAX_NSAPI]; /* Applicable NSAPIs (default 0) */ int max_cid; /* (default 15) */ int max_header; /* (default 168) */ uint8_t profile_len; /* (default 1) */ uint16_t profile[MAX_ROHC]; /* (default 0, ROHC uncompressed) */ }; /* According to: 3GPP TS 44.065, 6.5.4.2 Assignment of PCOMP values for ROHC */ enum gprs_sndcp_pcomp_rohc_pcomp { ROHC_PCOMP1, /* ROHC small CIDs */ ROHC_PCOMP2, /* ROHC large CIDs */ ROHC_PCOMP_NUM /* Number of pcomp values */ }; /* ROHC compression profiles, see also: http://www.iana.org/assignments/rohc-pro-ids/rohc-pro-ids.xhtml */ enum gprs_sndcp_xid_rohc_profiles { ROHC_UNCOMPRESSED = 0x0000, /* ROHC uncompressed [RFC5795] */ ROHC_RTP = 0x0001, /* ROHC RTP [RFC3095] */ ROHCV2_RTP = 0x0101, /* ROHCv2 RTP [RFC5225] */ ROHC_UDP = 0x0002, /* ROHC UDP [RFC3095] */ ROHCv2_UDP = 0x0102, /* ROHCv2 UDP [RFC5225] */ ROHC_ESP = 0x0003, /* ROHC ESP [RFC3095] */ ROHCV2_ESP = 0x0103, /* ROHCv2 ESP [RFC5225] */ ROHC_IP = 0x0004, /* ROHC IP [RFC3843] */ ROHCV2_IP = 0x0104, /* ROHCv2 IP [RFC5225] */ ROHC_LLA = 0x0005, /* ROHC LLA [RFC4362] */ ROHC_LLA_WITH_R_MODE = 0x0105, /* ROHC LLA with R-mode [RFC3408] */ ROHC_TCP = 0x0006, /* ROHC TCP [RFC6846] */ ROHC_RTP_UDP_LITE = 0x0007, /* ROHC RTP/UDP-Lite [RFC4019] */ ROHCV2_RTP_UDP_LITE = 0x0107, /* ROHCv2 RTP/UDP-Lite [RFC5225] */ ROHC_UDP_LITE = 0x0008, /* ROHC UDP-Lite [RFC4019] */ ROHCV2_UDP_LITE = 0x0108, /* ROHCv2 UDP-Lite [RFC5225] */ }; /* According to: 3GPP TS 44.065, 6.6.2.1 Parameters (Table 7a) */ struct gprs_sndcp_dcomp_v42bis_params { uint8_t nsapi_len; /* Number of applicable NSAPIs * (default 0) */ uint8_t nsapi[MAX_NSAPI]; /* Applicable NSAPIs (default 0) */ int p0; /* (default 3) */ int p1; /* (default 2048) */ int p2; /* (default 20) */ }; /* According to: 3GPP TS 44.065, 6.6.2.2 Assignment of DCOMP values */ enum gprs_sndcp_dcomp_v42bis_dcomp { V42BIS_DCOMP1, /* V.42bis enabled */ V42BIS_DCOMP_NUM /* Number of dcomp values */ }; /* According to: 3GPP TS 44.065, 6.6.3.1 Parameters (Table 7c) */ struct gprs_sndcp_dcomp_v44_params { uint8_t nsapi_len; /* Number of applicable NSAPIs * (default 0) */ uint8_t nsapi[MAX_NSAPI]; /* Applicable NSAPIs (default 0) */ int c0; /* (default 10000000) */ int p0; /* (default 3) */ int p1t; /* Refer to subclause 6.6.3.1.4 */ int p1r; /* Refer to subclause 6.6.3.1.5 */ int p3t; /* (default 3 x p1t) */ int p3r; /* (default 3 x p1r) */ }; /* According to: 3GPP TS 44.065, 6.6.3.2 Assignment of DCOMP values */ enum gprs_sndcp_dcomp_v44_dcomp { V44_DCOMP1, /* Packet method compressed */ V44_DCOMP2, /* Multi packet method compressed */ V44_DCOMP_NUM /* Number of dcomp values */ }; /* Transform a list with compression fields into an SNDCP-XID message (dst) */ int gprs_sndcp_compile_xid(uint8_t *dst, unsigned int dst_maxlen, const struct llist_head *comp_fields, int version); /* Transform an SNDCP-XID message (src) into a list of SNDCP-XID fields */ struct llist_head *gprs_sndcp_parse_xid(int *version, const void *ctx, const uint8_t *src, unsigned int src_len, const struct llist_head *comp_fields_req); /* Find out to which compression class the specified comp-field belongs * (header compression or data compression?) */ int gprs_sndcp_get_compression_class( const struct gprs_sndcp_comp_field *comp_field); /* Dump a list with SNDCP-XID fields (Debug) */ void gprs_sndcp_dump_comp_fields(const struct llist_head *comp_fields, unsigned int logl); osmo-sgsn-1.3.0/include/osmocom/sgsn/gprs_subscriber.h000066400000000000000000000015271327264017000230670ustar00rootroot00000000000000/* GPRS subscriber details for use in SGSN land */ #pragma once #include #include #include extern struct llist_head * const gprs_subscribers; struct gprs_subscr { struct llist_head entry; int use_count; char imsi[GSM23003_IMSI_MAX_DIGITS+1]; uint32_t tmsi; char imei[GSM23003_IMEISV_NUM_DIGITS+1]; bool authorized; bool keep_in_ram; uint32_t flags; uint16_t lac; struct sgsn_subscriber_data *sgsn_data; }; struct gprs_subscr *_gprs_subscr_get(struct gprs_subscr *gsub, const char *file, int line); struct gprs_subscr *_gprs_subscr_put(struct gprs_subscr *gsub, const char *file, int line); #define gprs_subscr_get(gsub) _gprs_subscr_get(gsub, __BASE_FILE__, __LINE__) #define gprs_subscr_put(gsub) _gprs_subscr_put(gsub, __BASE_FILE__, __LINE__) osmo-sgsn-1.3.0/include/osmocom/sgsn/gprs_utils.h000066400000000000000000000036301327264017000220610ustar00rootroot00000000000000/* GPRS utility functions */ /* (C) 2010 by Harald Welte * (C) 2010-2014 by On-Waves * (C) 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 . * */ #pragma once #include #include #include struct msgb; struct gprs_ra_id; struct msgb *gprs_msgb_copy(const struct msgb *msg, const char *name); int gprs_msgb_resize_area(struct msgb *msg, uint8_t *area, size_t old_size, size_t new_size); int gprs_str_to_apn(uint8_t *apn_enc, size_t max_len, const char *str); /* GSM 04.08, 10.5.7.3 GPRS Timer */ int gprs_tmr_to_secs(uint8_t tmr); uint8_t gprs_secs_to_tmr_floor(int secs); int gprs_is_mi_tmsi(const uint8_t *value, size_t value_len); int gprs_is_mi_imsi(const uint8_t *value, size_t value_len); int gprs_parse_mi_tmsi(const uint8_t *value, size_t value_len, uint32_t *tmsi); void gprs_parse_tmsi(const uint8_t *value, uint32_t *tmsi); int gprs_ra_id_equals(const struct gprs_ra_id *id1, const struct gprs_ra_id *id2); #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); } osmo-sgsn-1.3.0/include/osmocom/sgsn/gsup_client.h000066400000000000000000000034411327264017000222020ustar00rootroot00000000000000/* GPRS Subscriber Update Protocol client */ /* (C) 2014 by Sysmocom s.f.m.c. GmbH * All Rights Reserved * * Author: Jacob Erlbeck * * 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 #define GSUP_CLIENT_RECONNECT_INTERVAL 10 #define GSUP_CLIENT_PING_INTERVAL 20 struct msgb; struct ipa_client_conn; struct gsup_client; /* Expects message in msg->l2h */ typedef int (*gsup_client_read_cb_t)(struct gsup_client *gsupc, struct msgb *msg); struct gsup_client { const char *unit_name; struct ipa_client_conn *link; gsup_client_read_cb_t read_cb; void *data; struct oap_client_state oap_state; struct osmo_timer_list ping_timer; struct osmo_timer_list connect_timer; int is_connected; int got_ipa_pong; }; struct gsup_client *gsup_client_create(const char *unit_name, const char *ip_addr, unsigned int tcp_port, gsup_client_read_cb_t read_cb, struct oap_client_config *oapc_config); void gsup_client_destroy(struct gsup_client *gsupc); int gsup_client_send(struct gsup_client *gsupc, struct msgb *msg); struct msgb *gsup_client_msgb_alloc(void); osmo-sgsn-1.3.0/include/osmocom/sgsn/gtphub.h000066400000000000000000000431151327264017000211610ustar00rootroot00000000000000/* GTP Hub Implementation */ /* (C) 2015 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 . */ #pragma once #include #include #include #include #include #include /* support */ /* TODO move to osmocom/core/socket.c ? */ #include /* for IPPROTO_* etc */ struct osmo_sockaddr { struct sockaddr_storage a; socklen_t l; }; /* TODO move to osmocom/core/socket.c ? */ /*! \brief Initialize a sockaddr * \param[out] addr Valid osmo_sockaddr pointer to write result to * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP * \param[in] host Remote host name or IP address in string form * \param[in] port Remote port number in host byte order * \returns 0 on success, otherwise an error code (from getaddrinfo()). * * Copy the first result from a getaddrinfo() call with the given parameters to * *addr and *addr_len. On error, do not change *addr and return nonzero. */ int osmo_sockaddr_init(struct osmo_sockaddr *addr, uint16_t family, uint16_t type, uint8_t proto, const char *host, uint16_t port); /* Conveniently pass AF_UNSPEC, SOCK_DGRAM and IPPROTO_UDP to * osmo_sockaddr_init(). */ static inline int osmo_sockaddr_init_udp(struct osmo_sockaddr *addr, const char *host, uint16_t port) { return osmo_sockaddr_init(addr, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, host, port); } /*! \brief convert sockaddr to human readable string. * \param[out] addr_str Valid pointer to a buffer of length addr_str_len. * \param[in] addr_str_len Size of buffer addr_str points at. * \param[out] port_str Valid pointer to a buffer of length port_str_len. * \param[in] port_str_len Size of buffer port_str points at. * \param[in] addr Binary representation as returned by osmo_sockaddr_init(). * \param[in] flags flags as passed to getnameinfo(). * \returns 0 on success, an error code on error. * * Return the IPv4 or IPv6 address string and the port (a.k.a. service) string * representations of the given struct osmo_sockaddr in two caller provided * char buffers. Flags of (NI_NUMERICHOST | NI_NUMERICSERV) return numeric * address and port. Either one of addr_str or port_str may be NULL, in which * case nothing is returned there. * * See also osmo_sockaddr_to_str() (less flexible, but much more convenient). */ int osmo_sockaddr_to_strs(char *addr_str, size_t addr_str_len, char *port_str, size_t port_str_len, const struct osmo_sockaddr *addr, int flags); /*! \brief concatenate the parts returned by osmo_sockaddr_to_strs(). * \param[in] addr Binary representation as returned by osmo_sockaddr_init(). * \param[in] buf A buffer to use for string operations. * \param[in] buf_len Length of the buffer. * \returns Address string (in buffer). * * Compose a string of the numeric IP-address and port represented by *addr of * the form " port ". The returned string is valid until the * next invocation of this function. */ const char *osmo_sockaddr_to_strb(const struct osmo_sockaddr *addr, char *buf, size_t buf_len); /*! \brief conveniently return osmo_sockaddr_to_strb() in a static buffer. * \param[in] addr Binary representation as returned by osmo_sockaddr_init(). * \returns Address string in static buffer. * * See osmo_sockaddr_to_strb(). * * Note: only one osmo_sockaddr_to_str() call will work per print/log * statement. For two or more, use osmo_sockaddr_to_strb() with a separate * buffer each. */ const char *osmo_sockaddr_to_str(const struct osmo_sockaddr *addr); /*! \brief compare two osmo_sockaddr. * \param[in] a The first address to compare. * \param[in] b The other address to compare. * \returns 0 if equal, otherwise -1 or 1. */ int osmo_sockaddr_cmp(const struct osmo_sockaddr *a, const struct osmo_sockaddr *b); /*! \brief Overwrite *dst with *src. * Like memcpy(), but copy only the valid bytes. */ void osmo_sockaddr_copy(struct osmo_sockaddr *dst, const struct osmo_sockaddr *src); /* general */ enum gtphub_plane_idx { GTPH_PLANE_CTRL = 0, GTPH_PLANE_USER = 1, GTPH_PLANE_N }; enum gtphub_side_idx { GTPH_SIDE_SGSN = 0, GTPH_SIDE_GGSN = 1, GTPH_SIDE_N }; #define for_each_side(I) for (I = 0; I < GTPH_SIDE_N; I++) #define for_each_plane(I) for (I = 0; I < GTPH_PLANE_N; I++) #define for_each_side_and_plane(I,J) for_each_side(I) for_each_plane(J) static inline int other_side_idx(int side_idx) { return (side_idx + 1) & 1; } extern const char* const gtphub_plane_idx_names[GTPH_PLANE_N]; extern const uint16_t gtphub_plane_idx_default_port[GTPH_PLANE_N]; extern const char* const gtphub_side_idx_names[GTPH_SIDE_N]; /* A host address in the form that is expected in the 7.7.32 GSN Address IE. * len is either 4 (IPv4) or 16 (IPv6), any other value is invalid. If no * address is set, len shall be 0. */ struct gsn_addr { uint16_t len; uint8_t buf[16]; }; void gsn_addr_copy(struct gsn_addr *gsna, const struct gsn_addr *src); int gsn_addr_from_str(struct gsn_addr *gsna, const char *numeric_addr_str); /* Return gsna in numeric string form, in a static buffer. */ const char *gsn_addr_to_str(const struct gsn_addr *gsna); /* note: strbuf_len doesn't need to be larger than INET6_ADDRSTRLEN + 1. */ const char *gsn_addr_to_strb(const struct gsn_addr *gsna, char *strbuf, int strbuf_len); /* Return 1 on match, zero otherwise. */ int gsn_addr_same(const struct gsn_addr *a, const struct gsn_addr *b); /* Decode sa to gsna. Return 0 on success. If port is non-NULL, the port number * from sa is also returned. */ int gsn_addr_from_sockaddr(struct gsn_addr *gsna, uint16_t *port, const struct osmo_sockaddr *sa); /* expiry */ struct expiring_item; typedef void (*del_cb_t)(struct expiring_item *); struct expiring_item { struct llist_head entry; time_t expiry; del_cb_t del_cb; }; struct expiry { int expiry_in_seconds; struct llist_head items; }; /* Initialize an expiry queue. */ void expiry_init(struct expiry *exq, int expiry_in_seconds); /* Add a new mapping, or restart the expiry timeout for an already listed * mapping. */ void expiry_add(struct expiry *exq, struct expiring_item *item, time_t now); /* Initialize to all-empty; must be called before using the item in any way. */ void expiring_item_init(struct expiring_item *item); /* Remove the given item from its expiry queue, and call item->del_cb, if set. * This sets item->del_cb to NULL and is harmless when run a second time on the * same item, so the del_cb may choose to call this function, too, to allow * deleting items from several code paths. */ void expiring_item_del(struct expiring_item *item); /* Carry out due expiry of mappings. Must be invoked regularly. * 'now' is the current clock count in seconds and must correspond to the clock * count passed to nr_map_add(). A monotonous clock counter should be used. */ int expiry_tick(struct expiry *exq, time_t now); /* Expire all items. */ void expiry_clear(struct expiry *exq); /* number map */ /* A number map assigns a "random" mapped number to each user provided number. * If the same number is requested multiple times, the same mapped number is * returned. * * Number maps plug into possibly shared pools and expiry queues, for example: * * mapA -----------+-> pool1 <-+-- mapB * {10->1, 11->5} | {1, 2, 3, ...} | {10->2, 11->3} * | | * | | * /-> \-> expiry1 <-/ * | (30 seconds) * | * mapC -------+-----> pool2 <-+-- mapD * {10->1, 11->3} {1, 2, 3, ...} | {10->2, 11->5} * | * expiry2 <-/ * (60 seconds) * * A map contains mappings ("10->1"). Each map needs a number pool, which can * be shared with other maps. Each new mapping receives a number from the pool, * which is then unavailable to any other map using the same pool. * * A map may point at an expiry queue, in which case all mappings added to it * are also appended to the expiry queue (using a separate llist entry in the * mapping). Any number of maps may submit to the same expiry queue, if they * desire the same expiry timeout. An expiry queue stores the mappings in * chronological order, so that expiry checking is needed only from the start * of the queue; hence only mappings with identical expiry timeout can be added * to the same expiry queue. Upon expiry, a mapping is dropped from the map it * was submitted at. expiry_tick() needs to be called regularly for each expiry * queue. * * A nr_mapping can be embedded in a larger struct: each mapping can have a * distinct destructor (del_cb), and each del_cb can figure out the container * struct's address and free that upon expiry or manual deletion. So in expiry * queues (and even maps), mappings of different container types can be mixed. * This can help to drastically reduce the amount of unnecessary visits during * expiry checking, for the case that no expiry is pending. An expiry queue * always knows which mappings to expire next, because they are right at the * start of its list. * * Mapping allocation and a del_cb are provided by the caller. If del_cb is * NULL, no deallocation will be done (allowing statically allocated entries). */ typedef unsigned int nr_t; /* Generator for unused numbers. So far this counts upwards from zero, but the * implementation may change in the future. Treat this like an opaque struct. * If this becomes random, the tests need to be fixed. */ struct nr_pool { nr_t last_nr; nr_t nr_min; nr_t nr_max; }; struct nr_mapping { struct llist_head entry; struct expiring_item expiry_entry; void *origin; nr_t orig; nr_t repl; }; struct nr_map { struct nr_pool *pool; /* multiple nr_maps can share a nr_pool. */ struct expiry *add_items_to_expiry; struct llist_head mappings; }; void nr_pool_init(struct nr_pool *pool, nr_t nr_min, nr_t nr_max); /* Return the next unused number from the nr_pool. */ nr_t nr_pool_next(struct nr_pool *pool); /* Initialize the nr_mapping to zero/empty values. */ void nr_mapping_init(struct nr_mapping *mapping); /* Remove the given mapping from its parent map and expiry queue, and call * mapping->del_cb, if set. */ void nr_mapping_del(struct nr_mapping *mapping); /* Initialize an (already allocated) nr_map, and set the map's number pool. * Multiple nr_map instances may use the same nr_pool. Set the nr_map's expiry * queue to exq, so that all added mappings are automatically expired after the * time configured in exq. exq may be NULL to disable automatic expiry. */ void nr_map_init(struct nr_map *map, struct nr_pool *pool, struct expiry *exq); /* Add a new entry to the map. mapping->orig, mapping->origin and * mapping->del_cb must be set before calling this function. The remaining * fields of *mapping will be overwritten. mapping->repl is set to the next * available mapped number from map->pool. 'now' is the current clock count in * seconds; if no map->expiry is used, just pass 0 for 'now'. */ void nr_map_add(struct nr_map *map, struct nr_mapping *mapping, time_t now); /* Restart the timeout for the given mapping. mapping must be a member of map. */ void nr_map_refresh(struct nr_map *map, struct nr_mapping *mapping, time_t now); /* Return a known mapping from nr_orig and the given origin. If nr_orig is * unknown, return NULL. */ struct nr_mapping *nr_map_get(const struct nr_map *map, void *origin, nr_t nr_orig); /* Return a known mapping to nr_repl. If nr_repl is unknown, return NULL. */ struct nr_mapping *nr_map_get_inv(const struct nr_map *map, nr_t nr_repl); /* Remove all mappings from map. */ void nr_map_clear(struct nr_map *map); /* Return 1 if map has no entries, 0 otherwise. */ int nr_map_empty(const struct nr_map *map); /* config */ static const int GTPH_EXPIRE_QUICKLY_SECS = 30; /* TODO is there a spec for this? */ static const int GTPH_EXPIRE_SLOWLY_MINUTES = 6 * 60; /* TODO is there a spec for this? */ struct gtphub_cfg_addr { const char *addr_str; uint16_t port; }; struct gtphub_cfg_bind { struct gtphub_cfg_addr bind; }; struct gtphub_cfg { struct gtphub_cfg_bind to_gsns[GTPH_SIDE_N][GTPH_PLANE_N]; struct gtphub_cfg_addr proxy[GTPH_SIDE_N][GTPH_PLANE_N]; int sgsn_use_sender; /* Use sender, not GSN addr IE with std ports */ }; /* state */ struct gtphub_peer { struct llist_head entry; struct llist_head addresses; /* Alternatives, not load balancing. */ struct nr_pool seq_pool; struct nr_map seq_map; }; struct gtphub_peer_addr { struct llist_head entry; struct gtphub_peer *peer; struct gsn_addr addr; struct llist_head ports; }; struct gtphub_peer_port { struct llist_head entry; struct gtphub_peer_addr *peer_addr; uint16_t port; unsigned int ref_count; /* references from other peers' seq_maps */ struct osmo_sockaddr sa; /* a "cache" for (peer_addr->addr, port) */ int last_restart_count; /* 0..255 = valid, all else means unknown */ struct rate_ctr_group *counters_io; }; struct gtphub_tunnel_endpoint { struct gtphub_peer_port *peer; uint32_t tei_orig; /* from/to peer */ struct rate_ctr_group *counters_io; }; struct gtphub_tunnel { struct llist_head entry; struct expiring_item expiry_entry; uint32_t tei_repl; /* unique TEI to replace peers' TEIs */ struct gtphub_tunnel_endpoint endpoint[GTPH_SIDE_N][GTPH_PLANE_N]; }; struct gtphub_bind { struct gsn_addr local_addr; uint16_t local_port; struct osmo_fd ofd; /* list of struct gtphub_peer */ struct llist_head peers; const char *label; /* For logging */ struct rate_ctr_group *counters_io; }; struct gtphub_resolved_ggsn { struct llist_head entry; struct expiring_item expiry_entry; /* The APN OI, the Operator Identifier, is the combined address, * including parts of the IMSI and APN NI, and ending with ".gprs". */ char apn_oi_str[GSM_APN_LENGTH]; /* Which address and port we resolved that to. */ struct gtphub_peer_port *peer; }; struct gtphub { struct gtphub_bind to_gsns[GTPH_SIDE_N][GTPH_PLANE_N]; /* pointers to an entry of to_gsns[s][p].peers */ struct gtphub_peer_port *proxy[GTPH_SIDE_N][GTPH_PLANE_N]; /* The TEI numbers will simply wrap and be reused, which will work out * in practice. Problems would arise if one given peer maintained the * same TEI for a time long enough for the TEI nr map to wrap an entire * uint32_t; if a new TEI were mapped every second, this would take * more than 100 years (in which a single given TEI must not time out) * to cause a problem. */ struct nr_pool tei_pool; struct llist_head tunnels; /* struct gtphub_tunnel */ struct llist_head pending_deletes; /* opaque (gtphub.c) */ struct llist_head ggsn_lookups; /* opaque (gtphub_ares.c) */ struct llist_head resolved_ggsns; /* struct gtphub_resolved_ggsn */ struct osmo_timer_list gc_timer; struct expiry expire_quickly; struct expiry expire_slowly; uint8_t restart_counter; int sgsn_use_sender; }; struct gtp_packet_desc; /* api */ int gtphub_vty_init(struct gtphub *global_hub, struct gtphub_cfg *global_cfg); int gtphub_cfg_read(struct gtphub_cfg *cfg, const char *config_file); /* Initialize and start gtphub: bind to ports, run expiry timers. */ int gtphub_start(struct gtphub *hub, struct gtphub_cfg *cfg, uint8_t restart_counter); /* Close all sockets, expire all maps and peers and free all allocations. The * struct is then unusable, unless gtphub_start() is run on it again. */ void gtphub_stop(struct gtphub *hub); time_t gtphub_now(void); /* Remove expired items, empty peers, ... */ void gtphub_gc(struct gtphub *hub, time_t now); /* Return the string of the first address for this peer. */ const char *gtphub_peer_str(struct gtphub_peer *peer); /* Return a human readable description of tun in a static buffer. */ const char *gtphub_tunnel_str(struct gtphub_tunnel *tun); /* Return 1 if all of tun's endpoints are fully established, 0 otherwise. */ int gtphub_tunnel_complete(struct gtphub_tunnel *tun); int gtphub_handle_buf(struct gtphub *hub, unsigned int side_idx, unsigned int port_idx, const struct osmo_sockaddr *from_addr, uint8_t *buf, size_t received, time_t now, uint8_t **reply_buf, struct osmo_fd **to_ofd, struct osmo_sockaddr *to_addr); struct gtphub_peer_port *gtphub_port_have(struct gtphub *hub, struct gtphub_bind *bind, const struct gsn_addr *addr, uint16_t port); struct gtphub_peer_port *gtphub_port_find_sa(const struct gtphub_bind *bind, const struct osmo_sockaddr *addr); void gtphub_resolved_ggsn(struct gtphub *hub, const char *apn_oi_str, struct gsn_addr *resolved_addr, time_t now); const char *gtphub_port_str(struct gtphub_peer_port *port); int gtphub_write(const struct osmo_fd *to, const struct osmo_sockaddr *to_addr, const uint8_t *buf, size_t buf_len); osmo-sgsn-1.3.0/include/osmocom/sgsn/oap_client.h000066400000000000000000000057231327264017000220100ustar00rootroot00000000000000/* Osmocom Authentication Protocol API */ /* (C) 2015 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 . * */ #pragma once #include struct msgb; struct osmo_oap_message; /* This is the config part for vty. It is essentially copied in * oap_client_state, where values are copied over once the config is * considered valid. */ struct oap_client_config { uint16_t client_id; int secret_k_present; uint8_t secret_k[16]; int secret_opc_present; uint8_t secret_opc[16]; }; /* The runtime state of the OAP client. client_id and the secrets are in fact * duplicated from oap_client_config, so that a separate validation of the * config data is possible, and so that only a struct oap_client_state* is * passed around. */ struct oap_client_state { enum { OAP_UNINITIALIZED = 0, /* just allocated. */ OAP_DISABLED, /* disabled by config. */ OAP_INITIALIZED, /* enabled, config is valid. */ OAP_REQUESTED_CHALLENGE, OAP_SENT_CHALLENGE_RESULT, OAP_REGISTERED } state; uint16_t client_id; uint8_t secret_k[16]; uint8_t secret_opc[16]; int registration_failures; }; /* From config, initialize state. Return 0 on success. */ int oap_client_init(struct oap_client_config *config, struct oap_client_state *state); /* Construct an OAP registration message and return in *msg_tx. Use * state->client_id and update state->state. * Return 0 on success, or a negative value on error. * If an error is returned, *msg_tx is guaranteed to be NULL. */ int oap_client_register(struct oap_client_state *state, struct msgb **msg_tx); /* Decode and act on a received OAP message msg_rx. Update state->state. If a * non-NULL pointer is returned in *msg_tx, that msgb should be sent to the OAP * server (and freed) by the caller. The received msg_rx is not freed. * Return 0 on success, or a negative value on error. * If an error is returned, *msg_tx is guaranteed to be NULL. */ int oap_client_handle(struct oap_client_state *state, const struct msgb *msg_rx, struct msgb **msg_tx); /* Allocate a msgb and in it, return the encoded oap_client_msg. Return * NULL on error. (Like oap_client_encode(), but also allocates a msgb.) * About the name: the idea is do_something(oap_client_encoded(my_struct)) */ struct msgb *oap_client_encoded(const struct osmo_oap_message *oap_client_msg); osmo-sgsn-1.3.0/include/osmocom/sgsn/sgsn.h000066400000000000000000000106321327264017000206400ustar00rootroot00000000000000#ifndef _SGSN_H #define _SGSN_H #include #include #include #include #include #include #include "../../bscconfig.h" #if BUILD_IU #include #endif #include #include struct gprs_gsup_client; struct hostent; enum sgsn_auth_policy { SGSN_AUTH_POLICY_OPEN, SGSN_AUTH_POLICY_CLOSED, SGSN_AUTH_POLICY_ACL_ONLY, SGSN_AUTH_POLICY_REMOTE }; enum sgsn_rate_ctr_keys { CTR_LLC_DL_BYTES, CTR_LLC_UL_BYTES, CTR_LLC_DL_PACKETS, CTR_LLC_UL_PACKETS, CTR_GPRS_ATTACH_REQUEST, CTR_GPRS_ATTACH_ACKED, CTR_GPRS_ATTACH_REJECTED, CTR_GPRS_DETACH_REQUEST, CTR_GPRS_DETACH_ACKED, CTR_GPRS_ROUTING_AREA_REQUEST, CTR_GPRS_ROUTING_AREA_ACKED, CTR_GPRS_ROUTING_AREA_REJECT, /* PDP single packet counter / GSM 04.08 9.5.1 - 9.5.9 */ CTR_PDP_ACTIVATE_REQUEST, CTR_PDP_ACTIVATE_REJECT, CTR_PDP_ACTIVATE_ACCEPT, CTR_PDP_REQUEST_ACTIVATE, /* unused */ CTR_PDP_REQUEST_ACTIVATE_REJ, /* unused */ CTR_PDP_MODIFY_REQUEST, /* unsued */ CTR_PDP_MODIFY_ACCEPT, /* unused */ CTR_PDP_DL_DEACTIVATE_REQUEST, CTR_PDP_DL_DEACTIVATE_ACCEPT, CTR_PDP_UL_DEACTIVATE_REQUEST, CTR_PDP_UL_DEACTIVATE_ACCEPT, }; struct sgsn_cdr { char *filename; bool trap; int interval; }; struct sgsn_config { /* parsed from config file */ char *gtp_statedir; struct sockaddr_in gtp_listenaddr; /* misc */ struct gprs_ns_inst *nsi; enum sgsn_auth_policy auth_policy; enum gprs_ciph_algo cipher; struct llist_head imsi_acl; struct sockaddr_in gsup_server_addr; int gsup_server_port; int require_authentication; int require_update_location; /* CDR configuration */ struct sgsn_cdr cdr; struct { int T3312; int T3322; int T3350; int T3360; int T3370; int T3313; int T3314; int T3316; int T3385; int T3386; int T3395; int T3397; } timers; int dynamic_lookup; struct oap_client_config oap; /* RFC1144 TCP/IP header compression */ struct { int active; int passive; int s01; } pcomp_rfc1144; /* V.42vis data compression */ struct { int active; int passive; int p0; int p1; int p2; } dcomp_v42bis; #if BUILD_IU struct { enum ranap_nsap_addr_enc rab_assign_addr_enc; } iu; #endif }; struct sgsn_instance { char *config_file; struct sgsn_config cfg; /* File descriptor wrappers for LibGTP */ struct osmo_fd gtp_fd0; struct osmo_fd gtp_fd1c; struct osmo_fd gtp_fd1u; /* Timer for libGTP */ struct osmo_timer_list gtp_timer; /* GSN instance for libgtp */ struct gsn_t *gsn; /* Subscriber */ struct gsup_client *gsup_client; /* LLME inactivity timer */ struct osmo_timer_list llme_timer; /* c-ares event loop integration */ struct osmo_timer_list ares_timer; struct llist_head ares_fds; ares_channel ares_channel; struct ares_addr_node *ares_servers; struct rate_ctr_group *rate_ctrs; }; extern struct sgsn_instance *sgsn; /* sgsn_vty.c */ int sgsn_vty_init(struct sgsn_config *cfg); int sgsn_parse_config(const char *config_file); char *sgsn_gtp_ntoa(struct ul16_t *ul); /* sgsn.c */ /* Main input function for Gb proxy */ int sgsn_rcvmsg(struct msgb *msg, struct gprs_nsvc *nsvc, uint16_t ns_bvci); struct sgsn_pdp_ctx *sgsn_create_pdp_ctx(struct sgsn_ggsn_ctx *ggsn, struct sgsn_mm_ctx *mmctx, uint16_t nsapi, struct tlv_parsed *tp); int sgsn_delete_pdp_ctx(struct sgsn_pdp_ctx *pctx); void sgsn_pdp_upd_gtp_u(struct sgsn_pdp_ctx *pdp, void *addr, size_t alen); /* gprs_sndcp.c */ /* Entry point for the SNSM-ACTIVATE.indication */ int sndcp_sm_activate_ind(struct gprs_llc_lle *lle, uint8_t nsapi); /* Entry point for the SNSM-DEACTIVATE.indication */ int sndcp_sm_deactivate_ind(struct gprs_llc_lle *lle, uint8_t nsapi); /* Called by SNDCP when it has received/re-assembled a N-PDU */ int sgsn_rx_sndcp_ud_ind(struct gprs_ra_id *ra_id, int32_t tlli, uint8_t nsapi, struct msgb *msg, uint32_t npdu_len, uint8_t *npdu); int sndcp_unitdata_req(struct msgb *msg, struct gprs_llc_lle *lle, uint8_t nsapi, void *mmcontext); int sndcp_llunitdata_ind(struct msgb *msg, struct gprs_llc_lle *lle, uint8_t *hdr, uint16_t len); /* * CDR related functionality */ int sgsn_cdr_init(struct sgsn_instance *sgsn); /* * C-ARES related functionality */ int sgsn_ares_init(struct sgsn_instance *sgsn); int sgsn_ares_query(struct sgsn_instance *sgsm, const char *name, ares_host_callback cb, void *data); #endif osmo-sgsn-1.3.0/include/osmocom/sgsn/signal.h000066400000000000000000000026141327264017000211440ustar00rootroot00000000000000/* 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 enum signal_subsystems { SS_SGSN, }; /* GPRS SGSN signals SS_SGSN */ enum signal_sgsn { S_SGSN_ATTACH, S_SGSN_DETACH, S_SGSN_UPDATE, S_SGSN_PDP_ACT, S_SGSN_PDP_DEACT, S_SGSN_PDP_TERMINATE, S_SGSN_PDP_FREE, S_SGSN_MM_FREE, }; struct sgsn_mm_ctx; struct sgsn_signal_data { struct sgsn_mm_ctx *mm; struct sgsn_pdp_ctx *pdp; /* non-NULL for PDP_ACT, PDP_DEACT, PDP_FREE */ }; #endif osmo-sgsn-1.3.0/include/osmocom/sgsn/slhc.h000066400000000000000000000153571327264017000206300ustar00rootroot00000000000000#ifndef _SLHC_H #define _SLHC_H /* * Definitions for tcp compression routines. * * $Header: slcompress.h,v 1.10 89/12/31 08:53:02 van Exp $ * * Copyright (c) 1989 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by the University of California, Berkeley. The name of the * University may not be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: * - Initial distribution. * * * modified for KA9Q Internet Software Package by * Katie Stevens (dkstevens@ucdavis.edu) * University of California, Davis * Computing Services * - 01-31-90 initial adaptation * * - Feb 1991 Bill_Simpson@um.cc.umich.edu * variable number of conversation slots * allow zero or one slots * separate routines * status display */ /* * Compressed packet format: * * The first octet contains the packet type (top 3 bits), TCP * 'push' bit, and flags that indicate which of the 4 TCP sequence * numbers have changed (bottom 5 bits). The next octet is a * conversation number that associates a saved IP/TCP header with * the compressed packet. The next two octets are the TCP checksum * from the original datagram. The next 0 to 15 octets are * sequence number changes, one change per bit set in the header * (there may be no changes and there are two special cases where * the receiver implicitly knows what changed -- see below). * * There are 5 numbers which can change (they are always inserted * in the following order): TCP urgent pointer, window, * acknowledgment, sequence number and IP ID. (The urgent pointer * is different from the others in that its value is sent, not the * change in value.) Since typical use of SLIP links is biased * toward small packets (see comments on MTU/MSS below), changes * use a variable length coding with one octet for numbers in the * range 1 - 255 and 3 octets (0, MSB, LSB) for numbers in the * range 256 - 65535 or 0. (If the change in sequence number or * ack is more than 65535, an uncompressed packet is sent.) */ /* * Packet types (must not conflict with IP protocol version) * * The top nibble of the first octet is the packet type. There are * three possible types: IP (not proto TCP or tcp with one of the * control flags set); uncompressed TCP (a normal IP/TCP packet but * with the 8-bit protocol field replaced by an 8-bit connection id -- * this type of packet syncs the sender & receiver); and compressed * TCP (described above). * * LSB of 4-bit field is TCP "PUSH" bit (a worthless anachronism) and * is logically part of the 4-bit "changes" field that follows. Top * three bits are actual packet type. For backward compatibility * and in the interest of conserving bits, numbers are chosen so the * IP protocol version number (4) which normally appears in this nibble * means "IP packet". */ #include #include /* SLIP compression masks for len/vers byte */ #define SL_TYPE_IP 0x40 #define SL_TYPE_UNCOMPRESSED_TCP 0x70 #define SL_TYPE_COMPRESSED_TCP 0x80 #define SL_TYPE_ERROR 0x00 /* Bits in first octet of compressed packet */ #define NEW_C 0x40 /* flag bits for what changed in a packet */ #define NEW_I 0x20 #define NEW_S 0x08 #define NEW_A 0x04 #define NEW_W 0x02 #define NEW_U 0x01 /* reserved, special-case values of above */ #define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */ #define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */ #define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U) #define TCP_PUSH_BIT 0x10 /* * data type and sizes conversion assumptions: * * VJ code KA9Q style generic * u_char byte_t unsigned char 8 bits * u_short int16 unsigned short 16 bits * u_int int16 unsigned short 16 bits * u_long unsigned long unsigned long 32 bits * int int32 long 32 bits */ typedef __u8 byte_t; typedef __u32 int32; /* * "state" data for each active tcp conversation on the wire. This is * basically a copy of the entire IP/TCP header from the last packet * we saw from the conversation together with a small identifier * the transmit & receive ends of the line use to locate saved header. */ struct cstate { byte_t cs_this; /* connection id number (xmit) */ struct cstate *next; /* next in ring (xmit) */ struct iphdr cs_ip; /* ip/tcp hdr from most recent packet */ struct tcphdr cs_tcp; unsigned char cs_ipopt[64]; unsigned char cs_tcpopt[64]; int cs_hsize; }; #define NULLSLSTATE (struct cstate *)0 /* * all the state data for one serial line (we need one of these per line). */ struct slcompress { struct cstate *tstate; /* transmit connection states (array)*/ struct cstate *rstate; /* receive connection states (array)*/ byte_t tslot_limit; /* highest transmit slot id (0-l)*/ byte_t rslot_limit; /* highest receive slot id (0-l)*/ byte_t xmit_oldest; /* oldest xmit in ring */ byte_t xmit_current; /* most recent xmit id */ byte_t recv_current; /* most recent rcvd id */ byte_t flags; #define SLF_TOSS 0x01 /* tossing rcvd frames until id received */ int32 sls_o_nontcp; /* outbound non-TCP packets */ int32 sls_o_tcp; /* outbound TCP packets */ int32 sls_o_uncompressed; /* outbound uncompressed packets */ int32 sls_o_compressed; /* outbound compressed packets */ int32 sls_o_searches; /* searches for connection state */ int32 sls_o_misses; /* times couldn't find conn. state */ int32 sls_i_uncompressed; /* inbound uncompressed packets */ int32 sls_i_compressed; /* inbound compressed packets */ int32 sls_i_error; /* inbound error packets */ int32 sls_i_tossed; /* inbound packets tossed because of error */ int32 sls_i_runt; int32 sls_i_badcheck; }; #define NULLSLCOMPR (struct slcompress *)0 /* In slhc.c: */ struct slcompress *slhc_init(const void *ctx, int rslots, int tslots); void slhc_free(struct slcompress *comp); int slhc_compress(struct slcompress *comp, unsigned char *icp, int isize, unsigned char *ocp, unsigned char **cpp, int compress_cid); int slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize); int slhc_remember(struct slcompress *comp, unsigned char *icp, int isize); int slhc_toss(struct slcompress *comp); void slhc_i_status(struct slcompress *comp); void slhc_o_status(struct slcompress *comp); #endif /* _SLHC_H */ osmo-sgsn-1.3.0/include/osmocom/sgsn/v42bis.h000066400000000000000000000121621327264017000207770ustar00rootroot00000000000000/* * SpanDSP - a series of DSP components for telephony * * v42bis.h * * Written by Steve Underwood * * Copyright (C) 2005, 2011 Steve Underwood * * All rights reserved. * * This program 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 program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /*! \page v42bis_page V.42bis modem data compression \section v42bis_page_sec_1 What does it do? The v.42bis specification defines a data compression scheme, to work in conjunction with the error correction scheme defined in V.42. \section v42bis_page_sec_2 How does it work? */ #include #if !defined(_SPANDSP_V42BIS_H_) #define _SPANDSP_V42BIS_H_ #define SPAN_DECLARE(x) x #define V42BIS_MIN_STRING_SIZE 6 #define V42BIS_MAX_STRING_SIZE 250 #define V42BIS_MIN_DICTIONARY_SIZE 512 #define V42BIS_MAX_BITS 12 #define V42BIS_MAX_CODEWORDS 4096 /* 2^V42BIS_MAX_BITS */ #define V42BIS_MAX_OUTPUT_LENGTH 1024 enum { V42BIS_P0_NEITHER_DIRECTION = 0, V42BIS_P0_INITIATOR_RESPONDER, V42BIS_P0_RESPONDER_INITIATOR, V42BIS_P0_BOTH_DIRECTIONS }; enum { V42BIS_COMPRESSION_MODE_DYNAMIC = 0, V42BIS_COMPRESSION_MODE_ALWAYS, V42BIS_COMPRESSION_MODE_NEVER }; typedef void (*put_msg_func_t)(void *user_data, const uint8_t *msg, int len); /*! V.42bis compression/decompression descriptor. This defines the working state for a single instance of V.42bis compress/decompression. */ typedef struct v42bis_state_s v42bis_state_t; #if defined(__cplusplus) extern "C" { #endif /*! Compress a block of octets. \param s The V.42bis context. \param buf The data to be compressed. \param len The length of the data buffer. \return 0 */ SPAN_DECLARE(int) v42bis_compress(v42bis_state_t *s, const uint8_t buf[], int len); /*! Flush out any data remaining in a compression buffer. \param s The V.42bis context. \return 0 */ SPAN_DECLARE(int) v42bis_compress_flush(v42bis_state_t *s); /*! Decompress a block of octets. \param s The V.42bis context. \param buf The data to be decompressed. \param len The length of the data buffer. \return 0 */ SPAN_DECLARE(int) v42bis_decompress(v42bis_state_t *s, const uint8_t buf[], int len); /*! Flush out any data remaining in the decompression buffer. \param s The V.42bis context. \return 0 */ SPAN_DECLARE(int) v42bis_decompress_flush(v42bis_state_t *s); /*! Set the compression mode. \param s The V.42bis context. \param mode One of the V.42bis compression modes - V42BIS_COMPRESSION_MODE_DYNAMIC, V42BIS_COMPRESSION_MODE_ALWAYS, V42BIS_COMPRESSION_MODE_NEVER */ SPAN_DECLARE(void) v42bis_compression_control(v42bis_state_t *s, int mode); /*! Initialise a V.42bis context. \param s The V.42bis context. \param negotiated_p0 The negotiated P0 parameter, from the V.42bis spec. \param negotiated_p1 The negotiated P1 parameter, from the V.42bis spec. \param negotiated_p2 The negotiated P2 parameter, from the V.42bis spec. \param encode_handler Encode callback handler. \param encode_user_data An opaque pointer passed to the encode callback handler. \param max_encode_len The maximum length that should be passed to the encode handler. \param decode_handler Decode callback handler. \param decode_user_data An opaque pointer passed to the decode callback handler. \param max_decode_len The maximum length that should be passed to the decode handler. \return The V.42bis context. */ SPAN_DECLARE(v42bis_state_t *) v42bis_init(const void *ctx, v42bis_state_t *s, int negotiated_p0, int negotiated_p1, int negotiated_p2, put_msg_func_t encode_handler, void *encode_user_data, int max_encode_len, put_msg_func_t decode_handler, void *decode_user_data, int max_decode_len); /*! Release a V.42bis context. \param s The V.42bis context. \return 0 if OK */ SPAN_DECLARE(int) v42bis_release(v42bis_state_t *s); /*! Free a V.42bis context. \param s The V.42bis context. \return 0 if OK */ SPAN_DECLARE(int) v42bis_free(v42bis_state_t *s); #if defined(__cplusplus) } #endif #endif /*- End of file ------------------------------------------------------------*/ osmo-sgsn-1.3.0/include/osmocom/sgsn/v42bis_private.h000066400000000000000000000104601327264017000225300ustar00rootroot00000000000000/* * SpanDSP - a series of DSP components for telephony * * private/v42bis.h * * Written by Steve Underwood * * Copyright (C) 2005 Steve Underwood * * All rights reserved. * * This program 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 program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #if !defined(_SPANDSP_PRIVATE_V42BIS_H_) #define _SPANDSP_PRIVATE_V42BIS_H_ /*! V.42bis dictionary node. Note that 0 is not a valid node to point to (0 is always a control code), so 0 is used as a "no such value" marker in this structure. */ typedef struct { /*! \brief The value of the octet represented by the current dictionary node */ uint8_t node_octet; /*! \brief The parent of this node */ uint16_t parent; /*! \brief The first child of this node */ uint16_t child; /*! \brief The next node at the same depth */ uint16_t next; } v42bis_dict_node_t; /*! V.42bis compression or decompression. This defines the working state for a single instance of V.42bis compression or decompression. */ typedef struct { /*! \brief Compression enabled. */ int v42bis_parm_p0; /*! \brief Compression mode. */ int compression_mode; /*! \brief Callback function to handle output data. */ put_msg_func_t handler; /*! \brief An opaque pointer passed in calls to the data handler. */ void *user_data; /*! \brief The maximum amount to be passed to the data handler. */ int max_output_len; /*! \brief TRUE if we are in transparent (i.e. uncompressable) mode */ int transparent; /*! \brief Next empty dictionary entry */ uint16_t v42bis_parm_c1; /*! \brief Current codeword size */ uint16_t v42bis_parm_c2; /*! \brief Threshold for codeword size change */ uint16_t v42bis_parm_c3; /*! \brief The current update point in the dictionary */ uint16_t update_at; /*! \brief The last entry matched in the dictionary */ uint16_t last_matched; /*! \brief The last entry added to the dictionary */ uint16_t last_added; /*! \brief Total number of codewords in the dictionary */ int v42bis_parm_n2; /*! \brief Maximum permitted string length */ int v42bis_parm_n7; /*! \brief The dictionary */ v42bis_dict_node_t dict[V42BIS_MAX_CODEWORDS]; /*! \brief The octet string in progress */ uint8_t string[V42BIS_MAX_STRING_SIZE]; /*! \brief The current length of the octet string in progress */ int string_length; /*! \brief The amount of the octet string in progress which has already been flushed out of the buffer */ int flushed_length; /*! \brief Compression performance metric */ uint16_t compression_performance; /*! \brief Outgoing bit buffer (compression), or incoming bit buffer (decompression) */ uint32_t bit_buffer; /*! \brief Outgoing bit count (compression), or incoming bit count (decompression) */ int bit_count; /*! \brief The output composition buffer */ uint8_t output_buf[V42BIS_MAX_OUTPUT_LENGTH]; /*! \brief The length of the contents of the output composition buffer */ int output_octet_count; /*! \brief The current value of the escape code */ uint8_t escape_code; /*! \brief TRUE if we just hit an escape code, and are waiting for the following octet */ int escaped; } v42bis_comp_state_t; /*! V.42bis compression/decompression descriptor. This defines the working state for a single instance of V.42bis compress/decompression. */ struct v42bis_state_s { /*! \brief Compression state. */ v42bis_comp_state_t compress; /*! \brief Decompression state. */ v42bis_comp_state_t decompress; /*! \brief Error and flow logging control */ }; #endif /*- End of file ------------------------------------------------------------*/ osmo-sgsn-1.3.0/include/osmocom/sgsn/vty.h000066400000000000000000000002111327264017000205000ustar00rootroot00000000000000#pragma once #include enum bsc_vty_node { GBPROXY_NODE = _LAST_OSMOVTY_NODE + 1, SGSN_NODE, GTPHUB_NODE, }; osmo-sgsn-1.3.0/m4/000077500000000000000000000000001327264017000137625ustar00rootroot00000000000000osmo-sgsn-1.3.0/m4/README000066400000000000000000000002431327264017000146410ustar00rootroot00000000000000We 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-sgsn-1.3.0/m4/ax_check_compile_flag.m4000066400000000000000000000064021327264017000204740ustar00rootroot00000000000000# =========================================================================== # 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-sgsn-1.3.0/osmo-sgsn.pc.in000066400000000000000000000003671327264017000163260ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@/ Name: OsmoSGSN Description: Osmocom's Serving GPRS Support Node for 2G and 3G packet-switched mobile networks Requires: Version: @VERSION@ Cflags: -I${includedir} osmo-sgsn-1.3.0/osmoappdesc.py000066400000000000000000000024241327264017000163330ustar00rootroot00000000000000#!/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 = { "gbproxy": ["doc/examples/osmo-gbproxy/osmo-gbproxy.cfg", "doc/examples/osmo-gbproxy/osmo-gbproxy-legacy.cfg"], "sgsn": ["doc/examples/osmo-sgsn/osmo-sgsn.cfg"], "gtphub": ["doc/examples/osmo-gtphub/osmo-gtphub-1iface.cfg"] } apps = [(4246, "src/gprs/osmo-gbproxy", "OsmoGbProxy", "gbproxy"), (4245, "src/gprs/osmo-sgsn", "OsmoSGSN", "sgsn"), (4253, "src/gprs/osmo-gtphub", "OsmoGTPhub", "gtphub") ] vty_command = ["./src/gprs/osmo-sgsn", "-c", "doc/examples/osmo-sgsn/osmo-sgsn.cfg"] vty_app = apps[1] osmo-sgsn-1.3.0/src/000077500000000000000000000000001327264017000142315ustar00rootroot00000000000000osmo-sgsn-1.3.0/src/Makefile.am000066400000000000000000000000351327264017000162630ustar00rootroot00000000000000SUBDIRS = \ gprs \ $(NULL) osmo-sgsn-1.3.0/src/gprs/000077500000000000000000000000001327264017000152045ustar00rootroot00000000000000osmo-sgsn-1.3.0/src/gprs/.gitignore000066400000000000000000000000301327264017000171650ustar00rootroot00000000000000gsn_restart osmo_*.cfg* osmo-sgsn-1.3.0/src/gprs/Makefile.am000066400000000000000000000036741327264017000172520ustar00rootroot00000000000000AM_CPPFLAGS = \ $(all_includes) \ -I$(top_srcdir)/include \ -I$(top_builddir) \ $(NULL) AM_CFLAGS = \ -Wall \ -fno-strict-aliasing \ $(LIBOSMOCORE_CFLAGS) \ $(LIBOSMOGSM_CFLAGS) \ $(LIBOSMOVTY_CFLAGS) \ $(LIBOSMOCTRL_CFLAGS) \ $(LIBOSMOABIS_CFLAGS) \ $(LIBOSMOGB_CFLAGS) \ $(COVERAGE_CFLAGS) \ $(LIBCARES_CFLAGS) \ $(LIBGTP_CFLAGS) \ $(NULL) if BUILD_IU AM_CFLAGS += \ $(LIBASN1C_CFLAGS) \ $(LIBOSMOSIGTRAN_CFLAGS) \ $(LIBOSMORANAP_CFLAGS) \ $(NULL) endif OSMO_LIBS = \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOGSM_LIBS) \ $(LIBOSMOVTY_LIBS) \ $(LIBOSMOCTRL_LIBS) \ $(LIBOSMOGB_LIBS) \ $(LIBGTP_LIBS) \ $(NULL) bin_PROGRAMS = \ osmo-gbproxy \ osmo-sgsn \ osmo-gtphub \ $(NULL) osmo_gbproxy_SOURCES = \ gb_proxy.c \ gb_proxy_main.c \ gb_proxy_vty.c \ gb_proxy_patch.c \ gb_proxy_tlli.c \ gb_proxy_peer.c \ gprs_gb_parse.c \ gprs_llc_parse.c \ crc24.c \ gprs_utils.c \ $(NULL) osmo_gbproxy_LDADD = \ $(OSMO_LIBS) \ -lrt \ $(NULL) osmo_sgsn_SOURCES = \ gprs_gmm.c \ gprs_sgsn.c \ gprs_sndcp.c \ gprs_sndcp_comp.c \ gprs_sndcp_dcomp.c \ gprs_sndcp_pcomp.c \ gprs_sndcp_vty.c \ gprs_sndcp_xid.c \ sgsn_main.c \ sgsn_vty.c \ sgsn_libgtp.c \ gprs_llc.c \ gprs_llc_parse.c \ gprs_llc_vty.c \ crc24.c \ sgsn_ctrl.c \ sgsn_auth.c \ gprs_subscriber.c \ gprs_utils.c \ sgsn_cdr.c \ sgsn_ares.c \ slhc.c \ gprs_llc_xid.c \ v42bis.c \ gsup_client.c \ oap_client.c \ $(NULL) osmo_sgsn_LDADD = \ $(OSMO_LIBS) \ $(LIBOSMOABIS_LIBS) \ $(LIBCARES_LIBS) \ $(LIBGTP_LIBS) \ -lrt \ -lm \ $(NULL) if BUILD_IU osmo_sgsn_LDADD += \ $(LIBOSMOSIGTRAN_LIBS) \ $(LIBOSMORANAP_LIBS) \ $(LIBASN1C_LIBS) \ $(NULL) endif osmo_gtphub_SOURCES = \ gtphub_main.c \ gtphub.c \ gtphub_sock.c \ gtphub_ares.c \ gtphub_vty.c \ sgsn_ares.c \ gprs_utils.c \ $(NULL) osmo_gtphub_LDADD = \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOGSM_LIBS) \ $(LIBOSMOVTY_LIBS) \ $(LIBCARES_LIBS) \ $(LIBGTP_LIBS) \ $(LIBOSMOSIGTRAN_LIBS) \ -lrt \ $(NULL) osmo-sgsn-1.3.0/src/gprs/crc24.c000066400000000000000000000101271327264017000162660ustar00rootroot00000000000000/* GPRS LLC CRC-24 Implementation */ /* (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 /* CRC24 table - FCS */ static const uint32_t tbl_crc24[256] = { 0x00000000, 0x00d6a776, 0x00f64557, 0x0020e221, 0x00b78115, 0x00612663, 0x0041c442, 0x00976334, 0x00340991, 0x00e2aee7, 0x00c24cc6, 0x0014ebb0, 0x00838884, 0x00552ff2, 0x0075cdd3, 0x00a36aa5, 0x00681322, 0x00beb454, 0x009e5675, 0x0048f103, 0x00df9237, 0x00093541, 0x0029d760, 0x00ff7016, 0x005c1ab3, 0x008abdc5, 0x00aa5fe4, 0x007cf892, 0x00eb9ba6, 0x003d3cd0, 0x001ddef1, 0x00cb7987, 0x00d02644, 0x00068132, 0x00266313, 0x00f0c465, 0x0067a751, 0x00b10027, 0x0091e206, 0x00474570, 0x00e42fd5, 0x003288a3, 0x00126a82, 0x00c4cdf4, 0x0053aec0, 0x008509b6, 0x00a5eb97, 0x00734ce1, 0x00b83566, 0x006e9210, 0x004e7031, 0x0098d747, 0x000fb473, 0x00d91305, 0x00f9f124, 0x002f5652, 0x008c3cf7, 0x005a9b81, 0x007a79a0, 0x00acded6, 0x003bbde2, 0x00ed1a94, 0x00cdf8b5, 0x001b5fc3, 0x00fb4733, 0x002de045, 0x000d0264, 0x00dba512, 0x004cc626, 0x009a6150, 0x00ba8371, 0x006c2407, 0x00cf4ea2, 0x0019e9d4, 0x00390bf5, 0x00efac83, 0x0078cfb7, 0x00ae68c1, 0x008e8ae0, 0x00582d96, 0x00935411, 0x0045f367, 0x00651146, 0x00b3b630, 0x0024d504, 0x00f27272, 0x00d29053, 0x00043725, 0x00a75d80, 0x0071faf6, 0x005118d7, 0x0087bfa1, 0x0010dc95, 0x00c67be3, 0x00e699c2, 0x00303eb4, 0x002b6177, 0x00fdc601, 0x00dd2420, 0x000b8356, 0x009ce062, 0x004a4714, 0x006aa535, 0x00bc0243, 0x001f68e6, 0x00c9cf90, 0x00e92db1, 0x003f8ac7, 0x00a8e9f3, 0x007e4e85, 0x005eaca4, 0x00880bd2, 0x00437255, 0x0095d523, 0x00b53702, 0x00639074, 0x00f4f340, 0x00225436, 0x0002b617, 0x00d41161, 0x00777bc4, 0x00a1dcb2, 0x00813e93, 0x005799e5, 0x00c0fad1, 0x00165da7, 0x0036bf86, 0x00e018f0, 0x00ad85dd, 0x007b22ab, 0x005bc08a, 0x008d67fc, 0x001a04c8, 0x00cca3be, 0x00ec419f, 0x003ae6e9, 0x00998c4c, 0x004f2b3a, 0x006fc91b, 0x00b96e6d, 0x002e0d59, 0x00f8aa2f, 0x00d8480e, 0x000eef78, 0x00c596ff, 0x00133189, 0x0033d3a8, 0x00e574de, 0x007217ea, 0x00a4b09c, 0x008452bd, 0x0052f5cb, 0x00f19f6e, 0x00273818, 0x0007da39, 0x00d17d4f, 0x00461e7b, 0x0090b90d, 0x00b05b2c, 0x0066fc5a, 0x007da399, 0x00ab04ef, 0x008be6ce, 0x005d41b8, 0x00ca228c, 0x001c85fa, 0x003c67db, 0x00eac0ad, 0x0049aa08, 0x009f0d7e, 0x00bfef5f, 0x00694829, 0x00fe2b1d, 0x00288c6b, 0x00086e4a, 0x00dec93c, 0x0015b0bb, 0x00c317cd, 0x00e3f5ec, 0x0035529a, 0x00a231ae, 0x007496d8, 0x005474f9, 0x0082d38f, 0x0021b92a, 0x00f71e5c, 0x00d7fc7d, 0x00015b0b, 0x0096383f, 0x00409f49, 0x00607d68, 0x00b6da1e, 0x0056c2ee, 0x00806598, 0x00a087b9, 0x007620cf, 0x00e143fb, 0x0037e48d, 0x001706ac, 0x00c1a1da, 0x0062cb7f, 0x00b46c09, 0x00948e28, 0x0042295e, 0x00d54a6a, 0x0003ed1c, 0x00230f3d, 0x00f5a84b, 0x003ed1cc, 0x00e876ba, 0x00c8949b, 0x001e33ed, 0x008950d9, 0x005ff7af, 0x007f158e, 0x00a9b2f8, 0x000ad85d, 0x00dc7f2b, 0x00fc9d0a, 0x002a3a7c, 0x00bd5948, 0x006bfe3e, 0x004b1c1f, 0x009dbb69, 0x0086e4aa, 0x005043dc, 0x0070a1fd, 0x00a6068b, 0x003165bf, 0x00e7c2c9, 0x00c720e8, 0x0011879e, 0x00b2ed3b, 0x00644a4d, 0x0044a86c, 0x00920f1a, 0x00056c2e, 0x00d3cb58, 0x00f32979, 0x00258e0f, 0x00eef788, 0x003850fe, 0x0018b2df, 0x00ce15a9, 0x0059769d, 0x008fd1eb, 0x00af33ca, 0x007994bc, 0x00dafe19, 0x000c596f, 0x002cbb4e, 0x00fa1c38, 0x006d7f0c, 0x00bbd87a, 0x009b3a5b, 0x004d9d2d }; #define INIT_CRC24 0xffffff uint32_t crc24_calc(uint32_t fcs, uint8_t *cp, unsigned int len) { while (len--) fcs = (fcs >> 8) ^ tbl_crc24[(fcs ^ *cp++) & 0xff]; return fcs; } osmo-sgsn-1.3.0/src/gprs/gb_proxy.c000066400000000000000000001240571327264017000172120ustar00rootroot00000000000000/* NS-over-IP proxy */ /* (C) 2010 by Harald Welte * (C) 2010-2013 by On-Waves * (C) 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern void *tall_bsc_ctx; static const struct rate_ctr_desc global_ctr_description[] = { { "inv-bvci", "Invalid BVC Identifier " }, { "inv-lai", "Invalid Location Area Identifier" }, { "inv-rai", "Invalid Routing Area Identifier " }, { "inv-nsei", "No BVC established for NSEI " }, { "proto-err.bss", "BSSGP protocol error (BSS )" }, { "proto-err.sgsn", "BSSGP protocol error (SGSN)" }, { "not-supp.bss", "Feature not supported (BSS )" }, { "not-supp.sgsn", "Feature not supported (SGSN)" }, { "restart.sgsn", "Restarted RESET procedure (SGSN)" }, { "tx-err.sgsn", "NS Transmission error (SGSN)" }, { "error", "Other error " }, { "mod-peer-err", "Patch error: no peer " }, }; static const struct rate_ctr_group_desc global_ctrg_desc = { .group_name_prefix = "gbproxy:global", .group_description = "GBProxy Global Statistics", .num_ctr = ARRAY_SIZE(global_ctr_description), .ctr_desc = global_ctr_description, .class_id = OSMO_STATS_CLASS_GLOBAL, }; static int gbprox_relay2peer(struct msgb *old_msg, struct gbproxy_peer *peer, uint16_t ns_bvci); static int gbprox_relay2sgsn(struct gbproxy_config *cfg, struct msgb *old_msg, uint16_t ns_bvci, uint16_t sgsn_nsei); static void gbproxy_reset_imsi_acquisition(struct gbproxy_link_info* link_info); static int check_peer_nsei(struct gbproxy_peer *peer, uint16_t nsei) { if (peer->nsei != nsei) { LOGP(DGPRS, LOGL_NOTICE, "Peer entry doesn't match current NSEI " "BVCI=%u via NSEI=%u (expected NSEI=%u)\n", peer->bvci, nsei, peer->nsei); rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_INV_NSEI]); return 0; } return 1; } /* strip off the NS header */ static void strip_ns_hdr(struct msgb *msg) { int strip_len = msgb_bssgph(msg) - msg->data; msgb_pull(msg, strip_len); } /* Transmit Chapter 9.2.10 Identity Request */ static void gprs_put_identity_req(struct msgb *msg, uint8_t id_type) { struct gsm48_hdr *gh; id_type &= GSM_MI_TYPE_MASK; gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); gh->proto_discr = GSM48_PDISC_MM_GPRS; gh->msg_type = GSM48_MT_GMM_ID_REQ; gh->data[0] = id_type; } /* Transmit Chapter 9.4.6.2 Detach Accept (mobile originated detach) */ static void gprs_put_mo_detach_acc(struct msgb *msg) { struct gsm48_hdr *gh; gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); gh->proto_discr = GSM48_PDISC_MM_GPRS; gh->msg_type = GSM48_MT_GMM_DETACH_ACK; gh->data[0] = 0; /* no force to standby */ } static void gprs_push_llc_ui(struct msgb *msg, int is_uplink, unsigned sapi, unsigned nu) { const uint8_t e_bit = 0; const uint8_t pm_bit = 1; const uint8_t cr_bit = is_uplink ? 0 : 1; uint8_t *llc; uint8_t *fcs_field; uint32_t fcs; nu &= 0x01ff; /* 9 Bit */ llc = msgb_push(msg, 3); llc[0] = (cr_bit << 6) | (sapi & 0x0f); llc[1] = 0xc0 | (nu >> 6); /* UI frame */ llc[2] = (nu << 2) | ((e_bit & 1) << 1) | (pm_bit & 1); fcs = gprs_llc_fcs(llc, msgb_length(msg)); fcs_field = msgb_put(msg, 3); fcs_field[0] = (uint8_t)(fcs >> 0); fcs_field[1] = (uint8_t)(fcs >> 8); fcs_field[2] = (uint8_t)(fcs >> 16); } static void gprs_push_bssgp_dl_unitdata(struct msgb *msg, uint32_t tlli) { struct bssgp_ud_hdr *budh; uint8_t *llc = msgb_data(msg); size_t llc_size = msgb_length(msg); const size_t llc_ie_hdr_size = 3; const uint8_t qos_profile[] = {0x00, 0x50, 0x20}; /* hard-coded */ const uint8_t lifetime[] = {0x02, 0x58}; /* 6s hard-coded */ const size_t bssgp_overhead = sizeof(*budh) + TVLV_GROSS_LEN(sizeof(lifetime)) + llc_ie_hdr_size; uint8_t *ie; uint32_t tlli_be = htonl(tlli); budh = (struct bssgp_ud_hdr *)msgb_push(msg, bssgp_overhead); budh->pdu_type = BSSGP_PDUT_DL_UNITDATA; memcpy(&budh->tlli, &tlli_be, sizeof(budh->tlli)); memcpy(&budh->qos_profile, qos_profile, sizeof(budh->qos_profile)); ie = budh->data; tvlv_put(ie, BSSGP_IE_PDU_LIFETIME, sizeof(lifetime), lifetime); ie += TVLV_GROSS_LEN(sizeof(lifetime)); /* Note: Add alignment before the LLC IE if inserting other IE */ *(ie++) = BSSGP_IE_LLC_PDU; *(ie++) = llc_size / 256; *(ie++) = llc_size % 256; OSMO_ASSERT(ie == llc); msgb_bssgph(msg) = (uint8_t *)budh; msgb_tlli(msg) = tlli; } /* update peer according to the BSS message */ static void gbprox_update_current_raid(uint8_t *raid_enc, struct gbproxy_peer *peer, const char *log_text) { struct gbproxy_patch_state *state = &peer->patch_state; const struct osmo_plmn_id old_plmn = state->local_plmn; struct gprs_ra_id raid; if (!raid_enc) return; gsm48_parse_ra(&raid, raid_enc); /* save source side MCC/MNC */ if (!peer->cfg->core_plmn.mcc || raid.mcc == peer->cfg->core_plmn.mcc) { state->local_plmn.mcc = 0; } else { state->local_plmn.mcc = raid.mcc; } if (!peer->cfg->core_plmn.mnc || !osmo_mnc_cmp(raid.mnc, raid.mnc_3_digits, peer->cfg->core_plmn.mnc, peer->cfg->core_plmn.mnc_3_digits)) { state->local_plmn.mnc = 0; state->local_plmn.mnc_3_digits = false; } else { state->local_plmn.mnc = raid.mnc; state->local_plmn.mnc_3_digits = raid.mnc_3_digits; } if (osmo_plmn_cmp(&old_plmn, &state->local_plmn)) LOGP(DGPRS, LOGL_NOTICE, "Patching RAID %sactivated, msg: %s, " "local: %s, core: %s\n", state->local_plmn.mcc || state->local_plmn.mnc ? "" : "de", log_text, osmo_plmn_name(&state->local_plmn), osmo_plmn_name2(&peer->cfg->core_plmn)); } uint32_t gbproxy_make_bss_ptmsi(struct gbproxy_peer *peer, uint32_t sgsn_ptmsi) { uint32_t bss_ptmsi; int max_retries = 23, rc = 0; if (!peer->cfg->patch_ptmsi) { bss_ptmsi = sgsn_ptmsi; } else { do { rc = osmo_get_rand_id((uint8_t *) &bss_ptmsi, sizeof(bss_ptmsi)); if (rc < 0) { bss_ptmsi = GSM_RESERVED_TMSI; break; } bss_ptmsi = bss_ptmsi | 0xC0000000; if (gbproxy_link_info_by_ptmsi(peer, bss_ptmsi)) bss_ptmsi = GSM_RESERVED_TMSI; } while (bss_ptmsi == GSM_RESERVED_TMSI && max_retries--); } if (bss_ptmsi == GSM_RESERVED_TMSI) LOGP(DGPRS, LOGL_ERROR, "Failed to allocate a BSS P-TMSI: %d (%s)\n", rc, strerror(-rc)); return bss_ptmsi; } uint32_t gbproxy_make_sgsn_tlli(struct gbproxy_peer *peer, struct gbproxy_link_info *link_info, uint32_t bss_tlli) { uint32_t sgsn_tlli; int max_retries = 23, rc = 0; if (!peer->cfg->patch_ptmsi) { sgsn_tlli = bss_tlli; } else if (link_info->sgsn_tlli.ptmsi != GSM_RESERVED_TMSI && gprs_tlli_type(bss_tlli) == TLLI_FOREIGN) { sgsn_tlli = gprs_tmsi2tlli(link_info->sgsn_tlli.ptmsi, TLLI_FOREIGN); } else if (link_info->sgsn_tlli.ptmsi != GSM_RESERVED_TMSI && gprs_tlli_type(bss_tlli) == TLLI_LOCAL) { sgsn_tlli = gprs_tmsi2tlli(link_info->sgsn_tlli.ptmsi, TLLI_LOCAL); } else { do { /* create random TLLI, 0b01111xxx... */ rc = osmo_get_rand_id((uint8_t *) &sgsn_tlli, sizeof(sgsn_tlli)); if (rc < 0) { sgsn_tlli = 0; break; } sgsn_tlli = (sgsn_tlli & 0x7fffffff) | 0x78000000; if (gbproxy_link_info_by_any_sgsn_tlli(peer, sgsn_tlli)) sgsn_tlli = 0; } while (!sgsn_tlli && max_retries--); } if (!sgsn_tlli) LOGP(DGPRS, LOGL_ERROR, "Failed to allocate an SGSN TLLI: %d (%s)\n", rc, strerror(-rc)); return sgsn_tlli; } void gbproxy_reset_link(struct gbproxy_link_info *link_info) { gbproxy_reset_imsi_acquisition(link_info); } /* Returns != 0 iff IMSI acquisition was in progress */ static int gbproxy_restart_imsi_acquisition(struct gbproxy_link_info* link_info) { int in_progress = 0; if (!link_info) return 0; if (link_info->imsi_acq_pending) in_progress = 1; gbproxy_link_info_discard_messages(link_info); link_info->imsi_acq_pending = 0; return in_progress; } static void gbproxy_reset_imsi_acquisition(struct gbproxy_link_info* link_info) { gbproxy_restart_imsi_acquisition(link_info); link_info->vu_gen_tx_bss = GBPROXY_INIT_VU_GEN_TX; } static int gbproxy_flush_stored_messages(struct gbproxy_peer *peer, struct msgb *msg, time_t now, struct gbproxy_link_info* link_info, struct gprs_gb_parse_context *parse_ctx) { int rc; struct msgb *stored_msg; /* Got identity response with IMSI, assuming the request had * been generated by the gbproxy */ LOGP(DLLC, LOGL_DEBUG, "NSEI=%d(BSS) IMSI acquisition succeeded, " "flushing stored messages\n", msgb_nsei(msg)); /* Patch and flush stored messages towards the SGSN */ while ((stored_msg = msgb_dequeue(&link_info->stored_msgs))) { struct gprs_gb_parse_context tmp_parse_ctx = {0}; tmp_parse_ctx.to_bss = 0; tmp_parse_ctx.peer_nsei = msgb_nsei(stored_msg); int len_change = 0; gprs_gb_parse_bssgp(msgb_bssgph(stored_msg), msgb_bssgp_len(stored_msg), &tmp_parse_ctx); gbproxy_patch_bssgp(msg, msgb_bssgph(stored_msg), msgb_bssgp_len(stored_msg), peer, link_info, &len_change, &tmp_parse_ctx); rc = gbproxy_update_link_state_after(peer, link_info, now, &tmp_parse_ctx); if (rc == 1) { LOGP(DLLC, LOGL_NOTICE, "link_info deleted while flushing stored messages\n"); msgb_free(stored_msg); return -1; } rc = gbprox_relay2sgsn(peer->cfg, stored_msg, msgb_bvci(msg), link_info->sgsn_nsei); if (rc < 0) LOGP(DLLC, LOGL_ERROR, "NSEI=%d(BSS) failed to send stored message " "(%s)\n", msgb_nsei(msg), parse_ctx->llc_msg_name ? parse_ctx->llc_msg_name : "BSSGP"); msgb_free(stored_msg); } return 0; } static int gbproxy_gsm48_to_peer(struct gbproxy_peer *peer, struct gbproxy_link_info* link_info, uint16_t bvci, struct msgb *msg /* Takes msg ownership */) { int rc; /* Workaround to avoid N(U) collisions and to enable a restart * of the IMSI acquisition procedure. This will work unless the * SGSN has an initial V(UT) within [256-32, 256+n_retries] * (see GSM 04.64, 8.4.2). */ gprs_push_llc_ui(msg, 0, GPRS_SAPI_GMM, link_info->vu_gen_tx_bss); link_info->vu_gen_tx_bss = (link_info->vu_gen_tx_bss + 1) % 512; gprs_push_bssgp_dl_unitdata(msg, link_info->tlli.current); rc = gbprox_relay2peer(msg, peer, bvci); msgb_free(msg); return rc; } static void gbproxy_acquire_imsi(struct gbproxy_peer *peer, struct gbproxy_link_info* link_info, uint16_t bvci) { struct msgb *idreq_msg; /* Send IDENT REQ */ idreq_msg = gsm48_msgb_alloc_name("GSM 04.08 ACQ IMSI"); gprs_put_identity_req(idreq_msg, GSM_MI_TYPE_IMSI); gbproxy_gsm48_to_peer(peer, link_info, bvci, idreq_msg); } static void gbproxy_tx_detach_acc(struct gbproxy_peer *peer, struct gbproxy_link_info* link_info, uint16_t bvci) { struct msgb *detacc_msg; /* Send DETACH ACC */ detacc_msg = gsm48_msgb_alloc_name("GSM 04.08 DET ACC"); gprs_put_mo_detach_acc(detacc_msg); gbproxy_gsm48_to_peer(peer, link_info, bvci, detacc_msg); } /* Return != 0 iff msg still needs to be processed */ static int gbproxy_imsi_acquisition(struct gbproxy_peer *peer, struct msgb *msg, time_t now, struct gbproxy_link_info* link_info, struct gprs_gb_parse_context *parse_ctx) { struct msgb *stored_msg; if (!link_info) return 1; if (!link_info->imsi_acq_pending && link_info->imsi_len > 0) return 1; if (parse_ctx->g48_hdr) switch (parse_ctx->g48_hdr->msg_type) { case GSM48_MT_GMM_RA_UPD_REQ: case GSM48_MT_GMM_ATTACH_REQ: if (gbproxy_restart_imsi_acquisition(link_info)) { LOGP(DLLC, LOGL_INFO, "NSEI=%d(BSS) IMSI acquisition was in progress " "when receiving an %s.\n", msgb_nsei(msg), parse_ctx->llc_msg_name); } break; case GSM48_MT_GMM_DETACH_REQ: /* Nothing has been sent to the SGSN yet */ if (link_info->imsi_acq_pending) { LOGP(DLLC, LOGL_INFO, "NSEI=%d(BSS) IMSI acquisition was in progress " "when receiving a DETACH_REQ.\n", msgb_nsei(msg)); } if (!parse_ctx->invalidate_tlli) { LOGP(DLLC, LOGL_INFO, "NSEI=%d(BSS) IMSI not yet acquired, " "faking a DETACH_ACC.\n", msgb_nsei(msg)); gbproxy_tx_detach_acc(peer, link_info, msgb_bvci(msg)); parse_ctx->invalidate_tlli = 1; } gbproxy_reset_imsi_acquisition(link_info); gbproxy_update_link_state_after(peer, link_info, now, parse_ctx); return 0; } if (link_info->imsi_acq_pending && link_info->imsi_len > 0) { int is_ident_resp = parse_ctx->g48_hdr && gsm48_hdr_pdisc(parse_ctx->g48_hdr) == GSM48_PDISC_MM_GPRS && gsm48_hdr_msg_type(parse_ctx->g48_hdr) == GSM48_MT_GMM_ID_RESP; /* The IMSI is now available. If flushing the messages fails, * then link_info has been deleted and we should return * immediately. */ if (gbproxy_flush_stored_messages(peer, msg, now, link_info, parse_ctx) < 0) return 0; gbproxy_reset_imsi_acquisition(link_info); /* This message is most probably the response to the ident * request sent by gbproxy_acquire_imsi(). Don't forward it to * the SGSN. */ return !is_ident_resp; } /* The message cannot be processed since the IMSI is still missing */ /* Enqueue unpatched messages */ LOGP(DLLC, LOGL_INFO, "NSEI=%d(BSS) IMSI acquisition in progress, " "storing message (%s)\n", msgb_nsei(msg), parse_ctx->llc_msg_name ? parse_ctx->llc_msg_name : "BSSGP"); stored_msg = gprs_msgb_copy(msg, "process_bssgp_ul"); msgb_enqueue(&link_info->stored_msgs, stored_msg); if (!link_info->imsi_acq_pending) { LOGP(DLLC, LOGL_INFO, "NSEI=%d(BSS) IMSI is required but not available, " "initiating identification procedure (%s)\n", msgb_nsei(msg), parse_ctx->llc_msg_name ? parse_ctx->llc_msg_name : "BSSGP"); gbproxy_acquire_imsi(peer, link_info, msgb_bvci(msg)); /* There is no explicit retransmission handling, the * implementation relies on the MS doing proper retransmissions * of the triggering message instead */ link_info->imsi_acq_pending = 1; } return 0; } struct gbproxy_peer *gbproxy_find_peer(struct gbproxy_config *cfg, struct msgb *msg, struct gprs_gb_parse_context *parse_ctx) { struct gbproxy_peer *peer = NULL; if (msgb_bvci(msg) >= 2) peer = gbproxy_peer_by_bvci(cfg, msgb_bvci(msg)); if (!peer && !parse_ctx->to_bss) peer = gbproxy_peer_by_nsei(cfg, msgb_nsei(msg)); if (!peer) peer = gbproxy_peer_by_bssgp_tlv(cfg, &parse_ctx->bssgp_tp); if (!peer) { LOGP(DLLC, LOGL_INFO, "NSEI=%d(%s) patching: didn't find peer for message, " "PDU %d\n", msgb_nsei(msg), parse_ctx->to_bss ? "BSS" : "SGSN", parse_ctx->pdu_type); /* Increment counter */ rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_PATCH_PEER_ERR]); } return peer; } /* patch BSSGP message */ static int gbprox_process_bssgp_ul(struct gbproxy_config *cfg, struct msgb *msg, struct gbproxy_peer *peer) { struct gprs_gb_parse_context parse_ctx = {0}; int rc; int len_change = 0; time_t now; struct timespec ts = {0,}; struct gbproxy_link_info *link_info = NULL; uint32_t sgsn_nsei = cfg->nsip_sgsn_nsei; if (!cfg->core_plmn.mcc && !cfg->core_plmn.mnc && !cfg->core_apn && !cfg->acquire_imsi && !cfg->patch_ptmsi && !cfg->route_to_sgsn2) return 1; parse_ctx.to_bss = 0; parse_ctx.peer_nsei = msgb_nsei(msg); /* Parse BSSGP/LLC */ rc = gprs_gb_parse_bssgp(msgb_bssgph(msg), msgb_bssgp_len(msg), &parse_ctx); if (!rc && !parse_ctx.need_decryption) { LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(BSS) patching: failed to parse invalid %s message\n", msgb_nsei(msg), gprs_gb_message_name(&parse_ctx, "NS_UNITDATA")); gprs_gb_log_parse_context(LOGL_NOTICE, &parse_ctx, "NS_UNITDATA"); LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u(BSS) invalid message was: %s\n", msgb_nsei(msg), msgb_hexdump(msg)); return 0; } /* Get peer */ if (!peer) peer = gbproxy_find_peer(cfg, msg, &parse_ctx); if (!peer) return 0; clock_gettime(CLOCK_MONOTONIC, &ts); now = ts.tv_sec; gbprox_update_current_raid(parse_ctx.bssgp_raid_enc, peer, parse_ctx.llc_msg_name); gprs_gb_log_parse_context(LOGL_DEBUG, &parse_ctx, "NS_UNITDATA"); link_info = gbproxy_update_link_state_ul(peer, now, &parse_ctx); if (parse_ctx.g48_hdr) { switch (parse_ctx.g48_hdr->msg_type) { case GSM48_MT_GMM_ATTACH_REQ: rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_ATTACH_REQS]); break; case GSM48_MT_GMM_DETACH_REQ: rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_DETACH_REQS]); break; case GSM48_MT_GMM_ATTACH_COMPL: rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_ATTACH_COMPLS]); break; case GSM48_MT_GMM_RA_UPD_REQ: rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_RA_UPD_REQS]); break; case GSM48_MT_GMM_RA_UPD_COMPL: rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_RA_UPD_COMPLS]); break; case GSM48_MT_GMM_STATUS: rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_GMM_STATUS_BSS]); break; case GSM48_MT_GSM_ACT_PDP_REQ: rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_PDP_ACT_REQS]); break; case GSM48_MT_GSM_DEACT_PDP_REQ: rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_PDP_DEACT_REQS]); break; default: break; } } if (link_info && cfg->route_to_sgsn2) { if (cfg->acquire_imsi && link_info->imsi_len == 0) sgsn_nsei = 0xffff; else if (gbproxy_imsi_matches(cfg, GBPROX_MATCH_ROUTING, link_info)) sgsn_nsei = cfg->nsip_sgsn2_nsei; } if (link_info) link_info->sgsn_nsei = sgsn_nsei; /* Handle IMSI acquisition */ if (cfg->acquire_imsi) { rc = gbproxy_imsi_acquisition(peer, msg, now, link_info, &parse_ctx); if (rc <= 0) return rc; } gbproxy_patch_bssgp(msg, msgb_bssgph(msg), msgb_bssgp_len(msg), peer, link_info, &len_change, &parse_ctx); gbproxy_update_link_state_after(peer, link_info, now, &parse_ctx); if (sgsn_nsei != cfg->nsip_sgsn_nsei) { /* Send message directly to the selected SGSN */ rc = gbprox_relay2sgsn(cfg, msg, msgb_bvci(msg), sgsn_nsei); /* Don't let the calling code handle the transmission */ return 0; } return 1; } /* patch BSSGP message to use core_plmn.mcc/mnc on the SGSN side */ static void gbprox_process_bssgp_dl(struct gbproxy_config *cfg, struct msgb *msg, struct gbproxy_peer *peer) { struct gprs_gb_parse_context parse_ctx = {0}; int rc; int len_change = 0; time_t now; struct timespec ts = {0,}; struct gbproxy_link_info *link_info = NULL; if (!cfg->core_plmn.mcc && !cfg->core_plmn.mnc && !cfg->core_apn && !cfg->acquire_imsi && !cfg->patch_ptmsi && !cfg->route_to_sgsn2) return; parse_ctx.to_bss = 1; parse_ctx.peer_nsei = msgb_nsei(msg); rc = gprs_gb_parse_bssgp(msgb_bssgph(msg), msgb_bssgp_len(msg), &parse_ctx); if (!rc && !parse_ctx.need_decryption) { LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(SGSN) patching: failed to parse invalid %s message\n", msgb_nsei(msg), gprs_gb_message_name(&parse_ctx, "NS_UNITDATA")); gprs_gb_log_parse_context(LOGL_NOTICE, &parse_ctx, "NS_UNITDATA"); LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u(SGSN) invalid message was: %s\n", msgb_nsei(msg), msgb_hexdump(msg)); return; } /* Get peer */ if (!peer) peer = gbproxy_find_peer(cfg, msg, &parse_ctx); if (!peer) return; clock_gettime(CLOCK_MONOTONIC, &ts); now = ts.tv_sec; if (parse_ctx.g48_hdr) { switch (parse_ctx.g48_hdr->msg_type) { case GSM48_MT_GMM_ATTACH_ACK: rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_ATTACH_ACKS]); break; case GSM48_MT_GMM_ATTACH_REJ: rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_ATTACH_REJS]); break; case GSM48_MT_GMM_DETACH_ACK: rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_DETACH_ACKS]); break; case GSM48_MT_GMM_RA_UPD_ACK: rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_RA_UPD_ACKS]); break; case GSM48_MT_GMM_RA_UPD_REJ: rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_RA_UPD_REJS]); break; case GSM48_MT_GMM_STATUS: rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_GMM_STATUS_SGSN]); break; case GSM48_MT_GSM_ACT_PDP_ACK: rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_PDP_ACT_ACKS]); break; case GSM48_MT_GSM_ACT_PDP_REJ: rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_PDP_ACT_REJS]); break; case GSM48_MT_GSM_DEACT_PDP_ACK: rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_PDP_DEACT_ACKS]); break; default: break; } } gprs_gb_log_parse_context(LOGL_DEBUG, &parse_ctx, "NS_UNITDATA"); link_info = gbproxy_update_link_state_dl(peer, now, &parse_ctx); gbproxy_patch_bssgp(msg, msgb_bssgph(msg), msgb_bssgp_len(msg), peer, link_info, &len_change, &parse_ctx); gbproxy_update_link_state_after(peer, link_info, now, &parse_ctx); return; } /* feed a message down the NS-VC associated with the specified peer */ static int gbprox_relay2sgsn(struct gbproxy_config *cfg, struct msgb *old_msg, uint16_t ns_bvci, uint16_t sgsn_nsei) { /* create a copy of the message so the old one can * be free()d safely when we return from gbprox_rcvmsg() */ struct msgb *msg = gprs_msgb_copy(old_msg, "msgb_relay2sgsn"); int rc; DEBUGP(DGPRS, "NSEI=%u proxying BTS->SGSN (NS_BVCI=%u, NSEI=%u)\n", msgb_nsei(msg), ns_bvci, sgsn_nsei); msgb_bvci(msg) = ns_bvci; msgb_nsei(msg) = sgsn_nsei; strip_ns_hdr(msg); rc = gprs_ns_sendmsg(bssgp_nsi, msg); if (rc < 0) rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_TX_ERR_SGSN]); return rc; } /* feed a message down the NS-VC associated with the specified peer */ static int gbprox_relay2peer(struct msgb *old_msg, struct gbproxy_peer *peer, uint16_t ns_bvci) { /* create a copy of the message so the old one can * be free()d safely when we return from gbprox_rcvmsg() */ struct msgb *msg = gprs_msgb_copy(old_msg, "msgb_relay2peer"); int rc; DEBUGP(DGPRS, "NSEI=%u proxying SGSN->BSS (NS_BVCI=%u, NSEI=%u)\n", msgb_nsei(msg), ns_bvci, peer->nsei); msgb_bvci(msg) = ns_bvci; msgb_nsei(msg) = peer->nsei; /* Strip the old NS header, it will be replaced with a new one */ strip_ns_hdr(msg); rc = gprs_ns_sendmsg(bssgp_nsi, msg); if (rc < 0) rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_TX_ERR]); return rc; } static int block_unblock_peer(struct gbproxy_config *cfg, uint16_t ptp_bvci, uint8_t pdu_type) { struct gbproxy_peer *peer; peer = gbproxy_peer_by_bvci(cfg, ptp_bvci); if (!peer) { LOGP(DGPRS, LOGL_ERROR, "BVCI=%u: Cannot find BSS\n", ptp_bvci); rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_INV_BVCI]); return -ENOENT; } switch (pdu_type) { case BSSGP_PDUT_BVC_BLOCK_ACK: peer->blocked = 1; rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_BLOCKED]); break; case BSSGP_PDUT_BVC_UNBLOCK_ACK: peer->blocked = 0; rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_UNBLOCKED]); break; default: break; } return 0; } /* Send a message to a peer identified by ptp_bvci but using ns_bvci * in the NS hdr */ static int gbprox_relay2bvci(struct gbproxy_config *cfg, struct msgb *msg, uint16_t ptp_bvci, uint16_t ns_bvci) { struct gbproxy_peer *peer; peer = gbproxy_peer_by_bvci(cfg, ptp_bvci); if (!peer) { LOGP(DGPRS, LOGL_ERROR, "BVCI=%u: Cannot find BSS\n", ptp_bvci); rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_INV_BVCI]); return -ENOENT; } return gbprox_relay2peer(msg, peer, ns_bvci); } int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx) { return 0; } /* Receive an incoming PTP message from a BSS-side NS-VC */ static int gbprox_rx_ptp_from_bss(struct gbproxy_config *cfg, struct msgb *msg, uint16_t nsei, uint16_t nsvci, uint16_t ns_bvci) { struct gbproxy_peer *peer; struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg); uint8_t pdu_type = bgph->pdu_type; int rc; peer = gbproxy_peer_by_bvci(cfg, ns_bvci); if (!peer) { LOGP(DGPRS, LOGL_NOTICE, "Didn't find peer for " "BVCI=%u for PTP message from NSVC=%u/NSEI=%u (BSS), " "discarding message\n", ns_bvci, nsvci, nsei); return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, &ns_bvci, msg); } check_peer_nsei(peer, nsei); rc = gbprox_process_bssgp_ul(cfg, msg, peer); if (!rc) return 0; switch (pdu_type) { case BSSGP_PDUT_FLOW_CONTROL_BVC: if (!cfg->route_to_sgsn2) break; /* Send a copy to the secondary SGSN */ gbprox_relay2sgsn(cfg, msg, ns_bvci, cfg->nsip_sgsn2_nsei); break; default: break; } return gbprox_relay2sgsn(cfg, msg, ns_bvci, cfg->nsip_sgsn_nsei); } /* Receive an incoming PTP message from a SGSN-side NS-VC */ static int gbprox_rx_ptp_from_sgsn(struct gbproxy_config *cfg, struct msgb *msg, uint16_t nsei, uint16_t nsvci, uint16_t ns_bvci) { struct gbproxy_peer *peer; struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg); uint8_t pdu_type = bgph->pdu_type; peer = gbproxy_peer_by_bvci(cfg, ns_bvci); /* Send status messages before patching */ if (!peer) { LOGP(DGPRS, LOGL_INFO, "Didn't find peer for " "BVCI=%u for message from NSVC=%u/NSEI=%u (SGSN)\n", ns_bvci, nsvci, nsei); rate_ctr_inc(&cfg->ctrg-> ctr[GBPROX_GLOB_CTR_INV_BVCI]); return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, &ns_bvci, msg); } if (peer->blocked) { LOGP(DGPRS, LOGL_NOTICE, "Dropping PDU for " "blocked BVCI=%u via NSVC=%u/NSEI=%u\n", ns_bvci, nsvci, nsei); rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_DROPPED]); return bssgp_tx_status(BSSGP_CAUSE_BVCI_BLOCKED, &ns_bvci, msg); } switch (pdu_type) { case BSSGP_PDUT_FLOW_CONTROL_BVC_ACK: case BSSGP_PDUT_BVC_BLOCK_ACK: case BSSGP_PDUT_BVC_UNBLOCK_ACK: if (cfg->route_to_sgsn2 && nsei == cfg->nsip_sgsn2_nsei) /* Hide ACKs from the secondary SGSN, the primary SGSN * is responsible to send them. */ return 0; break; default: break; } /* Optionally patch the message */ gbprox_process_bssgp_dl(cfg, msg, peer); return gbprox_relay2peer(msg, peer, ns_bvci); } /* Receive an incoming signalling message from a BSS-side NS-VC */ static int gbprox_rx_sig_from_bss(struct gbproxy_config *cfg, struct msgb *msg, uint16_t nsei, uint16_t ns_bvci) { struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg); struct tlv_parsed tp; uint8_t pdu_type = bgph->pdu_type; int data_len = msgb_bssgp_len(msg) - sizeof(*bgph); struct gbproxy_peer *from_peer = NULL; struct gprs_ra_id raid; int copy_to_sgsn2 = 0; int rc; if (ns_bvci != 0 && ns_bvci != 1) { LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u BVCI=%u is not signalling\n", nsei, ns_bvci); return -EINVAL; } /* we actually should never see those two for BVCI == 0, but double-check * just to make sure */ if (pdu_type == BSSGP_PDUT_UL_UNITDATA || pdu_type == BSSGP_PDUT_DL_UNITDATA) { LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u UNITDATA not allowed in " "signalling\n", nsei); return -EINVAL; } bssgp_tlv_parse(&tp, bgph->data, data_len); switch (pdu_type) { case BSSGP_PDUT_SUSPEND: case BSSGP_PDUT_RESUME: /* We implement RAI snooping during SUSPEND/RESUME, since it * establishes a relationsip between BVCI/peer and the routeing * area identification. The snooped information is then used * for routing the {SUSPEND,RESUME}_[N]ACK back to the correct * BSSGP */ if (!TLVP_PRESENT(&tp, BSSGP_IE_ROUTEING_AREA)) goto err_mand_ie; from_peer = gbproxy_peer_by_nsei(cfg, nsei); if (!from_peer) goto err_no_peer; memcpy(from_peer->ra, TLVP_VAL(&tp, BSSGP_IE_ROUTEING_AREA), sizeof(from_peer->ra)); gsm48_parse_ra(&raid, from_peer->ra); LOGP(DGPRS, LOGL_INFO, "NSEI=%u BSSGP SUSPEND/RESUME " "RAI snooping: RAI %s behind BVCI=%u\n", nsei, osmo_rai_name(&raid), from_peer->bvci); /* FIXME: This only supports one BSS per RA */ break; case BSSGP_PDUT_BVC_RESET: /* If we receive a BVC reset on the signalling endpoint, we * don't want the SGSN to reset, as the signalling endpoint * is common for all point-to-point BVCs (and thus all BTS) */ if (TLVP_PRESENT(&tp, BSSGP_IE_BVCI)) { uint16_t bvci = ntohs(tlvp_val16_unal(&tp, BSSGP_IE_BVCI)); LOGP(DGPRS, LOGL_INFO, "NSEI=%u Rx BVC RESET (BVCI=%u)\n", nsei, bvci); if (bvci == 0) { /* FIXME: only do this if SGSN is alive! */ LOGP(DGPRS, LOGL_INFO, "NSEI=%u Tx fake " "BVC RESET ACK of BVCI=0\n", nsei); return bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_RESET_ACK, nsei, 0, ns_bvci); } from_peer = gbproxy_peer_by_bvci(cfg, bvci); if (!from_peer) { /* if a PTP-BVC is reset, and we don't know that * PTP-BVCI yet, we should allocate a new peer */ LOGP(DGPRS, LOGL_INFO, "Allocationg new peer for BVCI=%u via NSEI=%u\n", bvci, nsei); from_peer = gbproxy_peer_alloc(cfg, bvci); OSMO_ASSERT(from_peer); from_peer->nsei = nsei; } if (!check_peer_nsei(from_peer, nsei)) from_peer->nsei = nsei; if (TLVP_PRESENT(&tp, BSSGP_IE_CELL_ID)) { struct gprs_ra_id raid; /* We have a Cell Identifier present in this * PDU, this means we can extend our local * state information about this particular cell * */ memcpy(from_peer->ra, TLVP_VAL(&tp, BSSGP_IE_CELL_ID), sizeof(from_peer->ra)); gsm48_parse_ra(&raid, from_peer->ra); LOGP(DGPRS, LOGL_INFO, "NSEI=%u/BVCI=%u Cell ID %s\n", nsei, bvci, osmo_rai_name(&raid)); } if (cfg->route_to_sgsn2) copy_to_sgsn2 = 1; } break; } /* Normally, we can simply pass on all signalling messages from BSS to * SGSN */ rc = gbprox_process_bssgp_ul(cfg, msg, from_peer); if (!rc) return 0; if (copy_to_sgsn2) gbprox_relay2sgsn(cfg, msg, ns_bvci, cfg->nsip_sgsn2_nsei); return gbprox_relay2sgsn(cfg, msg, ns_bvci, cfg->nsip_sgsn_nsei); err_no_peer: LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(BSS) cannot find peer based on NSEI\n", nsei); rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_INV_NSEI]); return bssgp_tx_status(BSSGP_CAUSE_INV_MAND_INF, NULL, msg); err_mand_ie: LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(BSS) missing mandatory RA IE\n", nsei); rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_PROTO_ERR_BSS]); return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg); } /* Receive paging request from SGSN, we need to relay to proper BSS */ static int gbprox_rx_paging(struct gbproxy_config *cfg, struct msgb *msg, struct tlv_parsed *tp, uint32_t nsei, uint16_t ns_bvci) { struct gbproxy_peer *peer = NULL; int errctr = GBPROX_GLOB_CTR_PROTO_ERR_SGSN; LOGP(DGPRS, LOGL_INFO, "NSEI=%u(SGSN) BSSGP PAGING ", nsei); if (TLVP_PRESENT(tp, BSSGP_IE_BVCI)) { uint16_t bvci = ntohs(tlvp_val16_unal(tp, BSSGP_IE_BVCI)); LOGPC(DGPRS, LOGL_INFO, "routing by BVCI to peer BVCI=%u\n", bvci); errctr = GBPROX_GLOB_CTR_OTHER_ERR; } else if (TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) { peer = gbproxy_peer_by_rai(cfg, TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA)); LOGPC(DGPRS, LOGL_INFO, "routing by RAI to peer BVCI=%u\n", peer ? peer->bvci : -1); errctr = GBPROX_GLOB_CTR_INV_RAI; } else if (TLVP_PRESENT(tp, BSSGP_IE_LOCATION_AREA)) { peer = gbproxy_peer_by_lai(cfg, TLVP_VAL(tp, BSSGP_IE_LOCATION_AREA)); LOGPC(DGPRS, LOGL_INFO, "routing by LAI to peer BVCI=%u\n", peer ? peer->bvci : -1); errctr = GBPROX_GLOB_CTR_INV_LAI; } else LOGPC(DGPRS, LOGL_INFO, "\n"); if (!peer) { LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(SGSN) BSSGP PAGING: " "unable to route, missing IE\n", nsei); rate_ctr_inc(&cfg->ctrg->ctr[errctr]); return -EINVAL; } return gbprox_relay2peer(msg, peer, ns_bvci); } /* Receive an incoming BVC-RESET message from the SGSN */ static int rx_reset_from_sgsn(struct gbproxy_config *cfg, struct msgb *orig_msg, struct msgb *msg, struct tlv_parsed *tp, uint32_t nsei, uint16_t ns_bvci) { struct gbproxy_peer *peer; uint16_t ptp_bvci; if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI)) { rate_ctr_inc(&cfg->ctrg-> ctr[GBPROX_GLOB_CTR_PROTO_ERR_SGSN]); return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, orig_msg); } ptp_bvci = ntohs(tlvp_val16_unal(tp, BSSGP_IE_BVCI)); if (ptp_bvci >= 2) { /* A reset for a PTP BVC was received, forward it to its * respective peer */ peer = gbproxy_peer_by_bvci(cfg, ptp_bvci); if (!peer) { LOGP(DGPRS, LOGL_ERROR, "NSEI=%u BVCI=%u: Cannot find BSS\n", nsei, ptp_bvci); rate_ctr_inc(&cfg->ctrg-> ctr[GBPROX_GLOB_CTR_INV_BVCI]); return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, &ptp_bvci, orig_msg); } return gbprox_relay2peer(msg, peer, ns_bvci); } /* A reset for the Signalling entity has been received * from the SGSN. As the signalling BVCI is shared * among all the BSS's that we multiplex, it needs to * be relayed */ llist_for_each_entry(peer, &cfg->bts_peers, list) gbprox_relay2peer(msg, peer, ns_bvci); return 0; } /* Receive an incoming signalling message from the SGSN-side NS-VC */ static int gbprox_rx_sig_from_sgsn(struct gbproxy_config *cfg, struct msgb *orig_msg, uint32_t nsei, uint16_t ns_bvci) { struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(orig_msg); struct tlv_parsed tp; uint8_t pdu_type = bgph->pdu_type; int data_len; struct gbproxy_peer *peer; uint16_t bvci; struct msgb *msg; int rc = 0; int cause; if (ns_bvci != 0 && ns_bvci != 1) { LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u(SGSN) BVCI=%u is not " "signalling\n", nsei, ns_bvci); /* FIXME: Send proper error message */ return -EINVAL; } /* we actually should never see those two for BVCI == 0, but double-check * just to make sure */ if (pdu_type == BSSGP_PDUT_UL_UNITDATA || pdu_type == BSSGP_PDUT_DL_UNITDATA) { LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u(SGSN) UNITDATA not allowed in " "signalling\n", nsei); return bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, orig_msg); } msg = gprs_msgb_copy(orig_msg, "rx_sig_from_sgsn"); gbprox_process_bssgp_dl(cfg, msg, NULL); /* Update message info */ bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg); data_len = msgb_bssgp_len(orig_msg) - sizeof(*bgph); rc = bssgp_tlv_parse(&tp, bgph->data, data_len); switch (pdu_type) { case BSSGP_PDUT_BVC_RESET: rc = rx_reset_from_sgsn(cfg, msg, orig_msg, &tp, nsei, ns_bvci); break; case BSSGP_PDUT_BVC_RESET_ACK: if (cfg->route_to_sgsn2 && nsei == cfg->nsip_sgsn2_nsei) break; /* fall through */ case BSSGP_PDUT_FLUSH_LL: /* simple case: BVCI IE is mandatory */ if (!TLVP_PRESENT(&tp, BSSGP_IE_BVCI)) goto err_mand_ie; bvci = ntohs(tlvp_val16_unal(&tp, BSSGP_IE_BVCI)); rc = gbprox_relay2bvci(cfg, msg, bvci, ns_bvci); break; case BSSGP_PDUT_PAGING_PS: case BSSGP_PDUT_PAGING_CS: /* process the paging request (LAI/RAI lookup) */ rc = gbprox_rx_paging(cfg, msg, &tp, nsei, ns_bvci); break; case BSSGP_PDUT_STATUS: /* Some exception has occurred */ LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u(SGSN) BSSGP STATUS ", nsei); if (!TLVP_PRESENT(&tp, BSSGP_IE_CAUSE)) { LOGPC(DGPRS, LOGL_NOTICE, "\n"); goto err_mand_ie; } cause = *TLVP_VAL(&tp, BSSGP_IE_CAUSE); LOGPC(DGPRS, LOGL_NOTICE, "cause=0x%02x(%s) ", *TLVP_VAL(&tp, BSSGP_IE_CAUSE), bssgp_cause_str(cause)); if (TLVP_PRESENT(&tp, BSSGP_IE_BVCI)) { bvci = ntohs(tlvp_val16_unal(&tp, BSSGP_IE_BVCI)); LOGPC(DGPRS, LOGL_NOTICE, "BVCI=%u\n", bvci); if (cause == BSSGP_CAUSE_UNKNOWN_BVCI) rc = gbprox_relay2bvci(cfg, msg, bvci, ns_bvci); } else LOGPC(DGPRS, LOGL_NOTICE, "\n"); break; /* those only exist in the SGSN -> BSS direction */ case BSSGP_PDUT_SUSPEND_ACK: case BSSGP_PDUT_SUSPEND_NACK: case BSSGP_PDUT_RESUME_ACK: case BSSGP_PDUT_RESUME_NACK: /* RAI IE is mandatory */ if (!TLVP_PRESENT(&tp, BSSGP_IE_ROUTEING_AREA)) goto err_mand_ie; peer = gbproxy_peer_by_rai(cfg, TLVP_VAL(&tp, BSSGP_IE_ROUTEING_AREA)); if (!peer) goto err_no_peer; rc = gbprox_relay2peer(msg, peer, ns_bvci); break; case BSSGP_PDUT_BVC_BLOCK_ACK: case BSSGP_PDUT_BVC_UNBLOCK_ACK: if (!TLVP_PRESENT(&tp, BSSGP_IE_BVCI)) goto err_mand_ie; bvci = ntohs(tlvp_val16_unal(&tp, BSSGP_IE_BVCI)); if (bvci == 0) { LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u(SGSN) BSSGP " "%sBLOCK_ACK for signalling BVCI ?!?\n", nsei, pdu_type == BSSGP_PDUT_BVC_UNBLOCK_ACK ? "UN":""); /* should we send STATUS ? */ rate_ctr_inc(&cfg->ctrg-> ctr[GBPROX_GLOB_CTR_INV_BVCI]); } else { /* Mark BVC as (un)blocked */ block_unblock_peer(cfg, bvci, pdu_type); } rc = gbprox_relay2bvci(cfg, msg, bvci, ns_bvci); break; case BSSGP_PDUT_SGSN_INVOKE_TRACE: LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(SGSN) BSSGP INVOKE TRACE not supported\n",nsei); rate_ctr_inc(&cfg->ctrg-> ctr[GBPROX_GLOB_CTR_NOT_SUPPORTED_SGSN]); rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, orig_msg); break; default: LOGP(DGPRS, LOGL_NOTICE, "BSSGP PDU type %s not supported\n", bssgp_pdu_str(pdu_type)); rate_ctr_inc(&cfg->ctrg-> ctr[GBPROX_GLOB_CTR_PROTO_ERR_SGSN]); rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, orig_msg); break; } msgb_free(msg); return rc; err_mand_ie: LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(SGSN) missing mandatory IE\n", nsei); rate_ctr_inc(&cfg->ctrg-> ctr[GBPROX_GLOB_CTR_PROTO_ERR_SGSN]); msgb_free(msg); return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, orig_msg); err_no_peer: LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(SGSN) cannot find peer based on RAI\n", nsei); rate_ctr_inc(&cfg->ctrg-> ctr[GBPROX_GLOB_CTR_INV_RAI]); msgb_free(msg); return bssgp_tx_status(BSSGP_CAUSE_INV_MAND_INF, NULL, orig_msg); } static int gbproxy_is_sgsn_nsei(struct gbproxy_config *cfg, uint16_t nsei) { return nsei == cfg->nsip_sgsn_nsei || (cfg->route_to_sgsn2 && nsei == cfg->nsip_sgsn2_nsei); } /* Main input function for Gb proxy */ int gbprox_rcvmsg(struct gbproxy_config *cfg, struct msgb *msg, uint16_t nsei, uint16_t ns_bvci, uint16_t nsvci) { int rc; int remote_end_is_sgsn = gbproxy_is_sgsn_nsei(cfg, nsei); /* Only BVCI=0 messages need special treatment */ if (ns_bvci == 0 || ns_bvci == 1) { if (remote_end_is_sgsn) rc = gbprox_rx_sig_from_sgsn(cfg, msg, nsei, ns_bvci); else rc = gbprox_rx_sig_from_bss(cfg, msg, nsei, ns_bvci); } else { /* All other BVCI are PTP */ if (remote_end_is_sgsn) rc = gbprox_rx_ptp_from_sgsn(cfg, msg, nsei, nsvci, ns_bvci); else rc = gbprox_rx_ptp_from_bss(cfg, msg, nsei, nsvci, ns_bvci); } return rc; } int gbprox_reset_persistent_nsvcs(struct gprs_ns_inst *nsi) { struct gprs_nsvc *nsvc; llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) { if (!nsvc->persistent) continue; gprs_nsvc_reset(nsvc, NS_CAUSE_OM_INTERVENTION); } return 0; } /* Signal handler for signals from NS layer */ int gbprox_signal(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct gbproxy_config *cfg = handler_data; struct ns_signal_data *nssd = signal_data; struct gprs_nsvc *nsvc = nssd->nsvc; struct gbproxy_peer *peer; int remote_end_is_sgsn = gbproxy_is_sgsn_nsei(cfg, nsvc->nsei); if (subsys != SS_L_NS) return 0; if (signal == S_NS_RESET && remote_end_is_sgsn) { /* We have received a NS-RESET from the NSEI and NSVC * of the SGSN. This might happen with SGSN that start * their own NS-RESET procedure without waiting for our * NS-RESET */ nsvc->remote_end_is_sgsn = 1; } if (signal == S_NS_ALIVE_EXP && nsvc->remote_end_is_sgsn) { LOGP(DGPRS, LOGL_NOTICE, "Tns alive expired too often, " "re-starting RESET procedure\n"); rate_ctr_inc(&cfg->ctrg-> ctr[GBPROX_GLOB_CTR_RESTART_RESET_SGSN]); gprs_ns_nsip_connect(nsvc->nsi, &nsvc->ip.bts_addr, nsvc->nsei, nsvc->nsvci); } if (!nsvc->remote_end_is_sgsn) { /* from BSS to SGSN */ peer = gbproxy_peer_by_nsei(cfg, nsvc->nsei); if (!peer) { LOGP(DGPRS, LOGL_NOTICE, "signal '%s' for unknown peer NSEI=%u/NSVCI=%u\n", get_value_string(gprs_ns_signal_ns_names, signal), nsvc->nsei, nsvc->nsvci); return 0; } switch (signal) { case S_NS_RESET: case S_NS_BLOCK: if (!peer->blocked) break; LOGP(DGPRS, LOGL_NOTICE, "Converting '%s' from NSEI=%u/NSVCI=%u into BSSGP_BVC_BLOCK to SGSN\n", get_value_string(gprs_ns_signal_ns_names, signal), nsvc->nsei, nsvc->nsvci); bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_BLOCK, nsvc->nsei, peer->bvci, 0); break; } } else { /* Forward this message to all NS-VC to BSS */ struct gprs_ns_inst *nsi = cfg->nsi; struct gprs_nsvc *next_nsvc; llist_for_each_entry(next_nsvc, &nsi->gprs_nsvcs, list) { if (next_nsvc->remote_end_is_sgsn) continue; /* Note that the following does not start the full * procedures including timer based retransmissions. */ switch (signal) { case S_NS_RESET: gprs_ns_tx_reset(next_nsvc, nssd->cause); break; case S_NS_BLOCK: gprs_ns_tx_block(next_nsvc, nssd->cause); break; case S_NS_UNBLOCK: gprs_ns_tx_unblock(next_nsvc); break; } } } return 0; } void gbprox_reset(struct gbproxy_config *cfg) { struct gbproxy_peer *peer, *tmp; llist_for_each_entry_safe(peer, tmp, &cfg->bts_peers, list) gbproxy_peer_free(peer); rate_ctr_group_free(cfg->ctrg); gbproxy_init_config(cfg); } int gbproxy_init_config(struct gbproxy_config *cfg) { struct timespec tp; INIT_LLIST_HEAD(&cfg->bts_peers); cfg->ctrg = rate_ctr_group_alloc(tall_bsc_ctx, &global_ctrg_desc, 0); if (!cfg->ctrg) { LOGP(DGPRS, LOGL_ERROR, "Cannot allocate global counter group!\n"); return -1; } clock_gettime(CLOCK_REALTIME, &tp); return 0; } osmo-sgsn-1.3.0/src/gprs/gb_proxy_main.c000066400000000000000000000222041327264017000202050ustar00rootroot00000000000000/* NS-over-IP proxy */ /* (C) 2010 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 . * */ #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 _GNU_SOURCE #include void *tall_bsc_ctx; const char *openbsc_copyright = "Copyright (C) 2010 Harald Welte and On-Waves\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"; #define CONFIG_FILE_DEFAULT "osmo-gbproxy.cfg" #define CONFIG_FILE_LEGACY "osmo_gbproxy.cfg" static char *config_file = NULL; struct gbproxy_config gbcfg = {0}; static int daemonize = 0; /* Pointer to the SGSN peer */ extern struct gbprox_peer *gbprox_peer_sgsn; /* call-back function for the NS protocol */ static int proxy_ns_cb(enum gprs_ns_evt event, struct gprs_nsvc *nsvc, struct msgb *msg, uint16_t bvci) { int rc = 0; switch (event) { case GPRS_NS_EVT_UNIT_DATA: rc = gbprox_rcvmsg(&gbcfg, msg, nsvc->nsei, bvci, nsvc->nsvci); break; default: LOGP(DGPRS, LOGL_ERROR, "SGSN: Unknown event %u from NS\n", event); if (msg) msgb_free(msg); rc = -EIO; break; } return rc; } static void signal_handler(int signal) { fprintf(stdout, "signal %u received\n", signal); switch (signal) { case SIGINT: case SIGTERM: osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL); sleep(1); 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: talloc_report_full(tall_vty_ctx, stderr); break; default: break; } } static void print_usage() { printf("Usage: bsc_hack\n"); } static void print_help() { printf(" Some useful help...\n"); printf(" -h --help this text\n"); printf(" -d option --debug=DNS:DGPRS,0:0 enable debugging\n"); printf(" -D --daemonize Fork the process into a background daemon\n"); printf(" -c --config-file filename The config file to use [%s]\n", CONFIG_FILE_DEFAULT); printf(" -s --disable-color\n"); printf(" -T --timestamp Prefix every log line with a timestamp\n"); printf(" -V --version. Print the version of OpenBSC.\n"); printf(" -e --log-level number. Set a global loglevel.\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' }, { "log-level", 1, 0, 'e' }, { 0, 0, 0, 0 } }; c = getopt_long(argc, argv, "hd:Dc:sTVe:", 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 'e': log_set_log_level(osmo_stderr_target, atoi(optarg)); break; case 'V': print_version(1); exit(0); break; default: break; } } } int gbproxy_vty_is_config_node(struct vty *vty, int node) { switch (node) { /* add items that are not config */ case CONFIG_NODE: return 0; default: return 1; } } int gbproxy_vty_go_parent(struct vty *vty) { switch (vty->node) { case GBPROXY_NODE: default: if (gbproxy_vty_is_config_node(vty, vty->node)) vty->node = CONFIG_NODE; else vty->node = ENABLE_NODE; vty->index = NULL; } return vty->node; } static struct vty_app_info vty_info = { .name = "OsmoGbProxy", .version = PACKAGE_VERSION, .go_parent_cb = gbproxy_vty_go_parent, .is_config_node = gbproxy_vty_is_config_node, }; /* default categories */ static struct log_info_cat gprs_categories[] = { [DGPRS] = { .name = "DGPRS", .description = "GPRS Packet Service", .enabled = 1, .loglevel = LOGL_DEBUG, }, [DNS] = { .name = "DNS", .description = "GPRS Network Service (NS)", .enabled = 1, .loglevel = LOGL_INFO, }, [DBSSGP] = { .name = "DBSSGP", .description = "GPRS BSS Gateway Protocol (BSSGP)", .enabled = 1, .loglevel = LOGL_DEBUG, }, }; static const struct log_info gprs_log_info = { .filter_fn = gprs_log_filter_fn, .cat = gprs_categories, .num_cat = ARRAY_SIZE(gprs_categories), }; static bool file_exists(const char *path) { struct stat sb; return stat(path, &sb) ? false : true; } int main(int argc, char **argv) { int rc; tall_bsc_ctx = talloc_named_const(NULL, 0, "nsip_proxy"); msgb_talloc_ctx_init(tall_bsc_ctx, 0); vty_info.tall_ctx = tall_bsc_ctx; signal(SIGINT, &signal_handler); signal(SIGTERM, &signal_handler); signal(SIGABRT, &signal_handler); signal(SIGUSR1, &signal_handler); signal(SIGUSR2, &signal_handler); osmo_init_ignore_signals(); osmo_init_logging2(tall_bsc_ctx, &gprs_log_info); vty_info.copyright = openbsc_copyright; vty_init(&vty_info); logging_vty_add_cmds(NULL); osmo_talloc_vty_add_cmds(); osmo_stats_vty_add_cmds(&gprs_log_info); gbproxy_vty_init(); handle_options(argc, argv); /* Backwards compatibility: for years, the default config file name was * osmo_gbproxy.cfg. All other Osmocom programs use osmo-*.cfg with a * dash. To be able to use the new config file name without breaking * previous setups that might rely on the legacy default config file * name, we need to look for the old config file if no -c option was * passed AND no file exists with the new default file name. */ if (!config_file) { /* No -c option was passed */ if (file_exists(CONFIG_FILE_LEGACY) && !file_exists(CONFIG_FILE_DEFAULT)) config_file = CONFIG_FILE_LEGACY; else config_file = CONFIG_FILE_DEFAULT; } rate_ctr_init(tall_bsc_ctx); osmo_stats_init(tall_bsc_ctx); bssgp_nsi = gprs_ns_instantiate(&proxy_ns_cb, tall_bsc_ctx); if (!bssgp_nsi) { LOGP(DGPRS, LOGL_ERROR, "Unable to instantiate NS\n"); exit(1); } gbproxy_init_config(&gbcfg); gbcfg.nsi = bssgp_nsi; gprs_ns_vty_init(bssgp_nsi); gprs_ns_set_log_ss(DNS); bssgp_set_log_ss(DBSSGP); osmo_signal_register_handler(SS_L_NS, &gbprox_signal, &gbcfg); rc = gbproxy_parse_config(config_file, &gbcfg); if (rc < 0) { LOGP(DGPRS, LOGL_FATAL, "Cannot parse config file '%s'\n", config_file); exit(2); } /* start telnet after reading config for vty_get_bind_addr() */ rc = telnet_init_dynif(tall_bsc_ctx, NULL, vty_get_bind_addr(), OSMO_VTY_PORT_GBPROXY); if (rc < 0) exit(1); if (!gprs_nsvc_by_nsei(gbcfg.nsi, gbcfg.nsip_sgsn_nsei)) { LOGP(DGPRS, LOGL_FATAL, "You cannot proxy to NSEI %u " "without creating that NSEI before\n", gbcfg.nsip_sgsn_nsei); exit(2); } rc = gprs_ns_nsip_listen(bssgp_nsi); if (rc < 0) { LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen on NSIP socket\n"); exit(2); } rc = gprs_ns_frgre_listen(bssgp_nsi); if (rc < 0) { LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen GRE " "socket. Do you have CAP_NET_RAW?\n"); exit(2); } if (daemonize) { rc = osmo_daemonize(); if (rc < 0) { perror("Error during daemonize"); exit(1); } } /* Reset all the persistent NS-VCs that we've read from the config */ gbprox_reset_persistent_nsvcs(bssgp_nsi); while (1) { rc = osmo_select_main(0); if (rc < 0) exit(3); } exit(0); } osmo-sgsn-1.3.0/src/gprs/gb_proxy_patch.c000066400000000000000000000300461327264017000203630ustar00rootroot00000000000000/* Gb-proxy message patching */ /* (C) 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 extern void *tall_bsc_ctx; /* patch RA identifier in place */ static void gbproxy_patch_raid(struct gsm48_ra_id *raid_enc, struct gbproxy_peer *peer, int to_bss, const char *log_text) { struct gbproxy_patch_state *state = &peer->patch_state; struct osmo_plmn_id old_plmn; struct gprs_ra_id raid; enum gbproxy_peer_ctr counter = to_bss ? GBPROX_PEER_CTR_RAID_PATCHED_SGSN : GBPROX_PEER_CTR_RAID_PATCHED_BSS; if (!state->local_plmn.mcc || !state->local_plmn.mnc) return; gsm48_parse_ra(&raid, (uint8_t *)raid_enc); old_plmn = (struct osmo_plmn_id){ .mcc = raid.mcc, .mnc = raid.mnc, .mnc_3_digits = raid.mnc_3_digits, }; if (!to_bss) { /* BSS -> SGSN */ if (state->local_plmn.mcc) raid.mcc = peer->cfg->core_plmn.mcc; if (state->local_plmn.mnc) { raid.mnc = peer->cfg->core_plmn.mnc; raid.mnc_3_digits = peer->cfg->core_plmn.mnc_3_digits; } } else { /* SGSN -> BSS */ if (state->local_plmn.mcc) raid.mcc = state->local_plmn.mcc; if (state->local_plmn.mnc) { raid.mnc = state->local_plmn.mnc; raid.mnc_3_digits = state->local_plmn.mnc_3_digits; } } LOGP(DGPRS, LOGL_DEBUG, "Patching %s to %s: " "%s-%d-%d -> %s\n", log_text, to_bss ? "BSS" : "SGSN", osmo_plmn_name(&old_plmn), raid.lac, raid.rac, osmo_rai_name(&raid)); gsm48_encode_ra(raid_enc, &raid); rate_ctr_inc(&peer->ctrg->ctr[counter]); } static void gbproxy_patch_apn_ie(struct msgb *msg, uint8_t *apn_ie, size_t apn_ie_len, struct gbproxy_peer *peer, size_t *new_apn_ie_len, const char *log_text) { struct apn_ie_hdr { uint8_t iei; uint8_t apn_len; uint8_t apn[0]; } *hdr = (void *)apn_ie; size_t apn_len = hdr->apn_len; uint8_t *apn = hdr->apn; OSMO_ASSERT(apn_ie_len == apn_len + sizeof(struct apn_ie_hdr)); OSMO_ASSERT(apn_ie_len > 2 && apn_ie_len <= 102); if (peer->cfg->core_apn_size == 0) { char str1[110]; /* Remove the IE */ LOGP(DGPRS, LOGL_DEBUG, "Patching %s to SGSN: Removing APN '%s'\n", log_text, osmo_apn_to_str(str1, apn, apn_len)); *new_apn_ie_len = 0; gprs_msgb_resize_area(msg, apn_ie, apn_ie_len, 0); } else { /* Resize the IE */ char str1[110]; char str2[110]; OSMO_ASSERT(peer->cfg->core_apn_size <= 100); LOGP(DGPRS, LOGL_DEBUG, "Patching %s to SGSN: " "Replacing APN '%s' -> '%s'\n", log_text, osmo_apn_to_str(str1, apn, apn_len), osmo_apn_to_str(str2, peer->cfg->core_apn, peer->cfg->core_apn_size)); *new_apn_ie_len = peer->cfg->core_apn_size + 2; gprs_msgb_resize_area(msg, apn, apn_len, peer->cfg->core_apn_size); memcpy(apn, peer->cfg->core_apn, peer->cfg->core_apn_size); hdr->apn_len = peer->cfg->core_apn_size; } rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_APN_PATCHED]); } static int gbproxy_patch_tlli(uint8_t *tlli_enc, struct gbproxy_peer *peer, uint32_t new_tlli, int to_bss, const char *log_text) { uint32_t tlli_be; uint32_t tlli; enum gbproxy_peer_ctr counter = to_bss ? GBPROX_PEER_CTR_TLLI_PATCHED_SGSN : GBPROX_PEER_CTR_TLLI_PATCHED_BSS; memcpy(&tlli_be, tlli_enc, sizeof(tlli_be)); tlli = ntohl(tlli_be); if (tlli == new_tlli) return 0; LOGP(DGPRS, LOGL_DEBUG, "Patching %ss: " "Replacing %08x -> %08x\n", log_text, tlli, new_tlli); tlli_be = htonl(new_tlli); memcpy(tlli_enc, &tlli_be, sizeof(tlli_be)); rate_ctr_inc(&peer->ctrg->ctr[counter]); return 1; } static int gbproxy_patch_ptmsi(uint8_t *ptmsi_enc, struct gbproxy_peer *peer, uint32_t new_ptmsi, int to_bss, const char *log_text) { uint32_t ptmsi_be; uint32_t ptmsi; enum gbproxy_peer_ctr counter = to_bss ? GBPROX_PEER_CTR_PTMSI_PATCHED_SGSN : GBPROX_PEER_CTR_PTMSI_PATCHED_BSS; memcpy(&ptmsi_be, ptmsi_enc, sizeof(ptmsi_be)); ptmsi = ntohl(ptmsi_be); if (ptmsi == new_ptmsi) return 0; LOGP(DGPRS, LOGL_DEBUG, "Patching %ss: " "Replacing %08x -> %08x\n", log_text, ptmsi, new_ptmsi); ptmsi_be = htonl(new_ptmsi); memcpy(ptmsi_enc, &ptmsi_be, sizeof(ptmsi_be)); rate_ctr_inc(&peer->ctrg->ctr[counter]); return 1; } int gbproxy_patch_llc(struct msgb *msg, uint8_t *llc, size_t llc_len, struct gbproxy_peer *peer, struct gbproxy_link_info *link_info, int *len_change, struct gprs_gb_parse_context *parse_ctx) { struct gprs_llc_hdr_parsed *ghp = &parse_ctx->llc_hdr_parsed; int have_patched = 0; int fcs; struct gbproxy_config *cfg = peer->cfg; if (parse_ctx->ptmsi_enc && link_info && !parse_ctx->old_raid_is_foreign && peer->cfg->patch_ptmsi) { uint32_t ptmsi; if (parse_ctx->to_bss) ptmsi = link_info->tlli.ptmsi; else ptmsi = link_info->sgsn_tlli.ptmsi; if (ptmsi != GSM_RESERVED_TMSI) { if (gbproxy_patch_ptmsi(parse_ctx->ptmsi_enc, peer, ptmsi, parse_ctx->to_bss, "P-TMSI")) have_patched = 1; } else { /* TODO: invalidate old RAI if present (see below) */ } } if (parse_ctx->new_ptmsi_enc && link_info && cfg->patch_ptmsi) { uint32_t ptmsi; if (parse_ctx->to_bss) ptmsi = link_info->tlli.ptmsi; else ptmsi = link_info->sgsn_tlli.ptmsi; OSMO_ASSERT(ptmsi); if (gbproxy_patch_ptmsi(parse_ctx->new_ptmsi_enc, peer, ptmsi, parse_ctx->to_bss, "new P-TMSI")) have_patched = 1; } if (parse_ctx->raid_enc) { gbproxy_patch_raid((struct gsm48_ra_id *)parse_ctx->raid_enc, peer, parse_ctx->to_bss, parse_ctx->llc_msg_name); have_patched = 1; } if (parse_ctx->old_raid_enc && !parse_ctx->old_raid_is_foreign) { /* TODO: Patch to invalid if P-TMSI unknown. */ gbproxy_patch_raid((struct gsm48_ra_id *)parse_ctx->old_raid_enc, peer, parse_ctx->to_bss, parse_ctx->llc_msg_name); have_patched = 1; } if (parse_ctx->apn_ie && cfg->core_apn && !parse_ctx->to_bss && gbproxy_imsi_matches(cfg, GBPROX_MATCH_PATCHING, link_info) && cfg->core_apn) { size_t new_len; gbproxy_patch_apn_ie(msg, parse_ctx->apn_ie, parse_ctx->apn_ie_len, peer, &new_len, parse_ctx->llc_msg_name); *len_change += (int)new_len - (int)parse_ctx->apn_ie_len; have_patched = 1; } if (have_patched) { llc_len += *len_change; ghp->crc_length += *len_change; /* Fix FCS */ fcs = gprs_llc_fcs(llc, ghp->crc_length); LOGP(DLLC, LOGL_DEBUG, "Updated LLC message, CRC: %06x -> %06x\n", ghp->fcs, fcs); llc[llc_len - 3] = fcs & 0xff; llc[llc_len - 2] = (fcs >> 8) & 0xff; llc[llc_len - 1] = (fcs >> 16) & 0xff; } return have_patched; } /* patch BSSGP message to use core_plmn.mcc/mnc on the SGSN side */ void gbproxy_patch_bssgp(struct msgb *msg, uint8_t *bssgp, size_t bssgp_len, struct gbproxy_peer *peer, struct gbproxy_link_info *link_info, int *len_change, struct gprs_gb_parse_context *parse_ctx) { const char *err_info = NULL; int err_ctr = -1; if (parse_ctx->bssgp_raid_enc) gbproxy_patch_raid((struct gsm48_ra_id *)parse_ctx->bssgp_raid_enc, peer, parse_ctx->to_bss, "BSSGP"); if (parse_ctx->need_decryption && (peer->cfg->patch_ptmsi || peer->cfg->core_apn)) { /* Patching LLC messages has been requested * explicitly, but the message (including the * type) is encrypted, so we possibly fail to * patch the LLC part of the message. */ err_ctr = GBPROX_PEER_CTR_PATCH_CRYPT_ERR; err_info = "GMM message is encrypted"; goto patch_error; } if (!link_info && parse_ctx->tlli_enc && parse_ctx->to_bss) { /* Happens with unknown (not cached) TLLI coming from * the SGSN */ /* TODO: What shall be done with the message in this case? */ err_ctr = GBPROX_PEER_CTR_TLLI_UNKNOWN; err_info = "TLLI sent by the SGSN is unknown"; goto patch_error; } if (!link_info) return; if (parse_ctx->tlli_enc && peer->cfg->patch_ptmsi) { uint32_t tlli = gbproxy_map_tlli(parse_ctx->tlli, link_info, parse_ctx->to_bss); if (tlli) { gbproxy_patch_tlli(parse_ctx->tlli_enc, peer, tlli, parse_ctx->to_bss, "TLLI"); parse_ctx->tlli = tlli; } else { /* Internal error */ err_ctr = GBPROX_PEER_CTR_PATCH_ERR; err_info = "Replacement TLLI is 0"; goto patch_error; } } if (parse_ctx->bssgp_ptmsi_enc && peer->cfg->patch_ptmsi) { uint32_t ptmsi; if (parse_ctx->to_bss) ptmsi = link_info->tlli.ptmsi; else ptmsi = link_info->sgsn_tlli.ptmsi; if (ptmsi != GSM_RESERVED_TMSI) gbproxy_patch_ptmsi( parse_ctx->bssgp_ptmsi_enc, peer, ptmsi, parse_ctx->to_bss, "BSSGP P-TMSI"); } if (parse_ctx->llc) { uint8_t *llc = parse_ctx->llc; size_t llc_len = parse_ctx->llc_len; int llc_len_change = 0; gbproxy_patch_llc(msg, llc, llc_len, peer, link_info, &llc_len_change, parse_ctx); /* Note that the APN might have been resized here, but no * pointer int the parse_ctx will refer to an adress after the * APN. So it's possible to patch first and do the TLLI * handling afterwards. */ if (llc_len_change) { llc_len += llc_len_change; /* Fix LLC IE len */ /* TODO: This is a kludge, but the a pointer to the * start of the IE is not available here */ if (llc[-2] == BSSGP_IE_LLC_PDU && llc[-1] & 0x80) { /* most probably a one byte length */ if (llc_len > 127) { err_info = "Cannot increase size"; err_ctr = GBPROX_PEER_CTR_PATCH_ERR; goto patch_error; } llc[-1] = llc_len | 0x80; } else { llc[-2] = (llc_len >> 8) & 0x7f; llc[-1] = llc_len & 0xff; } *len_change += llc_len_change; } /* Note that the tp struct might contain invalid pointers here * if the LLC field has changed its size */ parse_ctx->llc_len = llc_len; } return; patch_error: OSMO_ASSERT(err_ctr >= 0); rate_ctr_inc(&peer->ctrg->ctr[err_ctr]); LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(%s) failed to patch BSSGP message as requested: %s.\n", msgb_nsei(msg), parse_ctx->to_bss ? "SGSN" : "BSS", err_info); } void gbproxy_clear_patch_filter(struct gbproxy_match *match) { if (match->enable) { regfree(&match->re_comp); match->enable = 0; } talloc_free(match->re_str); match->re_str = NULL; } int gbproxy_set_patch_filter(struct gbproxy_match *match, const char *filter, const char **err_msg) { static char err_buf[300]; int rc; gbproxy_clear_patch_filter(match); if (!filter) return 0; rc = regcomp(&match->re_comp, filter, REG_EXTENDED | REG_NOSUB | REG_ICASE); if (rc == 0) { match->enable = 1; match->re_str = talloc_strdup(tall_bsc_ctx, filter); return 0; } if (err_msg) { regerror(rc, &match->re_comp, err_buf, sizeof(err_buf)); *err_msg = err_buf; } return -1; } int gbproxy_check_imsi(struct gbproxy_match *match, const uint8_t *imsi, size_t imsi_len) { char mi_buf[200]; int rc; if (!match->enable) return 1; rc = gprs_is_mi_imsi(imsi, imsi_len); if (rc > 0) rc = gsm48_mi_to_string(mi_buf, sizeof(mi_buf), imsi, imsi_len); if (rc <= 0) { LOGP(DGPRS, LOGL_NOTICE, "Invalid IMSI %s\n", osmo_hexdump(imsi, imsi_len)); return -1; } LOGP(DGPRS, LOGL_DEBUG, "Checking IMSI '%s' (%d)\n", mi_buf, rc); rc = regexec(&match->re_comp, mi_buf, 0, NULL, 0); if (rc == REG_NOMATCH) { LOGP(DGPRS, LOGL_INFO, "IMSI '%s' doesn't match pattern '%s'\n", mi_buf, match->re_str); return 0; } return 1; } osmo-sgsn-1.3.0/src/gprs/gb_proxy_peer.c000066400000000000000000000151611327264017000202200ustar00rootroot00000000000000/* Gb proxy peer handling */ /* (C) 2010 by Harald Welte * (C) 2010-2013 by On-Waves * (C) 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 . * */ #include #include #include #include #include #include #include #include extern void *tall_bsc_ctx; static const struct rate_ctr_desc peer_ctr_description[] = { { "blocked", "BVC Block " }, { "unblocked", "BVC Unblock " }, { "dropped", "BVC blocked, dropped packet " }, { "inv-nsei", "NSEI mismatch " }, { "tx-err", "NS Transmission error " }, { "raid-mod.bss", "RAID patched (BSS )" }, { "raid-mod.sgsn", "RAID patched (SGSN)" }, { "apn-mod.sgsn", "APN patched " }, { "tlli-mod.bss", "TLLI patched (BSS )" }, { "tlli-mod.sgsn", "TLLI patched (SGSN)" }, { "ptmsi-mod.bss", "P-TMSI patched (BSS )" }, { "ptmsi-mod.sgsn","P-TMSI patched (SGSN)" }, { "mod-crypt-err", "Patch error: encrypted " }, { "mod-err", "Patch error: other " }, { "attach-reqs", "Attach Request count " }, { "attach-rejs", "Attach Reject count " }, { "attach-acks", "Attach Accept count " }, { "attach-cpls", "Attach Completed count " }, { "ra-upd-reqs", "RoutingArea Update Request count" }, { "ra-upd-rejs", "RoutingArea Update Reject count " }, { "ra-upd-acks", "RoutingArea Update Accept count " }, { "ra-upd-cpls", "RoutingArea Update Compltd count" }, { "gmm-status", "GMM Status count (BSS)" }, { "gmm-status", "GMM Status count (SGSN)" }, { "detach-reqs", "Detach Request count " }, { "detach-acks", "Detach Accept count " }, { "pdp-act-reqs", "PDP Activation Request count " }, { "pdp-act-rejs", "PDP Activation Reject count " }, { "pdp-act-acks", "PDP Activation Accept count " }, { "pdp-deact-reqs","PDP Deactivation Request count " }, { "pdp-deact-acks","PDP Deactivation Accept count " }, { "tlli-unknown", "TLLI from SGSN unknown " }, { "tlli-cache", "TLLI cache size " }, }; osmo_static_assert(ARRAY_SIZE(peer_ctr_description) == GBPROX_PEER_CTR_LAST, everything_described); static const struct rate_ctr_group_desc peer_ctrg_desc = { .group_name_prefix = "gbproxy:peer", .group_description = "GBProxy Peer Statistics", .num_ctr = ARRAY_SIZE(peer_ctr_description), .ctr_desc = peer_ctr_description, .class_id = OSMO_STATS_CLASS_PEER, }; /* Find the gbprox_peer by its BVCI */ struct gbproxy_peer *gbproxy_peer_by_bvci(struct gbproxy_config *cfg, uint16_t bvci) { struct gbproxy_peer *peer; llist_for_each_entry(peer, &cfg->bts_peers, list) { if (peer->bvci == bvci) return peer; } return NULL; } /* Find the gbprox_peer by its NSEI */ struct gbproxy_peer *gbproxy_peer_by_nsei(struct gbproxy_config *cfg, uint16_t nsei) { struct gbproxy_peer *peer; llist_for_each_entry(peer, &cfg->bts_peers, list) { if (peer->nsei == nsei) return peer; } return NULL; } /* look-up a peer by its Routeing Area Identification (RAI) */ struct gbproxy_peer *gbproxy_peer_by_rai(struct gbproxy_config *cfg, const uint8_t *ra) { struct gbproxy_peer *peer; llist_for_each_entry(peer, &cfg->bts_peers, list) { if (!memcmp(peer->ra, ra, 6)) return peer; } return NULL; } /* look-up a peer by its Location Area Identification (LAI) */ struct gbproxy_peer *gbproxy_peer_by_lai(struct gbproxy_config *cfg, const uint8_t *la) { struct gbproxy_peer *peer; llist_for_each_entry(peer, &cfg->bts_peers, list) { if (!memcmp(peer->ra, la, 5)) return peer; } return NULL; } /* look-up a peer by its Location Area Code (LAC) */ struct gbproxy_peer *gbproxy_peer_by_lac(struct gbproxy_config *cfg, const uint8_t *la) { struct gbproxy_peer *peer; llist_for_each_entry(peer, &cfg->bts_peers, list) { if (!memcmp(peer->ra + 3, la + 3, 2)) return peer; } return NULL; } struct gbproxy_peer *gbproxy_peer_by_bssgp_tlv(struct gbproxy_config *cfg, struct tlv_parsed *tp) { if (TLVP_PRESENT(tp, BSSGP_IE_BVCI)) { uint16_t bvci; bvci = ntohs(tlvp_val16_unal(tp, BSSGP_IE_BVCI)); if (bvci >= 2) return gbproxy_peer_by_bvci(cfg, bvci); } if (TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) { uint8_t *rai = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA); /* Only compare LAC part, since MCC/MNC are possibly patched. * Since the LAC of different BSS must be different when * MCC/MNC are patched, collisions shouldn't happen. */ return gbproxy_peer_by_lac(cfg, rai); } if (TLVP_PRESENT(tp, BSSGP_IE_LOCATION_AREA)) { uint8_t *lai = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_LOCATION_AREA); return gbproxy_peer_by_lac(cfg, lai); } return NULL; } struct gbproxy_peer *gbproxy_peer_alloc(struct gbproxy_config *cfg, uint16_t bvci) { struct gbproxy_peer *peer; peer = talloc_zero(tall_bsc_ctx, struct gbproxy_peer); if (!peer) return NULL; peer->bvci = bvci; peer->ctrg = rate_ctr_group_alloc(peer, &peer_ctrg_desc, bvci); if (!peer->ctrg) { talloc_free(peer); return NULL; } peer->cfg = cfg; llist_add(&peer->list, &cfg->bts_peers); INIT_LLIST_HEAD(&peer->patch_state.logical_links); return peer; } void gbproxy_peer_free(struct gbproxy_peer *peer) { llist_del(&peer->list); gbproxy_delete_link_infos(peer); rate_ctr_group_free(peer->ctrg); peer->ctrg = NULL; talloc_free(peer); } int gbproxy_cleanup_peers(struct gbproxy_config *cfg, uint16_t nsei, uint16_t bvci) { int counter = 0; struct gbproxy_peer *peer, *tmp; llist_for_each_entry_safe(peer, tmp, &cfg->bts_peers, list) { if (peer->nsei != nsei) continue; if (bvci && peer->bvci != bvci) continue; gbproxy_peer_free(peer); counter += 1; } return counter; } osmo-sgsn-1.3.0/src/gprs/gb_proxy_tlli.c000066400000000000000000000501671327264017000202360ustar00rootroot00000000000000/* Gb-proxy TLLI state handling */ /* (C) 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 struct gbproxy_link_info *gbproxy_link_info_by_tlli(struct gbproxy_peer *peer, uint32_t tlli) { struct gbproxy_link_info *link_info; struct gbproxy_patch_state *state = &peer->patch_state; if (!tlli) return NULL; llist_for_each_entry(link_info, &state->logical_links, list) if (link_info->tlli.current == tlli || link_info->tlli.assigned == tlli) return link_info; return NULL; } struct gbproxy_link_info *gbproxy_link_info_by_ptmsi( struct gbproxy_peer *peer, uint32_t ptmsi) { struct gbproxy_link_info *link_info; struct gbproxy_patch_state *state = &peer->patch_state; if (ptmsi == GSM_RESERVED_TMSI) return NULL; llist_for_each_entry(link_info, &state->logical_links, list) if (link_info->tlli.ptmsi == ptmsi) return link_info; return NULL; } struct gbproxy_link_info *gbproxy_link_info_by_any_sgsn_tlli( struct gbproxy_peer *peer, uint32_t tlli) { struct gbproxy_link_info *link_info; struct gbproxy_patch_state *state = &peer->patch_state; if (!tlli) return NULL; /* Don't care about the NSEI */ llist_for_each_entry(link_info, &state->logical_links, list) if (link_info->sgsn_tlli.current == tlli || link_info->sgsn_tlli.assigned == tlli) return link_info; return NULL; } struct gbproxy_link_info *gbproxy_link_info_by_sgsn_tlli( struct gbproxy_peer *peer, uint32_t tlli, uint32_t sgsn_nsei) { struct gbproxy_link_info *link_info; struct gbproxy_patch_state *state = &peer->patch_state; if (!tlli) return NULL; llist_for_each_entry(link_info, &state->logical_links, list) if ((link_info->sgsn_tlli.current == tlli || link_info->sgsn_tlli.assigned == tlli) && link_info->sgsn_nsei == sgsn_nsei) return link_info; return NULL; } struct gbproxy_link_info *gbproxy_link_info_by_imsi( struct gbproxy_peer *peer, const uint8_t *imsi, size_t imsi_len) { struct gbproxy_link_info *link_info; struct gbproxy_patch_state *state = &peer->patch_state; if (!gprs_is_mi_imsi(imsi, imsi_len)) return NULL; llist_for_each_entry(link_info, &state->logical_links, list) { if (link_info->imsi_len != imsi_len) continue; if (memcmp(link_info->imsi, imsi, imsi_len) != 0) continue; return link_info; } return NULL; } void gbproxy_link_info_discard_messages(struct gbproxy_link_info *link_info) { struct msgb *msg, *nxt; llist_for_each_entry_safe(msg, nxt, &link_info->stored_msgs, list) { llist_del(&msg->list); msgb_free(msg); } } void gbproxy_delete_link_info(struct gbproxy_peer *peer, struct gbproxy_link_info *link_info) { struct gbproxy_patch_state *state = &peer->patch_state; gbproxy_link_info_discard_messages(link_info); llist_del(&link_info->list); talloc_free(link_info); state->logical_link_count -= 1; peer->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current = state->logical_link_count; } void gbproxy_delete_link_infos(struct gbproxy_peer *peer) { struct gbproxy_link_info *link_info, *nxt; struct gbproxy_patch_state *state = &peer->patch_state; llist_for_each_entry_safe(link_info, nxt, &state->logical_links, list) gbproxy_delete_link_info(peer, link_info); OSMO_ASSERT(state->logical_link_count == 0); OSMO_ASSERT(llist_empty(&state->logical_links)); } void gbproxy_attach_link_info(struct gbproxy_peer *peer, time_t now, struct gbproxy_link_info *link_info) { struct gbproxy_patch_state *state = &peer->patch_state; link_info->timestamp = now; llist_add(&link_info->list, &state->logical_links); state->logical_link_count += 1; peer->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current = state->logical_link_count; } int gbproxy_remove_stale_link_infos(struct gbproxy_peer *peer, time_t now) { struct gbproxy_patch_state *state = &peer->patch_state; int exceeded_max_len = 0; int deleted_count = 0; int check_for_age; if (peer->cfg->tlli_max_len > 0) exceeded_max_len = state->logical_link_count - peer->cfg->tlli_max_len; check_for_age = peer->cfg->tlli_max_age > 0; for (; exceeded_max_len > 0; exceeded_max_len--) { struct gbproxy_link_info *link_info; OSMO_ASSERT(!llist_empty(&state->logical_links)); link_info = llist_entry(state->logical_links.prev, struct gbproxy_link_info, list); LOGP(DGPRS, LOGL_INFO, "Removing TLLI %08x from list " "(stale, length %d, max_len exceeded)\n", link_info->tlli.current, state->logical_link_count); gbproxy_delete_link_info(peer, link_info); deleted_count += 1; } while (check_for_age && !llist_empty(&state->logical_links)) { time_t age; struct gbproxy_link_info *link_info; link_info = llist_entry(state->logical_links.prev, struct gbproxy_link_info, list); age = now - link_info->timestamp; /* age < 0 only happens after system time jumps, discard entry */ if (age <= peer->cfg->tlli_max_age && age >= 0) { check_for_age = 0; continue; } LOGP(DGPRS, LOGL_INFO, "Removing TLLI %08x from list " "(stale, age %d, max_age exceeded)\n", link_info->tlli.current, (int)age); gbproxy_delete_link_info(peer, link_info); deleted_count += 1; } return deleted_count; } struct gbproxy_link_info *gbproxy_link_info_alloc( struct gbproxy_peer *peer) { struct gbproxy_link_info *link_info; link_info = talloc_zero(peer, struct gbproxy_link_info); link_info->tlli.ptmsi = GSM_RESERVED_TMSI; link_info->sgsn_tlli.ptmsi = GSM_RESERVED_TMSI; link_info->vu_gen_tx_bss = GBPROXY_INIT_VU_GEN_TX; INIT_LLIST_HEAD(&link_info->stored_msgs); return link_info; } void gbproxy_detach_link_info( struct gbproxy_peer *peer, struct gbproxy_link_info *link_info) { struct gbproxy_patch_state *state = &peer->patch_state; llist_del(&link_info->list); OSMO_ASSERT(state->logical_link_count > 0); state->logical_link_count -= 1; peer->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current = state->logical_link_count; } void gbproxy_update_link_info(struct gbproxy_link_info *link_info, const uint8_t *imsi, size_t imsi_len) { if (!gprs_is_mi_imsi(imsi, imsi_len)) return; link_info->imsi_len = imsi_len; link_info->imsi = talloc_realloc_size(link_info, link_info->imsi, imsi_len); OSMO_ASSERT(link_info->imsi != NULL); memcpy(link_info->imsi, imsi, imsi_len); } void gbproxy_reassign_tlli(struct gbproxy_tlli_state *tlli_state, struct gbproxy_peer *peer, uint32_t new_tlli) { if (new_tlli == tlli_state->current) return; LOGP(DGPRS, LOGL_INFO, "The TLLI has been reassigned from %08x to %08x\n", tlli_state->current, new_tlli); /* Remember assigned TLLI */ tlli_state->assigned = new_tlli; tlli_state->bss_validated = 0; tlli_state->net_validated = 0; } uint32_t gbproxy_map_tlli(uint32_t other_tlli, struct gbproxy_link_info *link_info, int to_bss) { uint32_t tlli = 0; struct gbproxy_tlli_state *src, *dst; if (to_bss) { src = &link_info->sgsn_tlli; dst = &link_info->tlli; } else { src = &link_info->tlli; dst = &link_info->sgsn_tlli; } if (src->current == other_tlli) tlli = dst->current; else if (src->assigned == other_tlli) tlli = dst->assigned; return tlli; } static void gbproxy_validate_tlli(struct gbproxy_tlli_state *tlli_state, uint32_t tlli, int to_bss) { LOGP(DGPRS, LOGL_DEBUG, "%s({current = %08x, assigned = %08x, net_vld = %d, bss_vld = %d}, %08x)\n", __func__, tlli_state->current, tlli_state->assigned, tlli_state->net_validated, tlli_state->bss_validated, tlli); if (!tlli_state->assigned || tlli_state->assigned != tlli) return; /* TODO: Is this ok? Check spec */ if (gprs_tlli_type(tlli) != TLLI_LOCAL) return; /* See GSM 04.08, 4.7.1.5 */ if (to_bss) tlli_state->net_validated = 1; else tlli_state->bss_validated = 1; if (!tlli_state->bss_validated || !tlli_state->net_validated) return; LOGP(DGPRS, LOGL_INFO, "The TLLI %08x has been validated (was %08x)\n", tlli_state->assigned, tlli_state->current); tlli_state->current = tlli; tlli_state->assigned = 0; } static void gbproxy_touch_link_info(struct gbproxy_peer *peer, struct gbproxy_link_info *link_info, time_t now) { gbproxy_detach_link_info(peer, link_info); gbproxy_attach_link_info(peer, now, link_info); } static int gbproxy_unregister_link_info(struct gbproxy_peer *peer, struct gbproxy_link_info *link_info) { if (!link_info) return 1; if (link_info->tlli.ptmsi == GSM_RESERVED_TMSI && !link_info->imsi_len) { LOGP(DGPRS, LOGL_INFO, "Removing TLLI %08x from list (P-TMSI or IMSI are not set)\n", link_info->tlli.current); gbproxy_delete_link_info(peer, link_info); return 1; } link_info->tlli.current = 0; link_info->tlli.assigned = 0; link_info->sgsn_tlli.current = 0; link_info->sgsn_tlli.assigned = 0; link_info->is_deregistered = 1; gbproxy_reset_link(link_info); return 0; } int gbproxy_imsi_matches(struct gbproxy_config *cfg, enum gbproxy_match_id match_id, struct gbproxy_link_info *link_info) { struct gbproxy_match *match; OSMO_ASSERT(match_id >= 0 && match_id < ARRAY_SIZE(cfg->matches)); match = &cfg->matches[match_id]; if (!match->enable) return 1; return link_info != NULL && link_info->is_matching[match_id]; } void gbproxy_assign_imsi(struct gbproxy_peer *peer, struct gbproxy_link_info *link_info, struct gprs_gb_parse_context *parse_ctx) { int imsi_matches; struct gbproxy_link_info *other_link_info; enum gbproxy_match_id match_id; /* Make sure that there is a second entry with the same IMSI */ other_link_info = gbproxy_link_info_by_imsi( peer, parse_ctx->imsi, parse_ctx->imsi_len); if (other_link_info && other_link_info != link_info) { char mi_buf[200]; mi_buf[0] = '\0'; gsm48_mi_to_string(mi_buf, sizeof(mi_buf), parse_ctx->imsi, parse_ctx->imsi_len); LOGP(DGPRS, LOGL_INFO, "Removing TLLI %08x from list (IMSI %s re-used)\n", other_link_info->tlli.current, mi_buf); gbproxy_delete_link_info(peer, other_link_info); } /* Update the IMSI field */ gbproxy_update_link_info(link_info, parse_ctx->imsi, parse_ctx->imsi_len); /* Check, whether the IMSI matches */ OSMO_ASSERT(ARRAY_SIZE(link_info->is_matching) == ARRAY_SIZE(peer->cfg->matches)); for (match_id = 0; match_id < ARRAY_SIZE(link_info->is_matching); ++match_id) { imsi_matches = gbproxy_check_imsi( &peer->cfg->matches[match_id], parse_ctx->imsi, parse_ctx->imsi_len); if (imsi_matches >= 0) link_info->is_matching[match_id] = imsi_matches; } } static int gbproxy_tlli_match(const struct gbproxy_tlli_state *a, const struct gbproxy_tlli_state *b) { if (a->current && a->current == b->current) return 1; if (a->assigned && a->assigned == b->assigned) return 1; if (a->ptmsi != GSM_RESERVED_TMSI && a->ptmsi == b->ptmsi) return 1; return 0; } static void gbproxy_remove_matching_link_infos( struct gbproxy_peer *peer, struct gbproxy_link_info *link_info) { struct gbproxy_link_info *info, *nxt; struct gbproxy_patch_state *state = &peer->patch_state; /* Make sure that there is no second entry with the same P-TMSI or TLLI */ llist_for_each_entry_safe(info, nxt, &state->logical_links, list) { if (info == link_info) continue; if (!gbproxy_tlli_match(&link_info->tlli, &info->tlli) && (link_info->sgsn_nsei != info->sgsn_nsei || !gbproxy_tlli_match(&link_info->sgsn_tlli, &info->sgsn_tlli))) continue; LOGP(DGPRS, LOGL_INFO, "Removing TLLI %08x from list (P-TMSI/TLLI re-used)\n", info->tlli.current); gbproxy_delete_link_info(peer, info); } } static struct gbproxy_link_info *gbproxy_get_link_info_ul( struct gbproxy_peer *peer, int *tlli_is_valid, struct gprs_gb_parse_context *parse_ctx) { struct gbproxy_link_info *link_info = NULL; if (parse_ctx->tlli_enc) { link_info = gbproxy_link_info_by_tlli(peer, parse_ctx->tlli); if (link_info) { *tlli_is_valid = 1; return link_info; } } *tlli_is_valid = 0; if (!link_info && parse_ctx->imsi) { link_info = gbproxy_link_info_by_imsi( peer, parse_ctx->imsi, parse_ctx->imsi_len); } if (!link_info && parse_ctx->ptmsi_enc && !parse_ctx->old_raid_is_foreign) { uint32_t bss_ptmsi; gprs_parse_tmsi(parse_ctx->ptmsi_enc, &bss_ptmsi); link_info = gbproxy_link_info_by_ptmsi(peer, bss_ptmsi); } if (!link_info) return NULL; link_info->is_deregistered = 0; return link_info; } struct gbproxy_link_info *gbproxy_update_link_state_ul( struct gbproxy_peer *peer, time_t now, struct gprs_gb_parse_context *parse_ctx) { struct gbproxy_link_info *link_info; int tlli_is_valid; link_info = gbproxy_get_link_info_ul(peer, &tlli_is_valid, parse_ctx); if (parse_ctx->tlli_enc && parse_ctx->llc) { uint32_t sgsn_tlli; if (!link_info) { LOGP(DGPRS, LOGL_INFO, "Adding TLLI %08x to list\n", parse_ctx->tlli); link_info = gbproxy_link_info_alloc(peer); gbproxy_attach_link_info(peer, now, link_info); /* Setup TLLIs */ sgsn_tlli = gbproxy_make_sgsn_tlli(peer, link_info, parse_ctx->tlli); link_info->sgsn_tlli.current = sgsn_tlli; link_info->tlli.current = parse_ctx->tlli; } else if (!tlli_is_valid) { /* New TLLI (info found by IMSI or P-TMSI) */ link_info->tlli.current = parse_ctx->tlli; link_info->tlli.assigned = 0; link_info->sgsn_tlli.current = gbproxy_make_sgsn_tlli(peer, link_info, parse_ctx->tlli); link_info->sgsn_tlli.assigned = 0; gbproxy_touch_link_info(peer, link_info, now); } else { sgsn_tlli = gbproxy_map_tlli(parse_ctx->tlli, link_info, 0); if (!sgsn_tlli) sgsn_tlli = gbproxy_make_sgsn_tlli(peer, link_info, parse_ctx->tlli); gbproxy_validate_tlli(&link_info->tlli, parse_ctx->tlli, 0); gbproxy_validate_tlli(&link_info->sgsn_tlli, sgsn_tlli, 0); gbproxy_touch_link_info(peer, link_info, now); } } else if (link_info) { gbproxy_touch_link_info(peer, link_info, now); } if (parse_ctx->imsi && link_info && link_info->imsi_len == 0) gbproxy_assign_imsi(peer, link_info, parse_ctx); return link_info; } static struct gbproxy_link_info *gbproxy_get_link_info_dl( struct gbproxy_peer *peer, struct gprs_gb_parse_context *parse_ctx) { struct gbproxy_link_info *link_info = NULL; /* Which key to use depends on its availability only, if that fails, do * not retry it with another key (e.g. IMSI). */ if (parse_ctx->tlli_enc) link_info = gbproxy_link_info_by_sgsn_tlli(peer, parse_ctx->tlli, parse_ctx->peer_nsei); /* TODO: Get link_info by (SGSN) P-TMSI if that is available (see * GSM 08.18, 7.2) instead of using the IMSI as key. */ else if (parse_ctx->imsi) link_info = gbproxy_link_info_by_imsi( peer, parse_ctx->imsi, parse_ctx->imsi_len); if (link_info) link_info->is_deregistered = 0; return link_info; } struct gbproxy_link_info *gbproxy_update_link_state_dl( struct gbproxy_peer *peer, time_t now, struct gprs_gb_parse_context *parse_ctx) { struct gbproxy_link_info *link_info = NULL; link_info = gbproxy_get_link_info_dl(peer, parse_ctx); if (parse_ctx->tlli_enc && parse_ctx->new_ptmsi_enc && link_info) { /* A new P-TMSI has been signalled in the message, * register new TLLI */ uint32_t new_sgsn_ptmsi; uint32_t new_bss_ptmsi = GSM_RESERVED_TMSI; gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_sgsn_ptmsi); if (link_info->sgsn_tlli.ptmsi == new_sgsn_ptmsi) new_bss_ptmsi = link_info->tlli.ptmsi; if (new_bss_ptmsi == GSM_RESERVED_TMSI) new_bss_ptmsi = gbproxy_make_bss_ptmsi(peer, new_sgsn_ptmsi); LOGP(DGPRS, LOGL_INFO, "Got new PTMSI %08x from SGSN, using %08x for BSS\n", new_sgsn_ptmsi, new_bss_ptmsi); /* Setup PTMSIs */ link_info->sgsn_tlli.ptmsi = new_sgsn_ptmsi; link_info->tlli.ptmsi = new_bss_ptmsi; } else if (parse_ctx->tlli_enc && parse_ctx->new_ptmsi_enc && !link_info && !peer->cfg->patch_ptmsi) { /* A new P-TMSI has been signalled in the message with an unknown * TLLI, create a new link_info */ /* TODO: Add a test case for this branch */ uint32_t new_ptmsi; gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_ptmsi); LOGP(DGPRS, LOGL_INFO, "Adding TLLI %08x to list (SGSN, new P-TMSI is %08x)\n", parse_ctx->tlli, new_ptmsi); link_info = gbproxy_link_info_alloc(peer); link_info->sgsn_tlli.current = parse_ctx->tlli; link_info->tlli.current = parse_ctx->tlli; link_info->sgsn_tlli.ptmsi = new_ptmsi; link_info->tlli.ptmsi = new_ptmsi; gbproxy_attach_link_info(peer, now, link_info); } else if (parse_ctx->tlli_enc && parse_ctx->llc && !link_info && !peer->cfg->patch_ptmsi) { /* Unknown SGSN TLLI, create a new link_info */ uint32_t new_ptmsi; link_info = gbproxy_link_info_alloc(peer); LOGP(DGPRS, LOGL_INFO, "Adding TLLI %08x to list (SGSN)\n", parse_ctx->tlli); gbproxy_attach_link_info(peer, now, link_info); /* Setup TLLIs */ link_info->sgsn_tlli.current = parse_ctx->tlli; link_info->tlli.current = parse_ctx->tlli; if (!parse_ctx->new_ptmsi_enc) return link_info; /* A new P-TMSI has been signalled in the message */ gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_ptmsi); LOGP(DGPRS, LOGL_INFO, "Assigning new P-TMSI %08x\n", new_ptmsi); /* Setup P-TMSIs */ link_info->sgsn_tlli.ptmsi = new_ptmsi; link_info->tlli.ptmsi = new_ptmsi; } else if (parse_ctx->tlli_enc && parse_ctx->llc && link_info) { uint32_t bss_tlli = gbproxy_map_tlli(parse_ctx->tlli, link_info, 1); gbproxy_validate_tlli(&link_info->sgsn_tlli, parse_ctx->tlli, 1); gbproxy_validate_tlli(&link_info->tlli, bss_tlli, 1); gbproxy_touch_link_info(peer, link_info, now); } else if (link_info) { gbproxy_touch_link_info(peer, link_info, now); } if (parse_ctx->imsi && link_info && link_info->imsi_len == 0) gbproxy_assign_imsi(peer, link_info, parse_ctx); return link_info; } int gbproxy_update_link_state_after( struct gbproxy_peer *peer, struct gbproxy_link_info *link_info, time_t now, struct gprs_gb_parse_context *parse_ctx) { int rc = 0; if (parse_ctx->invalidate_tlli && link_info) { int keep_info = peer->cfg->keep_link_infos == GBPROX_KEEP_ALWAYS || (peer->cfg->keep_link_infos == GBPROX_KEEP_REATTACH && parse_ctx->await_reattach) || (peer->cfg->keep_link_infos == GBPROX_KEEP_IDENTIFIED && link_info->imsi_len > 0); if (keep_info) { LOGP(DGPRS, LOGL_INFO, "Unregistering TLLI %08x\n", link_info->tlli.current); rc = gbproxy_unregister_link_info(peer, link_info); } else { LOGP(DGPRS, LOGL_INFO, "Removing TLLI %08x from list\n", link_info->tlli.current); gbproxy_delete_link_info(peer, link_info); rc = 1; } } else if (parse_ctx->to_bss && parse_ctx->tlli_enc && parse_ctx->new_ptmsi_enc && link_info) { /* A new PTMSI has been signaled in the message, * register new TLLI */ uint32_t new_sgsn_ptmsi = link_info->sgsn_tlli.ptmsi; uint32_t new_bss_ptmsi = link_info->tlli.ptmsi; uint32_t new_sgsn_tlli; uint32_t new_bss_tlli = 0; new_sgsn_tlli = gprs_tmsi2tlli(new_sgsn_ptmsi, TLLI_LOCAL); if (new_bss_ptmsi != GSM_RESERVED_TMSI) new_bss_tlli = gprs_tmsi2tlli(new_bss_ptmsi, TLLI_LOCAL); LOGP(DGPRS, LOGL_INFO, "Assigning new TLLI %08x to SGSN, %08x to BSS\n", new_sgsn_tlli, new_bss_tlli); gbproxy_reassign_tlli(&link_info->sgsn_tlli, peer, new_sgsn_tlli); gbproxy_reassign_tlli(&link_info->tlli, peer, new_bss_tlli); gbproxy_remove_matching_link_infos(peer, link_info); } gbproxy_remove_stale_link_infos(peer, now); return rc; } osmo-sgsn-1.3.0/src/gprs/gb_proxy_vty.c000066400000000000000000000547121327264017000201140ustar00rootroot00000000000000/* * (C) 2010 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static struct gbproxy_config *g_cfg = NULL; /* * vty code for mgcp below */ static struct cmd_node gbproxy_node = { GBPROXY_NODE, "%s(config-gbproxy)# ", 1, }; static const struct value_string keep_modes[] = { {GBPROX_KEEP_NEVER, "never"}, {GBPROX_KEEP_REATTACH, "re-attach"}, {GBPROX_KEEP_IDENTIFIED, "identified"}, {GBPROX_KEEP_ALWAYS, "always"}, {0, NULL} }; static const struct value_string match_ids[] = { {GBPROX_MATCH_PATCHING, "patching"}, {GBPROX_MATCH_ROUTING, "routing"}, {0, NULL} }; static void gbprox_vty_print_peer(struct vty *vty, struct gbproxy_peer *peer) { struct gprs_ra_id raid; gsm48_parse_ra(&raid, peer->ra); vty_out(vty, "NSEI %5u, PTP-BVCI %5u, " "RAI %s", peer->nsei, peer->bvci, osmo_rai_name(&raid)); if (peer->blocked) vty_out(vty, " [BVC-BLOCKED]"); vty_out(vty, "%s", VTY_NEWLINE); } static int config_write_gbproxy(struct vty *vty) { enum gbproxy_match_id match_id; vty_out(vty, "gbproxy%s", VTY_NEWLINE); vty_out(vty, " sgsn nsei %u%s", g_cfg->nsip_sgsn_nsei, VTY_NEWLINE); if (g_cfg->core_plmn.mcc > 0) vty_out(vty, " core-mobile-country-code %s%s", osmo_mcc_name(g_cfg->core_plmn.mcc), VTY_NEWLINE); if (g_cfg->core_plmn.mnc > 0) vty_out(vty, " core-mobile-network-code %s%s", osmo_mnc_name(g_cfg->core_plmn.mnc, g_cfg->core_plmn.mnc_3_digits), VTY_NEWLINE); for (match_id = 0; match_id < ARRAY_SIZE(g_cfg->matches); ++match_id) { struct gbproxy_match *match = &g_cfg->matches[match_id]; if (match->re_str) vty_out(vty, " match-imsi %s %s%s", get_value_string(match_ids, match_id), match->re_str, VTY_NEWLINE); } if (g_cfg->core_apn != NULL) { if (g_cfg->core_apn_size > 0) { char str[500] = {0}; vty_out(vty, " core-access-point-name %s%s", osmo_apn_to_str(str, g_cfg->core_apn, g_cfg->core_apn_size), VTY_NEWLINE); } else { vty_out(vty, " core-access-point-name none%s", VTY_NEWLINE); } } if (g_cfg->route_to_sgsn2) vty_out(vty, " secondary-sgsn nsei %u%s", g_cfg->nsip_sgsn2_nsei, VTY_NEWLINE); if (g_cfg->tlli_max_age > 0) vty_out(vty, " link-list max-age %d%s", g_cfg->tlli_max_age, VTY_NEWLINE); if (g_cfg->tlli_max_len > 0) vty_out(vty, " link-list max-length %d%s", g_cfg->tlli_max_len, VTY_NEWLINE); vty_out(vty, " link-list keep-mode %s%s", get_value_string(keep_modes, g_cfg->keep_link_infos), VTY_NEWLINE); return CMD_SUCCESS; } DEFUN(cfg_gbproxy, cfg_gbproxy_cmd, "gbproxy", "Configure the Gb proxy") { vty->node = GBPROXY_NODE; return CMD_SUCCESS; } DEFUN(cfg_nsip_sgsn_nsei, cfg_nsip_sgsn_nsei_cmd, "sgsn nsei <0-65534>", "SGSN information\n" "NSEI to be used in the connection with the SGSN\n" "The NSEI\n") { unsigned int nsei = atoi(argv[0]); if (g_cfg->route_to_sgsn2 && g_cfg->nsip_sgsn2_nsei == nsei) { vty_out(vty, "SGSN NSEI %d conflicts with secondary SGSN NSEI%s", nsei, VTY_NEWLINE); return CMD_WARNING; } g_cfg->nsip_sgsn_nsei = nsei; return CMD_SUCCESS; } #define GBPROXY_CORE_MNC_STR "Use this network code for the core network\n" DEFUN(cfg_gbproxy_core_mnc, cfg_gbproxy_core_mnc_cmd, "core-mobile-network-code <1-999>", GBPROXY_CORE_MNC_STR "NCC value\n") { uint16_t mnc; bool mnc_3_digits; if (osmo_mnc_from_str(argv[0], &mnc, &mnc_3_digits)) { vty_out(vty, "%% Invalid MNC: %s%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } g_cfg->core_plmn.mnc = mnc; g_cfg->core_plmn.mnc_3_digits = mnc_3_digits; return CMD_SUCCESS; } DEFUN(cfg_gbproxy_no_core_mnc, cfg_gbproxy_no_core_mnc_cmd, "no core-mobile-network-code", NO_STR GBPROXY_CORE_MNC_STR) { g_cfg->core_plmn.mnc = 0; g_cfg->core_plmn.mnc_3_digits = false; return CMD_SUCCESS; } #define GBPROXY_CORE_MCC_STR "Use this country code for the core network\n" DEFUN(cfg_gbproxy_core_mcc, cfg_gbproxy_core_mcc_cmd, "core-mobile-country-code <1-999>", GBPROXY_CORE_MCC_STR "MCC value\n") { g_cfg->core_plmn.mcc = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_gbproxy_no_core_mcc, cfg_gbproxy_no_core_mcc_cmd, "no core-mobile-country-code", NO_STR GBPROXY_CORE_MCC_STR) { g_cfg->core_plmn.mcc = 0; return CMD_SUCCESS; } #define GBPROXY_MATCH_IMSI_STR "Restrict actions to certain IMSIs\n" DEFUN(cfg_gbproxy_match_imsi, cfg_gbproxy_match_imsi_cmd, "match-imsi (patching|routing) .REGEXP", GBPROXY_MATCH_IMSI_STR "Patch MS related information elements on match only\n" "Route to the secondary SGSN on match only\n" "Regular expression for the IMSI match\n") { const char *filter = argv[1]; const char *err_msg = NULL; struct gbproxy_match *match; enum gbproxy_match_id match_id = get_string_value(match_ids, argv[0]); OSMO_ASSERT(match_id >= GBPROX_MATCH_PATCHING && match_id < GBPROX_MATCH_LAST); match = &g_cfg->matches[match_id]; if (gbproxy_set_patch_filter(match, filter, &err_msg) != 0) { vty_out(vty, "Match expression invalid: %s%s", err_msg, VTY_NEWLINE); return CMD_WARNING; } g_cfg->acquire_imsi = 1; return CMD_SUCCESS; } DEFUN(cfg_gbproxy_no_match_imsi, cfg_gbproxy_no_match_imsi_cmd, "no match-imsi", NO_STR GBPROXY_MATCH_IMSI_STR) { enum gbproxy_match_id match_id; for (match_id = 0; match_id < ARRAY_SIZE(g_cfg->matches); ++match_id) gbproxy_clear_patch_filter(&g_cfg->matches[match_id]); g_cfg->acquire_imsi = 0; return CMD_SUCCESS; } #define GBPROXY_CORE_APN_STR "Use this access point name (APN) for the backbone\n" #define GBPROXY_CORE_APN_ARG_STR "Replace APN by this string\n" "Remove APN\n" static int set_core_apn(struct vty *vty, const char *apn) { int apn_len; if (!apn) { talloc_free(g_cfg->core_apn); g_cfg->core_apn = NULL; g_cfg->core_apn_size = 0; return CMD_SUCCESS; } apn_len = strlen(apn); if (apn_len >= 100) { vty_out(vty, "APN string too long (max 99 chars)%s", VTY_NEWLINE); return CMD_WARNING; } if (apn_len == 0) { talloc_free(g_cfg->core_apn); /* TODO: replace NULL */ g_cfg->core_apn = talloc_zero_size(NULL, 2); g_cfg->core_apn_size = 0; } else { /* TODO: replace NULL */ g_cfg->core_apn = talloc_realloc_size(NULL, g_cfg->core_apn, apn_len + 1); g_cfg->core_apn_size = gprs_str_to_apn(g_cfg->core_apn, apn_len + 1, apn); } return CMD_SUCCESS; } DEFUN(cfg_gbproxy_core_apn, cfg_gbproxy_core_apn_cmd, "core-access-point-name (APN|none)", GBPROXY_CORE_APN_STR GBPROXY_CORE_APN_ARG_STR) { if (strcmp(argv[0], "none") == 0) return set_core_apn(vty, ""); else return set_core_apn(vty, argv[0]); } DEFUN(cfg_gbproxy_no_core_apn, cfg_gbproxy_no_core_apn_cmd, "no core-access-point-name", NO_STR GBPROXY_CORE_APN_STR) { return set_core_apn(vty, NULL); } /* TODO: Remove the patch-ptmsi command, since P-TMSI patching is enabled * automatically when needed. This command is only left for manual testing * (e.g. doing P-TMSI patching without using a secondary SGSN) */ #define GBPROXY_PATCH_PTMSI_STR "Patch P-TMSI/TLLI\n" DEFUN(cfg_gbproxy_patch_ptmsi, cfg_gbproxy_patch_ptmsi_cmd, "patch-ptmsi", GBPROXY_PATCH_PTMSI_STR) { g_cfg->patch_ptmsi = 1; return CMD_SUCCESS; } DEFUN(cfg_gbproxy_no_patch_ptmsi, cfg_gbproxy_no_patch_ptmsi_cmd, "no patch-ptmsi", NO_STR GBPROXY_PATCH_PTMSI_STR) { g_cfg->patch_ptmsi = 0; return CMD_SUCCESS; } /* TODO: Remove the acquire-imsi command, since that feature is enabled * automatically when IMSI matching is enabled. This command is only left for * manual testing (e.g. doing IMSI acquisition without IMSI based patching) */ #define GBPROXY_ACQUIRE_IMSI_STR "Acquire the IMSI before establishing a LLC connection (Experimental)\n" DEFUN(cfg_gbproxy_acquire_imsi, cfg_gbproxy_acquire_imsi_cmd, "acquire-imsi", GBPROXY_ACQUIRE_IMSI_STR) { g_cfg->acquire_imsi = 1; return CMD_SUCCESS; } DEFUN(cfg_gbproxy_no_acquire_imsi, cfg_gbproxy_no_acquire_imsi_cmd, "no acquire-imsi", NO_STR GBPROXY_ACQUIRE_IMSI_STR) { g_cfg->acquire_imsi = 0; return CMD_SUCCESS; } #define GBPROXY_SECOND_SGSN_STR "Route matching LLC connections to a second SGSN (Experimental)\n" DEFUN(cfg_gbproxy_secondary_sgsn, cfg_gbproxy_secondary_sgsn_cmd, "secondary-sgsn nsei <0-65534>", GBPROXY_SECOND_SGSN_STR "NSEI to be used in the connection with the SGSN\n" "The NSEI\n") { unsigned int nsei = atoi(argv[0]); if (g_cfg->nsip_sgsn_nsei == nsei) { vty_out(vty, "Secondary SGSN NSEI %d conflicts with primary SGSN NSEI%s", nsei, VTY_NEWLINE); return CMD_WARNING; } g_cfg->route_to_sgsn2 = 1; g_cfg->nsip_sgsn2_nsei = nsei; g_cfg->patch_ptmsi = 1; return CMD_SUCCESS; } DEFUN(cfg_gbproxy_no_secondary_sgsn, cfg_gbproxy_no_secondary_sgsn_cmd, "no secondary-sgsn", NO_STR GBPROXY_SECOND_SGSN_STR) { g_cfg->route_to_sgsn2 = 0; g_cfg->nsip_sgsn2_nsei = 0xFFFF; g_cfg->patch_ptmsi = 0; return CMD_SUCCESS; } #define GBPROXY_LINK_LIST_STR "Set TLLI list parameters\n" #define GBPROXY_MAX_AGE_STR "Limit maximum age\n" DEFUN(cfg_gbproxy_link_list_max_age, cfg_gbproxy_link_list_max_age_cmd, "link-list max-age <1-999999>", GBPROXY_LINK_LIST_STR GBPROXY_MAX_AGE_STR "Maximum age in seconds\n") { g_cfg->tlli_max_age = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_gbproxy_link_list_no_max_age, cfg_gbproxy_link_list_no_max_age_cmd, "no link-list max-age", NO_STR GBPROXY_LINK_LIST_STR GBPROXY_MAX_AGE_STR) { g_cfg->tlli_max_age = 0; return CMD_SUCCESS; } #define GBPROXY_MAX_LEN_STR "Limit list length\n" DEFUN(cfg_gbproxy_link_list_max_len, cfg_gbproxy_link_list_max_len_cmd, "link-list max-length <1-99999>", GBPROXY_LINK_LIST_STR GBPROXY_MAX_LEN_STR "Maximum number of logical links in the list\n") { g_cfg->tlli_max_len = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_gbproxy_link_list_no_max_len, cfg_gbproxy_link_list_no_max_len_cmd, "no link-list max-length", NO_STR GBPROXY_LINK_LIST_STR GBPROXY_MAX_LEN_STR) { g_cfg->tlli_max_len = 0; return CMD_SUCCESS; } DEFUN(cfg_gbproxy_link_list_keep_mode, cfg_gbproxy_link_list_keep_mode_cmd, "link-list keep-mode (never|re-attach|identified|always)", GBPROXY_LINK_LIST_STR "How to keep entries for detached logical links\n" "Discard entry immediately after detachment\n" "Keep entry if a re-attachment has be requested\n" "Keep entry if it associated with an IMSI\n" "Don't discard entries after detachment\n") { int val = get_string_value(keep_modes, argv[0]); OSMO_ASSERT(val >= GBPROX_KEEP_NEVER && val <= GBPROX_KEEP_ALWAYS); g_cfg->keep_link_infos = val; return CMD_SUCCESS; } DEFUN(show_gbproxy, show_gbproxy_cmd, "show gbproxy [stats]", SHOW_STR "Display information about the Gb proxy\n" "Show statistics\n") { struct gbproxy_peer *peer; int show_stats = argc >= 1; if (show_stats) vty_out_rate_ctr_group(vty, "", g_cfg->ctrg); llist_for_each_entry(peer, &g_cfg->bts_peers, list) { gbprox_vty_print_peer(vty, peer); if (show_stats) vty_out_rate_ctr_group(vty, " ", peer->ctrg); } return CMD_SUCCESS; } DEFUN(show_gbproxy_links, show_gbproxy_links_cmd, "show gbproxy links", SHOW_STR "Display information about the Gb proxy\n" "Show logical links\n") { struct gbproxy_peer *peer; char mi_buf[200]; time_t now; struct timespec ts = {0,}; clock_gettime(CLOCK_MONOTONIC, &ts); now = ts.tv_sec; llist_for_each_entry(peer, &g_cfg->bts_peers, list) { struct gbproxy_link_info *link_info; struct gbproxy_patch_state *state = &peer->patch_state; gbprox_vty_print_peer(vty, peer); llist_for_each_entry(link_info, &state->logical_links, list) { time_t age = now - link_info->timestamp; int stored_msgs = 0; struct llist_head *iter; llist_for_each(iter, &link_info->stored_msgs) stored_msgs++; if (link_info->imsi > 0) { snprintf(mi_buf, sizeof(mi_buf), "(invalid)"); gsm48_mi_to_string(mi_buf, sizeof(mi_buf), link_info->imsi, link_info->imsi_len); } else { snprintf(mi_buf, sizeof(mi_buf), "(none)"); } vty_out(vty, " TLLI %08x, IMSI %s, AGE %d", link_info->tlli.current, mi_buf, (int)age); if (stored_msgs) vty_out(vty, ", STORED %d", stored_msgs); if (g_cfg->route_to_sgsn2) vty_out(vty, ", SGSN NSEI %d", link_info->sgsn_nsei); if (link_info->is_deregistered) vty_out(vty, ", DE-REGISTERED"); vty_out(vty, "%s", VTY_NEWLINE); } } return CMD_SUCCESS; } DEFUN(delete_gb_bvci, delete_gb_bvci_cmd, "delete-gbproxy-peer <0-65534> bvci <2-65534>", "Delete a GBProxy peer by NSEI and optionally BVCI\n" "NSEI number\n" "Only delete peer with a matching BVCI\n" "BVCI number\n") { const uint16_t nsei = atoi(argv[0]); const uint16_t bvci = atoi(argv[1]); int counter; counter = gbproxy_cleanup_peers(g_cfg, nsei, bvci); if (counter == 0) { vty_out(vty, "BVC not found%s", VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } DEFUN(delete_gb_nsei, delete_gb_nsei_cmd, "delete-gbproxy-peer <0-65534> (only-bvc|only-nsvc|all) [dry-run]", "Delete a GBProxy peer by NSEI and optionally BVCI\n" "NSEI number\n" "Only delete BSSGP connections (BVC)\n" "Only delete dynamic NS connections (NS-VC)\n" "Delete BVC and dynamic NS connections\n" "Show what would be deleted instead of actually deleting\n" ) { const uint16_t nsei = atoi(argv[0]); const char *mode = argv[1]; int dry_run = argc > 2; int delete_bvc = 0; int delete_nsvc = 0; int counter; if (strcmp(mode, "only-bvc") == 0) delete_bvc = 1; else if (strcmp(mode, "only-nsvc") == 0) delete_nsvc = 1; else delete_bvc = delete_nsvc = 1; if (delete_bvc) { if (!dry_run) counter = gbproxy_cleanup_peers(g_cfg, nsei, 0); else { struct gbproxy_peer *peer; counter = 0; llist_for_each_entry(peer, &g_cfg->bts_peers, list) { if (peer->nsei != nsei) continue; vty_out(vty, "BVC: "); gbprox_vty_print_peer(vty, peer); counter += 1; } } vty_out(vty, "%sDeleted %d BVC%s", dry_run ? "Not " : "", counter, VTY_NEWLINE); } if (delete_nsvc) { struct gprs_ns_inst *nsi = g_cfg->nsi; struct gprs_nsvc *nsvc, *nsvc2; counter = 0; llist_for_each_entry_safe(nsvc, nsvc2, &nsi->gprs_nsvcs, list) { if (nsvc->nsei != nsei) continue; if (nsvc->persistent) continue; if (!dry_run) gprs_nsvc_delete(nsvc); else vty_out(vty, "NS-VC: NSEI %5u, NS-VCI %5u, " "remote %s%s", nsvc->nsei, nsvc->nsvci, gprs_ns_ll_str(nsvc), VTY_NEWLINE); counter += 1; } vty_out(vty, "%sDeleted %d NS-VC%s", dry_run ? "Not " : "", counter, VTY_NEWLINE); } return CMD_SUCCESS; } #define GBPROXY_DELETE_LINK_STR \ "Delete a GBProxy logical link entry by NSEI and identification\nNSEI number\n" DEFUN(delete_gb_link_by_id, delete_gb_link_by_id_cmd, "delete-gbproxy-link <0-65534> (tlli|imsi|sgsn-nsei) IDENT", GBPROXY_DELETE_LINK_STR "Delete entries with a matching TLLI (hex)\n" "Delete entries with a matching IMSI\n" "Delete entries with a matching SGSN NSEI\n" "Identification to match\n") { const uint16_t nsei = atoi(argv[0]); enum {MATCH_TLLI = 't', MATCH_IMSI = 'i', MATCH_SGSN = 's'} match; uint32_t ident = 0; const char *imsi = NULL; struct gbproxy_peer *peer = 0; struct gbproxy_link_info *link_info, *nxt; struct gbproxy_patch_state *state; char mi_buf[200]; int found = 0; match = argv[1][0]; switch (match) { case MATCH_TLLI: ident = strtoll(argv[2], NULL, 16); break; case MATCH_IMSI: imsi = argv[2]; break; case MATCH_SGSN: ident = strtoll(argv[2], NULL, 0); break; }; peer = gbproxy_peer_by_nsei(g_cfg, nsei); if (!peer) { vty_out(vty, "Didn't find peer with NSEI %d%s", nsei, VTY_NEWLINE); return CMD_WARNING; } state = &peer->patch_state; llist_for_each_entry_safe(link_info, nxt, &state->logical_links, list) { switch (match) { case MATCH_TLLI: if (link_info->tlli.current != ident) continue; break; case MATCH_SGSN: if (link_info->sgsn_nsei != ident) continue; break; case MATCH_IMSI: if (!link_info->imsi) continue; mi_buf[0] = '\0'; gsm48_mi_to_string(mi_buf, sizeof(mi_buf), link_info->imsi, link_info->imsi_len); if (strcmp(mi_buf, imsi) != 0) continue; break; } vty_out(vty, "Deleting link with TLLI %08x%s", link_info->tlli.current, VTY_NEWLINE); gbproxy_delete_link_info(peer, link_info); found += 1; } if (!found && argc >= 2) { vty_out(vty, "Didn't find link entry with %s %s%s", argv[1], argv[2], VTY_NEWLINE); } return CMD_SUCCESS; } DEFUN(delete_gb_link, delete_gb_link_cmd, "delete-gbproxy-link <0-65534> (stale|de-registered)", GBPROXY_DELETE_LINK_STR "Delete stale entries\n" "Delete de-registered entries\n") { const uint16_t nsei = atoi(argv[0]); enum {MATCH_STALE = 's', MATCH_DEREGISTERED = 'd'} match; struct gbproxy_peer *peer = 0; struct gbproxy_link_info *link_info, *nxt; struct gbproxy_patch_state *state; time_t now; struct timespec ts = {0,}; int found = 0; match = argv[1][0]; peer = gbproxy_peer_by_nsei(g_cfg, nsei); if (!peer) { vty_out(vty, "Didn't find peer with NSEI %d%s", nsei, VTY_NEWLINE); return CMD_WARNING; } state = &peer->patch_state; clock_gettime(CLOCK_MONOTONIC, &ts); now = ts.tv_sec; if (match == MATCH_STALE) { found = gbproxy_remove_stale_link_infos(peer, now); if (found) vty_out(vty, "Deleted %d stale logical link%s%s", found, found == 1 ? "" : "s", VTY_NEWLINE); } else { llist_for_each_entry_safe(link_info, nxt, &state->logical_links, list) { if (!link_info->is_deregistered) continue; gbproxy_delete_link_info(peer, link_info); found += 1; } } if (found) vty_out(vty, "Deleted %d %s logical link%s%s", found, argv[1], found == 1 ? "" : "s", VTY_NEWLINE); return CMD_SUCCESS; } /* * legacy commands to provide an upgrade path from "broken" releases * or pre-releases */ DEFUN_DEPRECATED(cfg_gbproxy_broken_apn_match, cfg_gbproxy_broken_apn_match_cmd, "core-access-point-name none match-imsi .REGEXP", GBPROXY_CORE_APN_STR GBPROXY_MATCH_IMSI_STR "Remove APN\n" "Patch MS related information elements on match only\n" "Route to the secondary SGSN on match only\n" "Regular expression for the IMSI match\n") { const char *filter = argv[0]; const char *err_msg = NULL; struct gbproxy_match *match; enum gbproxy_match_id match_id = get_string_value(match_ids, "patching"); /* apply APN none */ set_core_apn(vty, ""); /* do the matching... with copy and paste */ OSMO_ASSERT(match_id >= GBPROX_MATCH_PATCHING && match_id < GBPROX_MATCH_LAST); match = &g_cfg->matches[match_id]; if (gbproxy_set_patch_filter(match, filter, &err_msg) != 0) { vty_out(vty, "Match expression invalid: %s%s", err_msg, VTY_NEWLINE); return CMD_WARNING; } g_cfg->acquire_imsi = 1; return CMD_SUCCESS; } #define GBPROXY_TLLI_LIST_STR "Set TLLI list parameters\n" #define GBPROXY_MAX_LEN_STR "Limit list length\n" DEFUN_DEPRECATED(cfg_gbproxy_depr_tlli_list_max_len, cfg_gbproxy_depr_tlli_list_max_len_cmd, "tlli-list max-length <1-99999>", GBPROXY_TLLI_LIST_STR GBPROXY_MAX_LEN_STR "Maximum number of TLLIs in the list\n") { g_cfg->tlli_max_len = atoi(argv[0]); return CMD_SUCCESS; } int gbproxy_vty_init(void) { install_element_ve(&show_gbproxy_cmd); install_element_ve(&show_gbproxy_links_cmd); install_element(ENABLE_NODE, &delete_gb_bvci_cmd); install_element(ENABLE_NODE, &delete_gb_nsei_cmd); install_element(ENABLE_NODE, &delete_gb_link_by_id_cmd); install_element(ENABLE_NODE, &delete_gb_link_cmd); install_element(CONFIG_NODE, &cfg_gbproxy_cmd); install_node(&gbproxy_node, config_write_gbproxy); install_element(GBPROXY_NODE, &cfg_nsip_sgsn_nsei_cmd); install_element(GBPROXY_NODE, &cfg_gbproxy_core_mcc_cmd); install_element(GBPROXY_NODE, &cfg_gbproxy_core_mnc_cmd); install_element(GBPROXY_NODE, &cfg_gbproxy_match_imsi_cmd); install_element(GBPROXY_NODE, &cfg_gbproxy_core_apn_cmd); install_element(GBPROXY_NODE, &cfg_gbproxy_secondary_sgsn_cmd); install_element(GBPROXY_NODE, &cfg_gbproxy_patch_ptmsi_cmd); install_element(GBPROXY_NODE, &cfg_gbproxy_acquire_imsi_cmd); install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_max_age_cmd); install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_max_len_cmd); install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_keep_mode_cmd); install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_mcc_cmd); install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_mnc_cmd); install_element(GBPROXY_NODE, &cfg_gbproxy_no_match_imsi_cmd); install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_apn_cmd); install_element(GBPROXY_NODE, &cfg_gbproxy_no_secondary_sgsn_cmd); install_element(GBPROXY_NODE, &cfg_gbproxy_no_patch_ptmsi_cmd); install_element(GBPROXY_NODE, &cfg_gbproxy_no_acquire_imsi_cmd); install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_no_max_age_cmd); install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_no_max_len_cmd); /* broken or deprecated to allow an upgrade path */ install_element(GBPROXY_NODE, &cfg_gbproxy_broken_apn_match_cmd); install_element(GBPROXY_NODE, &cfg_gbproxy_depr_tlli_list_max_len_cmd); return 0; } int gbproxy_parse_config(const char *config_file, struct gbproxy_config *cfg) { int rc; g_cfg = cfg; rc = vty_read_config_file(config_file, NULL); if (rc < 0) { fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file); return rc; } return 0; } osmo-sgsn-1.3.0/src/gprs/gprs_gb_parse.c000066400000000000000000000377201327264017000201760ustar00rootroot00000000000000/* GPRS Gb message parser */ /* (C) 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 static int gprs_gb_parse_gmm_attach_req(uint8_t *data, size_t data_len, struct gprs_gb_parse_context *parse_ctx) { uint8_t *value; size_t value_len; parse_ctx->llc_msg_name = "ATTACH_REQ"; /* Skip MS network capability */ if (osmo_shift_lv(&data, &data_len, NULL, &value_len) <= 0 || value_len < 1 || value_len > 8) /* invalid */ return 0; /* Skip Attach type */ /* Skip Ciphering key sequence number */ /* Skip DRX parameter */ osmo_shift_v_fixed(&data, &data_len, 3, NULL); /* Get Mobile identity */ if (osmo_shift_lv(&data, &data_len, &value, &value_len) <= 0 || value_len < 5 || value_len > 8) /* invalid */ return 0; if (gprs_is_mi_tmsi(value, value_len)) { parse_ctx->ptmsi_enc = value + 1; } else if (gprs_is_mi_imsi(value, value_len)) { parse_ctx->imsi = value; parse_ctx->imsi_len = value_len; } if (osmo_shift_v_fixed(&data, &data_len, 6, &value) <= 0) return 0; parse_ctx->old_raid_enc = value; return 1; } static int gprs_gb_parse_gmm_attach_ack(uint8_t *data, size_t data_len, struct gprs_gb_parse_context *parse_ctx) { uint8_t *value; size_t value_len; parse_ctx->llc_msg_name = "ATTACH_ACK"; /* Skip Attach result */ /* Skip Force to standby */ /* Skip Periodic RA update timer */ /* Skip Radio priority for SMS */ /* Skip Spare half octet */ osmo_shift_v_fixed(&data, &data_len, 3, NULL); if (osmo_shift_v_fixed(&data, &data_len, 6, &value) <= 0) return 0; parse_ctx->raid_enc = value; /* Skip P-TMSI signature (P-TMSI signature, opt, TV, length 4) */ osmo_match_shift_tv_fixed(&data, &data_len, GSM48_IE_GMM_PTMSI_SIG, 3, NULL); /* Skip Negotiated READY timer value (GPRS timer, opt, TV, length 2) */ osmo_match_shift_tv_fixed(&data, &data_len, GSM48_IE_GMM_TIMER_READY, 1, NULL); /* Allocated P-TMSI (Mobile identity, opt, TLV, length 7) */ if (osmo_match_shift_tlv(&data, &data_len, GSM48_IE_GMM_ALLOC_PTMSI, &value, &value_len) > 0 && gprs_is_mi_tmsi(value, value_len)) parse_ctx->new_ptmsi_enc = value + 1; return 1; } static int gprs_gb_parse_gmm_attach_rej(uint8_t *data, size_t data_len, struct gprs_gb_parse_context *parse_ctx) { uint8_t *value; parse_ctx->llc_msg_name = "ATTACH_REJ"; /* GMM cause */ if (osmo_shift_v_fixed(&data, &data_len, 1, &value) <= 0) return 0; parse_ctx->invalidate_tlli = 1; return 1; } static int gprs_gb_parse_gmm_detach_req(uint8_t *data, size_t data_len, struct gprs_gb_parse_context *parse_ctx) { uint8_t *value; size_t value_len; int detach_type; int power_off; parse_ctx->llc_msg_name = "DETACH_REQ"; /* Skip spare half octet */ /* Get Detach type */ if (osmo_shift_v_fixed(&data, &data_len, 1, &value) <= 0) /* invalid */ return 0; detach_type = *value & 0x07; power_off = *value & 0x08 ? 1 : 0; if (parse_ctx->to_bss) { /* Network originated */ if (detach_type == GPRS_DET_T_MT_REATT_REQ) parse_ctx->await_reattach = 1; } else { /* Mobile originated */ if (power_off) parse_ctx->invalidate_tlli = 1; /* Get P-TMSI (Mobile identity), see GSM 24.008, 9.4.5.2 */ if (osmo_match_shift_tlv(&data, &data_len, GSM48_IE_GMM_ALLOC_PTMSI, &value, &value_len) > 0) { if (gprs_is_mi_tmsi(value, value_len)) parse_ctx->ptmsi_enc = value + 1; } } return 1; } static int gprs_gb_parse_gmm_ra_upd_req(uint8_t *data, size_t data_len, struct gprs_gb_parse_context *parse_ctx) { uint8_t *value; parse_ctx->llc_msg_name = "RA_UPD_REQ"; /* Skip Update type */ /* Skip GPRS ciphering key sequence number */ osmo_shift_v_fixed(&data, &data_len, 1, NULL); if (osmo_shift_v_fixed(&data, &data_len, 6, &value) <= 0) return 0; parse_ctx->old_raid_enc = value; return 1; } static int gprs_gb_parse_gmm_ra_upd_rej(uint8_t *data, size_t data_len, struct gprs_gb_parse_context *parse_ctx) { uint8_t *value; uint8_t cause; int force_standby; parse_ctx->llc_msg_name = "RA_UPD_REJ"; /* GMM cause */ if (osmo_shift_v_fixed(&data, &data_len, 1, &value) <= 0) return 0; cause = value[0]; /* Force to standby, 1/2 */ /* spare bits, 1/2 */ if (osmo_shift_v_fixed(&data, &data_len, 1, &value) <= 0) return 0; force_standby = (value[0] & 0x07) == 0x01; if (cause == GMM_CAUSE_IMPL_DETACHED && !force_standby) parse_ctx->await_reattach = 1; parse_ctx->invalidate_tlli = 1; return 1; } static int gprs_gb_parse_gmm_ra_upd_ack(uint8_t *data, size_t data_len, struct gprs_gb_parse_context *parse_ctx) { uint8_t *value; size_t value_len; parse_ctx->llc_msg_name = "RA_UPD_ACK"; /* Skip Force to standby */ /* Skip Update result */ /* Skip Periodic RA update timer */ osmo_shift_v_fixed(&data, &data_len, 2, NULL); if (osmo_shift_v_fixed(&data, &data_len, 6, &value) <= 0) return 0; parse_ctx->raid_enc = value; /* Skip P-TMSI signature (P-TMSI signature, opt, TV, length 4) */ osmo_match_shift_tv_fixed(&data, &data_len, GSM48_IE_GMM_PTMSI_SIG, 3, NULL); /* Allocated P-TMSI (Mobile identity, opt, TLV, length 7) */ if (osmo_match_shift_tlv(&data, &data_len, GSM48_IE_GMM_ALLOC_PTMSI, &value, &value_len) > 0 && gprs_is_mi_tmsi(value, value_len)) parse_ctx->new_ptmsi_enc = value + 1; return 1; } static int gprs_gb_parse_gmm_ptmsi_reall_cmd(uint8_t *data, size_t data_len, struct gprs_gb_parse_context *parse_ctx) { uint8_t *value; size_t value_len; parse_ctx->llc_msg_name = "PTMSI_REALL_CMD"; LOGP(DLLC, LOGL_NOTICE, "Got P-TMSI Reallocation Command which is not covered by unit tests yet.\n"); /* Allocated P-TMSI */ if (osmo_shift_lv(&data, &data_len, &value, &value_len) > 0 && gprs_is_mi_tmsi(value, value_len)) parse_ctx->new_ptmsi_enc = value + 1; if (osmo_shift_v_fixed(&data, &data_len, 6, &value) <= 0) return 0; parse_ctx->raid_enc = value; return 1; } static int gprs_gb_parse_gmm_id_resp(uint8_t *data, size_t data_len, struct gprs_gb_parse_context *parse_ctx) { uint8_t *value; size_t value_len; parse_ctx->llc_msg_name = "ID_RESP"; /* Mobile identity, Mobile identity 10.5.1.4, M LV 2-10 */ if (osmo_shift_lv(&data, &data_len, &value, &value_len) <= 0 || value_len < 1 || value_len > 9) /* invalid */ return 0; if (gprs_is_mi_tmsi(value, value_len)) { parse_ctx->ptmsi_enc = value + 1; } else if (gprs_is_mi_imsi(value, value_len)) { parse_ctx->imsi = value; parse_ctx->imsi_len = value_len; } return 1; } static int gprs_gb_parse_gsm_act_pdp_req(uint8_t *data, size_t data_len, struct gprs_gb_parse_context *parse_ctx) { ssize_t old_len; uint8_t *value; size_t value_len; parse_ctx->llc_msg_name = "ACT_PDP_REQ"; /* Skip Requested NSAPI */ /* Skip Requested LLC SAPI */ osmo_shift_v_fixed(&data, &data_len, 2, NULL); /* Skip Requested QoS (support 04.08 and 24.008) */ if (osmo_shift_lv(&data, &data_len, NULL, &value_len) <= 0 || value_len < 4 || value_len > 14) /* invalid */ return 0; /* Skip Requested PDP address */ if (osmo_shift_lv(&data, &data_len, NULL, &value_len) <= 0 || value_len < 2 || value_len > 18) /* invalid */ return 0; /* Access point name */ old_len = osmo_match_shift_tlv(&data, &data_len, GSM48_IE_GSM_APN, &value, &value_len); if (old_len > 0 && value_len >=1 && value_len <= 100) { parse_ctx->apn_ie = data - old_len; parse_ctx->apn_ie_len = old_len; } return 1; } int gprs_gb_parse_dtap(uint8_t *data, size_t data_len, struct gprs_gb_parse_context *parse_ctx) { struct gsm48_hdr *g48h; uint8_t pdisc; uint8_t msg_type; if (osmo_shift_v_fixed(&data, &data_len, sizeof(*g48h), (uint8_t **)&g48h) <= 0) return 0; parse_ctx->g48_hdr = g48h; pdisc = gsm48_hdr_pdisc(g48h); if (pdisc != GSM48_PDISC_MM_GPRS && pdisc != GSM48_PDISC_SM_GPRS) return 1; msg_type = gsm48_hdr_msg_type(g48h); switch (msg_type) { case GSM48_MT_GMM_ATTACH_REQ: return gprs_gb_parse_gmm_attach_req(data, data_len, parse_ctx); case GSM48_MT_GMM_ATTACH_REJ: return gprs_gb_parse_gmm_attach_rej(data, data_len, parse_ctx); case GSM48_MT_GMM_ATTACH_ACK: return gprs_gb_parse_gmm_attach_ack(data, data_len, parse_ctx); case GSM48_MT_GMM_RA_UPD_REQ: return gprs_gb_parse_gmm_ra_upd_req(data, data_len, parse_ctx); case GSM48_MT_GMM_RA_UPD_REJ: return gprs_gb_parse_gmm_ra_upd_rej(data, data_len, parse_ctx); case GSM48_MT_GMM_RA_UPD_ACK: return gprs_gb_parse_gmm_ra_upd_ack(data, data_len, parse_ctx); case GSM48_MT_GMM_PTMSI_REALL_CMD: return gprs_gb_parse_gmm_ptmsi_reall_cmd(data, data_len, parse_ctx); case GSM48_MT_GSM_ACT_PDP_REQ: return gprs_gb_parse_gsm_act_pdp_req(data, data_len, parse_ctx); case GSM48_MT_GMM_ID_RESP: return gprs_gb_parse_gmm_id_resp(data, data_len, parse_ctx); case GSM48_MT_GMM_DETACH_REQ: return gprs_gb_parse_gmm_detach_req(data, data_len, parse_ctx); case GSM48_MT_GMM_DETACH_ACK: parse_ctx->llc_msg_name = "DETACH_ACK"; parse_ctx->invalidate_tlli = 1; break; default: LOGP(DLLC, LOGL_NOTICE, "Unhandled GSM 04.08 message type %s for protocol discriminator %s.\n", get_value_string(gprs_msgt_gmm_names, msg_type), get_value_string(gsm48_pdisc_names, pdisc)); break; }; return 1; } int gprs_gb_parse_llc(uint8_t *llc, size_t llc_len, struct gprs_gb_parse_context *parse_ctx) { struct gprs_llc_hdr_parsed *ghp = &parse_ctx->llc_hdr_parsed; int rc; int fcs; /* parse LLC */ rc = gprs_llc_hdr_parse(ghp, llc, llc_len); gprs_llc_hdr_dump(ghp, NULL); if (rc != 0) { LOGP(DLLC, LOGL_NOTICE, "Error during LLC header parsing\n"); return 0; } fcs = gprs_llc_fcs(llc, ghp->crc_length); LOGP(DLLC, LOGL_DEBUG, "Got LLC message, CRC: %06x (computed %06x)\n", ghp->fcs, fcs); if (!ghp->data) return 0; if (ghp->sapi != GPRS_SAPI_GMM) return 1; if (ghp->cmd != GPRS_LLC_UI) return 1; if (ghp->is_encrypted) { parse_ctx->need_decryption = 1; return 0; } return gprs_gb_parse_dtap(ghp->data, ghp->data_len, parse_ctx); } int gprs_gb_parse_bssgp(uint8_t *bssgp, size_t bssgp_len, struct gprs_gb_parse_context *parse_ctx) { struct bssgp_normal_hdr *bgph; struct bssgp_ud_hdr *budh = NULL; struct tlv_parsed *tp = &parse_ctx->bssgp_tp; uint8_t pdu_type; uint8_t *data; size_t data_len; int rc; if (bssgp_len < sizeof(struct bssgp_normal_hdr)) return 0; bgph = (struct bssgp_normal_hdr *)bssgp; pdu_type = bgph->pdu_type; if (pdu_type == BSSGP_PDUT_UL_UNITDATA || pdu_type == BSSGP_PDUT_DL_UNITDATA) { if (bssgp_len < sizeof(struct bssgp_ud_hdr)) return 0; budh = (struct bssgp_ud_hdr *)bssgp; bgph = NULL; data = budh->data; data_len = bssgp_len - sizeof(*budh); } else { data = bgph->data; data_len = bssgp_len - sizeof(*bgph); } parse_ctx->pdu_type = pdu_type; parse_ctx->bud_hdr = budh; parse_ctx->bgp_hdr = bgph; parse_ctx->bssgp_data = data; parse_ctx->bssgp_data_len = data_len; if (bssgp_tlv_parse(tp, data, data_len) < 0) return 0; if (budh) parse_ctx->tlli_enc = (uint8_t *)&budh->tlli; if (TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) parse_ctx->bssgp_raid_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA); if (TLVP_PRESENT(tp, BSSGP_IE_CELL_ID)) parse_ctx->bssgp_raid_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_CELL_ID); if (TLVP_PRESENT(tp, BSSGP_IE_IMSI)) { parse_ctx->imsi = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_IMSI); parse_ctx->imsi_len = TLVP_LEN(tp, BSSGP_IE_IMSI); } if (TLVP_PRESENT(tp, BSSGP_IE_TLLI)) { if (parse_ctx->tlli_enc) /* This is TLLI old, don't confuse it with TLLI current */ parse_ctx->old_tlli_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_TLLI); else parse_ctx->tlli_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_TLLI); } if (TLVP_PRESENT(tp, BSSGP_IE_TMSI) && pdu_type == BSSGP_PDUT_PAGING_PS) parse_ctx->bssgp_ptmsi_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_TMSI); if (TLVP_PRESENT(tp, BSSGP_IE_LLC_PDU)) { uint8_t *llc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_LLC_PDU); size_t llc_len = TLVP_LEN(tp, BSSGP_IE_LLC_PDU); rc = gprs_gb_parse_llc(llc, llc_len, parse_ctx); if (!rc) return 0; parse_ctx->llc = llc; parse_ctx->llc_len = llc_len; } if (parse_ctx->tlli_enc) { uint32_t tmp_tlli; memcpy(&tmp_tlli, parse_ctx->tlli_enc, sizeof(tmp_tlli)); parse_ctx->tlli = ntohl(tmp_tlli); } if (parse_ctx->bssgp_raid_enc && parse_ctx->old_raid_enc && memcmp(parse_ctx->bssgp_raid_enc, parse_ctx->old_raid_enc, 6) != 0) parse_ctx->old_raid_is_foreign = 1; return 1; } void gprs_gb_log_parse_context(int log_level, struct gprs_gb_parse_context *parse_ctx, const char *default_msg_name) { const char *msg_name; const char *sep = ""; if (!parse_ctx->tlli_enc && !parse_ctx->ptmsi_enc && !parse_ctx->new_ptmsi_enc && !parse_ctx->bssgp_ptmsi_enc && !parse_ctx->imsi) return; msg_name = gprs_gb_message_name(parse_ctx, default_msg_name); if (parse_ctx->llc_msg_name) msg_name = parse_ctx->llc_msg_name; LOGP(DGPRS, log_level, "%s: Got", msg_name); if (parse_ctx->tlli_enc) { LOGPC(DGPRS, log_level, "%s TLLI %08x", sep, parse_ctx->tlli); sep = ","; } if (parse_ctx->old_tlli_enc) { LOGPC(DGPRS, log_level, "%s old TLLI %02x%02x%02x%02x", sep, parse_ctx->old_tlli_enc[0], parse_ctx->old_tlli_enc[1], parse_ctx->old_tlli_enc[2], parse_ctx->old_tlli_enc[3]); sep = ","; } if (parse_ctx->bssgp_raid_enc) { struct gprs_ra_id raid; gsm48_parse_ra(&raid, parse_ctx->bssgp_raid_enc); LOGPC(DGPRS, log_level, "%s BSSGP RAID %s", sep, osmo_rai_name(&raid)); sep = ","; } if (parse_ctx->raid_enc) { struct gprs_ra_id raid; gsm48_parse_ra(&raid, parse_ctx->raid_enc); LOGPC(DGPRS, log_level, "%s RAID %s", sep, osmo_rai_name(&raid)); sep = ","; } if (parse_ctx->old_raid_enc) { struct gprs_ra_id raid; gsm48_parse_ra(&raid, parse_ctx->old_raid_enc); LOGPC(DGPRS, log_level, "%s old RAID %s", sep, osmo_rai_name(&raid)); sep = ","; } if (parse_ctx->bssgp_ptmsi_enc) { uint32_t ptmsi = GSM_RESERVED_TMSI; gprs_parse_tmsi(parse_ctx->bssgp_ptmsi_enc, &ptmsi); LOGPC(DGPRS, log_level, "%s BSSGP PTMSI %08x", sep, ptmsi); sep = ","; } if (parse_ctx->ptmsi_enc) { uint32_t ptmsi = GSM_RESERVED_TMSI; gprs_parse_tmsi(parse_ctx->ptmsi_enc, &ptmsi); LOGPC(DGPRS, log_level, "%s PTMSI %08x", sep, ptmsi); sep = ","; } if (parse_ctx->new_ptmsi_enc) { uint32_t new_ptmsi = GSM_RESERVED_TMSI; gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_ptmsi); LOGPC(DGPRS, log_level, "%s new PTMSI %08x", sep, new_ptmsi); sep = ","; } if (parse_ctx->imsi) { char mi_buf[200]; mi_buf[0] = '\0'; gsm48_mi_to_string(mi_buf, sizeof(mi_buf), parse_ctx->imsi, parse_ctx->imsi_len); LOGPC(DGPRS, log_level, "%s IMSI %s", sep, mi_buf); sep = ","; } if (parse_ctx->invalidate_tlli) { LOGPC(DGPRS, log_level, "%s invalidate", sep); sep = ","; } if (parse_ctx->await_reattach) { LOGPC(DGPRS, log_level, "%s re-attach", sep); sep = ","; } LOGPC(DGPRS, log_level, "\n"); } const char *gprs_gb_message_name(const struct gprs_gb_parse_context *parse_ctx, const char *default_msg_name) { if (parse_ctx->llc_msg_name) return parse_ctx->llc_msg_name; if (parse_ctx->g48_hdr) return "GMM"; if (parse_ctx->llc) return "LLC"; if (parse_ctx->bud_hdr) return "BSSGP-UNITDATA"; if (parse_ctx->bgp_hdr) return "BSSGP"; return "unknown"; } osmo-sgsn-1.3.0/src/gprs/gprs_gmm.c000066400000000000000000002510161327264017000171700ustar00rootroot00000000000000/* 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 */ /* (C) 2009-2015 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 . * */ #include #include #include #include #include #include #include #include #include #include "bscconfig.h" #include #include #include #include #include #include #include #include #include #include #include #ifdef BUILD_IU #include #include #include #endif #include #include #include #include #include #include #include #include #include #include #define PTMSI_ALLOC extern struct sgsn_instance *sgsn; extern void *tall_bsc_ctx; static const struct tlv_definition gsm48_gmm_att_tlvdef = { .def = { [GSM48_IE_GMM_CIPH_CKSN] = { TLV_TYPE_FIXED, 1 }, [GSM48_IE_GMM_TIMER_READY] = { TLV_TYPE_TV, 1 }, [GSM48_IE_GMM_ALLOC_PTMSI] = { TLV_TYPE_TLV, 0 }, [GSM48_IE_GMM_PTMSI_SIG] = { TLV_TYPE_FIXED, 3 }, [GSM48_IE_GMM_AUTH_RAND] = { TLV_TYPE_FIXED, 16 }, [GSM48_IE_GMM_AUTH_SRES] = { TLV_TYPE_FIXED, 4 }, [GSM48_IE_GMM_AUTH_RES_EXT] = { TLV_TYPE_TLV, 0 }, [GSM48_IE_GMM_AUTH_FAIL_PAR] = { TLV_TYPE_TLV, 0 }, [GSM48_IE_GMM_IMEISV] = { TLV_TYPE_TLV, 0 }, [GSM48_IE_GMM_DRX_PARAM] = { TLV_TYPE_FIXED, 2 }, [GSM48_IE_GMM_MS_NET_CAPA] = { TLV_TYPE_TLV, 0 }, [GSM48_IE_GMM_PDP_CTX_STATUS] = { TLV_TYPE_TLV, 0 }, [GSM48_IE_GMM_PS_LCS_CAPA] = { TLV_TYPE_TLV, 0 }, [GSM48_IE_GMM_GMM_MBMS_CTX_ST] = { TLV_TYPE_TLV, 0 }, }, }; static const struct tlv_definition gsm48_sm_att_tlvdef = { .def = { [GSM48_IE_GSM_APN] = { TLV_TYPE_TLV, 0 }, [GSM48_IE_GSM_PROTO_CONF_OPT] = { TLV_TYPE_TLV, 0 }, [GSM48_IE_GSM_PDP_ADDR] = { TLV_TYPE_TLV, 0 }, [GSM48_IE_GSM_AA_TMR] = { TLV_TYPE_TV, 1 }, [GSM48_IE_GSM_NAME_FULL] = { TLV_TYPE_TLV, 0 }, [GSM48_IE_GSM_NAME_SHORT] = { TLV_TYPE_TLV, 0 }, [GSM48_IE_GSM_TIMEZONE] = { TLV_TYPE_FIXED, 1 }, [GSM48_IE_GSM_UTC_AND_TZ] = { TLV_TYPE_FIXED, 7 }, [GSM48_IE_GSM_LSA_ID] = { TLV_TYPE_TLV, 0 }, }, }; static const struct value_string gprs_pmm_state_names[] = { { PMM_DETACHED, "PMM DETACH" }, { PMM_CONNECTED, "PMM CONNECTED" }, { PMM_IDLE, "PMM IDLE" }, { MM_IDLE, "MM IDLE" }, { MM_READY, "MM READY" }, { MM_STANDBY, "MM STANDBY" }, { 0, NULL } }; static int gsm48_gmm_authorize(struct sgsn_mm_ctx *ctx); static void mmctx_change_gtpu_endpoints_to_sgsn(struct sgsn_mm_ctx *mm_ctx) { struct sgsn_pdp_ctx *pdp; llist_for_each_entry(pdp, &mm_ctx->pdp_list, list) { LOGMMCTXP(LOGL_INFO, mm_ctx, "Changing GTP-U endpoints %s -> %s\n", sgsn_gtp_ntoa(&pdp->lib->gsnlu), inet_ntoa(sgsn->cfg.gtp_listenaddr.sin_addr)); sgsn_pdp_upd_gtp_u(pdp, &sgsn->cfg.gtp_listenaddr.sin_addr, sizeof(sgsn->cfg.gtp_listenaddr.sin_addr)); } } void mmctx_set_pmm_state(struct sgsn_mm_ctx *ctx, enum gprs_pmm_state state) { if (ctx->ran_type != MM_CTX_T_UTRAN_Iu) return; if (ctx->pmm_state == state) return; LOGMMCTXP(LOGL_INFO, ctx, "Changing PMM state from %s to %s\n", get_value_string(gprs_pmm_state_names, ctx->pmm_state), get_value_string(gprs_pmm_state_names, state)); switch (state) { case PMM_IDLE: /* TODO: start RA Upd timer */ mmctx_change_gtpu_endpoints_to_sgsn(ctx); break; case PMM_CONNECTED: break; default: break; } ctx->pmm_state = state; } void mmctx_set_mm_state(struct sgsn_mm_ctx *ctx, enum gprs_pmm_state state) { if (ctx->ran_type != MM_CTX_T_GERAN_Gb) return; if (ctx->pmm_state == state) return; LOGMMCTXP(LOGL_INFO, ctx, "Changing MM state from %s to %s\n", get_value_string(gprs_pmm_state_names, ctx->pmm_state), get_value_string(gprs_pmm_state_names, state)); ctx->pmm_state = state; } #ifdef BUILD_IU int sgsn_ranap_rab_ass_resp(struct sgsn_mm_ctx *ctx, RANAP_RAB_SetupOrModifiedItemIEs_t *setup_ies); int sgsn_ranap_iu_event(struct ranap_ue_conn_ctx *ctx, enum ranap_iu_event_type type, void *data) { struct sgsn_mm_ctx *mm; int rc = -1; mm = sgsn_mm_ctx_by_ue_ctx(ctx); #define REQUIRE_MM \ if (!mm) { \ LOGP(DRANAP, LOGL_NOTICE, "Cannot find mm ctx for IU event %d\n", type); \ return rc; \ } switch (type) { case RANAP_IU_EVENT_RAB_ASSIGN: REQUIRE_MM rc = sgsn_ranap_rab_ass_resp(mm, (RANAP_RAB_SetupOrModifiedItemIEs_t *)data); break; case RANAP_IU_EVENT_IU_RELEASE: /* fall thru */ case RANAP_IU_EVENT_LINK_INVALIDATED: /* Clean up ranap_ue_conn_ctx here */ if (mm) LOGMMCTXP(LOGL_INFO, mm, "IU release for imsi %s\n", mm->imsi); else LOGMMCTXP(LOGL_INFO, mm, "IU release for UE conn 0x%x\n", ctx->conn_id); if (mm && mm->pmm_state == PMM_CONNECTED) mmctx_set_pmm_state(mm, PMM_IDLE); rc = 0; break; case RANAP_IU_EVENT_SECURITY_MODE_COMPLETE: REQUIRE_MM /* Continue authentication here */ mm->iu.ue_ctx->integrity_active = 1; rc = gsm48_gmm_authorize(mm); break; default: LOGP(DRANAP, LOGL_NOTICE, "Unknown event received: %i\n", type); rc = -1; break; } return rc; } #endif /* Our implementation, should be kept in SGSN */ static void mmctx_timer_cb(void *_mm); static void mmctx_timer_start(struct sgsn_mm_ctx *mm, unsigned int T, unsigned int seconds) { if (osmo_timer_pending(&mm->timer)) LOGMMCTXP(LOGL_ERROR, mm, "Starting MM timer %u while old " "timer %u pending\n", T, mm->T); mm->T = T; mm->num_T_exp = 0; /* FIXME: we should do this only once ? */ osmo_timer_setup(&mm->timer, mmctx_timer_cb, mm); osmo_timer_schedule(&mm->timer, seconds, 0); } static void mmctx_timer_stop(struct sgsn_mm_ctx *mm, unsigned int T) { if (mm->T != T) LOGMMCTXP(LOGL_ERROR, mm, "Stopping MM timer %u but " "%u is running\n", T, mm->T); osmo_timer_del(&mm->timer); } time_t gprs_max_time_to_idle(void) { return sgsn->cfg.timers.T3314 + (sgsn->cfg.timers.T3312 + 4 * 60); } /* Send a message through the underlying layer. * For param encryptable, see 3GPP TS 24.008 § 4.7.1.2 and * gsm48_hdr_gmm_cipherable(). Pass false for not cipherable messages. */ static int gsm48_gmm_sendmsg(struct msgb *msg, int command, struct sgsn_mm_ctx *mm, bool encryptable) { if (mm) { rate_ctr_inc(&mm->ctrg->ctr[GMM_CTR_PKTS_SIG_OUT]); #ifdef BUILD_IU if (mm->ran_type == MM_CTX_T_UTRAN_Iu) return ranap_iu_tx(msg, GPRS_SAPI_GMM); #endif } #ifdef BUILD_IU /* In Iu mode, msg->dst contains the ranap_ue_conn_ctx pointer, in Gb mode * dst is empty. */ /* FIXME: have a more explicit indicator for Iu messages */ if (msg->dst) return ranap_iu_tx(msg, GPRS_SAPI_GMM); #endif /* caller needs to provide TLLI, BVCI and NSEI */ return gprs_llc_tx_ui(msg, GPRS_SAPI_GMM, command, mm, encryptable); } /* copy identifiers from old message to new message, this * is required so lower layers can route it correctly */ static void gmm_copy_id(struct msgb *msg, const struct msgb *old) { msgb_tlli(msg) = msgb_tlli(old); msgb_bvci(msg) = msgb_bvci(old); msgb_nsei(msg) = msgb_nsei(old); msg->dst = old->dst; } /* Store BVCI/NSEI in MM context */ static void msgid2mmctx(struct sgsn_mm_ctx *mm, const struct msgb *msg) { mm->gb.bvci = msgb_bvci(msg); mm->gb.nsei = msgb_nsei(msg); /* In case a Iu connection is reconnected we need to update the ue ctx */ mm->iu.ue_ctx = msg->dst; if (mm->ran_type == MM_CTX_T_UTRAN_Iu && mm->iu.ue_ctx) { #ifdef BUILD_IU mm->iu.ue_ctx->rab_assign_addr_enc = sgsn->cfg.iu.rab_assign_addr_enc; #endif } } /* Store BVCI/NSEI in MM context */ static void mmctx2msgid(struct msgb *msg, const struct sgsn_mm_ctx *mm) { msgb_tlli(msg) = mm->gb.tlli; msgb_bvci(msg) = mm->gb.bvci; msgb_nsei(msg) = mm->gb.nsei; msg->dst = mm->iu.ue_ctx; } static void mm_ctx_cleanup_free(struct sgsn_mm_ctx *ctx, const char *log_text) { LOGMMCTXP(LOGL_INFO, ctx, "Cleaning MM context due to %s\n", log_text); /* Mark MM state as deregistered */ ctx->gmm_state = GMM_DEREGISTERED; mmctx_set_pmm_state(ctx, PMM_DETACHED); mmctx_set_pmm_state(ctx, MM_IDLE); sgsn_mm_ctx_cleanup_free(ctx); } /* Chapter 9.4.18 */ static int _tx_status(struct msgb *msg, uint8_t cause, struct sgsn_mm_ctx *mmctx, int sm) { struct gsm48_hdr *gh; /* MMCTX might be NULL! */ DEBUGP(DMM, "<- GPRS MM STATUS (cause: %s)\n", get_value_string(gsm48_gmm_cause_names, cause)); gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); if (sm) { gh->proto_discr = GSM48_PDISC_SM_GPRS; gh->msg_type = GSM48_MT_GSM_STATUS; } else { gh->proto_discr = GSM48_PDISC_MM_GPRS; gh->msg_type = GSM48_MT_GMM_STATUS; } gh->data[0] = cause; return gsm48_gmm_sendmsg(msg, 0, mmctx, true); } static int gsm48_tx_gmm_status(struct sgsn_mm_ctx *mmctx, uint8_t cause) { struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 GMM STATUS"); mmctx2msgid(msg, mmctx); return _tx_status(msg, cause, mmctx, 0); } static int gsm48_tx_sm_status(struct sgsn_mm_ctx *mmctx, uint8_t cause) { struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 SM STATUS"); mmctx2msgid(msg, mmctx); return _tx_status(msg, cause, mmctx, 1); } static int _tx_detach_req(struct msgb *msg, uint8_t detach_type, uint8_t cause, struct sgsn_mm_ctx *mmctx) { struct gsm48_hdr *gh; /* MMCTX might be NULL! */ DEBUGP(DMM, "<- GPRS MM DETACH REQ (type: %s, cause: %s)\n", get_value_string(gprs_det_t_mt_strs, detach_type), get_value_string(gsm48_gmm_cause_names, cause)); gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); gh->proto_discr = GSM48_PDISC_MM_GPRS; gh->msg_type = GSM48_MT_GMM_DETACH_REQ; gh->data[0] = detach_type & 0x07; msgb_tv_put(msg, GSM48_IE_GMM_CAUSE, cause); return gsm48_gmm_sendmsg(msg, 0, mmctx, true); } static int gsm48_tx_gmm_detach_req(struct sgsn_mm_ctx *mmctx, uint8_t detach_type, uint8_t cause) { struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 DET REQ"); mmctx2msgid(msg, mmctx); return _tx_detach_req(msg, detach_type, cause, mmctx); } static int gsm48_tx_gmm_detach_req_oldmsg(struct msgb *oldmsg, uint8_t detach_type, uint8_t cause) { struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 DET OLD"); gmm_copy_id(msg, oldmsg); return _tx_detach_req(msg, detach_type, cause, NULL); } static struct gsm48_qos default_qos = { .delay_class = 4, /* best effort */ .reliab_class = GSM48_QOS_RC_LLC_UN_RLC_ACK_DATA_PROT, .peak_tput = GSM48_QOS_PEAK_TPUT_32000bps, .preced_class = GSM48_QOS_PC_NORMAL, .mean_tput = GSM48_QOS_MEAN_TPUT_BEST_EFFORT, .traf_class = GSM48_QOS_TC_INTERACTIVE, .deliv_order = GSM48_QOS_DO_UNORDERED, .deliv_err_sdu = GSM48_QOS_ERRSDU_YES, .max_sdu_size = GSM48_QOS_MAXSDU_1520, .max_bitrate_up = GSM48_QOS_MBRATE_63k, .max_bitrate_down = GSM48_QOS_MBRATE_63k, .resid_ber = GSM48_QOS_RBER_5e_2, .sdu_err_ratio = GSM48_QOS_SERR_1e_2, .handling_prio = 3, .xfer_delay = 0x10, /* 200ms */ .guar_bitrate_up = GSM48_QOS_MBRATE_0k, .guar_bitrate_down = GSM48_QOS_MBRATE_0k, .sig_ind = 0, /* not optimised for signalling */ .max_bitrate_down_ext = 0, /* use octet 9 */ .guar_bitrate_down_ext = 0, /* use octet 13 */ }; /* Chapter 9.4.2: Attach accept */ static int gsm48_tx_gmm_att_ack(struct sgsn_mm_ctx *mm) { struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 ATT ACK"); struct gsm48_hdr *gh; struct gsm48_attach_ack *aa; uint8_t *mid; #if 0 uint8_t *ptsig; #endif LOGMMCTXP(LOGL_INFO, mm, "<- GPRS ATTACH ACCEPT (new P-TMSI=0x%08x)\n", mm->p_tmsi); rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_GPRS_ATTACH_ACKED]); mmctx2msgid(msg, mm); gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->proto_discr = GSM48_PDISC_MM_GPRS; gh->msg_type = GSM48_MT_GMM_ATTACH_ACK; aa = (struct gsm48_attach_ack *) msgb_put(msg, sizeof(*aa)); aa->force_stby = 0; /* not indicated */ aa->att_result = 1; /* GPRS only */ aa->ra_upd_timer = gprs_secs_to_tmr_floor(sgsn->cfg.timers.T3312); aa->radio_prio = 4; /* lowest */ gsm48_encode_ra(&aa->ra_id, &mm->ra); #if 0 /* Optional: P-TMSI signature */ msgb_v_put(msg, GSM48_IE_GMM_PTMSI_SIG); ptsig = msgb_put(msg, 3); ptsig[0] = mm->p_tmsi_sig >> 16; ptsig[1] = mm->p_tmsi_sig >> 8; ptsig[2] = mm->p_tmsi_sig & 0xff; #endif /* Optional: Negotiated Ready timer value * (fixed 44s, default value, GSM 04.08, table 11.4a) to safely limit * the inactivity time READY->STANDBY. */ msgb_tv_put(msg, GSM48_IE_GMM_TIMER_READY, gprs_secs_to_tmr_floor(sgsn->cfg.timers.T3314)); #ifdef PTMSI_ALLOC /* Optional: Allocated P-TMSI */ mid = msgb_put(msg, GSM48_MID_TMSI_LEN); gsm48_generate_mid_from_tmsi(mid, mm->p_tmsi); mid[0] = GSM48_IE_GMM_ALLOC_PTMSI; #endif /* Optional: MS-identity (combined attach) */ /* Optional: GMM cause (partial attach result for combined attach) */ return gsm48_gmm_sendmsg(msg, 0, mm, true); } /* Chapter 9.4.5: Attach reject */ static int _tx_gmm_att_rej(struct msgb *msg, uint8_t gmm_cause, const struct sgsn_mm_ctx *mm) { struct gsm48_hdr *gh; LOGMMCTXP(LOGL_NOTICE, mm, "<- GPRS ATTACH REJECT: %s\n", get_value_string(gsm48_gmm_cause_names, gmm_cause)); rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_GPRS_ATTACH_REJECTED]); gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); gh->proto_discr = GSM48_PDISC_MM_GPRS; gh->msg_type = GSM48_MT_GMM_ATTACH_REJ; gh->data[0] = gmm_cause; return gsm48_gmm_sendmsg(msg, 0, NULL, false); } static int gsm48_tx_gmm_att_rej_oldmsg(const struct msgb *old_msg, uint8_t gmm_cause) { struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 ATT REJ OLD"); gmm_copy_id(msg, old_msg); return _tx_gmm_att_rej(msg, gmm_cause, NULL); } static int gsm48_tx_gmm_att_rej(struct sgsn_mm_ctx *mm, uint8_t gmm_cause) { struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 ATT REJ"); mmctx2msgid(msg, mm); return _tx_gmm_att_rej(msg, gmm_cause, mm); } /* Chapter 9.4.6.2 Detach accept */ static int _tx_detach_ack(struct msgb *msg, uint8_t force_stby, struct sgsn_mm_ctx *mm) { struct gsm48_hdr *gh; /* MMCTX might be NULL! */ DEBUGP(DMM, "<- GPRS MM DETACH ACC (force-standby: %d)\n", force_stby); rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_GPRS_DETACH_ACKED]); gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); gh->proto_discr = GSM48_PDISC_MM_GPRS; gh->msg_type = GSM48_MT_GMM_DETACH_ACK; gh->data[0] = force_stby; return gsm48_gmm_sendmsg(msg, 0, mm, true); } static int gsm48_tx_gmm_det_ack(struct sgsn_mm_ctx *mm, uint8_t force_stby) { struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 DET ACK"); mmctx2msgid(msg, mm); return _tx_detach_ack(msg, force_stby, mm); } static int gsm48_tx_gmm_det_ack_oldmsg(struct msgb *oldmsg, uint8_t force_stby) { struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 DET ACK OLD"); gmm_copy_id(msg, oldmsg); return _tx_detach_ack(msg, force_stby, NULL); } /* Transmit Chapter 9.4.12 Identity Request */ static int gsm48_tx_gmm_id_req(struct sgsn_mm_ctx *mm, uint8_t id_type) { struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 ID REQ"); struct gsm48_hdr *gh; LOGMMCTXP(LOGL_DEBUG, mm, "<- GPRS IDENTITY REQUEST: mi_type=%s\n", gsm48_mi_type_name(id_type)); mmctx2msgid(msg, mm); gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); gh->proto_discr = GSM48_PDISC_MM_GPRS; gh->msg_type = GSM48_MT_GMM_ID_REQ; /* 10.5.5.9 ID type 2 + identity type and 10.5.5.7 'force to standby' IE */ gh->data[0] = id_type & 0xf; return gsm48_gmm_sendmsg(msg, 1, mm, false); } /* determine if the MS/UE supports R99 or later */ static bool mmctx_is_r99(const struct sgsn_mm_ctx *mm) { if (mm->ms_network_capa.len < 1) return false; if (mm->ms_network_capa.buf[0] & 0x01) return true; return false; } /* 3GPP TS 24.008 Section 9.4.9: Authentication and Ciphering Request */ static int gsm48_tx_gmm_auth_ciph_req(struct sgsn_mm_ctx *mm, const struct osmo_auth_vector *vec, uint8_t key_seq, bool force_standby) { struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 AUTH CIPH REQ"); struct gsm48_hdr *gh; struct gsm48_auth_ciph_req *acreq; uint8_t *m_rand, *m_cksn, rbyte; int rc; LOGMMCTXP(LOGL_INFO, mm, "<- GPRS AUTH AND CIPHERING REQ (rand = %s," " mmctx_is_r99=%d, vec->auth_types=0x%x", osmo_hexdump(vec->rand, sizeof(vec->rand)), mmctx_is_r99(mm), vec->auth_types); if (mmctx_is_r99(mm) && vec && (vec->auth_types & OSMO_AUTH_TYPE_UMTS)) { LOGPC(DMM, LOGL_INFO, ", autn = %s)\n", osmo_hexdump(vec->autn, sizeof(vec->autn))); } else LOGPC(DMM, LOGL_INFO, ")\n"); mmctx2msgid(msg, mm); gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->proto_discr = GSM48_PDISC_MM_GPRS; gh->msg_type = GSM48_MT_GMM_AUTH_CIPH_REQ; acreq = (struct gsm48_auth_ciph_req *) msgb_put(msg, sizeof(*acreq)); acreq->ciph_alg = mm->ciph_algo & 0xf; /* § 10.5.5.10: */ acreq->imeisv_req = 0x1; /* § 10.5.5.7: */ acreq->force_stby = force_standby; /* 3GPP TS 24.008 § 10.5.5.19: */ rc = osmo_get_rand_id(&rbyte, 1); if (rc < 0) { LOGP(DMM, LOGL_ERROR, "osmo_get_rand_id() failed for A&C ref: %s\n", strerror(-rc)); return rc; } acreq->ac_ref_nr = rbyte; mm->ac_ref_nr_used = acreq->ac_ref_nr; /* Only if authentication is requested we need to set RAND + CKSN */ if (vec) { m_rand = msgb_put(msg, sizeof(vec->rand) + 1); m_rand[0] = GSM48_IE_GMM_AUTH_RAND; memcpy(m_rand + 1, vec->rand, sizeof(vec->rand)); /* § 10.5.1.2: */ m_cksn = msgb_put(msg, 1); m_cksn[0] = (GSM48_IE_GMM_CIPH_CKSN << 4) | (key_seq & 0x07); /* A Release99 or higher MS/UE must be able to handle * the optional AUTN IE. If a classic GSM SIM is * inserted, it will simply ignore AUTN and just use * RAND */ if (mmctx_is_r99(mm) && (vec->auth_types & OSMO_AUTH_TYPE_UMTS)) { msgb_tlv_put(msg, GSM48_IE_GMM_AUTN, sizeof(vec->autn), vec->autn); } } return gsm48_gmm_sendmsg(msg, 1, mm, false); } /* Section 9.4.11: Authentication and Ciphering Reject */ static int gsm48_tx_gmm_auth_ciph_rej(struct sgsn_mm_ctx *mm) { struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 AUTH CIPH REJ"); struct gsm48_hdr *gh; LOGMMCTXP(LOGL_NOTICE, mm, "<- GPRS AUTH AND CIPH REJECT\n"); mmctx2msgid(msg, mm); gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->proto_discr = GSM48_PDISC_MM_GPRS; gh->msg_type = GSM48_MT_GMM_AUTH_CIPH_REJ; return gsm48_gmm_sendmsg(msg, 0, mm, false); } /* check if the received authentication response matches */ static enum osmo_sub_auth_type check_auth_resp(struct sgsn_mm_ctx *ctx, bool is_utran, const struct osmo_auth_vector *vec, const uint8_t *res, uint8_t res_len) { const uint8_t *expect_res; uint8_t expect_res_len; enum osmo_sub_auth_type expect_type; const char *expect_str; /* On UTRAN (3G) we always expect UMTS AKA. On GERAN (2G) we sent AUTN * and expect UMTS AKA if there is R99 capability and our vector * supports UMTS AKA, otherwise we expect GSM AKA. * However, on GERAN, even if we sent a UMTS AKA Authentication Request, the MS may decide to * instead reply with a GSM AKA SRES response. */ if (is_utran || (mmctx_is_r99(ctx) && (vec->auth_types & OSMO_AUTH_TYPE_UMTS) && (res_len > sizeof(vec->sres)))) { expect_type = OSMO_AUTH_TYPE_UMTS; expect_str = "UMTS RES"; expect_res = vec->res; expect_res_len = vec->res_len; } else { expect_type = OSMO_AUTH_TYPE_GSM; expect_str = "GSM SRES"; expect_res = vec->sres; expect_res_len = sizeof(vec->sres); } if (!(vec->auth_types & expect_type)) { LOGMMCTXP(LOGL_ERROR, ctx, "Auth error: auth vector does" " not provide the expected auth type:" " expected %s = 0x%x, auth_types are 0x%x\n", expect_str, expect_type, vec->auth_types); return OSMO_AUTH_TYPE_NONE; } if (!res) goto auth_mismatch; if (res_len != expect_res_len) goto auth_mismatch; if (memcmp(res, expect_res, res_len) != 0) goto auth_mismatch; /* Authorized! */ return expect_type; auth_mismatch: LOGMMCTXP(LOGL_ERROR, ctx, "Auth mismatch: expected %s = %s\n", expect_str, osmo_hexdump_nospc(expect_res, expect_res_len)); return OSMO_AUTH_TYPE_NONE; } /* Section 9.4.10: Authentication and Ciphering Response */ static int gsm48_rx_gmm_auth_ciph_resp(struct sgsn_mm_ctx *ctx, struct msgb *msg) { struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); struct gsm48_auth_ciph_resp *acr = (struct gsm48_auth_ciph_resp *)gh->data; struct tlv_parsed tp; struct gsm_auth_tuple *at; const char *res_name = "(no response)"; uint8_t res[16]; uint8_t res_len; int rc; LOGMMCTXP(LOGL_INFO, ctx, "-> GPRS AUTH AND CIPH RESPONSE\n"); if (ctx->auth_triplet.key_seq == GSM_KEY_SEQ_INVAL) { LOGMMCTXP(LOGL_NOTICE, ctx, "Unexpected Auth & Ciph Response (ignored)\n"); return 0; } if (acr->ac_ref_nr != ctx->ac_ref_nr_used) { LOGMMCTXP(LOGL_NOTICE, ctx, "Reference mismatch for Auth & Ciph" " Response: %u received, %u expected\n", acr->ac_ref_nr, ctx->ac_ref_nr_used); return 0; } /* Stop T3360 */ mmctx_timer_stop(ctx, 3360); tlv_parse(&tp, &gsm48_gmm_att_tlvdef, acr->data, (msg->data + msg->len) - acr->data, 0, 0); if (!TLVP_PRESENT(&tp, GSM48_IE_GMM_AUTH_SRES) || !TLVP_PRESENT(&tp, GSM48_IE_GMM_IMEISV) || TLVP_LEN(&tp,GSM48_IE_GMM_AUTH_SRES) != 4) { /* TODO: missing mandatory IE, return STATUS or REJ? */ LOGMMCTXP(LOGL_ERROR, ctx, "Missing mandantory IE\n"); return -EINVAL; } /* Start with the good old 4-byte SRES */ memcpy(res, TLVP_VAL(&tp, GSM48_IE_GMM_AUTH_SRES), 4); res_len = 4; res_name = "GSM SRES"; /* Append extended RES as part of UMTS AKA, if any */ if (TLVP_PRESENT(&tp, GSM48_IE_GMM_AUTH_RES_EXT)) { unsigned int l = TLVP_LEN(&tp, GSM48_IE_GMM_AUTH_RES_EXT); if (l > sizeof(res)-4) l = sizeof(res)-4; memcpy(res+4, TLVP_VAL(&tp, GSM48_IE_GMM_AUTH_RES_EXT), l); res_len += l; res_name = "UMTS RES"; } at = &ctx->auth_triplet; LOGMMCTXP(LOGL_DEBUG, ctx, "checking auth: received %s = %s\n", res_name, osmo_hexdump(res, res_len)); ctx->sec_ctx = check_auth_resp(ctx, false, &at->vec, res, res_len); if (!sgsn_mm_ctx_is_authenticated(ctx)) { rc = gsm48_tx_gmm_auth_ciph_rej(ctx); mm_ctx_cleanup_free(ctx, "GPRS AUTH AND CIPH REJECT"); return rc; } if (ctx->ran_type == MM_CTX_T_UTRAN_Iu) ctx->iu.new_key = 1; /* FIXME: enable LLC cipheirng */ /* Check if we can let the mobile station enter */ return gsm48_gmm_authorize(ctx); } /* Section 9.4.10: Authentication and Ciphering Failure */ static int gsm48_rx_gmm_auth_ciph_fail(struct sgsn_mm_ctx *ctx, struct msgb *msg) { struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); struct tlv_parsed tp; const uint8_t gmm_cause = gh->data[0]; const uint8_t *auts; int rc; LOGMMCTXP(LOGL_INFO, ctx, "-> GPRS AUTH AND CIPH FAILURE (cause = %s)\n", get_value_string(gsm48_gmm_cause_names, gmm_cause)); tlv_parse(&tp, &gsm48_gmm_att_tlvdef, gh->data+1, msg->len - 1, 0, 0); /* Only if GMM cause is present and the AUTS is provided, we can * start re-sync procedure */ if (gmm_cause == GMM_CAUSE_SYNC_FAIL && TLVP_PRESENT(&tp, GSM48_IE_GMM_AUTH_FAIL_PAR)) { if (TLVP_LEN(&tp, GSM48_IE_GMM_AUTH_FAIL_PAR) != 14) { LOGMMCTXP(LOGL_ERROR, ctx, "AUTS IE has wrong size:" " expected %d, got %u\n", 14, TLVP_LEN(&tp, GSM48_IE_GMM_AUTH_FAIL_PAR)); return -EINVAL; } auts = TLVP_VAL(&tp, GSM48_IE_GMM_AUTH_FAIL_PAR); LOGMMCTXP(LOGL_INFO, ctx, "R99 AUTHENTICATION SYNCH (AUTS = %s)\n", osmo_hexdump_nospc(auts, 14)); /* make sure we'll refresh the auth_triplet in * sgsn_auth_update() */ ctx->auth_triplet.key_seq = GSM_KEY_SEQ_INVAL; /* make sure we'll retry authentication after the resync */ ctx->auth_state = SGSN_AUTH_UMTS_RESYNC; /* Send AUTS to HLR and wait for new Auth Info Result */ rc = gprs_subscr_request_auth_info(ctx, auts, ctx->auth_triplet.vec.rand); if (!rc) return 0; /* on error, fall through to send a reject */ LOGMMCTXP(LOGL_ERROR, ctx, "Sending AUTS to HLR failed (rc = %d)\n", rc); } LOGMMCTXP(LOGL_NOTICE, ctx, "Authentication failed\n"); rc = gsm48_tx_gmm_auth_ciph_rej(ctx); mm_ctx_cleanup_free(ctx, "GPRS AUTH FAILURE"); return rc; } static void extract_subscr_msisdn(struct sgsn_mm_ctx *ctx) { struct gsm_mncc_number called; uint8_t msisdn[sizeof(ctx->subscr->sgsn_data->msisdn) + 1]; /* Convert MSISDN from encoded to string.. */ if (!ctx->subscr) return; if (ctx->subscr->sgsn_data->msisdn_len < 1) return; /* prepare the data for the decoder */ memset(&called, 0, sizeof(called)); msisdn[0] = ctx->subscr->sgsn_data->msisdn_len; memcpy(&msisdn[1], ctx->subscr->sgsn_data->msisdn, ctx->subscr->sgsn_data->msisdn_len); /* decode the string now */ gsm48_decode_called(&called, msisdn); /* Prepend a '+' for international numbers */ if (called.plan == 1 && called.type == 1) { ctx->msisdn[0] = '+'; osmo_strlcpy(&ctx->msisdn[1], called.number, sizeof(ctx->msisdn)); } else { osmo_strlcpy(ctx->msisdn, called.number, sizeof(ctx->msisdn)); } } static void extract_subscr_hlr(struct sgsn_mm_ctx *ctx) { struct gsm_mncc_number called; uint8_t hlr_number[sizeof(ctx->subscr->sgsn_data->hlr) + 1]; if (!ctx->subscr) return; if (ctx->subscr->sgsn_data->hlr_len < 1) return; /* prepare the data for the decoder */ memset(&called, 0, sizeof(called)); hlr_number[0] = ctx->subscr->sgsn_data->hlr_len; memcpy(&hlr_number[1], ctx->subscr->sgsn_data->hlr, ctx->subscr->sgsn_data->hlr_len); /* decode the string now */ gsm48_decode_called(&called, hlr_number); if (called.plan != 1) { LOGMMCTXP(LOGL_ERROR, ctx, "Numbering plan(%d) not allowed\n", called.plan); return; } if (called.type != 1) { LOGMMCTXP(LOGL_ERROR, ctx, "Numbering type(%d) not allowed\n", called.type); return; } osmo_strlcpy(ctx->hlr, called.number, sizeof(ctx->hlr)); } #ifdef BUILD_IU /* Chapter 9.4.21: Service accept */ static int gsm48_tx_gmm_service_ack(struct sgsn_mm_ctx *mm) { struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 SERVICE ACK"); struct gsm48_hdr *gh; LOGMMCTXP(LOGL_INFO, mm, "<- GPRS SERVICE ACCEPT (P-TMSI=0x%08x)\n", mm->p_tmsi); mmctx2msgid(msg, mm); gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->proto_discr = GSM48_PDISC_MM_GPRS; gh->msg_type = GSM48_MT_GMM_SERVICE_ACK; /* Optional: PDP context status */ /* Optional: MBMS context status */ return gsm48_gmm_sendmsg(msg, 0, mm, false); } #endif /* Chapter 9.4.22: Service reject */ static int _tx_gmm_service_rej(struct msgb *msg, uint8_t gmm_cause, const struct sgsn_mm_ctx *mm) { struct gsm48_hdr *gh; LOGMMCTXP(LOGL_NOTICE, mm, "<- GPRS SERVICE REJECT: %s\n", get_value_string(gsm48_gmm_cause_names, gmm_cause)); gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); gh->proto_discr = GSM48_PDISC_MM_GPRS; gh->msg_type = GSM48_MT_GMM_SERVICE_REJ; gh->data[0] = gmm_cause; return gsm48_gmm_sendmsg(msg, 0, NULL, true); } static int gsm48_tx_gmm_service_rej_oldmsg(const struct msgb *old_msg, uint8_t gmm_cause) { struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 SERVICE REJ OLD"); gmm_copy_id(msg, old_msg); return _tx_gmm_service_rej(msg, gmm_cause, NULL); } #if 0 -- currently unused -- static int gsm48_tx_gmm_service_rej(struct sgsn_mm_ctx *mm, uint8_t gmm_cause) { struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 SERVICE REJ"); mmctx2msgid(msg, mm); return _tx_gmm_service_rej(msg, gmm_cause, mm); } #endif static int gsm48_tx_gmm_ra_upd_ack(struct sgsn_mm_ctx *mm); #ifdef BUILD_IU /* Send RAB activation requests for all PDP contexts */ void activate_pdp_rabs(struct sgsn_mm_ctx *ctx) { struct sgsn_pdp_ctx *pdp; if (ctx->ran_type != MM_CTX_T_UTRAN_Iu) return; llist_for_each_entry(pdp, &ctx->pdp_list, list) { iu_rab_act_ps(pdp->nsapi, pdp); } } #endif /* Check if we can already authorize a subscriber */ static int gsm48_gmm_authorize(struct sgsn_mm_ctx *ctx) { #ifdef BUILD_IU int rc; #endif #ifndef PTMSI_ALLOC struct sgsn_signal_data sig_data; #endif /* Request IMSI and IMEI from the MS if they are unknown */ if (!strlen(ctx->imei)) { ctx->t3370_id_type = GSM_MI_TYPE_IMEI; mmctx_timer_start(ctx, 3370, sgsn->cfg.timers.T3370); return gsm48_tx_gmm_id_req(ctx, GSM_MI_TYPE_IMEI); } if (!strlen(ctx->imsi)) { ctx->t3370_id_type = GSM_MI_TYPE_IMSI; mmctx_timer_start(ctx, 3370, sgsn->cfg.timers.T3370); return gsm48_tx_gmm_id_req(ctx, GSM_MI_TYPE_IMSI); } /* All information required for authentication is available */ ctx->t3370_id_type = GSM_MI_TYPE_NONE; if (ctx->auth_state == SGSN_AUTH_UNKNOWN) { /* Request authorization, this leads to a call to * sgsn_auth_update which in turn calls * gsm0408_gprs_access_granted or gsm0408_gprs_access_denied */ sgsn_auth_request(ctx); /* Note that gsm48_gmm_authorize can be called recursively via * sgsn_auth_request iff ctx->auth_info changes to AUTH_ACCEPTED */ return 0; } if (ctx->auth_state == SGSN_AUTH_AUTHENTICATE && !sgsn_mm_ctx_is_authenticated(ctx)) { struct gsm_auth_tuple *at = &ctx->auth_triplet; mmctx_timer_start(ctx, 3360, sgsn->cfg.timers.T3360); return gsm48_tx_gmm_auth_ciph_req(ctx, &at->vec, at->key_seq, false); } if (ctx->auth_state == SGSN_AUTH_AUTHENTICATE && sgsn_mm_ctx_is_authenticated(ctx) && ctx->auth_triplet.key_seq != GSM_KEY_SEQ_INVAL) { /* Check again for authorization */ sgsn_auth_request(ctx); return 0; } if (ctx->auth_state != SGSN_AUTH_ACCEPTED) { LOGMMCTXP(LOGL_NOTICE, ctx, "authorization is denied, aborting procedure\n"); return -EACCES; } /* The MS is authorized */ #ifdef BUILD_IU if (ctx->ran_type == MM_CTX_T_UTRAN_Iu && !ctx->iu.ue_ctx->integrity_active) { rc = ranap_iu_tx_sec_mode_cmd(ctx->iu.ue_ctx, &ctx->auth_triplet.vec, 0, ctx->iu.new_key); ctx->iu.new_key = 0; return rc; } #endif switch (ctx->pending_req) { case 0: LOGMMCTXP(LOGL_INFO, ctx, "no pending request, authorization completed\n"); break; case GSM48_MT_GMM_ATTACH_REQ: ctx->pending_req = 0; extract_subscr_msisdn(ctx); extract_subscr_hlr(ctx); #ifdef PTMSI_ALLOC /* Start T3350 and re-transmit up to 5 times until ATTACH COMPLETE */ mmctx_timer_start(ctx, 3350, sgsn->cfg.timers.T3350); ctx->t3350_mode = GMM_T3350_MODE_ATT; #else memset(&sig_data, 0, sizeof(sig_data)); sig_data.mm = mmctx; osmo_signal_dispatch(SS_SGSN, S_SGSN_ATTACH, &sig_data); ctx->gmm_state = GMM_REGISTERED_NORMAL; #endif return gsm48_tx_gmm_att_ack(ctx); #ifdef BUILD_IU case GSM48_MT_GMM_SERVICE_REQ: ctx->pending_req = 0; mmctx_set_pmm_state(ctx, PMM_CONNECTED); rc = gsm48_tx_gmm_service_ack(ctx); if (ctx->iu.service.type != GPRS_SERVICE_T_SIGNALLING) activate_pdp_rabs(ctx); return rc; #endif case GSM48_MT_GMM_RA_UPD_REQ: ctx->pending_req = 0; /* Send RA UPDATE ACCEPT */ return gsm48_tx_gmm_ra_upd_ack(ctx); default: LOGMMCTXP(LOGL_ERROR, ctx, "only Attach Request is supported yet, " "got request type %u\n", ctx->pending_req); break; } return 0; } void gsm0408_gprs_authenticate(struct sgsn_mm_ctx *ctx) { ctx->sec_ctx = OSMO_AUTH_TYPE_NONE; gsm48_gmm_authorize(ctx); } void gsm0408_gprs_access_granted(struct sgsn_mm_ctx *ctx) { switch (ctx->gmm_state) { case GMM_COMMON_PROC_INIT: LOGMMCTXP(LOGL_NOTICE, ctx, "Authorized, continuing procedure, IMSI=%s\n", ctx->imsi); /* Continue with the authorization */ gsm48_gmm_authorize(ctx); break; default: LOGMMCTXP(LOGL_INFO, ctx, "Authorized, ignored, IMSI=%s\n", ctx->imsi); } } void gsm0408_gprs_access_denied(struct sgsn_mm_ctx *ctx, int gmm_cause) { if (gmm_cause == SGSN_ERROR_CAUSE_NONE) gmm_cause = GMM_CAUSE_GPRS_NOTALLOWED; switch (ctx->gmm_state) { case GMM_COMMON_PROC_INIT: LOGMMCTXP(LOGL_NOTICE, ctx, "Not authorized, rejecting ATTACH REQUEST " "with cause '%s' (%d)\n", get_value_string(gsm48_gmm_cause_names, gmm_cause), gmm_cause); gsm48_tx_gmm_att_rej(ctx, gmm_cause); mm_ctx_cleanup_free(ctx, "GPRS ATTACH REJECT"); break; case GMM_REGISTERED_NORMAL: case GMM_REGISTERED_SUSPENDED: LOGMMCTXP(LOGL_NOTICE, ctx, "Authorization lost, detaching " "with cause '%s' (%d)\n", get_value_string(gsm48_gmm_cause_names, gmm_cause), gmm_cause); gsm48_tx_gmm_detach_req( ctx, GPRS_DET_T_MT_REATT_NOTREQ, gmm_cause); mm_ctx_cleanup_free(ctx, "auth lost"); break; default: LOGMMCTXP(LOGL_INFO, ctx, "Authorization lost, cause is '%s' (%d)\n", get_value_string(gsm48_gmm_cause_names, gmm_cause), gmm_cause); mm_ctx_cleanup_free(ctx, "auth lost"); } } void gsm0408_gprs_access_cancelled(struct sgsn_mm_ctx *ctx, int gmm_cause) { if (gmm_cause != SGSN_ERROR_CAUSE_NONE) { LOGMMCTXP(LOGL_INFO, ctx, "Cancelled with cause '%s' (%d), deleting context\n", get_value_string(gsm48_gmm_cause_names, gmm_cause), gmm_cause); gsm0408_gprs_access_denied(ctx, gmm_cause); return; } LOGMMCTXP(LOGL_INFO, ctx, "Cancelled, deleting context silently\n"); mm_ctx_cleanup_free(ctx, "access cancelled"); } /* Parse Chapter 9.4.13 Identity Response */ static int gsm48_rx_gmm_id_resp(struct sgsn_mm_ctx *ctx, struct msgb *msg) { struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); uint8_t mi_type = gh->data[1] & GSM_MI_TYPE_MASK; char mi_string[GSM48_MI_SIZE]; gsm48_mi_to_string(mi_string, sizeof(mi_string), &gh->data[1], gh->data[0]); if (!ctx) { DEBUGP(DMM, "from unknown TLLI 0x%08x?!? This should not happen\n", msgb_tlli(msg)); return -EINVAL; } LOGMMCTXP(LOGL_DEBUG, ctx, "-> GMM IDENTITY RESPONSE: MI(%s)=%s\n", gsm48_mi_type_name(mi_type), mi_string); if (ctx->t3370_id_type == GSM_MI_TYPE_NONE) { LOGMMCTXP(LOGL_NOTICE, ctx, "Got unexpected IDENTITY RESPONSE: MI(%s)=%s, " "ignoring message\n", gsm48_mi_type_name(mi_type), mi_string); return -EINVAL; } if (mi_type == ctx->t3370_id_type) mmctx_timer_stop(ctx, 3370); switch (mi_type) { case GSM_MI_TYPE_IMSI: /* we already have a mm context with current TLLI, but no * P-TMSI / IMSI yet. What we now need to do is to fill * this initial context with data from the HLR */ if (strlen(ctx->imsi) == 0) { /* Check if we already have a MM context for this IMSI */ struct sgsn_mm_ctx *ictx; ictx = sgsn_mm_ctx_by_imsi(mi_string); if (ictx) { /* Handle it like in gsm48_rx_gmm_det_req, * except that no messages are sent to the BSS */ LOGMMCTXP(LOGL_NOTICE, ctx, "Deleting old MM Context for same IMSI " "p_tmsi_old=0x%08x\n", ictx->p_tmsi); mm_ctx_cleanup_free(ictx, "GPRS IMSI re-use"); } } osmo_strlcpy(ctx->imsi, mi_string, sizeof(ctx->imsi)); break; case GSM_MI_TYPE_IMEI: osmo_strlcpy(ctx->imei, mi_string, sizeof(ctx->imei)); break; case GSM_MI_TYPE_IMEISV: break; } /* Check if we can let the mobile station enter */ return gsm48_gmm_authorize(ctx); } /* Allocate a new P-TMSI and change context state */ static inline void ptmsi_update(struct sgsn_mm_ctx *ctx) { uint32_t ptmsi; /* Don't change the P-TMSI if a P-TMSI re-assignment is under way */ if (ctx->gmm_state != GMM_COMMON_PROC_INIT) { ptmsi = sgsn_alloc_ptmsi(); if (ptmsi != GSM_RESERVED_TMSI) { ctx->p_tmsi_old = ctx->p_tmsi; ctx->p_tmsi = ptmsi; } else LOGMMCTXP(LOGL_ERROR, ctx, "P-TMSI allocation failure: using old one.\n"); } ctx->gmm_state = GMM_COMMON_PROC_INIT; } /* Section 9.4.1 Attach request */ static int gsm48_rx_gmm_att_req(struct sgsn_mm_ctx *ctx, struct msgb *msg, struct gprs_llc_llme *llme) { struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); uint8_t *cur = gh->data, *msnc, *mi, *ms_ra_acc_cap; uint8_t msnc_len, att_type, mi_len, mi_type, ms_ra_acc_cap_len; uint16_t drx_par; uint32_t tmsi; char mi_string[GSM48_MI_SIZE]; struct gprs_ra_id ra_id; uint16_t cid = 0; enum gsm48_gmm_cause reject_cause; int rc; LOGMMCTXP(LOGL_INFO, ctx, "-> GMM ATTACH REQUEST "); rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_GPRS_ATTACH_REQUEST]); /* As per TS 04.08 Chapter 4.7.1.4, the attach request arrives either * with a foreign TLLI (P-TMSI that was allocated to the MS before), * or with random TLLI. */ /* In Iu mode, msg->dst contains the ranap_ue_conn_ctx pointer, in Gb mode * dst is empty. */ /* FIXME: have a more explicit indicator for Iu messages */ if (!msg->dst) { /* Gb mode */ cid = bssgp_parse_cell_id(&ra_id, msgb_bcid(msg)); } else { #ifdef BUILD_IU ra_id = ((struct ranap_ue_conn_ctx*)msg->dst)->ra_id; #else LOGMMCTXP(LOGL_ERROR, ctx, "Cannot handle Iu Attach Request, built without Iu support\n"); return -ENOTSUP; #endif } /* MS network capability 10.5.5.12 */ msnc_len = *cur++; msnc = cur; if (msnc_len > sizeof(ctx->ms_network_capa.buf)) goto err_inval; cur += msnc_len; /* TODO: In iu mode - handle follow-on request */ /* aTTACH Type 10.5.5.2 */ att_type = *cur++ & 0x07; /* DRX parameter 10.5.5.6 */ drx_par = *cur++ << 8; drx_par |= *cur++; /* Mobile Identity (P-TMSI or IMSI) 10.5.1.4 */ mi_len = *cur++; mi = cur; if (mi_len > 8) goto err_inval; mi_type = *mi & GSM_MI_TYPE_MASK; cur += mi_len; gsm48_mi_to_string(mi_string, sizeof(mi_string), mi, mi_len); DEBUGPC(DMM, "MI(%s) type=\"%s\" ", mi_string, get_value_string(gprs_att_t_strs, att_type)); /* Old routing area identification 10.5.5.15. Skip it */ cur += 6; /* MS Radio Access Capability 10.5.5.12a */ ms_ra_acc_cap_len = *cur++; ms_ra_acc_cap = cur; if (ms_ra_acc_cap_len > sizeof(ctx->ms_radio_access_capa.buf)) goto err_inval; cur += ms_ra_acc_cap_len; LOGPC(DMM, LOGL_INFO, "\n"); /* Optional: Old P-TMSI Signature, Requested READY timer, TMSI Status */ switch (mi_type) { case GSM_MI_TYPE_IMSI: /* Try to find MM context based on IMSI */ if (!ctx) ctx = sgsn_mm_ctx_by_imsi(mi_string); if (!ctx) { if (msg->dst) ctx = sgsn_mm_ctx_alloc_iu(msg->dst); else ctx = sgsn_mm_ctx_alloc_gb(0, &ra_id); if (!ctx) { reject_cause = GMM_CAUSE_NET_FAIL; goto rejected; } osmo_strlcpy(ctx->imsi, mi_string, sizeof(ctx->imsi)); } if (ctx->ran_type == MM_CTX_T_GERAN_Gb) { ctx->gb.tlli = msgb_tlli(msg); ctx->gb.llme = llme; } msgid2mmctx(ctx, msg); break; case GSM_MI_TYPE_TMSI: memcpy(&tmsi, mi+1, 4); tmsi = ntohl(tmsi); /* Try to find MM context based on P-TMSI */ if (!ctx) ctx = sgsn_mm_ctx_by_ptmsi(tmsi); if (!ctx) { /* Allocate a context as most of our code expects one. * Context will not have an IMSI ultil ID RESP is received */ if (msg->dst) ctx = sgsn_mm_ctx_alloc_iu(msg->dst); else ctx = sgsn_mm_ctx_alloc_gb(msgb_tlli(msg), &ra_id); if (!ctx) { reject_cause = GMM_CAUSE_NET_FAIL; goto rejected; } ctx->p_tmsi = tmsi; } if (ctx->ran_type == MM_CTX_T_GERAN_Gb) { ctx->gb.tlli = msgb_tlli(msg); ctx->gb.llme = llme; } msgid2mmctx(ctx, msg); break; default: LOGMMCTXP(LOGL_NOTICE, ctx, "Rejecting ATTACH REQUEST with " "MI type %s\n", gsm48_mi_type_name(mi_type)); reject_cause = GMM_CAUSE_MS_ID_NOT_DERIVED; goto rejected; } /* Update MM Context with currient RA and Cell ID */ ctx->ra = ra_id; if (ctx->ran_type == MM_CTX_T_GERAN_Gb) ctx->gb.cell_id = cid; /* Update MM Context with other data */ ctx->drx_parms = drx_par; ctx->ms_radio_access_capa.len = ms_ra_acc_cap_len; memcpy(ctx->ms_radio_access_capa.buf, ms_ra_acc_cap, ctx->ms_radio_access_capa.len); ctx->ms_network_capa.len = msnc_len; memcpy(ctx->ms_network_capa.buf, msnc, msnc_len); if (!gprs_ms_net_cap_gea_supported(ctx->ms_network_capa.buf, msnc_len, ctx->ciph_algo)) { reject_cause = GMM_CAUSE_PROTO_ERR_UNSPEC; LOGMMCTXP(LOGL_NOTICE, ctx, "Rejecting ATTACH REQUEST with MI " "type %s because MS do not support required %s " "encryption\n", gsm48_mi_type_name(mi_type), get_value_string(gprs_cipher_names,ctx->ciph_algo)); goto rejected; } #ifdef PTMSI_ALLOC /* Allocate a new P-TMSI (+ P-TMSI signature) and update TLLI */ ptmsi_update(ctx); #endif if (ctx->ran_type == MM_CTX_T_GERAN_Gb) { /* Even if there is no P-TMSI allocated, the MS will * switch from foreign TLLI to local TLLI */ ctx->gb.tlli_new = gprs_tmsi2tlli(ctx->p_tmsi, TLLI_LOCAL); /* Inform LLC layer about new TLLI but keep old active */ if (sgsn_mm_ctx_is_authenticated(ctx)) gprs_llme_copy_key(ctx, ctx->gb.llme); gprs_llgmm_assign(ctx->gb.llme, ctx->gb.tlli, ctx->gb.tlli_new); } ctx->pending_req = GSM48_MT_GMM_ATTACH_REQ; return gsm48_gmm_authorize(ctx); err_inval: LOGPC(DMM, LOGL_INFO, "\n"); reject_cause = GMM_CAUSE_SEM_INCORR_MSG; rejected: /* Send ATTACH REJECT */ LOGMMCTXP(LOGL_NOTICE, ctx, "Rejecting Attach Request with cause '%s' (%d)\n", get_value_string(gsm48_gmm_cause_names, reject_cause), reject_cause); rc = gsm48_tx_gmm_att_rej_oldmsg(msg, reject_cause); if (ctx) mm_ctx_cleanup_free(ctx, "GPRS ATTACH REJ"); else if (llme) gprs_llgmm_unassign(llme); return rc; } /* Section 4.7.4.1 / 9.4.5.2 MO Detach request */ static int gsm48_rx_gmm_det_req(struct sgsn_mm_ctx *ctx, struct msgb *msg) { struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); uint8_t detach_type, power_off; int rc = 0; detach_type = gh->data[0] & 0x7; power_off = gh->data[0] & 0x8; /* FIXME: In 24.008 there is an optional P-TMSI and P-TMSI signature IE */ rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_GPRS_DETACH_REQUEST]); LOGMMCTXP(LOGL_INFO, ctx, "-> GMM DETACH REQUEST TLLI=0x%08x type=%s %s\n", msgb_tlli(msg), get_value_string(gprs_det_t_mo_strs, detach_type), power_off ? "Power-off" : ""); /* Only send the Detach Accept (MO) if power off isn't indicated, * see 04.08, 4.7.4.1.2/3 for details */ if (!power_off) { /* force_stby = 0 */ if (ctx) rc = gsm48_tx_gmm_det_ack(ctx, 0); else rc = gsm48_tx_gmm_det_ack_oldmsg(msg, 0); } if (ctx) { struct sgsn_signal_data sig_data; memset(&sig_data, 0, sizeof(sig_data)); sig_data.mm = ctx; osmo_signal_dispatch(SS_SGSN, S_SGSN_DETACH, &sig_data); mm_ctx_cleanup_free(ctx, "GPRS DETACH REQUEST"); } return rc; } /* Chapter 9.4.15: Routing area update accept */ static int gsm48_tx_gmm_ra_upd_ack(struct sgsn_mm_ctx *mm) { struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 UPD ACK"); struct gsm48_hdr *gh; struct gsm48_ra_upd_ack *rua; uint8_t *mid; rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_GPRS_ROUTING_AREA_ACKED]); LOGMMCTXP(LOGL_INFO, mm, "<- ROUTING AREA UPDATE ACCEPT\n"); mmctx2msgid(msg, mm); gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->proto_discr = GSM48_PDISC_MM_GPRS; gh->msg_type = GSM48_MT_GMM_RA_UPD_ACK; rua = (struct gsm48_ra_upd_ack *) msgb_put(msg, sizeof(*rua)); rua->force_stby = 0; /* not indicated */ rua->upd_result = 0; /* RA updated */ rua->ra_upd_timer = gprs_secs_to_tmr_floor(sgsn->cfg.timers.T3312); gsm48_encode_ra(&rua->ra_id, &mm->ra); #if 0 /* Optional: P-TMSI signature */ msgb_v_put(msg, GSM48_IE_GMM_PTMSI_SIG); ptsig = msgb_put(msg, 3); ptsig[0] = mm->p_tmsi_sig >> 16; ptsig[1] = mm->p_tmsi_sig >> 8; ptsig[2] = mm->p_tmsi_sig & 0xff; #endif #ifdef PTMSI_ALLOC /* Optional: Allocated P-TMSI */ mid = msgb_put(msg, GSM48_MID_TMSI_LEN); gsm48_generate_mid_from_tmsi(mid, mm->p_tmsi); mid[0] = GSM48_IE_GMM_ALLOC_PTMSI; #endif /* Optional: Negotiated READY timer value */ msgb_tv_put(msg, GSM48_IE_GMM_TIMER_READY, gprs_secs_to_tmr_floor(sgsn->cfg.timers.T3314)); /* Option: MS ID, ... */ return gsm48_gmm_sendmsg(msg, 0, mm, true); } /* Chapter 9.4.17: Routing area update reject */ static int gsm48_tx_gmm_ra_upd_rej(struct msgb *old_msg, uint8_t cause) { struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 RA UPD REJ"); struct gsm48_hdr *gh; LOGP(DMM, LOGL_NOTICE, "<- ROUTING AREA UPDATE REJECT\n"); rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_GPRS_ROUTING_AREA_REJECT]); gmm_copy_id(msg, old_msg); gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 2); gh->proto_discr = GSM48_PDISC_MM_GPRS; gh->msg_type = GSM48_MT_GMM_RA_UPD_REJ; gh->data[0] = cause; gh->data[1] = 0; /* ? */ /* Option: P-TMSI signature, allocated P-TMSI, MS ID, ... */ return gsm48_gmm_sendmsg(msg, 0, NULL, false); } static void process_ms_ctx_status(struct sgsn_mm_ctx *mmctx, const uint8_t *pdp_status) { struct sgsn_pdp_ctx *pdp, *pdp2; /* 24.008 4.7.5.1.3: If the PDP context status information element is * included in ROUTING AREA UPDATE REQUEST message, then the network * shall deactivate all those PDP contexts locally (without peer to * peer signalling between the MS and the network), which are not in SM * state PDP-INACTIVE on network side but are indicated by the MS as * being in state PDP-INACTIVE. */ llist_for_each_entry_safe(pdp, pdp2, &mmctx->pdp_list, list) { if (pdp->nsapi < 8) { if (!(pdp_status[0] & (1 << pdp->nsapi))) { LOGMMCTXP(LOGL_NOTICE, mmctx, "Dropping PDP context for NSAPI=%u " "due to PDP CTX STATUS IE= 0x%02x%02x\n", pdp->nsapi, pdp_status[1], pdp_status[0]); sgsn_delete_pdp_ctx(pdp); } } else { if (!(pdp_status[1] & (1 << (pdp->nsapi - 8)))) { LOGMMCTXP(LOGL_NOTICE, mmctx, "Dropping PDP context for NSAPI=%u " "due to PDP CTX STATUS IE= 0x%02x%02x\n", pdp->nsapi, pdp_status[1], pdp_status[0]); sgsn_delete_pdp_ctx(pdp); } } } } /* Chapter 9.4.14: Routing area update request */ static int gsm48_rx_gmm_ra_upd_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg, struct gprs_llc_llme *llme) { #ifndef PTMSI_ALLOC struct sgsn_signal_data sig_data; #endif struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); uint8_t *cur = gh->data; uint8_t ms_ra_acc_cap_len; struct gprs_ra_id old_ra_id; struct tlv_parsed tp; uint8_t upd_type; enum gsm48_gmm_cause reject_cause = GMM_CAUSE_PROTO_ERR_UNSPEC; int rc; /* TODO: In iu mode - handle follow-on request */ /* Update Type 10.5.5.18 */ upd_type = *cur++ & 0x07; rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_GPRS_ROUTING_AREA_REQUEST]); LOGP(DMM, LOGL_INFO, "-> GMM RA UPDATE REQUEST type=\"%s\"\n", get_value_string(gprs_upd_t_strs, upd_type)); /* Old routing area identification 10.5.5.15 */ gsm48_parse_ra(&old_ra_id, cur); cur += 6; /* MS Radio Access Capability 10.5.5.12a */ ms_ra_acc_cap_len = *cur++; if (ms_ra_acc_cap_len > 52) { LOGP(DMM, LOGL_ERROR, "Rejecting GMM RA Update Request: MS Radio Access Capability too long" " (ms_ra_acc_cap_len = %u > 52)\n", ms_ra_acc_cap_len); reject_cause = GMM_CAUSE_PROTO_ERR_UNSPEC; goto rejected; } cur += ms_ra_acc_cap_len; /* Optional: Old P-TMSI Signature, Requested READY timer, TMSI Status, * DRX parameter, MS network capability */ tlv_parse(&tp, &gsm48_gmm_att_tlvdef, cur, (msg->data + msg->len) - cur, 0, 0); switch (upd_type) { case GPRS_UPD_T_RA_LA: case GPRS_UPD_T_RA_LA_IMSI_ATT: LOGP(DMM, LOGL_NOTICE, "Update type %i unsupported in Mode III, is your SI13 corrupt?\n", upd_type); reject_cause = GMM_CAUSE_PROTO_ERR_UNSPEC; goto rejected; case GPRS_UPD_T_RA: case GPRS_UPD_T_PERIODIC: break; } if (!mmctx) { /* BSSGP doesn't give us an mmctx */ /* TODO: Check if there is an MM CTX with old_ra_id and * the P-TMSI (if given, reguired for UMTS) or as last resort * if the TLLI matches foreign_tlli (P-TMSI). Note that this * is an optimization to avoid the RA reject (impl detached) * below, which will cause a new attach cycle. */ /* Look-up the MM context based on old RA-ID and TLLI */ /* In Iu mode, msg->dst contains the ranap_ue_conn_ctx pointer, in Gb * mode dst is empty. */ /* FIXME: have a more explicit indicator for Iu messages */ if (!msg->dst) { mmctx = sgsn_mm_ctx_by_tlli_and_ptmsi(msgb_tlli(msg), &old_ra_id); } else if (TLVP_PRESENT(&tp, GSM48_IE_GMM_ALLOC_PTMSI)) { #ifdef BUILD_IU /* In Iu mode search only for ptmsi */ char mi_string[GSM48_MI_SIZE]; uint8_t mi_len = TLVP_LEN(&tp, GSM48_IE_GMM_ALLOC_PTMSI); const uint8_t *mi = TLVP_VAL(&tp, GSM48_IE_GMM_ALLOC_PTMSI); uint8_t mi_type = *mi & GSM_MI_TYPE_MASK; uint32_t tmsi; gsm48_mi_to_string(mi_string, sizeof(mi_string), mi, mi_len); if (mi_type == GSM_MI_TYPE_TMSI) { memcpy(&tmsi, mi+1, 4); tmsi = ntohl(tmsi); mmctx = sgsn_mm_ctx_by_ptmsi(tmsi); } #else LOGP(DMM, LOGL_ERROR, "Rejecting GMM RA Update Request: No Iu support\n"); goto rejected; #endif } if (mmctx) { LOGMMCTXP(LOGL_INFO, mmctx, "Looked up by matching TLLI and P_TMSI. " "BSSGP TLLI: %08x, P-TMSI: %08x (%08x), " "TLLI: %08x (%08x), RA: %s\n", msgb_tlli(msg), mmctx->p_tmsi, mmctx->p_tmsi_old, mmctx->gb.tlli, mmctx->gb.tlli_new, osmo_rai_name(&mmctx->ra)); mmctx->gmm_state = GMM_COMMON_PROC_INIT; } } else if (!gprs_ra_id_equals(&mmctx->ra, &old_ra_id) || mmctx->gmm_state == GMM_DEREGISTERED) { /* We cannot use the mmctx */ LOGMMCTXP(LOGL_INFO, mmctx, "The MM context cannot be used, RA: %03d-%0*d-%d-%d\n", mmctx->ra.mcc, mmctx->ra.mnc_3_digits, mmctx->ra.mnc, mmctx->ra.lac, mmctx->ra.rac); mmctx = NULL; } if (!mmctx) { if (llme) { /* send a XID reset to re-set all LLC sequence numbers * in the MS */ LOGMMCTXP(LOGL_NOTICE, mmctx, "LLC XID RESET\n"); gprs_llgmm_reset(llme); } /* The MS has to perform GPRS attach */ /* Device is still IMSI attached for CS but initiate GPRS ATTACH, * see GSM 04.08, 4.7.5.1.4 and G.6 */ LOGMMCTXP(LOGL_ERROR, mmctx, "Rejecting GMM RA Update Request: MS should GMM Attach first\n"); reject_cause = GMM_CAUSE_IMPL_DETACHED; goto rejected; } /* Store new BVCI/NSEI in MM context (FIXME: delay until we ack?) */ msgid2mmctx(mmctx, msg); /* Bump the statistics of received signalling msgs for this MM context */ rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PKTS_SIG_IN]); /* Update the MM context with the new RA-ID */ if (mmctx->ran_type == MM_CTX_T_GERAN_Gb) { bssgp_parse_cell_id(&mmctx->ra, msgb_bcid(msg)); /* Update the MM context with the new (i.e. foreign) TLLI */ mmctx->gb.tlli = msgb_tlli(msg); } /* FIXME: Update the MM context with the MS radio acc capabilities */ /* FIXME: Update the MM context with the MS network capabilities */ rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_RA_UPDATE]); #ifdef PTMSI_ALLOC ptmsi_update(mmctx); /* Start T3350 and re-transmit up to 5 times until ATTACH COMPLETE */ mmctx->t3350_mode = GMM_T3350_MODE_RAU; mmctx_timer_start(mmctx, 3350, sgsn->cfg.timers.T3350); #else /* Make sure we are NORMAL (i.e. not SUSPENDED anymore) */ mmctx->gmm_state = GMM_REGISTERED_NORMAL; memset(&sig_data, 0, sizeof(sig_data)); sig_data.mm = mmctx; osmo_signal_dispatch(SS_SGSN, S_SGSN_UPDATE, &sig_data); #endif if (mmctx->ran_type == MM_CTX_T_GERAN_Gb) { /* Even if there is no P-TMSI allocated, the MS will switch from * foreign TLLI to local TLLI */ mmctx->gb.tlli_new = gprs_tmsi2tlli(mmctx->p_tmsi, TLLI_LOCAL); /* Inform LLC layer about new TLLI but keep old active */ gprs_llgmm_assign(mmctx->gb.llme, mmctx->gb.tlli, mmctx->gb.tlli_new); } /* Look at PDP Context Status IE and see if MS's view of * activated/deactivated NSAPIs agrees with our view */ if (TLVP_PRESENT(&tp, GSM48_IE_GMM_PDP_CTX_STATUS)) { const uint8_t *pdp_status = TLVP_VAL(&tp, GSM48_IE_GMM_PDP_CTX_STATUS); process_ms_ctx_status(mmctx, pdp_status); } /* Send RA UPDATE ACCEPT. In Iu, the RA upd request can be called from * a new Iu connection, so we might need to re-authenticate the * connection as well as turn on integrity protection. */ mmctx->pending_req = GSM48_MT_GMM_RA_UPD_REQ; return gsm48_gmm_authorize(mmctx); rejected: /* Send RA UPDATE REJECT */ LOGMMCTXP(LOGL_NOTICE, mmctx, "Rejecting RA Update Request with cause '%s' (%d)\n", get_value_string(gsm48_gmm_cause_names, reject_cause), reject_cause); rc = gsm48_tx_gmm_ra_upd_rej(msg, reject_cause); if (mmctx) mm_ctx_cleanup_free(mmctx, "GPRS RA UPDATE REJ"); else { if (llme) gprs_llgmm_unassign(llme); } return rc; } /* 3GPP TS 24.008 Section 9.4.20 Service request. * In Iu, a UE in PMM-IDLE mode can use GSM48_MT_GMM_SERVICE_REQ to switch back * to PMM-CONNECTED mode. */ static int gsm48_rx_gmm_service_req(struct sgsn_mm_ctx *ctx, struct msgb *msg) { struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); uint8_t *cur = gh->data, *mi; uint8_t service_type, mi_len, mi_type; uint32_t tmsi; struct tlv_parsed tp; char mi_string[GSM48_MI_SIZE]; enum gsm48_gmm_cause reject_cause; int rc; LOGMMCTXP(LOGL_INFO, ctx, "-> GMM SERVICE REQUEST "); /* This message is only valid in Iu mode */ if (!msg->dst) { LOGPC(DMM, LOGL_INFO, "Invalid if not in Iu mode\n"); return -1; } /* Skip Ciphering key sequence number 10.5.1.2 */ /* uint8_t ciph_seq_nr = *cur & 0x07; */ /* Service type 10.5.5.20 */ service_type = (*cur++ >> 4) & 0x07; /* Mobile Identity (P-TMSI or IMSI) 10.5.1.4 */ mi_len = *cur++; mi = cur; if (mi_len > 8) goto err_inval; mi_type = *mi & GSM_MI_TYPE_MASK; cur += mi_len; gsm48_mi_to_string(mi_string, sizeof(mi_string), mi, mi_len); DEBUGPC(DMM, "MI(%s) type=\"%s\" ", mi_string, get_value_string(gprs_service_t_strs, service_type)); LOGPC(DMM, LOGL_INFO, "\n"); /* Optional: PDP context status, MBMS context status, Uplink data status, Device properties */ tlv_parse(&tp, &gsm48_gmm_att_tlvdef, cur, (msg->data + msg->len) - cur, 0, 0); switch (mi_type) { case GSM_MI_TYPE_IMSI: /* Try to find MM context based on IMSI */ if (!ctx) ctx = sgsn_mm_ctx_by_imsi(mi_string); if (!ctx) { /* FIXME: We need to have a context for service request? */ reject_cause = GMM_CAUSE_NET_FAIL; goto rejected; } msgid2mmctx(ctx, msg); break; case GSM_MI_TYPE_TMSI: memcpy(&tmsi, mi+1, 4); tmsi = ntohl(tmsi); /* Try to find MM context based on P-TMSI */ if (!ctx) ctx = sgsn_mm_ctx_by_ptmsi(tmsi); if (!ctx) { /* FIXME: We need to have a context for service request? */ reject_cause = GMM_CAUSE_NET_FAIL; goto rejected; } msgid2mmctx(ctx, msg); break; default: LOGMMCTXP(LOGL_NOTICE, ctx, "Rejecting SERVICE REQUEST with " "MI type %s\n", gsm48_mi_type_name(mi_type)); reject_cause = GMM_CAUSE_MS_ID_NOT_DERIVED; goto rejected; } ctx->gmm_state = GMM_COMMON_PROC_INIT; ctx->iu.service.type = service_type; /* TODO: Handle those only in case of accept? */ /* Look at PDP Context Status IE and see if MS's view of * activated/deactivated NSAPIs agrees with our view */ if (TLVP_PRESENT(&tp, GSM48_IE_GMM_PDP_CTX_STATUS)) { const uint8_t *pdp_status = TLVP_VAL(&tp, GSM48_IE_GMM_PDP_CTX_STATUS); process_ms_ctx_status(ctx, pdp_status); } ctx->pending_req = GSM48_MT_GMM_SERVICE_REQ; return gsm48_gmm_authorize(ctx); err_inval: LOGPC(DMM, LOGL_INFO, "\n"); reject_cause = GMM_CAUSE_SEM_INCORR_MSG; rejected: /* Send SERVICE REJECT */ LOGMMCTXP(LOGL_NOTICE, ctx, "Rejecting Service Request with cause '%s' (%d)\n", get_value_string(gsm48_gmm_cause_names, reject_cause), reject_cause); rc = gsm48_tx_gmm_service_rej_oldmsg(msg, reject_cause); return rc; } static int gsm48_rx_gmm_status(struct sgsn_mm_ctx *mmctx, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); LOGMMCTXP(LOGL_INFO, mmctx, "-> GPRS MM STATUS (cause: %s)\n", get_value_string(gsm48_gmm_cause_names, gh->data[0])); return 0; } /* GPRS Mobility Management */ static int gsm0408_rcv_gmm(struct sgsn_mm_ctx *mmctx, struct msgb *msg, struct gprs_llc_llme *llme, bool drop_cipherable) { struct sgsn_signal_data sig_data; struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); int rc; /* MMCTX can be NULL when called */ if (drop_cipherable && gsm48_hdr_gmm_cipherable(gh)) { LOGMMCTXP(LOGL_NOTICE, mmctx, "Dropping cleartext GMM %s which " "is expected to be encrypted for TLLI 0x%08x\n", get_value_string(gprs_msgt_gmm_names, gh->msg_type), llme->tlli); return -EBADMSG; } if (llme && !mmctx && gh->msg_type != GSM48_MT_GMM_ATTACH_REQ && gh->msg_type != GSM48_MT_GMM_RA_UPD_REQ) { LOGP(DMM, LOGL_NOTICE, "Cannot handle GMM for unknown MM CTX\n"); /* 4.7.10 */ if (gh->msg_type == GSM48_MT_GMM_STATUS) { /* TLLI unassignment */ gprs_llgmm_unassign(llme); return 0; } /* Don't reply or establish a LLME on DETACH_ACK */ if (gh->msg_type == GSM48_MT_GMM_DETACH_ACK) return gprs_llgmm_unassign(llme); /* Don't reply to deatch requests, reason power off */ if (gh->msg_type == GSM48_MT_GMM_DETACH_REQ && gh->data[0] & 0x8) { return 0; } gprs_llgmm_reset(llme); /* Don't force it into re-attachment */ if (gh->msg_type == GSM48_MT_GMM_DETACH_REQ) { /* Handle Detach Request */ rc = gsm48_rx_gmm_det_req(NULL, msg); /* TLLI unassignment */ gprs_llgmm_unassign(llme); return rc; } /* Force the MS to re-attach */ rc = gsm0408_gprs_force_reattach_oldmsg(msg, llme); /* TLLI unassignment */ gprs_llgmm_unassign(llme); return rc; } /* * For a few messages, mmctx may be NULL. For most, we want to ensure a * non-NULL mmctx. At the same time, we want to keep the message * validity check intact, so that all message types appear in the * switch statement and the default case thus means "unknown message". * If we split the switch in two parts to check non-NULL halfway, the * unknown-message check breaks, or we'd need to duplicate the switch * cases in both parts. Just keep one large switch and add some gotos. */ switch (gh->msg_type) { case GSM48_MT_GMM_RA_UPD_REQ: rc = gsm48_rx_gmm_ra_upd_req(mmctx, msg, llme); break; case GSM48_MT_GMM_ATTACH_REQ: rc = gsm48_rx_gmm_att_req(mmctx, msg, llme); break; case GSM48_MT_GMM_SERVICE_REQ: rc = gsm48_rx_gmm_service_req(mmctx, msg); break; /* For all the following types mmctx can not be NULL */ case GSM48_MT_GMM_ID_RESP: if (!mmctx) goto null_mmctx; rc = gsm48_rx_gmm_id_resp(mmctx, msg); break; case GSM48_MT_GMM_STATUS: if (!mmctx) goto null_mmctx; rc = gsm48_rx_gmm_status(mmctx, msg); break; case GSM48_MT_GMM_DETACH_REQ: if (!mmctx) goto null_mmctx; rc = gsm48_rx_gmm_det_req(mmctx, msg); break; case GSM48_MT_GMM_DETACH_ACK: if (!mmctx) goto null_mmctx; LOGMMCTXP(LOGL_INFO, mmctx, "-> DETACH ACK\n"); mm_ctx_cleanup_free(mmctx, "GPRS DETACH ACK"); rc = 0; break; case GSM48_MT_GMM_ATTACH_COMPL: if (!mmctx) goto null_mmctx; /* only in case SGSN offered new P-TMSI */ LOGMMCTXP(LOGL_INFO, mmctx, "-> ATTACH COMPLETE\n"); mmctx_timer_stop(mmctx, 3350); mmctx->t3350_mode = GMM_T3350_MODE_NONE; mmctx->p_tmsi_old = 0; mmctx->pending_req = 0; if (mmctx->ran_type == MM_CTX_T_GERAN_Gb) { /* Unassign the old TLLI */ mmctx->gb.tlli = mmctx->gb.tlli_new; gprs_llme_copy_key(mmctx, mmctx->gb.llme); gprs_llgmm_assign(mmctx->gb.llme, 0xffffffff, mmctx->gb.tlli_new); } mmctx->gmm_state = GMM_REGISTERED_NORMAL; mmctx_set_pmm_state(mmctx, PMM_CONNECTED); mmctx_set_mm_state(mmctx, MM_READY); rc = 0; memset(&sig_data, 0, sizeof(sig_data)); sig_data.mm = mmctx; osmo_signal_dispatch(SS_SGSN, S_SGSN_ATTACH, &sig_data); break; case GSM48_MT_GMM_RA_UPD_COMPL: if (!mmctx) goto null_mmctx; /* only in case SGSN offered new P-TMSI */ LOGMMCTXP(LOGL_INFO, mmctx, "-> ROUTING AREA UPDATE COMPLETE\n"); mmctx_timer_stop(mmctx, 3350); mmctx->t3350_mode = GMM_T3350_MODE_NONE; mmctx->p_tmsi_old = 0; mmctx->pending_req = 0; if (mmctx->ran_type == MM_CTX_T_GERAN_Gb) { /* Unassign the old TLLI */ mmctx->gb.tlli = mmctx->gb.tlli_new; gprs_llgmm_assign(mmctx->gb.llme, 0xffffffff, mmctx->gb.tlli_new); } mmctx->gmm_state = GMM_REGISTERED_NORMAL; mmctx_set_pmm_state(mmctx, PMM_CONNECTED); mmctx_set_mm_state(mmctx, MM_READY); rc = 0; memset(&sig_data, 0, sizeof(sig_data)); sig_data.mm = mmctx; osmo_signal_dispatch(SS_SGSN, S_SGSN_UPDATE, &sig_data); break; case GSM48_MT_GMM_PTMSI_REALL_COMPL: if (!mmctx) goto null_mmctx; LOGMMCTXP(LOGL_INFO, mmctx, "-> PTMSI REALLLICATION COMPLETE\n"); mmctx_timer_stop(mmctx, 3350); mmctx->t3350_mode = GMM_T3350_MODE_NONE; mmctx->p_tmsi_old = 0; mmctx->pending_req = 0; if (mmctx->ran_type == MM_CTX_T_GERAN_Gb) { /* Unassign the old TLLI */ mmctx->gb.tlli = mmctx->gb.tlli_new; //gprs_llgmm_assign(mmctx->gb.llme, 0xffffffff, mmctx->gb.tlli_new, GPRS_ALGO_GEA0, NULL); } rc = 0; break; case GSM48_MT_GMM_AUTH_CIPH_RESP: if (!mmctx) goto null_mmctx; rc = gsm48_rx_gmm_auth_ciph_resp(mmctx, msg); break; case GSM48_MT_GMM_AUTH_CIPH_FAIL: rc = gsm48_rx_gmm_auth_ciph_fail(mmctx, msg); break; default: LOGMMCTXP(LOGL_NOTICE, mmctx, "Unknown GSM 04.08 GMM msg type 0x%02x\n", gh->msg_type); if (mmctx) rc = gsm48_tx_gmm_status(mmctx, GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL); else rc = -EINVAL; break; } return rc; null_mmctx: LOGP(DMM, LOGL_ERROR, "Received GSM 04.08 message type 0x%02x," " but no MM context available\n", gh->msg_type); return -EINVAL; } static void mmctx_timer_cb(void *_mm) { struct sgsn_mm_ctx *mm = _mm; struct gsm_auth_tuple *at; int rc; mm->num_T_exp++; switch (mm->T) { case 3350: /* waiting for ATTACH COMPLETE */ if (mm->num_T_exp >= 5) { LOGMMCTXP(LOGL_NOTICE, mm, "T3350 expired >= 5 times\n"); mm_ctx_cleanup_free(mm, "T3350"); /* FIXME: should we return some error? */ break; } /* re-transmit the respective msg and re-start timer */ switch (mm->t3350_mode) { case GMM_T3350_MODE_ATT: gsm48_tx_gmm_att_ack(mm); break; case GMM_T3350_MODE_RAU: gsm48_tx_gmm_ra_upd_ack(mm); break; case GMM_T3350_MODE_PTMSI_REALL: /* FIXME */ break; case GMM_T3350_MODE_NONE: LOGMMCTXP(LOGL_NOTICE, mm, "T3350 mode wasn't set, ignoring timeout\n"); break; } osmo_timer_schedule(&mm->timer, sgsn->cfg.timers.T3350, 0); break; case 3360: /* waiting for AUTH AND CIPH RESP */ if (mm->num_T_exp >= 5) { LOGMMCTXP(LOGL_NOTICE, mm, "T3360 expired >= 5 times\n"); mm_ctx_cleanup_free(mm, "T3360"); break; } /* Re-transmit the respective msg and re-start timer */ if (mm->auth_triplet.key_seq == GSM_KEY_SEQ_INVAL) { LOGMMCTXP(LOGL_ERROR, mm, "timeout: invalid auth triplet reference\n"); mm_ctx_cleanup_free(mm, "T3360"); break; } at = &mm->auth_triplet; rc = gsm48_tx_gmm_auth_ciph_req(mm, &at->vec, at->key_seq, false); if (rc < 0) LOGMMCTXP(LOGL_ERROR, mm, "failed sending Auth. & Ciph. Reuqest: %s \n", strerror(-rc)); else osmo_timer_schedule(&mm->timer, sgsn->cfg.timers.T3360, 0); break; case 3370: /* waiting for IDENTITY RESPONSE */ if (mm->num_T_exp >= 5) { LOGMMCTXP(LOGL_NOTICE, mm, "T3370 expired >= 5 times\n"); gsm48_tx_gmm_att_rej(mm, GMM_CAUSE_MS_ID_NOT_DERIVED); mm_ctx_cleanup_free(mm, "GPRS ATTACH REJECT (T3370)"); break; } /* re-tranmit IDENTITY REQUEST and re-start timer */ gsm48_tx_gmm_id_req(mm, mm->t3370_id_type); osmo_timer_schedule(&mm->timer, sgsn->cfg.timers.T3370, 0); break; default: LOGMMCTXP(LOGL_ERROR, mm, "timer expired in unknown mode %u\n", mm->T); } } /* GPRS SESSION MANAGEMENT */ static void pdpctx_timer_cb(void *_mm); static void pdpctx_timer_start(struct sgsn_pdp_ctx *pdp, unsigned int T, unsigned int seconds) { if (osmo_timer_pending(&pdp->timer)) LOGPDPCTXP(LOGL_ERROR, pdp, "Starting PDP timer %u while old " "timer %u pending\n", T, pdp->T); pdp->T = T; pdp->num_T_exp = 0; /* FIXME: we should do this only once ? */ osmo_timer_setup(&pdp->timer, pdpctx_timer_cb, pdp); osmo_timer_schedule(&pdp->timer, seconds, 0); } static void pdpctx_timer_stop(struct sgsn_pdp_ctx *pdp, unsigned int T) { if (pdp->T != T) LOGPDPCTXP(LOGL_ERROR, pdp, "Stopping PDP timer %u but " "%u is running\n", T, pdp->T); osmo_timer_del(&pdp->timer); } #if 0 static void msgb_put_pdp_addr_ipv4(struct msgb *msg, uint32_t ipaddr) { uint8_t v[6]; v[0] = PDP_TYPE_ORG_IETF; v[1] = PDP_TYPE_N_IETF_IPv4; *(uint32_t *)(v+2) = htonl(ipaddr); msgb_tlv_put(msg, GSM48_IE_GSM_PDP_ADDR, sizeof(v), v); } static void msgb_put_pdp_addr_ppp(struct msgb *msg) { uint8_t v[2]; v[0] = PDP_TYPE_ORG_ETSI; v[1] = PDP_TYPE_N_ETSI_PPP; msgb_tlv_put(msg, GSM48_IE_GSM_PDP_ADDR, sizeof(v), v); } #endif /* Section 9.5.2: Activate PDP Context Accept */ int gsm48_tx_gsm_act_pdp_acc(struct sgsn_pdp_ctx *pdp) { struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 PDP ACC"); struct gsm48_hdr *gh; uint8_t transaction_id = pdp->ti ^ 0x8; /* flip */ LOGPDPCTXP(LOGL_INFO, pdp, "<- ACTIVATE PDP CONTEXT ACK\n"); rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_PDP_ACTIVATE_ACCEPT]); mmctx2msgid(msg, pdp->mm); gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->proto_discr = GSM48_PDISC_SM_GPRS | (transaction_id << 4); gh->msg_type = GSM48_MT_GSM_ACT_PDP_ACK; /* Negotiated LLC SAPI */ msgb_v_put(msg, pdp->sapi); /* FIXME: copy QoS parameters from original request */ //msgb_lv_put(msg, pdp->lib->qos_neg.l, pdp->lib->qos_neg.v); msgb_lv_put(msg, sizeof(default_qos), (uint8_t *)&default_qos); /* Radio priority 10.5.7.2 */ msgb_v_put(msg, pdp->lib->radio_pri); /* PDP address */ /* Highest 4 bits of first byte need to be set to 1, otherwise * the IE is identical with the 04.08 PDP Address IE */ pdp->lib->eua.v[0] &= ~0xf0; msgb_tlv_put(msg, GSM48_IE_GSM_PDP_ADDR, pdp->lib->eua.l, pdp->lib->eua.v); pdp->lib->eua.v[0] |= 0xf0; /* Optional: Protocol configuration options (FIXME: why 'req') */ if (pdp->lib->pco_req.l) msgb_tlv_put(msg, GSM48_IE_GSM_PROTO_CONF_OPT, pdp->lib->pco_req.l, pdp->lib->pco_req.v); /* Optional: Packet Flow Identifier */ return gsm48_gmm_sendmsg(msg, 0, pdp->mm, true); } /* Section 9.5.3: Activate PDP Context reject */ int gsm48_tx_gsm_act_pdp_rej(struct sgsn_mm_ctx *mm, uint8_t tid, uint8_t cause, uint8_t pco_len, uint8_t *pco_v) { struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 PDP REJ"); struct gsm48_hdr *gh; uint8_t transaction_id = tid ^ 0x8; /* flip */ LOGMMCTXP(LOGL_NOTICE, mm, "<- ACTIVATE PDP CONTEXT REJ(cause=%u)\n", cause); rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_PDP_ACTIVATE_REJECT]); mmctx2msgid(msg, mm); gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->proto_discr = GSM48_PDISC_SM_GPRS | (transaction_id << 4); gh->msg_type = GSM48_MT_GSM_ACT_PDP_REJ; msgb_v_put(msg, cause); if (pco_len && pco_v) msgb_tlv_put(msg, GSM48_IE_GSM_PROTO_CONF_OPT, pco_len, pco_v); return gsm48_gmm_sendmsg(msg, 0, mm, true); } /* Section 9.5.8: Deactivate PDP Context Request */ static int _gsm48_tx_gsm_deact_pdp_req(struct sgsn_mm_ctx *mm, uint8_t tid, uint8_t sm_cause) { struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 PDP DET REQ"); struct gsm48_hdr *gh; uint8_t transaction_id = tid ^ 0x8; /* flip */ LOGMMCTXP(LOGL_INFO, mm, "<- DEACTIVATE PDP CONTEXT REQ\n"); rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_PDP_DL_DEACTIVATE_REQUEST]); mmctx2msgid(msg, mm); gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->proto_discr = GSM48_PDISC_SM_GPRS | (transaction_id << 4); gh->msg_type = GSM48_MT_GSM_DEACT_PDP_REQ; msgb_v_put(msg, sm_cause); return gsm48_gmm_sendmsg(msg, 0, mm, true); } int gsm48_tx_gsm_deact_pdp_req(struct sgsn_pdp_ctx *pdp, uint8_t sm_cause) { pdpctx_timer_start(pdp, 3395, sgsn->cfg.timers.T3395); return _gsm48_tx_gsm_deact_pdp_req(pdp->mm, pdp->ti, sm_cause); } /* Section 9.5.9: Deactivate PDP Context Accept */ static int _gsm48_tx_gsm_deact_pdp_acc(struct sgsn_mm_ctx *mm, uint8_t tid) { struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 PDP DET ACC"); struct gsm48_hdr *gh; uint8_t transaction_id = tid ^ 0x8; /* flip */ LOGMMCTXP(LOGL_INFO, mm, "<- DEACTIVATE PDP CONTEXT ACK\n"); rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_PDP_DL_DEACTIVATE_ACCEPT]); mmctx2msgid(msg, mm); gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->proto_discr = GSM48_PDISC_SM_GPRS | (transaction_id << 4); gh->msg_type = GSM48_MT_GSM_DEACT_PDP_ACK; return gsm48_gmm_sendmsg(msg, 0, mm, true); } int gsm48_tx_gsm_deact_pdp_acc(struct sgsn_pdp_ctx *pdp) { return _gsm48_tx_gsm_deact_pdp_acc(pdp->mm, pdp->ti); } static int activate_ggsn(struct sgsn_mm_ctx *mmctx, struct sgsn_ggsn_ctx *ggsn, const uint8_t transaction_id, const uint8_t req_nsapi, const uint8_t req_llc_sapi, struct tlv_parsed *tp, int destroy_ggsn) { struct sgsn_pdp_ctx *pdp; LOGMMCTXP(LOGL_DEBUG, mmctx, "Using GGSN %u\n", ggsn->id); ggsn->gsn = sgsn->gsn; pdp = sgsn_create_pdp_ctx(ggsn, mmctx, req_nsapi, tp); if (!pdp) return -1; /* Store SAPI and Transaction Identifier */ pdp->sapi = req_llc_sapi; pdp->ti = transaction_id; pdp->destroy_ggsn = destroy_ggsn; return 0; } static void ggsn_lookup_cb(void *arg, int status, int timeouts, struct hostent *hostent) { struct sgsn_ggsn_ctx *ggsn; struct sgsn_ggsn_lookup *lookup = arg; struct in_addr *addr = NULL; /* The context is gone while we made a request */ if (!lookup->mmctx) { talloc_free(lookup->orig_msg); talloc_free(lookup); return; } if (status != ARES_SUCCESS) { struct sgsn_mm_ctx *mmctx = lookup->mmctx; LOGMMCTXP(LOGL_ERROR, mmctx, "DNS query failed.\n"); /* Need to try with three digits now */ if (lookup->state == SGSN_GGSN_2DIGIT) { char *hostname; int rc; lookup->state = SGSN_GGSN_3DIGIT; hostname = osmo_apn_qualify_from_imsi(mmctx->imsi, lookup->apn_str, 1); LOGMMCTXP(LOGL_DEBUG, mmctx, "Going to query %s\n", hostname); rc = sgsn_ares_query(sgsn, hostname, ggsn_lookup_cb, lookup); if (rc != 0) { LOGMMCTXP(LOGL_ERROR, mmctx, "Couldn't start GGSN\n"); goto reject_due_failure; } return; } LOGMMCTXP(LOGL_ERROR, mmctx, "Couldn't resolve GGSN\n"); goto reject_due_failure; } if (hostent->h_length != sizeof(struct in_addr)) { LOGMMCTXP(LOGL_ERROR, lookup->mmctx, "Wrong addr size(%zu)\n", sizeof(struct in_addr)); goto reject_due_failure; } /* Get the first addr from the list */ addr = (struct in_addr *) hostent->h_addr_list[0]; if (!addr) { LOGMMCTXP(LOGL_ERROR, lookup->mmctx, "No host address.\n"); goto reject_due_failure; } ggsn = sgsn_ggsn_ctx_alloc(UINT32_MAX); if (!ggsn) { LOGMMCTXP(LOGL_ERROR, lookup->mmctx, "Failed to create ggsn.\n"); goto reject_due_failure; } ggsn->remote_addr = *addr; LOGMMCTXP(LOGL_NOTICE, lookup->mmctx, "Selected %s as GGSN.\n", inet_ntoa(*addr)); /* forget about the ggsn look-up */ lookup->mmctx->ggsn_lookup = NULL; activate_ggsn(lookup->mmctx, ggsn, lookup->ti, lookup->nsapi, lookup->sapi, &lookup->tp, 1); /* Now free it */ talloc_free(lookup->orig_msg); talloc_free(lookup); return; reject_due_failure: gsm48_tx_gsm_act_pdp_rej(lookup->mmctx, lookup->ti, GMM_CAUSE_NET_FAIL, 0, NULL); lookup->mmctx->ggsn_lookup = NULL; talloc_free(lookup->orig_msg); talloc_free(lookup); } static int do_act_pdp_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg, bool *delete) { struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); struct gsm48_act_pdp_ctx_req *act_req = (struct gsm48_act_pdp_ctx_req *) gh->data; uint8_t req_qos_len, req_pdpa_len; uint8_t *req_qos, *req_pdpa; struct tlv_parsed tp; uint8_t transaction_id = gsm48_hdr_trans_id(gh); struct sgsn_ggsn_ctx *ggsn; struct sgsn_pdp_ctx *pdp; enum gsm48_gsm_cause gsm_cause; char apn_str[GSM_APN_LENGTH] = { 0, }; char *hostname; int rc; struct gprs_llc_lle *lle; LOGMMCTXP(LOGL_INFO, mmctx, "-> ACTIVATE PDP CONTEXT REQ: SAPI=%u NSAPI=%u ", act_req->req_llc_sapi, act_req->req_nsapi); /* FIXME: length checks! */ req_qos_len = act_req->data[0]; req_qos = act_req->data + 1; /* 10.5.6.5 */ req_pdpa_len = act_req->data[1 + req_qos_len]; req_pdpa = act_req->data + 1 + req_qos_len + 1; /* 10.5.6.4 */ switch (req_pdpa[0] & 0xf) { case 0x0: DEBUGPC(DMM, "ETSI "); break; case 0x1: DEBUGPC(DMM, "IETF "); break; case 0xf: DEBUGPC(DMM, "Empty "); break; } switch (req_pdpa[1]) { case 0x21: DEBUGPC(DMM, "IPv4 "); if (req_pdpa_len >= 6) { struct in_addr ia; ia.s_addr = ntohl(*((uint32_t *) (req_pdpa+2))); DEBUGPC(DMM, "%s ", inet_ntoa(ia)); } break; case 0x57: DEBUGPC(DMM, "IPv6 "); if (req_pdpa_len >= 18) { /* FIXME: print IPv6 address */ } break; default: DEBUGPC(DMM, "0x%02x ", req_pdpa[1]); break; } LOGPC(DMM, LOGL_INFO, "\n"); /* Check if NSAPI is out of range (TS 04.65 / 7.2) */ if (act_req->req_nsapi < 5 || act_req->req_nsapi > 15) { /* Send reject with GSM_CAUSE_INV_MAND_INFO */ return gsm48_tx_gsm_act_pdp_rej(mmctx, transaction_id, GSM_CAUSE_INV_MAND_INFO, 0, NULL); } /* Optional: Access Point Name, Protocol Config Options */ if (req_pdpa + req_pdpa_len < msg->data + msg->len) tlv_parse(&tp, &gsm48_sm_att_tlvdef, req_pdpa + req_pdpa_len, (msg->data + msg->len) - (req_pdpa + req_pdpa_len), 0, 0); else memset(&tp, 0, sizeof(tp)); /* put the non-TLV elements in the TLV parser structure to * pass them on to the SGSN / GTP code */ tp.lv[OSMO_IE_GSM_REQ_QOS].len = req_qos_len; tp.lv[OSMO_IE_GSM_REQ_QOS].val = req_qos; tp.lv[OSMO_IE_GSM_REQ_PDP_ADDR].len = req_pdpa_len; tp.lv[OSMO_IE_GSM_REQ_PDP_ADDR].val = req_pdpa; /* Check if NSAPI is already in use */ pdp = sgsn_pdp_ctx_by_nsapi(mmctx, act_req->req_nsapi); if (pdp) { /* We already have a PDP context for this TLLI + NSAPI tuple */ if (pdp->sapi == act_req->req_llc_sapi && pdp->ti == transaction_id) { /* This apparently is a re-transmission of a PDP CTX * ACT REQ (our ACT ACK must have got dropped) */ rc = gsm48_tx_gsm_act_pdp_acc(pdp); if (rc < 0) return rc; if (pdp->mm->ran_type == MM_CTX_T_GERAN_Gb) { /* Also re-transmit the SNDCP XID message */ lle = &pdp->mm->gb.llme->lle[pdp->sapi]; rc = sndcp_sn_xid_req(lle,pdp->nsapi); if (rc < 0) return rc; } return 0; } /* Send reject with GSM_CAUSE_NSAPI_IN_USE */ return gsm48_tx_gsm_act_pdp_rej(mmctx, transaction_id, GSM_CAUSE_NSAPI_IN_USE, 0, NULL); } if (mmctx->ggsn_lookup) { if (mmctx->ggsn_lookup->sapi == act_req->req_llc_sapi && mmctx->ggsn_lookup->ti == transaction_id) { LOGMMCTXP(LOGL_NOTICE, mmctx, "Re-transmission while doing look-up. Ignoring.\n"); return 0; } } /* Only increment counter for a real activation, after we checked * for re-transmissions */ rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PDP_CTX_ACT]); /* Determine GGSN based on APN and subscription options */ ggsn = sgsn_mm_ctx_find_ggsn_ctx(mmctx, &tp, &gsm_cause, apn_str); if (ggsn) return activate_ggsn(mmctx, ggsn, transaction_id, act_req->req_nsapi, act_req->req_llc_sapi, &tp, 0); if (strlen(apn_str) == 0) goto no_context; if (!sgsn->cfg.dynamic_lookup) goto no_context; /* schedule a dynamic look-up */ mmctx->ggsn_lookup = talloc_zero(tall_bsc_ctx, struct sgsn_ggsn_lookup); if (!mmctx->ggsn_lookup) goto no_context; mmctx->ggsn_lookup->state = SGSN_GGSN_2DIGIT; mmctx->ggsn_lookup->mmctx = mmctx; strcpy(mmctx->ggsn_lookup->apn_str, apn_str); mmctx->ggsn_lookup->orig_msg = msg; mmctx->ggsn_lookup->tp = tp; mmctx->ggsn_lookup->ti = transaction_id; mmctx->ggsn_lookup->nsapi = act_req->req_nsapi; mmctx->ggsn_lookup->sapi = act_req->req_llc_sapi; hostname = osmo_apn_qualify_from_imsi(mmctx->imsi, mmctx->ggsn_lookup->apn_str, 0); LOGMMCTXP(LOGL_DEBUG, mmctx, "Going to query %s\n", hostname); rc = sgsn_ares_query(sgsn, hostname, ggsn_lookup_cb, mmctx->ggsn_lookup); if (rc != 0) { LOGMMCTXP(LOGL_ERROR, mmctx, "Failed to start ares query.\n"); goto no_context; } *delete = 0; return 0; no_context: LOGMMCTXP(LOGL_ERROR, mmctx, "No GGSN context found!\n"); return gsm48_tx_gsm_act_pdp_rej(mmctx, transaction_id, gsm_cause, 0, NULL); } /* Section 9.5.1: Activate PDP Context Request */ static int gsm48_rx_gsm_act_pdp_req(struct sgsn_mm_ctx *mmctx, struct msgb *_msg) { bool delete = 1; struct msgb *msg; int rc; rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_PDP_ACTIVATE_REQUEST]); /* * This is painful. We might not have a static GGSN * configuration and then would need to copy the msg * and re-do most of this routine (or call it again * and make sure it only goes through the dynamic * resolving. The question is what to optimize for * and the dynamic resolution will be the right thing * in the long run. */ msg = gprs_msgb_copy(_msg, __func__); if (!msg) { struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(_msg); uint8_t transaction_id = gsm48_hdr_trans_id(gh); LOGMMCTXP(LOGL_ERROR, mmctx, "-> ACTIVATE PDP CONTEXT REQ failed copy.\n"); /* Send reject with GSM_CAUSE_INV_MAND_INFO */ return gsm48_tx_gsm_act_pdp_rej(mmctx, transaction_id, GSM_CAUSE_NET_FAIL, 0, NULL); } rc = do_act_pdp_req(mmctx, msg, &delete); if (delete) msgb_free(msg); return rc; } /* Section 9.5.8: Deactivate PDP Context Request */ static int gsm48_rx_gsm_deact_pdp_req(struct sgsn_mm_ctx *mm, struct msgb *msg) { struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); uint8_t transaction_id = gsm48_hdr_trans_id(gh); struct sgsn_pdp_ctx *pdp; LOGMMCTXP(LOGL_INFO, mm, "-> DEACTIVATE PDP CONTEXT REQ (cause: %s)\n", get_value_string(gsm48_gsm_cause_names, gh->data[0])); rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_PDP_UL_DEACTIVATE_REQUEST]); pdp = sgsn_pdp_ctx_by_tid(mm, transaction_id); if (!pdp) { LOGMMCTXP(LOGL_NOTICE, mm, "Deactivate PDP Context Request for " "non-existing PDP Context (IMSI=%s, TI=%u)\n", mm->imsi, transaction_id); return _gsm48_tx_gsm_deact_pdp_acc(mm, transaction_id); } return sgsn_delete_pdp_ctx(pdp); } /* Section 9.5.9: Deactivate PDP Context Accept */ static int gsm48_rx_gsm_deact_pdp_ack(struct sgsn_mm_ctx *mm, struct msgb *msg) { struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); uint8_t transaction_id = gsm48_hdr_trans_id(gh); struct sgsn_pdp_ctx *pdp; LOGMMCTXP(LOGL_INFO, mm, "-> DEACTIVATE PDP CONTEXT ACK\n"); rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_PDP_UL_DEACTIVATE_ACCEPT]); pdp = sgsn_pdp_ctx_by_tid(mm, transaction_id); if (!pdp) { LOGMMCTXP(LOGL_NOTICE, mm, "Deactivate PDP Context Accept for " "non-existing PDP Context (IMSI=%s, TI=%u)\n", mm->imsi, transaction_id); return 0; } /* stop timer 3395 */ pdpctx_timer_stop(pdp, 3395); return sgsn_delete_pdp_ctx(pdp); } static int gsm48_rx_gsm_status(struct sgsn_mm_ctx *ctx, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); LOGMMCTXP(LOGL_INFO, ctx, "-> GPRS SM STATUS (cause: %s)\n", get_value_string(gsm48_gsm_cause_names, gh->data[0])); return 0; } static void pdpctx_timer_cb(void *_pdp) { struct sgsn_pdp_ctx *pdp = _pdp; pdp->num_T_exp++; switch (pdp->T) { case 3395: /* waiting for PDP CTX DEACT ACK */ if (pdp->num_T_exp >= 4) { LOGPDPCTXP(LOGL_NOTICE, pdp, "T3395 expired >= 5 times\n"); pdp->state = PDP_STATE_INACTIVE; sgsn_delete_pdp_ctx(pdp); break; } gsm48_tx_gsm_deact_pdp_req(pdp, GSM_CAUSE_NET_FAIL); break; default: LOGPDPCTXP(LOGL_ERROR, pdp, "timer expired in unknown mode %u\n", pdp->T); } } /* GPRS Session Management */ static int gsm0408_rcv_gsm(struct sgsn_mm_ctx *mmctx, struct msgb *msg, struct gprs_llc_llme *llme) { struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); int rc; /* MMCTX can be NULL when called */ if (!mmctx) { LOGP(DMM, LOGL_NOTICE, "Cannot handle SM for unknown MM CTX\n"); /* 6.1.3.6 */ if (gh->msg_type == GSM48_MT_GSM_STATUS) return 0; return gsm0408_gprs_force_reattach_oldmsg(msg, llme); } switch (gh->msg_type) { case GSM48_MT_GSM_ACT_PDP_REQ: rc = gsm48_rx_gsm_act_pdp_req(mmctx, msg); break; case GSM48_MT_GSM_DEACT_PDP_REQ: rc = gsm48_rx_gsm_deact_pdp_req(mmctx, msg); break; case GSM48_MT_GSM_DEACT_PDP_ACK: rc = gsm48_rx_gsm_deact_pdp_ack(mmctx, msg); break; case GSM48_MT_GSM_STATUS: rc = gsm48_rx_gsm_status(mmctx, msg); break; case GSM48_MT_GSM_REQ_PDP_ACT_REJ: case GSM48_MT_GSM_ACT_AA_PDP_REQ: case GSM48_MT_GSM_DEACT_AA_PDP_REQ: LOGMMCTXP(LOGL_NOTICE, mmctx, "Unimplemented GSM 04.08 GSM msg type 0x%02x: %s\n", gh->msg_type, osmo_hexdump((uint8_t *)gh, msgb_l3len(msg))); rc = gsm48_tx_sm_status(mmctx, GSM_CAUSE_MSGT_NOTEXIST_NOTIMPL); break; default: LOGMMCTXP(LOGL_NOTICE, mmctx, "Unknown GSM 04.08 GSM msg type 0x%02x: %s\n", gh->msg_type, osmo_hexdump((uint8_t *)gh, msgb_l3len(msg))); rc = gsm48_tx_sm_status(mmctx, GSM_CAUSE_MSGT_NOTEXIST_NOTIMPL); break; } return rc; } int gsm0408_gprs_force_reattach_oldmsg(struct msgb *msg, struct gprs_llc_llme *llme) { int rc; if (llme) gprs_llgmm_reset_oldmsg(msg, GPRS_SAPI_GMM, llme); rc = gsm48_tx_gmm_detach_req_oldmsg( msg, GPRS_DET_T_MT_REATT_REQ, GMM_CAUSE_IMPL_DETACHED); return rc; } int gsm0408_gprs_force_reattach(struct sgsn_mm_ctx *mmctx) { int rc; if (mmctx->ran_type == MM_CTX_T_GERAN_Gb) gprs_llgmm_reset(mmctx->gb.llme); rc = gsm48_tx_gmm_detach_req( mmctx, GPRS_DET_T_MT_REATT_REQ, GMM_CAUSE_IMPL_DETACHED); mm_ctx_cleanup_free(mmctx, "forced reattach"); return rc; } /* Main entry point for incoming 04.08 GPRS messages from Iu */ int gsm0408_gprs_rcvmsg_iu(struct msgb *msg, struct gprs_ra_id *ra_id, uint16_t *sai) { struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); uint8_t pdisc = gsm48_hdr_pdisc(gh); struct sgsn_mm_ctx *mmctx; int rc = -EINVAL; mmctx = sgsn_mm_ctx_by_ue_ctx(msg->dst); if (mmctx) { rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PKTS_SIG_IN]); if (ra_id) memcpy(&mmctx->ra, ra_id, sizeof(mmctx->ra)); } /* MMCTX can be NULL */ switch (pdisc) { case GSM48_PDISC_MM_GPRS: rc = gsm0408_rcv_gmm(mmctx, msg, NULL, false); #pragma message "set drop_cipherable arg for gsm0408_rcv_gmm() from IuPS?" break; case GSM48_PDISC_SM_GPRS: rc = gsm0408_rcv_gsm(mmctx, msg, NULL); break; default: LOGMMCTXP(LOGL_NOTICE, mmctx, "Unknown GSM 04.08 discriminator 0x%02x: %s\n", pdisc, osmo_hexdump((uint8_t *)gh, msgb_l3len(msg))); /* FIXME: return status message */ break; } /* MMCTX can be invalid */ return rc; } /* Main entry point for incoming 04.08 GPRS messages from Gb */ int gsm0408_gprs_rcvmsg_gb(struct msgb *msg, struct gprs_llc_llme *llme, bool drop_cipherable) { struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); uint8_t pdisc = gsm48_hdr_pdisc(gh); struct sgsn_mm_ctx *mmctx; struct gprs_ra_id ra_id; int rc = -EINVAL; bssgp_parse_cell_id(&ra_id, msgb_bcid(msg)); mmctx = sgsn_mm_ctx_by_tlli(msgb_tlli(msg), &ra_id); if (mmctx) { msgid2mmctx(mmctx, msg); rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PKTS_SIG_IN]); mmctx->gb.llme = llme; } /* MMCTX can be NULL */ switch (pdisc) { case GSM48_PDISC_MM_GPRS: rc = gsm0408_rcv_gmm(mmctx, msg, llme, drop_cipherable); break; case GSM48_PDISC_SM_GPRS: rc = gsm0408_rcv_gsm(mmctx, msg, llme); break; default: LOGMMCTXP(LOGL_NOTICE, mmctx, "Unknown GSM 04.08 discriminator 0x%02x: %s\n", pdisc, osmo_hexdump((uint8_t *)gh, msgb_l3len(msg))); /* FIXME: return status message */ break; } /* MMCTX can be invalid */ return rc; } int gprs_gmm_rx_suspend(struct gprs_ra_id *raid, uint32_t tlli) { struct sgsn_mm_ctx *mmctx; mmctx = sgsn_mm_ctx_by_tlli(tlli, raid); if (!mmctx) { LOGP(DMM, LOGL_NOTICE, "SUSPEND request for unknown " "TLLI=%08x\n", tlli); return -EINVAL; } if (mmctx->gmm_state != GMM_REGISTERED_NORMAL && mmctx->gmm_state != GMM_REGISTERED_SUSPENDED) { LOGMMCTXP(LOGL_NOTICE, mmctx, "SUSPEND request while state " "!= REGISTERED (TLLI=%08x)\n", tlli); return -EINVAL; } /* Transition from REGISTERED_NORMAL to REGISTERED_SUSPENDED */ mmctx->gmm_state = GMM_REGISTERED_SUSPENDED; return 0; } int gprs_gmm_rx_resume(struct gprs_ra_id *raid, uint32_t tlli, uint8_t suspend_ref) { struct sgsn_mm_ctx *mmctx; /* FIXME: make use of suspend reference? */ mmctx = sgsn_mm_ctx_by_tlli(tlli, raid); if (!mmctx) { LOGP(DMM, LOGL_NOTICE, "RESUME request for unknown " "TLLI=%08x\n", tlli); return -EINVAL; } if (mmctx->gmm_state != GMM_REGISTERED_NORMAL && mmctx->gmm_state != GMM_REGISTERED_SUSPENDED) { LOGMMCTXP(LOGL_NOTICE, mmctx, "RESUME request while state " "!= SUSPENDED (TLLI=%08x)\n", tlli); /* FIXME: should we not simply ignore it? */ return -EINVAL; } /* Transition from SUSPENDED to NORMAL */ mmctx->gmm_state = GMM_REGISTERED_NORMAL; return 0; } #ifdef BUILD_IU int iu_rab_act_ps(uint8_t rab_id, struct sgsn_pdp_ctx *pdp) { struct msgb *msg; struct sgsn_mm_ctx *mm = pdp->mm; struct ranap_ue_conn_ctx *uectx; uint32_t ggsn_ip; bool use_x213_nsap; uectx = mm->iu.ue_ctx; use_x213_nsap = (uectx->rab_assign_addr_enc == RANAP_NSAP_ADDR_ENC_X213); /* Get the IP address for ggsn user plane */ memcpy(&ggsn_ip, pdp->lib->gsnru.v, pdp->lib->gsnru.l); ggsn_ip = htonl(ggsn_ip); LOGP(DRANAP, LOGL_DEBUG, "Assigning RAB: rab_id=%d, ggsn_ip=%x," " teid_gn=%x, use_x213_nsap=%d\n", rab_id, ggsn_ip, pdp->lib->teid_gn, use_x213_nsap); msg = ranap_new_msg_rab_assign_data(rab_id, ggsn_ip, pdp->lib->teid_gn, use_x213_nsap); msg->l2h = msg->data; return ranap_iu_rab_act(uectx, msg); } #endif osmo-sgsn-1.3.0/src/gprs/gprs_llc.c000066400000000000000000000747201327264017000171670ustar00rootroot00000000000000/* GPRS LLC protocol implementation as per 3GPP TS 04.64 */ /* (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 #include #include #include static struct gprs_llc_llme *llme_alloc(uint32_t tlli); static int gprs_llc_tx_xid(struct gprs_llc_lle *lle, struct msgb *msg, int command); static int gprs_llc_tx_u(struct msgb *msg, uint8_t sapi, int command, enum gprs_llc_u_cmd u_cmd, int pf_bit); /* BEGIN XID RELATED */ /* Generate XID message */ static int gprs_llc_generate_xid(uint8_t *bytes, int bytes_len, struct gprs_llc_xid_field *l3_xid_field, struct gprs_llc_llme *llme) { /* Note: Called by gprs_ll_xid_req() */ LLIST_HEAD(xid_fields); struct gprs_llc_xid_field xid_version; struct gprs_llc_xid_field xid_n201u; struct gprs_llc_xid_field xid_n201i; xid_version.type = GPRS_LLC_XID_T_VERSION; xid_version.data = (uint8_t *) "\x00"; xid_version.data_len = 1; xid_n201u.type = GPRS_LLC_XID_T_N201_U; xid_n201u.data = (uint8_t *) "\x05\xf0"; xid_n201u.data_len = 2; xid_n201i.type = GPRS_LLC_XID_T_N201_I; xid_n201i.data = (uint8_t *) "\x05\xf0"; xid_n201i.data_len = 2; /* Add locally managed XID Fields */ llist_add(&xid_version.list, &xid_fields); llist_add(&xid_n201u.list, &xid_fields); llist_add(&xid_n201i.list, &xid_fields); /* Append layer 3 XID field (if present) */ if (l3_xid_field) { /* Enforce layer 3 XID type (just to be sure) */ l3_xid_field->type = GPRS_LLC_XID_T_L3_PAR; /* Add Layer 3 XID field to the list */ llist_add(&l3_xid_field->list, &xid_fields); } /* Store generated XID for later reference */ talloc_free(llme->xid); llme->xid = gprs_llc_copy_xid(llme, &xid_fields); return gprs_llc_compile_xid(bytes, bytes_len, &xid_fields); } /* Generate XID message that will cause the GMM to reset */ static int gprs_llc_generate_xid_for_gmm_reset(uint8_t *bytes, int bytes_len, uint32_t iov_ui, struct gprs_llc_llme *llme) { /* Called by gprs_llgmm_reset() and * gprs_llgmm_reset_oldmsg() */ LLIST_HEAD(xid_fields); struct gprs_llc_xid_field xid_reset; struct gprs_llc_xid_field xid_iovui; /* First XID component must be RESET */ xid_reset.type = GPRS_LLC_XID_T_RESET; xid_reset.data = NULL; xid_reset.data_len = 0; /* Add new IOV-UI */ xid_iovui.type = GPRS_LLC_XID_T_IOV_UI; xid_iovui.data = (uint8_t *) & iov_ui; xid_iovui.data_len = 4; /* Add locally managed XID Fields */ llist_add(&xid_iovui.list, &xid_fields); llist_add(&xid_reset.list, &xid_fields); /* Store generated XID for later reference */ talloc_free(llme->xid); llme->xid = gprs_llc_copy_xid(llme, &xid_fields); return gprs_llc_compile_xid(bytes, bytes_len, &xid_fields); } /* Process an incoming XID confirmation */ static int gprs_llc_process_xid_conf(uint8_t *bytes, int bytes_len, struct gprs_llc_lle *lle) { /* Note: This function handles the response of a network originated * XID-Request. There XID messages reflected by the MS are analyzed * and processed here. The caller is called by rx_llc_xid(). */ struct llist_head *xid_fields; struct gprs_llc_xid_field *xid_field; struct gprs_llc_xid_field *xid_field_request; struct gprs_llc_xid_field *xid_field_request_l3 = NULL; /* Pick layer3 XID from the XID request we have sent last */ if (lle->llme->xid) { llist_for_each_entry(xid_field_request, lle->llme->xid, list) { if (xid_field_request->type == GPRS_LLC_XID_T_L3_PAR) xid_field_request_l3 = xid_field_request; } } /* Parse and analyze XID-Response */ xid_fields = gprs_llc_parse_xid(NULL, bytes, bytes_len); if (xid_fields) { gprs_llc_dump_xid_fields(xid_fields, LOGL_DEBUG); llist_for_each_entry(xid_field, xid_fields, list) { /* Forward SNDCP-XID fields to Layer 3 (SNDCP) */ if (xid_field->type == GPRS_LLC_XID_T_L3_PAR && xid_field_request_l3) { sndcp_sn_xid_conf(xid_field, xid_field_request_l3, lle); } /* Process LLC-XID fields: */ else { /* FIXME: Do something more useful with the * echoed XID-Information. Currently we * just ignore the response completely and * by doing so we blindly accept any changes * the MS might have done to the our XID * inquiry. There is a remainig risk of * malfunction! */ LOGP(DLLC, LOGL_NOTICE, "Ignoring XID-Field: XID: type %s, data_len=%d, data=%s\n", get_value_string(gprs_llc_xid_type_names, xid_field->type), xid_field->data_len, osmo_hexdump_nospc(xid_field->data, xid_field->data_len)); } } talloc_free(xid_fields); } /* Flush pending XID fields */ talloc_free(lle->llme->xid); lle->llme->xid = NULL; return 0; } /* Process an incoming XID indication and generate an appropiate response */ static int gprs_llc_process_xid_ind(uint8_t *bytes_request, int bytes_request_len, uint8_t *bytes_response, int bytes_response_maxlen, struct gprs_llc_lle *lle) { /* Note: This function computes the response that is sent back to the * MS when a mobile originated XID is received. The function is * called by rx_llc_xid() */ int rc = -EINVAL; struct llist_head *xid_fields; struct llist_head *xid_fields_response; struct gprs_llc_xid_field *xid_field; struct gprs_llc_xid_field *xid_field_response; /* Parse and analyze XID-Request */ xid_fields = gprs_llc_parse_xid(lle->llme, bytes_request, bytes_request_len); if (xid_fields) { xid_fields_response = talloc_zero(lle->llme, struct llist_head); INIT_LLIST_HEAD(xid_fields_response); gprs_llc_dump_xid_fields(xid_fields, LOGL_DEBUG); /* Process LLC-XID fields: */ llist_for_each_entry(xid_field, xid_fields, list) { if (xid_field->type != GPRS_LLC_XID_T_L3_PAR) { /* FIXME: Check the incoming XID parameters for * for validity. Currently we just blindly * accept all XID fields by just echoing them. * There is a remaining risk of malfunction * when a MS submits values which defer from * the default! */ LOGP(DLLC, LOGL_NOTICE, "Echoing XID-Field: XID: type %s, data_len=%d, data=%s\n", get_value_string(gprs_llc_xid_type_names, xid_field->type), xid_field->data_len, osmo_hexdump_nospc(xid_field->data, xid_field->data_len)); xid_field_response = gprs_llc_dup_xid_field (lle->llme, xid_field); llist_add(&xid_field_response->list, xid_fields_response); } } /* Forward SNDCP-XID fields to Layer 3 (SNDCP) */ llist_for_each_entry(xid_field, xid_fields, list) { if (xid_field->type == GPRS_LLC_XID_T_L3_PAR) { xid_field_response = talloc_zero(lle->llme, struct gprs_llc_xid_field); rc = sndcp_sn_xid_ind(xid_field, xid_field_response, lle); if (rc == 0) llist_add(&xid_field_response->list, xid_fields_response); else talloc_free(xid_field_response); } } rc = gprs_llc_compile_xid(bytes_response, bytes_response_maxlen, xid_fields_response); talloc_free(xid_fields_response); talloc_free(xid_fields); } return rc; } /* Dispatch XID indications and responses comming from the MS */ static void rx_llc_xid(struct gprs_llc_lle *lle, struct gprs_llc_hdr_parsed *gph) { uint8_t response[1024]; int response_len; /* FIXME: 8.5.3.3: check if XID is invalid */ if (gph->is_cmd) { LOGP(DLLC, LOGL_NOTICE, "Received XID indication from MS.\n"); struct msgb *resp; uint8_t *xid; resp = msgb_alloc_headroom(4096, 1024, "LLC_XID"); response_len = gprs_llc_process_xid_ind(gph->data, gph->data_len, response, sizeof(response), lle); if (response_len < 0) { LOGP(DLLC, LOGL_ERROR, "invalid XID indication received!\n"); } else { xid = msgb_put(resp, response_len); memcpy(xid, response, response_len); } gprs_llc_tx_xid(lle, resp, 0); } else { LOGP(DLLC, LOGL_NOTICE, "Received XID confirmation from MS.\n"); gprs_llc_process_xid_conf(gph->data, gph->data_len, lle); /* FIXME: if we had sent a XID reset, send * LLGMM-RESET.conf to GMM */ } } /* Set of LL-XID negotiation (See also: TS 101 351, Section 7.2.2.4) */ int gprs_ll_xid_req(struct gprs_llc_lle *lle, struct gprs_llc_xid_field *l3_xid_field) { /* Note: This functions is calle from gprs_sndcp.c */ uint8_t xid_bytes[1024];; int xid_bytes_len; uint8_t *xid; struct msgb *msg; const char *ftype; /* Generate XID */ xid_bytes_len = gprs_llc_generate_xid(xid_bytes, sizeof(xid_bytes), l3_xid_field, lle->llme); /* Only perform XID sending if the XID message contains something */ if (xid_bytes_len > 0) { /* Transmit XID bytes */ msg = msgb_alloc_headroom(4096, 1024, "LLC_XID"); xid = msgb_put(msg, xid_bytes_len); memcpy(xid, xid_bytes, xid_bytes_len); if (l3_xid_field) ftype = get_value_string(gprs_llc_xid_type_names, l3_xid_field->type); else ftype = "NULL"; LOGP(DLLC, LOGL_NOTICE, "Sending XID type %s (%d bytes) request" " to MS...\n", ftype, xid_bytes_len); gprs_llc_tx_xid(lle, msg, 1); } else { LOGP(DLLC, LOGL_ERROR, "XID-Message generation failed, XID not sent!\n"); return -EINVAL; } return 0; } /* END XID RELATED */ /* Entry function from upper level (LLC), asking us to transmit a BSSGP PDU * to a remote MS (identified by TLLI) at a BTS identified by its BVCI and NSEI */ static int _bssgp_tx_dl_ud(struct msgb *msg, struct sgsn_mm_ctx *mmctx) { struct bssgp_dl_ud_par dup; const uint8_t qos_profile_default[3] = { 0x00, 0x00, 0x20 }; memset(&dup, 0, sizeof(dup)); /* before we have received some identity from the MS, we might * not yet have a MMC context (e.g. XID negotiation of primarly * LLC connection from GMM sapi). */ if (mmctx) { dup.imsi = mmctx->imsi; dup.drx_parms = mmctx->drx_parms; dup.ms_ra_cap.len = mmctx->ms_radio_access_capa.len; dup.ms_ra_cap.v = mmctx->ms_radio_access_capa.buf; /* make sure we only send it to the right llme */ if (!(msgb_tlli(msg) == mmctx->gb.llme->tlli || msgb_tlli(msg) == mmctx->gb.llme->old_tlli)) { LOGP(DLLC, LOGL_ERROR, "_bssgp_tx_dl_ud(): Attempt to send Downlink Unitdata to wrong LLME:" " msgb_tlli=0x%x mmctx->gb.llme->tlli=0x%x ->old_tlli=0x%x\n", msgb_tlli(msg), mmctx->gb.llme->tlli, mmctx->gb.llme->old_tlli); msgb_free(msg); return -EINVAL; } } memcpy(&dup.qos_profile, qos_profile_default, sizeof(qos_profile_default)); return bssgp_tx_dl_ud(msg, 1000, &dup); } /* Section 8.9.9 LLC layer parameter default values */ static const struct gprs_llc_params llc_default_params[NUM_SAPIS] = { [1] = { .t200_201 = 5, .n200 = 3, .n201_u = 400, }, [2] = { .t200_201 = 5, .n200 = 3, .n201_u = 270, }, [3] = { .iov_i_exp = 27, .t200_201 = 5, .n200 = 3, .n201_u = 500, .n201_i = 1503, .mD = 1520, .mU = 1520, .kD = 16, .kU = 16, }, [5] = { .iov_i_exp = 27, .t200_201 = 10, .n200 = 3, .n201_u = 500, .n201_i = 1503, .mD = 760, .mU = 760, .kD = 8, .kU = 8, }, [7] = { .t200_201 = 20, .n200 = 3, .n201_u = 270, }, [8] = { .t200_201 = 20, .n200 = 3, .n201_u = 270, }, [9] = { .iov_i_exp = 27, .t200_201 = 20, .n200 = 3, .n201_u = 500, .n201_i = 1503, .mD = 380, .mU = 380, .kD = 4, .kU = 4, }, [11] = { .iov_i_exp = 27, .t200_201 = 40, .n200 = 3, .n201_u = 500, .n201_i = 1503, .mD = 190, .mU = 190, .kD = 2, .kU = 2, }, }; LLIST_HEAD(gprs_llc_llmes); void *llc_tall_ctx; /* lookup LLC Entity based on DLCI (TLLI+SAPI tuple) */ static struct gprs_llc_lle *lle_by_tlli_sapi(const uint32_t tlli, uint8_t sapi) { struct gprs_llc_llme *llme; llist_for_each_entry(llme, &gprs_llc_llmes, list) { if (llme->tlli == tlli || llme->old_tlli == tlli) return &llme->lle[sapi]; } return NULL; } struct gprs_llc_lle *gprs_lle_get_or_create(const uint32_t tlli, uint8_t sapi) { struct gprs_llc_llme *llme; struct gprs_llc_lle *lle; lle = lle_by_tlli_sapi(tlli, sapi); if (lle) return lle; LOGP(DLLC, LOGL_NOTICE, "LLC: unknown TLLI 0x%08x, " "creating LLME on the fly\n", tlli); llme = llme_alloc(tlli); lle = &llme->lle[sapi]; return lle; } struct llist_head *gprs_llme_list(void) { return &gprs_llc_llmes; } /* lookup LLC Entity for RX based on DLCI (TLLI+SAPI tuple) */ static struct gprs_llc_lle *lle_for_rx_by_tlli_sapi(const uint32_t tlli, uint8_t sapi, enum gprs_llc_cmd cmd) { struct gprs_llc_lle *lle; /* We already know about this TLLI */ lle = lle_by_tlli_sapi(tlli, sapi); if (lle) return lle; /* Maybe it is a routing area update but we already know this sapi? */ if (gprs_tlli_type(tlli) == TLLI_FOREIGN) { lle = lle_by_tlli_sapi(tlli, sapi); if (lle) { LOGP(DLLC, LOGL_NOTICE, "LLC RX: Found a local entry for TLLI 0x%08x\n", tlli); return lle; } } /* 7.2.1.1 LLC belonging to unassigned TLLI+SAPI shall be discarded, * except UID and XID frames with SAPI=1 */ if (sapi == GPRS_SAPI_GMM && (cmd == GPRS_LLC_XID || cmd == GPRS_LLC_UI)) { struct gprs_llc_llme *llme; /* FIXME: don't use the TLLI but the 0xFFFF unassigned? */ llme = llme_alloc(tlli); LOGP(DLLC, LOGL_NOTICE, "LLC RX: unknown TLLI 0x%08x, " "creating LLME on the fly\n", tlli); lle = &llme->lle[sapi]; return lle; } LOGP(DLLC, LOGL_NOTICE, "unknown TLLI(0x%08x)/SAPI(%d): Silently dropping\n", tlli, sapi); return NULL; } static void lle_init(struct gprs_llc_llme *llme, uint8_t sapi) { struct gprs_llc_lle *lle = &llme->lle[sapi]; lle->llme = llme; lle->sapi = sapi; lle->state = GPRS_LLES_UNASSIGNED; /* Initialize according to parameters */ memcpy(&lle->params, &llc_default_params[sapi], sizeof(lle->params)); } static struct gprs_llc_llme *llme_alloc(uint32_t tlli) { struct gprs_llc_llme *llme; uint32_t i; llme = talloc_zero(llc_tall_ctx, struct gprs_llc_llme); if (!llme) return NULL; llme->tlli = tlli; llme->old_tlli = 0xffffffff; llme->state = GPRS_LLMS_UNASSIGNED; llme->age_timestamp = GPRS_LLME_RESET_AGE; llme->cksn = GSM_KEY_SEQ_INVAL; for (i = 0; i < ARRAY_SIZE(llme->lle); i++) lle_init(llme, i); llist_add(&llme->list, &gprs_llc_llmes); llme->comp.proto = gprs_sndcp_comp_alloc(llme); llme->comp.data = gprs_sndcp_comp_alloc(llme); return llme; } static void llme_free(struct gprs_llc_llme *llme) { gprs_sndcp_comp_free(llme->comp.proto); gprs_sndcp_comp_free(llme->comp.data); talloc_free(llme->xid); llist_del(&llme->list); talloc_free(llme); } #if 0 /* FIXME: Unused code... */ static void t200_expired(void *data) { struct gprs_llc_lle *lle = data; /* 8.5.1.3: Expiry of T200 */ if (lle->retrans_ctr >= lle->params.n200) { /* FIXME: LLGM-STATUS-IND, LL-RELEASE-IND/CNF */ lle->state = GPRS_LLES_ASSIGNED_ADM; } switch (lle->state) { case GPRS_LLES_LOCAL_EST: /* FIXME: retransmit SABM */ /* FIXME: re-start T200 */ lle->retrans_ctr++; break; case GPRS_LLES_LOCAL_REL: /* FIXME: retransmit DISC */ /* FIXME: re-start T200 */ lle->retrans_ctr++; break; default: LOGP(DLLC, LOGL_ERROR, "LLC unhandled state: %d\n", lle->state); break; } } static void t201_expired(void *data) { struct gprs_llc_lle *lle = data; if (lle->retrans_ctr < lle->params.n200) { /* FIXME: transmit apropriate supervisory frame (8.6.4.1) */ /* FIXME: set timer T201 */ lle->retrans_ctr++; } } #endif int gprs_llc_tx_u(struct msgb *msg, uint8_t sapi, int command, enum gprs_llc_u_cmd u_cmd, int pf_bit) { uint8_t *fcs, *llch; uint8_t addr, ctrl; uint32_t fcs_calc; /* Identifiers from UP: (TLLI, SAPI) + (BVCI, NSEI) */ /* Address Field */ addr = sapi & 0xf; if (command) addr |= 0x40; /* 6.3 Figure 8 */ ctrl = 0xe0 | u_cmd; if (pf_bit) ctrl |= 0x10; /* prepend LLC UI header */ llch = msgb_push(msg, 2); llch[0] = addr; llch[1] = ctrl; /* append FCS to end of frame */ fcs = msgb_put(msg, 3); fcs_calc = gprs_llc_fcs(llch, fcs - llch); fcs[0] = fcs_calc & 0xff; fcs[1] = (fcs_calc >> 8) & 0xff; fcs[2] = (fcs_calc >> 16) & 0xff; /* Identifiers passed down: (BVCI, NSEI) */ rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_LLC_DL_PACKETS]); rate_ctr_add(&sgsn->rate_ctrs->ctr[CTR_LLC_DL_BYTES], msg->len); /* Send BSSGP-DL-UNITDATA.req */ return _bssgp_tx_dl_ud(msg, NULL); } /* Send XID response to LLE */ static int gprs_llc_tx_xid(struct gprs_llc_lle *lle, struct msgb *msg, int command) { /* copy identifiers from LLE to ensure lower layers can route */ msgb_tlli(msg) = lle->llme->tlli; msgb_bvci(msg) = lle->llme->bvci; msgb_nsei(msg) = lle->llme->nsei; return gprs_llc_tx_u(msg, lle->sapi, command, GPRS_LLC_U_XID, 1); } /* encrypt information field + FCS, if needed! */ static int apply_gea(struct gprs_llc_lle *lle, uint16_t crypt_len, uint16_t nu, uint32_t oc, uint8_t sapi, uint8_t *fcs, uint8_t *data) { uint8_t cipher_out[GSM0464_CIPH_MAX_BLOCK]; if (lle->llme->algo == GPRS_ALGO_GEA0) return -EINVAL; /* Compute the 'Input' Paraemeter */ uint32_t fcs_calc, iv = gprs_cipher_gen_input_ui(lle->llme->iov_ui, sapi, nu, oc); /* Compute gamma that we need to XOR with the data */ int r = gprs_cipher_run(cipher_out, crypt_len, lle->llme->algo, lle->llme->kc, iv, fcs ? GPRS_CIPH_SGSN2MS : GPRS_CIPH_MS2SGSN); if (r < 0) { LOGP(DLLC, LOGL_ERROR, "Error producing %s gamma for UI " "frame: %d\n", get_value_string(gprs_cipher_names, lle->llme->algo), r); return -ENOMSG; } if (fcs) { /* Mark frame as encrypted and update FCS */ data[2] |= 0x02; fcs_calc = gprs_llc_fcs(data, fcs - data); fcs[0] = fcs_calc & 0xff; fcs[1] = (fcs_calc >> 8) & 0xff; fcs[2] = (fcs_calc >> 16) & 0xff; data += 3; } /* XOR the cipher output with the data */ for (r = 0; r < crypt_len; r++) *(data + r) ^= cipher_out[r]; return 0; } /* Transmit a UI frame over the given SAPI: 'encryptable' indicates whether particular message can be encrypted according to 3GPP TS 24.008 § 4.7.1.2 */ int gprs_llc_tx_ui(struct msgb *msg, uint8_t sapi, int command, struct sgsn_mm_ctx *mmctx, bool encryptable) { struct gprs_llc_lle *lle; uint8_t *fcs, *llch; uint8_t addr, ctrl[2]; uint32_t fcs_calc; uint16_t nu = 0; uint32_t oc; /* Identifiers from UP: (TLLI, SAPI) + (BVCI, NSEI) */ /* look-up or create the LL Entity for this (TLLI, SAPI) tuple */ lle = gprs_lle_get_or_create(msgb_tlli(msg), sapi); if (msg->len > lle->params.n201_u) { LOGP(DLLC, LOGL_ERROR, "Cannot Tx %u bytes (N201-U=%u)\n", msg->len, lle->params.n201_u); msgb_free(msg); return -EFBIG; } gprs_llme_copy_key(mmctx, lle->llme); /* Update LLE's (BVCI, NSEI) tuple */ lle->llme->bvci = msgb_bvci(msg); lle->llme->nsei = msgb_nsei(msg); /* Obtain current values for N(u) and OC */ nu = lle->vu_send; oc = lle->oc_ui_send; /* Increment V(U) */ lle->vu_send = (lle->vu_send + 1) % 512; /* Increment Overflow Counter, if needed */ if ((lle->vu_send + 1) / 512) lle->oc_ui_send += 512; /* Address Field */ addr = sapi & 0xf; if (command) addr |= 0x40; /* Control Field */ ctrl[0] = 0xc0; ctrl[0] |= nu >> 6; ctrl[1] = (nu << 2) & 0xfc; ctrl[1] |= 0x01; /* Protected Mode */ /* prepend LLC UI header */ llch = msgb_push(msg, 3); llch[0] = addr; llch[1] = ctrl[0]; llch[2] = ctrl[1]; /* append FCS to end of frame */ fcs = msgb_put(msg, 3); fcs_calc = gprs_llc_fcs(llch, fcs - llch); fcs[0] = fcs_calc & 0xff; fcs[1] = (fcs_calc >> 8) & 0xff; fcs[2] = (fcs_calc >> 16) & 0xff; if (lle->llme->algo != GPRS_ALGO_GEA0 && encryptable) { int rc = apply_gea(lle, fcs - llch, nu, oc, sapi, fcs, llch); if (rc < 0) { msgb_free(msg); return rc; } } rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_LLC_DL_PACKETS]); rate_ctr_add(&sgsn->rate_ctrs->ctr[CTR_LLC_DL_BYTES], msg->len); /* Identifiers passed down: (BVCI, NSEI) */ /* Send BSSGP-DL-UNITDATA.req */ return _bssgp_tx_dl_ud(msg, mmctx); } static int gprs_llc_hdr_rx(struct gprs_llc_hdr_parsed *gph, struct gprs_llc_lle *lle) { switch (gph->cmd) { case GPRS_LLC_SABM: /* Section 6.4.1.1 */ lle->v_sent = lle->v_ack = lle->v_recv = 0; if (lle->state == GPRS_LLES_ASSIGNED_ADM) { /* start re-establishment (8.7.1) */ } lle->state = GPRS_LLES_REMOTE_EST; /* FIXME: Send UA */ lle->state = GPRS_LLES_ABM; /* FIXME: process data */ break; case GPRS_LLC_DISC: /* Section 6.4.1.2 */ /* FIXME: Send UA */ /* terminate ABM */ lle->state = GPRS_LLES_ASSIGNED_ADM; break; case GPRS_LLC_UA: /* Section 6.4.1.3 */ if (lle->state == GPRS_LLES_LOCAL_EST) lle->state = GPRS_LLES_ABM; break; case GPRS_LLC_DM: /* Section 6.4.1.4: ABM cannot be performed */ if (lle->state == GPRS_LLES_LOCAL_EST) lle->state = GPRS_LLES_ASSIGNED_ADM; break; case GPRS_LLC_FRMR: /* Section 6.4.1.5 */ break; case GPRS_LLC_XID: /* Section 6.4.1.6 */ rx_llc_xid(lle, gph); break; case GPRS_LLC_UI: if (gprs_llc_is_retransmit(gph->seq_tx, lle->vu_recv)) { LOGP(DLLC, LOGL_NOTICE, "TLLI=%08x dropping UI, N(U=%d) not in window V(URV(UR:%d).\n", lle->llme ? lle->llme->tlli : -1, gph->seq_tx, lle->vu_recv); /* HACK: non-standard recovery handling. If remote LLE * is re-transmitting the same sequence number for * three times, don't discard the frame but pass it on * and 'learn' the new sequence number */ if (gph->seq_tx != lle->vu_recv_last) { lle->vu_recv_last = gph->seq_tx; lle->vu_recv_duplicates = 0; } else { lle->vu_recv_duplicates++; if (lle->vu_recv_duplicates < 3) return -EIO; LOGP(DLLC, LOGL_NOTICE, "TLLI=%08x recovering " "N(U=%d) after receiving %u duplicates\n", lle->llme ? lle->llme->tlli : -1, gph->seq_tx, lle->vu_recv_duplicates); } } /* Increment the sequence number that we expect in the next frame */ lle->vu_recv = (gph->seq_tx + 1) % 512; /* Increment Overflow Counter */ if ((gph->seq_tx + 1) / 512) lle->oc_ui_recv += 512; break; default: LOGP(DLLC, LOGL_NOTICE, "Unhandled command: %d\n", gph->cmd); break; } return 0; } /* receive an incoming LLC PDU (BSSGP-UL-UNITDATA-IND, 7.2.4.2) */ int gprs_llc_rcvmsg(struct msgb *msg, struct tlv_parsed *tv) { struct gprs_llc_hdr *lh = (struct gprs_llc_hdr *) msgb_llch(msg); struct gprs_llc_hdr_parsed llhp; struct gprs_llc_lle *lle = NULL; bool drop_cipherable = false; int rc = 0; /* Identifiers from DOWN: NSEI, BVCI, TLLI */ memset(&llhp, 0, sizeof(llhp)); rc = gprs_llc_hdr_parse(&llhp, (uint8_t *) lh, TLVP_LEN(tv, BSSGP_IE_LLC_PDU)); if (rc < 0) { LOGP(DLLC, LOGL_NOTICE, "Error during LLC header parsing\n"); return rc; } switch (gprs_tlli_type(msgb_tlli(msg))) { case TLLI_LOCAL: case TLLI_FOREIGN: case TLLI_RANDOM: case TLLI_AUXILIARY: break; default: LOGP(DLLC, LOGL_ERROR, "Discarding frame with strange TLLI type\n"); break; } /* find the LLC Entity for this TLLI+SAPI tuple */ lle = lle_for_rx_by_tlli_sapi(msgb_tlli(msg), llhp.sapi, llhp.cmd); if (!lle) { switch (llhp.sapi) { case GPRS_SAPI_SNDCP3: case GPRS_SAPI_SNDCP5: case GPRS_SAPI_SNDCP9: case GPRS_SAPI_SNDCP11: /* Ask an upper layer for help. */ return gsm0408_gprs_force_reattach_oldmsg(msg, NULL); default: break; } return 0; } gprs_llc_hdr_dump(&llhp, lle); /* reset age computation */ lle->llme->age_timestamp = GPRS_LLME_RESET_AGE; /* decrypt information field + FCS, if needed! */ if (llhp.is_encrypted) { if (lle->llme->algo != GPRS_ALGO_GEA0) { rc = apply_gea(lle, llhp.data_len + 3, llhp.seq_tx, lle->oc_ui_recv, lle->sapi, NULL, llhp.data); if (rc < 0) return rc; llhp.fcs = *(llhp.data + llhp.data_len); llhp.fcs |= *(llhp.data + llhp.data_len + 1) << 8; llhp.fcs |= *(llhp.data + llhp.data_len + 2) << 16; } else { LOGP(DLLC, LOGL_NOTICE, "encrypted frame for LLC that " "has no KC/Algo! Dropping.\n"); return 0; } } else { if (lle->llme->algo != GPRS_ALGO_GEA0 && lle->llme->cksn != GSM_KEY_SEQ_INVAL) drop_cipherable = true; } /* We have to do the FCS check _after_ decryption */ llhp.fcs_calc = gprs_llc_fcs((uint8_t *)lh, llhp.crc_length); if (llhp.fcs != llhp.fcs_calc) { LOGP(DLLC, LOGL_INFO, "Dropping frame with invalid FCS\n"); return -EIO; } /* Update LLE's (BVCI, NSEI) tuple */ lle->llme->bvci = msgb_bvci(msg); lle->llme->nsei = msgb_nsei(msg); /* Receive and Process the actual LLC frame */ rc = gprs_llc_hdr_rx(&llhp, lle); if (rc < 0) return rc; rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_LLC_UL_PACKETS]); rate_ctr_add(&sgsn->rate_ctrs->ctr[CTR_LLC_UL_BYTES], msg->len); /* llhp.data is only set when we need to send LL_[UNIT]DATA_IND up */ if (llhp.cmd == GPRS_LLC_UI && llhp.data && llhp.data_len) { msgb_gmmh(msg) = llhp.data; switch (llhp.sapi) { case GPRS_SAPI_GMM: /* send LL_UNITDATA_IND to GMM */ rc = gsm0408_gprs_rcvmsg_gb(msg, lle->llme, drop_cipherable); break; case GPRS_SAPI_SNDCP3: case GPRS_SAPI_SNDCP5: case GPRS_SAPI_SNDCP9: case GPRS_SAPI_SNDCP11: /* send LL_DATA_IND/LL_UNITDATA_IND to SNDCP */ rc = sndcp_llunitdata_ind(msg, lle, llhp.data, llhp.data_len); break; case GPRS_SAPI_SMS: /* FIXME */ case GPRS_SAPI_TOM2: case GPRS_SAPI_TOM8: /* FIXME: send LL_DATA_IND/LL_UNITDATA_IND to TOM */ default: LOGP(DLLC, LOGL_NOTICE, "Unsupported SAPI %u\n", llhp.sapi); rc = -EINVAL; break; } } return rc; } /* Propagate crypto parameters MM -> LLME */ void gprs_llme_copy_key(struct sgsn_mm_ctx *mm, struct gprs_llc_llme *llme) { if (!mm) return; if (mm->ciph_algo != GPRS_ALGO_GEA0) { llme->algo = mm->ciph_algo; if (llme->cksn != mm->auth_triplet.key_seq && mm->auth_triplet.key_seq != GSM_KEY_SEQ_INVAL) { memcpy(llme->kc, mm->auth_triplet.vec.kc, gprs_cipher_key_length(mm->ciph_algo)); llme->cksn = mm->auth_triplet.key_seq; } } else llme->cksn = GSM_KEY_SEQ_INVAL; } /* 04.64 Chapter 7.2.1.1 LLGMM-ASSIGN */ int gprs_llgmm_assign(struct gprs_llc_llme *llme, uint32_t old_tlli, uint32_t new_tlli) { unsigned int i; if (old_tlli == 0xffffffff && new_tlli != 0xffffffff) { /* TLLI Assignment 8.3.1 */ /* New TLLI shall be assigned and used when (re)transmitting LLC frames */ /* If old TLLI != 0xffffffff was assigned to LLME, then TLLI * old is unassigned. Only TLLI new shall be accepted when * received from peer. */ if (llme->old_tlli != 0xffffffff) { llme->old_tlli = 0xffffffff; llme->tlli = new_tlli; } else { /* If TLLI old == 0xffffffff was assigned to LLME, then this is * TLLI assignmemt according to 8.3.1 */ llme->old_tlli = 0xffffffff; llme->tlli = new_tlli; llme->state = GPRS_LLMS_ASSIGNED; /* 8.5.3.1 For all LLE's */ for (i = 0; i < ARRAY_SIZE(llme->lle); i++) { struct gprs_llc_lle *l = &llme->lle[i]; l->vu_send = l->vu_recv = 0; l->retrans_ctr = 0; l->state = GPRS_LLES_ASSIGNED_ADM; /* FIXME Set parameters according to table 9 */ } } } else if (old_tlli != 0xffffffff && new_tlli != 0xffffffff) { /* TLLI Change 8.3.2 */ /* Both TLLI Old and TLLI New are assigned; use New when * (re)transmitting. Accept both Old and New on Rx */ llme->old_tlli = old_tlli; llme->tlli = new_tlli; llme->state = GPRS_LLMS_ASSIGNED; } else if (old_tlli != 0xffffffff && new_tlli == 0xffffffff) { /* TLLI Unassignment 8.3.3) */ llme->tlli = llme->old_tlli = 0; llme->state = GPRS_LLMS_UNASSIGNED; for (i = 0; i < ARRAY_SIZE(llme->lle); i++) { struct gprs_llc_lle *l = &llme->lle[i]; l->state = GPRS_LLES_UNASSIGNED; } llme_free(llme); } else return -EINVAL; return 0; } /* TLLI unassignment */ int gprs_llgmm_unassign(struct gprs_llc_llme *llme) { return gprs_llgmm_assign(llme, llme->tlli, 0xffffffff); } /* Chapter 7.2.1.2 LLGMM-RESET.req */ int gprs_llgmm_reset(struct gprs_llc_llme *llme) { struct msgb *msg = msgb_alloc_headroom(4096, 1024, "LLC_XID"); struct gprs_llc_lle *lle = &llme->lle[1]; uint8_t xid_bytes[1024]; int xid_bytes_len, rc; uint8_t *xid; LOGP(DLLC, LOGL_NOTICE, "LLGM Reset\n"); rc = osmo_get_rand_id((uint8_t *) &llme->iov_ui, 4); if (rc < 0) { LOGP(DLLC, LOGL_ERROR, "osmo_get_rand_id() failed for LLC XID reset: %s\n", strerror(-rc)); return rc; } /* Generate XID message */ xid_bytes_len = gprs_llc_generate_xid_for_gmm_reset(xid_bytes, sizeof(xid_bytes),llme->iov_ui,llme); if (xid_bytes_len < 0) return -EINVAL; xid = msgb_put(msg, xid_bytes_len); memcpy(xid, xid_bytes, xid_bytes_len); /* Reset some of the LLC parameters. See GSM 04.64, 8.5.3.1 */ lle->vu_recv = 0; lle->vu_send = 0; lle->oc_ui_send = 0; lle->oc_ui_recv = 0; /* FIXME: Start T200, wait for XID response */ return gprs_llc_tx_xid(lle, msg, 1); } int gprs_llgmm_reset_oldmsg(struct msgb* oldmsg, uint8_t sapi, struct gprs_llc_llme *llme) { struct msgb *msg = msgb_alloc_headroom(4096, 1024, "LLC_XID"); uint8_t xid_bytes[1024]; int xid_bytes_len, rc; uint8_t *xid; LOGP(DLLC, LOGL_NOTICE, "LLGM Reset\n"); rc = osmo_get_rand_id((uint8_t *) &llme->iov_ui, 4); if (rc < 0) { LOGP(DLLC, LOGL_ERROR, "osmo_get_rand_id() failed for LLC XID reset: %s\n", strerror(-rc)); return rc; } /* Generate XID message */ xid_bytes_len = gprs_llc_generate_xid_for_gmm_reset(xid_bytes, sizeof(xid_bytes),llme->iov_ui,llme); if (xid_bytes_len < 0) return -EINVAL; xid = msgb_put(msg, xid_bytes_len); memcpy(xid, xid_bytes, xid_bytes_len); /* FIXME: Start T200, wait for XID response */ msgb_tlli(msg) = msgb_tlli(oldmsg); msgb_bvci(msg) = msgb_bvci(oldmsg); msgb_nsei(msg) = msgb_nsei(oldmsg); return gprs_llc_tx_u(msg, sapi, 1, GPRS_LLC_U_XID, 1); } int gprs_llc_init(const char *cipher_plugin_path) { return gprs_cipher_load(cipher_plugin_path); } osmo-sgsn-1.3.0/src/gprs/gprs_llc_parse.c000066400000000000000000000132321327264017000203500ustar00rootroot00000000000000/* GPRS LLC protocol implementation as per 3GPP TS 04.64 */ /* (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 static const struct value_string llc_cmd_strs[] = { { GPRS_LLC_NULL, "NULL" }, { GPRS_LLC_RR, "RR" }, { GPRS_LLC_ACK, "ACK" }, { GPRS_LLC_RNR, "RNR" }, { GPRS_LLC_SACK, "SACK" }, { GPRS_LLC_DM, "DM" }, { GPRS_LLC_DISC, "DISC" }, { GPRS_LLC_UA, "UA" }, { GPRS_LLC_SABM, "SABM" }, { GPRS_LLC_FRMR, "FRMR" }, { GPRS_LLC_XID, "XID" }, { GPRS_LLC_UI, "UI" }, { 0, NULL } }; #define LLC_ALLOC_SIZE 16384 #define UI_HDR_LEN 3 #define N202 4 #define CRC24_LENGTH 3 int gprs_llc_fcs(uint8_t *data, unsigned int len) { uint32_t fcs_calc; fcs_calc = crc24_calc(INIT_CRC24, data, len); fcs_calc = ~fcs_calc; fcs_calc &= 0xffffff; return fcs_calc; } void gprs_llc_hdr_dump(struct gprs_llc_hdr_parsed *gph, struct gprs_llc_lle *lle) { const char *gea; uint32_t iov_ui = 0; if (lle) { gea = get_value_string(gprs_cipher_names, lle->llme->algo); iov_ui = lle->llme->iov_ui; } else gea = "GEA?"; DEBUGP(DLLC, "LLC SAPI=%u %c %c %c %s IOV-UI=0x%06x FCS=0x%06x ", gph->sapi, gph->is_cmd ? 'C' : 'R', gph->ack_req ? 'A' : ' ', gph->is_encrypted ? 'E' : 'U', gea, iov_ui, gph->fcs); if (gph->cmd) DEBUGPC(DLLC, "CMD=%s ", get_value_string(llc_cmd_strs, gph->cmd)); if (gph->data) DEBUGPC(DLLC, "DATA "); DEBUGPC(DLLC, "\n"); } /* parse a GPRS LLC header, also check for invalid frames */ int gprs_llc_hdr_parse(struct gprs_llc_hdr_parsed *ghp, uint8_t *llc_hdr, int len) { uint8_t *ctrl = llc_hdr+1; if (len <= CRC24_LENGTH) return -EIO; ghp->crc_length = len - CRC24_LENGTH; ghp->ack_req = 0; /* Section 5.5: FCS */ ghp->fcs = *(llc_hdr + len - 3); ghp->fcs |= *(llc_hdr + len - 2) << 8; ghp->fcs |= *(llc_hdr + len - 1) << 16; /* Section 6.2.1: invalid PD field */ if (llc_hdr[0] & 0x80) return -EIO; /* This only works for the MS->SGSN direction */ if (llc_hdr[0] & 0x40) ghp->is_cmd = 0; else ghp->is_cmd = 1; ghp->sapi = llc_hdr[0] & 0xf; /* Section 6.2.3: check for reserved SAPI */ switch (ghp->sapi) { case 0: case 4: case 6: case 0xa: case 0xc: case 0xd: case 0xf: return -EINVAL; } if ((ctrl[0] & 0x80) == 0) { /* I (Information transfer + Supervisory) format */ uint8_t k; ghp->data = ctrl + 3; if (ctrl[0] & 0x40) ghp->ack_req = 1; ghp->seq_tx = (ctrl[0] & 0x1f) << 4; ghp->seq_tx |= (ctrl[1] >> 4); ghp->seq_rx = (ctrl[1] & 0x7) << 6; ghp->seq_rx |= (ctrl[2] >> 2); switch (ctrl[2] & 0x03) { case 0: ghp->cmd = GPRS_LLC_RR; break; case 1: ghp->cmd = GPRS_LLC_ACK; break; case 2: ghp->cmd = GPRS_LLC_RNR; break; case 3: ghp->cmd = GPRS_LLC_SACK; k = ctrl[3] & 0x1f; ghp->data += 1 + k; break; } ghp->data_len = (llc_hdr + len - 3) - ghp->data; } else if ((ctrl[0] & 0xc0) == 0x80) { /* S (Supervisory) format */ ghp->data = NULL; ghp->data_len = 0; if (ctrl[0] & 0x20) ghp->ack_req = 1; ghp->seq_rx = (ctrl[0] & 0x7) << 6; ghp->seq_rx |= (ctrl[1] >> 2); switch (ctrl[1] & 0x03) { case 0: ghp->cmd = GPRS_LLC_RR; break; case 1: ghp->cmd = GPRS_LLC_ACK; break; case 2: ghp->cmd = GPRS_LLC_RNR; break; case 3: ghp->cmd = GPRS_LLC_SACK; break; } } else if ((ctrl[0] & 0xe0) == 0xc0) { /* UI (Unconfirmed Inforamtion) format */ ghp->cmd = GPRS_LLC_UI; ghp->data = ctrl + 2; ghp->data_len = (llc_hdr + len - 3) - ghp->data; ghp->seq_tx = (ctrl[0] & 0x7) << 6; ghp->seq_tx |= (ctrl[1] >> 2); if (ctrl[1] & 0x02) { ghp->is_encrypted = 1; /* FIXME: encryption */ } if (ctrl[1] & 0x01) { /* FCS over hdr + all inf fields */ } else { /* FCS over hdr + N202 octets (4) */ if (ghp->crc_length > UI_HDR_LEN + N202) ghp->crc_length = UI_HDR_LEN + N202; } } else { /* U (Unnumbered) format: 1 1 1 P/F M4 M3 M2 M1 */ ghp->data = NULL; ghp->data_len = 0; switch (ctrl[0] & 0xf) { case GPRS_LLC_U_NULL_CMD: ghp->cmd = GPRS_LLC_NULL; break; case GPRS_LLC_U_DM_RESP: ghp->cmd = GPRS_LLC_DM; break; case GPRS_LLC_U_DISC_CMD: ghp->cmd = GPRS_LLC_DISC; break; case GPRS_LLC_U_UA_RESP: ghp->cmd = GPRS_LLC_UA; break; case GPRS_LLC_U_SABM_CMD: ghp->cmd = GPRS_LLC_SABM; break; case GPRS_LLC_U_FRMR_RESP: ghp->cmd = GPRS_LLC_FRMR; break; case GPRS_LLC_U_XID: ghp->cmd = GPRS_LLC_XID; ghp->data = ctrl + 1; ghp->data_len = (llc_hdr + len - 3) - ghp->data; break; default: return -EIO; } } /* FIXME: parse sack frame */ if (ghp->cmd == GPRS_LLC_SACK) { LOGP(DLLC, LOGL_NOTICE, "Unsupported SACK frame\n"); return -EIO; } return 0; } osmo-sgsn-1.3.0/src/gprs/gprs_llc_vty.c000066400000000000000000000066441327264017000200710ustar00rootroot00000000000000/* VTY interface for our GPRS LLC implementation */ /* (C) 2010 by Harald Welte * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 struct value_string gprs_llc_state_strs[] = { { GPRS_LLES_UNASSIGNED, "TLLI Unassigned" }, { GPRS_LLES_ASSIGNED_ADM, "TLLI Assigned" }, { GPRS_LLES_LOCAL_EST, "Local Establishment" }, { GPRS_LLES_REMOTE_EST, "Remote Establishment" }, { GPRS_LLES_ABM, "Asynchronous Balanced Mode" }, { GPRS_LLES_LOCAL_REL, "Local Release" }, { GPRS_LLES_TIMER_REC, "Timer Recovery" }, { 0, NULL } }; static void vty_dump_lle(struct vty *vty, struct gprs_llc_lle *lle) { struct gprs_llc_params *par = &lle->params; vty_out(vty, " SAPI %2u State %s VUsend=%u, VUrecv=%u", lle->sapi, get_value_string(gprs_llc_state_strs, lle->state), lle->vu_send, lle->vu_recv); vty_out(vty, " Vsent=%u Vack=%u Vrecv=%u, RetransCtr=%u%s", lle->v_sent, lle->v_ack, lle->v_recv, lle->retrans_ctr, VTY_NEWLINE); vty_out(vty, " T200=%u, N200=%u, N201-U=%u, N201-I=%u, mD=%u, " "mU=%u, kD=%u, kU=%u%s", par->t200_201, par->n200, par->n201_u, par->n201_i, par->mD, par->mU, par->kD, par->kU, VTY_NEWLINE); } static uint8_t valid_sapis[] = { 1, 2, 3, 5, 7, 8, 9, 11 }; static void vty_dump_llme(struct vty *vty, struct gprs_llc_llme *llme) { unsigned int i; struct timespec now_tp = {0}; clock_gettime(CLOCK_MONOTONIC, &now_tp); vty_out(vty, "TLLI %08x (Old TLLI %08x) BVCI=%u NSEI=%u %s: " "IOV-UI=0x%06x CKSN=%d Age=%d: State %s%s", llme->tlli, llme->old_tlli, llme->bvci, llme->nsei, get_value_string(gprs_cipher_names, llme->algo), llme->iov_ui, llme->cksn, llme->age_timestamp == GPRS_LLME_RESET_AGE ? 0 : (int)(now_tp.tv_sec - (time_t)llme->age_timestamp), get_value_string(gprs_llc_state_strs, llme->state), VTY_NEWLINE); for (i = 0; i < ARRAY_SIZE(valid_sapis); i++) { struct gprs_llc_lle *lle; uint8_t sapi = valid_sapis[i]; if (sapi >= ARRAY_SIZE(llme->lle)) continue; lle = &llme->lle[sapi]; vty_dump_lle(vty, lle); } } DEFUN(show_llc, show_llc_cmd, "show llc", SHOW_STR "Display information about the LLC protocol") { struct gprs_llc_llme *llme; vty_out(vty, "State of LLC Entities%s", VTY_NEWLINE); llist_for_each_entry(llme, &gprs_llc_llmes, list) { vty_dump_llme(vty, llme); } return CMD_SUCCESS; } int gprs_llc_vty_init(void) { install_element_ve(&show_llc_cmd); return 0; } osmo-sgsn-1.3.0/src/gprs/gprs_llc_xid.c000066400000000000000000000160471327264017000200310ustar00rootroot00000000000000/* GPRS LLC XID field encoding/decoding as per 3GPP TS 44.064 */ /* (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 #include #include #include #include #include const struct value_string gprs_llc_xid_type_names[] = { { GPRS_LLC_XID_T_VERSION, "VERSION"}, { GPRS_LLC_XID_T_IOV_UI, "IOV_UI"}, { GPRS_LLC_XID_T_IOV_I, "IOV_I"}, { GPRS_LLC_XID_T_T200, "T200"}, { GPRS_LLC_XID_T_N200, "N200"}, { GPRS_LLC_XID_T_N201_U, "N201_"}, { GPRS_LLC_XID_T_N201_I, "N201_I"}, { GPRS_LLC_XID_T_mD, "mD"}, { GPRS_LLC_XID_T_mU, "mU"}, { GPRS_LLC_XID_T_kD, "kD"}, { GPRS_LLC_XID_T_kU, "kU"}, { GPRS_LLC_XID_T_L3_PAR, "L3_PAR"}, { GPRS_LLC_XID_T_RESET, "RESET"}, { 0, NULL }, }; /* Parse XID parameter field */ static int decode_xid_field(struct gprs_llc_xid_field *xid_field, const uint8_t *src, uint8_t src_len) { uint8_t xl; uint8_t type; uint8_t len; int src_counter = 0; /* Exit immediately if it is clear that no * parseable data is present */ if (src_len < 1 || !src) return -EINVAL; /* Extract header info */ xl = (*src >> 7) & 1; type = (*src >> 2) & 0x1F; /* Extract length field */ len = (*src) & 0x3; src++; src_counter++; if (xl) { if (src_len < 2) return -EINVAL; len = (len << 6) & 0xC0; len |= ((*src) >> 2) & 0x3F; src++; src_counter++; } /* Fill out struct */ xid_field->type = type; xid_field->data_len = len; if (len > 0) { if (src_len < src_counter + len) return -EINVAL; xid_field->data = talloc_memdup(xid_field,src,xid_field->data_len); } else xid_field->data = NULL; /* Return consumed length */ return src_counter + len; } /* Encode XID parameter field */ static int encode_xid_field(uint8_t *dst, int dst_maxlen, const struct gprs_llc_xid_field *xid_field) { int xl = 0; /* When the length does not fit into 2 bits, * we need extended length fields */ if (xid_field->data_len > 3) xl = 1; /* Exit immediately if it is clear that no * encoding result can be stored */ if (dst_maxlen < xid_field->data_len + 1 + xl) return -EINVAL; /* There are only 5 bits reserved for the type, exit on exceed */ if (xid_field->type > 31) return -EINVAL; /* Encode header */ memset(dst, 0, dst_maxlen); if (xl) dst[0] |= 0x80; dst[0] |= (((xid_field->type) & 0x1F) << 2); if (xl) { dst[0] |= (((xid_field->data_len) >> 6) & 0x03); dst[1] = ((xid_field->data_len) << 2) & 0xFC; } else dst[0] |= ((xid_field->data_len) & 0x03); /* Append payload data */ if (xid_field->data && xid_field->data_len) memcpy(dst + 1 + xl, xid_field->data, xid_field->data_len); /* Return generated length */ return xid_field->data_len + 1 + xl; } /* Transform a list with XID fields into a XID message (dst) */ int gprs_llc_compile_xid(uint8_t *dst, int dst_maxlen, const struct llist_head *xid_fields) { struct gprs_llc_xid_field *xid_field; int rc; int byte_counter = 0; OSMO_ASSERT(xid_fields); OSMO_ASSERT(dst); llist_for_each_entry_reverse(xid_field, xid_fields, list) { /* Encode XID-Field */ rc = encode_xid_field(dst, dst_maxlen, xid_field); if (rc < 0) return -EINVAL; /* Advance pointer and lower maxlen for the * next encoding round */ dst += rc; byte_counter += rc; dst_maxlen -= rc; } /* Return generated length */ return byte_counter; } /* Transform a XID message (dst) into a list of XID fields */ struct llist_head *gprs_llc_parse_xid(const void *ctx, const uint8_t *src, int src_len) { struct gprs_llc_xid_field *xid_field; struct llist_head *xid_fields; int rc; int max_loops = src_len; OSMO_ASSERT(src); xid_fields = talloc_zero(ctx, struct llist_head); INIT_LLIST_HEAD(xid_fields); while (1) { /* Bail in case decode_xid_field() constantly returns zero */ if (max_loops <= 0) { talloc_free(xid_fields); return NULL; } /* Decode XID field */ xid_field = talloc_zero(xid_fields, struct gprs_llc_xid_field); rc = decode_xid_field(xid_field, src, src_len); /* Immediately stop on error */ if (rc < 0) { talloc_free(xid_fields); return NULL; } /* Add parsed XID field to list */ llist_add(&xid_field->list, xid_fields); /* Advance pointer and lower dst_len for the next * decoding round */ src += rc; src_len -= rc; /* We are (scuccessfully) done when no further byes are left */ if (src_len == 0) return xid_fields; max_loops--; } } /* Create a duplicate of an XID-Field */ struct gprs_llc_xid_field *gprs_llc_dup_xid_field(const void *ctx, const struct gprs_llc_xid_field *xid_field) { struct gprs_llc_xid_field *dup; OSMO_ASSERT(xid_field); /* Create a copy of the XID field in memory */ dup = talloc_memdup(ctx, xid_field, sizeof(*xid_field)); dup->data = talloc_memdup(ctx, xid_field->data, xid_field->data_len); /* Unlink duplicate from source list */ INIT_LLIST_HEAD(&dup->list); return dup; } /* Copy an llist with xid fields */ struct llist_head *gprs_llc_copy_xid(const void *ctx, const struct llist_head *xid_fields) { struct gprs_llc_xid_field *xid_field; struct llist_head *xid_fields_copy; OSMO_ASSERT(xid_fields); xid_fields_copy = talloc_zero(ctx, struct llist_head); INIT_LLIST_HEAD(xid_fields_copy); /* Create duplicates and add them to the target list */ llist_for_each_entry(xid_field, xid_fields, list) { llist_add(&gprs_llc_dup_xid_field(ctx, xid_field)->list, xid_fields_copy); } return xid_fields_copy; } /* Dump a list with XID fields (Debug) */ void gprs_llc_dump_xid_fields(const struct llist_head *xid_fields, unsigned int logl) { struct gprs_llc_xid_field *xid_field; OSMO_ASSERT(xid_fields); llist_for_each_entry(xid_field, xid_fields, list) { if (xid_field->data_len) { OSMO_ASSERT(xid_field->data); LOGP(DLLC, logl, "XID: type %s, data_len=%d, data=%s\n", get_value_string(gprs_llc_xid_type_names, xid_field->type), xid_field->data_len, osmo_hexdump_nospc(xid_field->data, xid_field->data_len)); } else { LOGP(DLLC, logl, "XID: type=%d, data_len=%d, data=NULL\n", xid_field->type, xid_field->data_len); } } } osmo-sgsn-1.3.0/src/gprs/gprs_sgsn.c000066400000000000000000000610541327264017000173630ustar00rootroot00000000000000/* GPRS SGSN functionality */ /* (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 #include #include #include #include "../../bscconfig.h" #if BUILD_IU #include #endif #define GPRS_LLME_CHECK_TICK 30 extern struct sgsn_instance *sgsn; extern void *tall_bsc_ctx; LLIST_HEAD(sgsn_mm_ctxts); LLIST_HEAD(sgsn_ggsn_ctxts); LLIST_HEAD(sgsn_apn_ctxts); LLIST_HEAD(sgsn_pdp_ctxts); static const struct rate_ctr_desc mmctx_ctr_description[] = { { "sign:packets:in", "Signalling Messages ( In)" }, { "sign:packets:out", "Signalling Messages (Out)" }, { "udata:packets:in", "User Data Messages ( In)" }, { "udata:packets:out", "User Data Messages (Out)" }, { "udata:bytes:in", "User Data Bytes ( In)" }, { "udata:bytes:out", "User Data Bytes (Out)" }, { "pdp_ctx_act", "PDP Context Activations " }, { "suspend", "SUSPEND Count " }, { "paging:ps", "Paging Packet Switched " }, { "paging:cs", "Paging Circuit Switched " }, { "ra_update", "Routing Area Update " }, }; static const struct rate_ctr_group_desc mmctx_ctrg_desc = { .group_name_prefix = "sgsn:mmctx", .group_description = "SGSN MM Context Statistics", .num_ctr = ARRAY_SIZE(mmctx_ctr_description), .ctr_desc = mmctx_ctr_description, .class_id = OSMO_STATS_CLASS_SUBSCRIBER, }; static const struct rate_ctr_desc pdpctx_ctr_description[] = { { "udata:packets:in", "User Data Messages ( In)" }, { "udata:packets:out", "User Data Messages (Out)" }, { "udata:bytes:in", "User Data Bytes ( In)" }, { "udata:bytes:out", "User Data Bytes (Out)" }, }; static const struct rate_ctr_group_desc pdpctx_ctrg_desc = { .group_name_prefix = "sgsn:pdpctx", .group_description = "SGSN PDP Context Statistics", .num_ctr = ARRAY_SIZE(pdpctx_ctr_description), .ctr_desc = pdpctx_ctr_description, .class_id = OSMO_STATS_CLASS_SUBSCRIBER, }; static const struct rate_ctr_desc sgsn_ctr_description[] = { { "llc:dl_bytes", "Count sent LLC bytes before giving it to the bssgp layer" }, { "llc:ul_bytes", "Count sucessful received LLC bytes (encrypt & fcs correct)" }, { "llc:dl_packets", "Count sucessful sent LLC packets before giving it to the bssgp layer" }, { "llc:ul_packets", "Count sucessful received LLC packets (encrypt & fcs correct)" }, { "gprs:attach_requested", "Received attach requests" }, { "gprs:attach_accepted", "Sent attach accepts" }, { "gprs:attach_rejected", "Sent attach rejects" }, { "gprs:detach_requested", "Received detach requests" }, { "gprs:detach_acked", "Sent detach acks" }, { "gprs:routing_area_requested", "Received routing area requests" }, { "gprs:routing_area_requested", "Sent routing area acks" }, { "gprs:routing_area_requested", "Sent routing area rejects" }, { "pdp:activate_requested", "Received activate requests" }, { "pdp:activate_rejected", "Sent activate rejects" }, { "pdp:activate_accepted", "Sent activate accepts" }, { "pdp:request_activated", "unused" }, { "pdp:request_activate_rejected", "unused" }, { "pdp:modify_requested", "unused" }, { "pdp:modify_accepted", "unused" }, { "pdp:dl_deactivate_requested", "Sent deactivate requests" }, { "pdp:dl_deactivate_accepted", "Sent deactivate accepted" }, { "pdp:ul_deactivate_requested", "Received deactivate requests" }, { "pdp:ul_deactivate_accepted", "Received deactivate accepts" }, }; static const struct rate_ctr_group_desc sgsn_ctrg_desc = { "sgsn", "SGSN Overall Statistics", OSMO_STATS_CLASS_GLOBAL, ARRAY_SIZE(sgsn_ctr_description), sgsn_ctr_description, }; void sgsn_rate_ctr_init() { sgsn->rate_ctrs = rate_ctr_group_alloc(tall_bsc_ctx, &sgsn_ctrg_desc, 0); OSMO_ASSERT(sgsn->rate_ctrs); } /* look-up an SGSN MM context based on Iu UE context (struct ue_conn_ctx)*/ struct sgsn_mm_ctx *sgsn_mm_ctx_by_ue_ctx(const void *uectx) { struct sgsn_mm_ctx *ctx; llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) { if (ctx->ran_type == MM_CTX_T_UTRAN_Iu && uectx == ctx->iu.ue_ctx) return ctx; } return NULL; } /* look-up a SGSN MM context based on TLLI + RAI */ struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli(uint32_t tlli, const struct gprs_ra_id *raid) { struct sgsn_mm_ctx *ctx; llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) { if ((tlli == ctx->gb.tlli || tlli == ctx->gb.tlli_new) && gprs_ra_id_equals(raid, &ctx->ra)) return ctx; } return NULL; } struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli_and_ptmsi(uint32_t tlli, const struct gprs_ra_id *raid) { struct sgsn_mm_ctx *ctx; int tlli_type; /* TODO: Also check the P_TMSI signature to be safe. That signature * should be different (at least with a sufficiently high probability) * after SGSN restarts and for multiple SGSN instances. */ tlli_type = gprs_tlli_type(tlli); if (tlli_type != TLLI_FOREIGN && tlli_type != TLLI_LOCAL) return NULL; llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) { if ((gprs_tmsi2tlli(ctx->p_tmsi, tlli_type) == tlli || gprs_tmsi2tlli(ctx->p_tmsi_old, tlli_type) == tlli) && gprs_ra_id_equals(raid, &ctx->ra)) return ctx; } return NULL; } struct sgsn_mm_ctx *sgsn_mm_ctx_by_ptmsi(uint32_t p_tmsi) { struct sgsn_mm_ctx *ctx; llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) { if (p_tmsi == ctx->p_tmsi || (ctx->p_tmsi_old && ctx->p_tmsi_old == p_tmsi)) return ctx; } return NULL; } struct sgsn_mm_ctx *sgsn_mm_ctx_by_imsi(const char *imsi) { struct sgsn_mm_ctx *ctx; llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) { if (!strcmp(imsi, ctx->imsi)) return ctx; } return NULL; } /* Allocate a new SGSN MM context for GERAN_Gb */ struct sgsn_mm_ctx *sgsn_mm_ctx_alloc_gb(uint32_t tlli, const struct gprs_ra_id *raid) { struct sgsn_mm_ctx *ctx; ctx = talloc_zero(tall_bsc_ctx, struct sgsn_mm_ctx); if (!ctx) return NULL; memcpy(&ctx->ra, raid, sizeof(ctx->ra)); ctx->ran_type = MM_CTX_T_GERAN_Gb; ctx->gb.tlli = tlli; ctx->gmm_state = GMM_DEREGISTERED; ctx->pmm_state = MM_IDLE; ctx->auth_triplet.key_seq = GSM_KEY_SEQ_INVAL; ctx->ciph_algo = sgsn->cfg.cipher; LOGMMCTXP(LOGL_DEBUG, ctx, "Allocated with %s cipher.\n", get_value_string(gprs_cipher_names, ctx->ciph_algo)); ctx->ctrg = rate_ctr_group_alloc(ctx, &mmctx_ctrg_desc, tlli); if (!ctx->ctrg) { LOGMMCTXP(LOGL_ERROR, ctx, "Cannot allocate counter group\n"); talloc_free(ctx); return NULL; } INIT_LLIST_HEAD(&ctx->pdp_list); llist_add(&ctx->list, &sgsn_mm_ctxts); return ctx; } /* Allocate a new SGSN MM context */ struct sgsn_mm_ctx *sgsn_mm_ctx_alloc_iu(void *uectx) { #if BUILD_IU struct sgsn_mm_ctx *ctx; struct ranap_ue_conn_ctx *ue_ctx = uectx; ctx = talloc_zero(tall_bsc_ctx, struct sgsn_mm_ctx); if (!ctx) return NULL; ctx->ran_type = MM_CTX_T_UTRAN_Iu; ctx->iu.ue_ctx = ue_ctx; ctx->iu.ue_ctx->rab_assign_addr_enc = sgsn->cfg.iu.rab_assign_addr_enc; ctx->iu.new_key = 1; ctx->gmm_state = GMM_DEREGISTERED; ctx->pmm_state = PMM_DETACHED; ctx->auth_triplet.key_seq = GSM_KEY_SEQ_INVAL; ctx->ctrg = rate_ctr_group_alloc(ctx, &mmctx_ctrg_desc, ue_ctx->conn_id); if (!ctx->ctrg) { LOGMMCTXP(LOGL_ERROR, ctx, "Cannot allocate counter group for %s.%u\n", mmctx_ctrg_desc.group_name_prefix, ue_ctx->conn_id); talloc_free(ctx); return NULL; } /* Need to get RAID from IU conn */ ctx->ra = ctx->iu.ue_ctx->ra_id; INIT_LLIST_HEAD(&ctx->pdp_list); llist_add(&ctx->list, &sgsn_mm_ctxts); return ctx; #else return NULL; #endif } /* this is a hard _free_ function, it doesn't clean up the PDP contexts * in libgtp! */ static void sgsn_mm_ctx_free(struct sgsn_mm_ctx *mm) { struct sgsn_pdp_ctx *pdp, *pdp2; /* Unlink from global list of MM contexts */ llist_del(&mm->list); /* Free all PDP contexts */ llist_for_each_entry_safe(pdp, pdp2, &mm->pdp_list, list) sgsn_pdp_ctx_free(pdp); rate_ctr_group_free(mm->ctrg); talloc_free(mm); } void sgsn_mm_ctx_cleanup_free(struct sgsn_mm_ctx *mm) { struct gprs_llc_llme *llme = NULL; uint32_t tlli = mm->gb.tlli; struct sgsn_pdp_ctx *pdp, *pdp2; struct sgsn_signal_data sig_data; if (mm->ran_type == MM_CTX_T_GERAN_Gb) llme = mm->gb.llme; else OSMO_ASSERT(mm->gb.llme == NULL); /* Forget about ongoing look-ups */ if (mm->ggsn_lookup) { LOGMMCTXP(LOGL_NOTICE, mm, "Cleaning mmctx with on-going query.\n"); mm->ggsn_lookup->mmctx = NULL; mm->ggsn_lookup = NULL; } /* delete all existing PDP contexts for this MS */ llist_for_each_entry_safe(pdp, pdp2, &mm->pdp_list, list) { LOGMMCTXP(LOGL_NOTICE, mm, "Dropping PDP context for NSAPI=%u\n", pdp->nsapi); sgsn_pdp_ctx_terminate(pdp); } if (osmo_timer_pending(&mm->timer)) { LOGMMCTXP(LOGL_INFO, mm, "Cancelling MM timer %u\n", mm->T); osmo_timer_del(&mm->timer); } memset(&sig_data, 0, sizeof(sig_data)); sig_data.mm = mm; osmo_signal_dispatch(SS_SGSN, S_SGSN_MM_FREE, &sig_data); /* Detach from subscriber which is possibly freed then */ if (mm->subscr) { struct gprs_subscr *subscr = gprs_subscr_get(mm->subscr); gprs_subscr_cleanup(subscr); gprs_subscr_put(subscr); } sgsn_mm_ctx_free(mm); mm = NULL; if (llme) { /* TLLI unassignment, must be called after sgsn_mm_ctx_free */ gprs_llgmm_assign(llme, tlli, 0xffffffff); } } /* look up PDP context by MM context and NSAPI */ struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_nsapi(const struct sgsn_mm_ctx *mm, uint8_t nsapi) { struct sgsn_pdp_ctx *pdp; llist_for_each_entry(pdp, &mm->pdp_list, list) { if (pdp->nsapi == nsapi) return pdp; } return NULL; } /* look up PDP context by MM context and transaction ID */ struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_tid(const struct sgsn_mm_ctx *mm, uint8_t tid) { struct sgsn_pdp_ctx *pdp; llist_for_each_entry(pdp, &mm->pdp_list, list) { if (pdp->ti == tid) return pdp; } return NULL; } /* you don't want to use this directly, call sgsn_create_pdp_ctx() */ struct sgsn_pdp_ctx *sgsn_pdp_ctx_alloc(struct sgsn_mm_ctx *mm, uint8_t nsapi) { struct sgsn_pdp_ctx *pdp; pdp = sgsn_pdp_ctx_by_nsapi(mm, nsapi); if (pdp) return NULL; pdp = talloc_zero(tall_bsc_ctx, struct sgsn_pdp_ctx); if (!pdp) return NULL; pdp->mm = mm; pdp->nsapi = nsapi; pdp->ctrg = rate_ctr_group_alloc(pdp, &pdpctx_ctrg_desc, nsapi); if (!pdp->ctrg) { LOGPDPCTXP(LOGL_ERROR, pdp, "Error allocation counter group\n"); talloc_free(pdp); return NULL; } llist_add(&pdp->list, &mm->pdp_list); llist_add(&pdp->g_list, &sgsn_pdp_ctxts); return pdp; } /* * This function will not trigger any GSM DEACT PDP ACK messages, so you * probably want to call sgsn_delete_pdp_ctx() instead if the connection * isn't detached already. */ void sgsn_pdp_ctx_terminate(struct sgsn_pdp_ctx *pdp) { struct sgsn_signal_data sig_data; OSMO_ASSERT(pdp->mm != NULL); /* There might still be pending callbacks in libgtp. So the parts of * this object relevant to GTP need to remain intact in this case. */ LOGPDPCTXP(LOGL_INFO, pdp, "Forcing release of PDP context\n"); if (pdp->mm->ran_type == MM_CTX_T_GERAN_Gb) { /* Force the deactivation of the SNDCP layer */ sndcp_sm_deactivate_ind(&pdp->mm->gb.llme->lle[pdp->sapi], pdp->nsapi); } memset(&sig_data, 0, sizeof(sig_data)); sig_data.pdp = pdp; osmo_signal_dispatch(SS_SGSN, S_SGSN_PDP_TERMINATE, &sig_data); /* Detach from MM context */ llist_del(&pdp->list); pdp->mm = NULL; sgsn_delete_pdp_ctx(pdp); } /* * Don't call this function directly unless you know what you are doing. * In normal conditions use sgsn_delete_pdp_ctx and in unspecified or * implementation dependent abnormal ones sgsn_pdp_ctx_terminate. */ void sgsn_pdp_ctx_free(struct sgsn_pdp_ctx *pdp) { struct sgsn_signal_data sig_data; memset(&sig_data, 0, sizeof(sig_data)); sig_data.pdp = pdp; osmo_signal_dispatch(SS_SGSN, S_SGSN_PDP_FREE, &sig_data); rate_ctr_group_free(pdp->ctrg); if (pdp->mm) llist_del(&pdp->list); llist_del(&pdp->g_list); /* _if_ we still have a library handle, at least set it to NULL * to avoid any dereferences of the now-deleted PDP context from * sgsn_libgtp:cb_data_ind() */ if (pdp->lib) { struct pdp_t *lib = pdp->lib; LOGPDPCTXP(LOGL_NOTICE, pdp, "freeing PDP context that still " "has a libgtp handle attached to it, this shouldn't " "happen!\n"); osmo_generate_backtrace(); lib->priv = NULL; } if (pdp->destroy_ggsn) sgsn_ggsn_ctx_free(pdp->ggsn); talloc_free(pdp); } /* GGSN contexts */ struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_alloc(uint32_t id) { struct sgsn_ggsn_ctx *ggc; ggc = talloc_zero(tall_bsc_ctx, struct sgsn_ggsn_ctx); if (!ggc) return NULL; ggc->id = id; ggc->gtp_version = 1; ggc->remote_restart_ctr = -1; /* if we are called from config file parse, this gsn doesn't exist yet */ ggc->gsn = sgsn->gsn; llist_add(&ggc->list, &sgsn_ggsn_ctxts); return ggc; } void sgsn_ggsn_ctx_free(struct sgsn_ggsn_ctx *ggc) { llist_del(&ggc->list); talloc_free(ggc); } struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_id(uint32_t id) { struct sgsn_ggsn_ctx *ggc; llist_for_each_entry(ggc, &sgsn_ggsn_ctxts, list) { if (id == ggc->id) return ggc; } return NULL; } struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_addr(struct in_addr *addr) { struct sgsn_ggsn_ctx *ggc; llist_for_each_entry(ggc, &sgsn_ggsn_ctxts, list) { if (!memcmp(addr, &ggc->remote_addr, sizeof(*addr))) return ggc; } return NULL; } struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_find_alloc(uint32_t id) { struct sgsn_ggsn_ctx *ggc; ggc = sgsn_ggsn_ctx_by_id(id); if (!ggc) ggc = sgsn_ggsn_ctx_alloc(id); return ggc; } /* APN contexts */ static struct apn_ctx *sgsn_apn_ctx_alloc(const char *ap_name, const char *imsi_prefix) { struct apn_ctx *actx; actx = talloc_zero(tall_bsc_ctx, struct apn_ctx); if (!actx) return NULL; actx->name = talloc_strdup(actx, ap_name); actx->imsi_prefix = talloc_strdup(actx, imsi_prefix); llist_add_tail(&actx->list, &sgsn_apn_ctxts); return actx; } void sgsn_apn_ctx_free(struct apn_ctx *actx) { llist_del(&actx->list); talloc_free(actx); } struct apn_ctx *sgsn_apn_ctx_match(const char *name, const char *imsi) { struct apn_ctx *actx; struct apn_ctx *found_actx = NULL; size_t imsi_prio = 0; size_t name_prio = 0; size_t name_req_len = strlen(name); llist_for_each_entry(actx, &sgsn_apn_ctxts, list) { size_t name_ref_len, imsi_ref_len; const char *name_ref_start, *name_match_start; imsi_ref_len = strlen(actx->imsi_prefix); if (strncmp(actx->imsi_prefix, imsi, imsi_ref_len) != 0) continue; if (imsi_ref_len < imsi_prio) continue; /* IMSI matches */ name_ref_start = &actx->name[0]; if (name_ref_start[0] == '*') { /* Suffix match */ name_ref_start += 1; name_ref_len = strlen(name_ref_start); if (name_ref_len > name_req_len) continue; } else { name_ref_len = strlen(name_ref_start); if (name_ref_len != name_req_len) continue; } name_match_start = name + (name_req_len - name_ref_len); if (strcasecmp(name_match_start, name_ref_start) != 0) continue; /* IMSI and name match */ if (imsi_ref_len == imsi_prio && name_ref_len < name_prio) /* Lower priority, skip */ continue; imsi_prio = imsi_ref_len; name_prio = name_ref_len; found_actx = actx; } return found_actx; } struct apn_ctx *sgsn_apn_ctx_by_name(const char *name, const char *imsi_prefix) { struct apn_ctx *actx; llist_for_each_entry(actx, &sgsn_apn_ctxts, list) { if (strcasecmp(name, actx->name) == 0 && strcasecmp(imsi_prefix, actx->imsi_prefix) == 0) return actx; } return NULL; } struct apn_ctx *sgsn_apn_ctx_find_alloc(const char *name, const char *imsi_prefix) { struct apn_ctx *actx; actx = sgsn_apn_ctx_by_name(name, imsi_prefix); if (!actx) actx = sgsn_apn_ctx_alloc(name, imsi_prefix); return actx; } uint32_t sgsn_alloc_ptmsi(void) { struct sgsn_mm_ctx *mm; uint32_t ptmsi = 0xdeadbeef; int max_retries = 100, rc = 0; restart: rc = osmo_get_rand_id((uint8_t *) &ptmsi, sizeof(ptmsi)); if (rc < 0) goto failed; /* Enforce that the 2 MSB are set without loosing the distance between * identical values. Since rand() has no duplicate values within a * period (because the size of the state is the same like the size of * the random value), this leads to a distance of period/4 when the * distribution of the 2 MSB is uniform. This approach fails with a * probability of (3/4)^max_retries, only 1% of the approaches will * need more than 16 numbers (even distribution assumed). * * Alternatively, a freeze list could be used if another PRNG is used * or when this approach proves to be not sufficient. */ if (ptmsi >= 0xC0000000) { if (!max_retries--) goto failed; goto restart; } ptmsi |= 0xC0000000; if (ptmsi == GSM_RESERVED_TMSI) { if (!max_retries--) goto failed; goto restart; } llist_for_each_entry(mm, &sgsn_mm_ctxts, list) { if (mm->p_tmsi == ptmsi) { if (!max_retries--) goto failed; goto restart; } } return ptmsi; failed: LOGP(DGPRS, LOGL_ERROR, "Failed to allocate a P-TMSI: %d (%s)\n", rc, strerror(-rc)); return GSM_RESERVED_TMSI; } static void drop_one_pdp(struct sgsn_pdp_ctx *pdp) { if (pdp->mm->gmm_state == GMM_REGISTERED_NORMAL) gsm48_tx_gsm_deact_pdp_req(pdp, GSM_CAUSE_NET_FAIL); else { /* FIXME: GPRS paging in case MS is SUSPENDED */ LOGPDPCTXP(LOGL_NOTICE, pdp, "Hard-dropping PDP ctx due to GGSN " "recovery\n"); /* FIXME: how to tell this to libgtp? */ sgsn_pdp_ctx_free(pdp); } } /* High-level function to be called in case a GGSN has disappeared or * otherwise lost state (recovery procedure) */ int drop_all_pdp_for_ggsn(struct sgsn_ggsn_ctx *ggsn) { struct sgsn_mm_ctx *mm; int num = 0; llist_for_each_entry(mm, &sgsn_mm_ctxts, list) { struct sgsn_pdp_ctx *pdp; llist_for_each_entry(pdp, &mm->pdp_list, list) { if (pdp->ggsn == ggsn) { drop_one_pdp(pdp); num++; } } } return num; } void sgsn_update_subscriber_data(struct sgsn_mm_ctx *mmctx) { OSMO_ASSERT(mmctx != NULL); LOGMMCTXP(LOGL_INFO, mmctx, "Subscriber data update\n"); sgsn_auth_update(mmctx); } static void insert_extra(struct tlv_parsed *tp, struct sgsn_subscriber_data *data, struct sgsn_subscriber_pdp_data *pdp) { tp->lv[OSMO_IE_GSM_SUB_QOS].len = pdp->qos_subscribed_len; tp->lv[OSMO_IE_GSM_SUB_QOS].val = pdp->qos_subscribed; /* Prefer PDP charging characteristics of per subscriber one */ if (pdp->has_pdp_charg) { tp->lv[OSMO_IE_GSM_CHARG_CHAR].len = sizeof(pdp->pdp_charg); tp->lv[OSMO_IE_GSM_CHARG_CHAR].val = &pdp->pdp_charg[0]; } else if (data->has_pdp_charg) { tp->lv[OSMO_IE_GSM_CHARG_CHAR].len = sizeof(data->pdp_charg); tp->lv[OSMO_IE_GSM_CHARG_CHAR].val = &data->pdp_charg[0]; } } /** * The tlv_parsed tp parameter will be modified to insert a * OSMO_IE_GSM_SUB_QOS in case the data is available in the * PDP context handling. */ struct sgsn_ggsn_ctx *sgsn_mm_ctx_find_ggsn_ctx(struct sgsn_mm_ctx *mmctx, struct tlv_parsed *tp, enum gsm48_gsm_cause *gsm_cause, char *out_apn_str) { char req_apn_str[GSM_APN_LENGTH] = {0}; const struct apn_ctx *apn_ctx = NULL; const char *selected_apn_str = NULL; struct sgsn_subscriber_pdp_data *pdp; struct sgsn_ggsn_ctx *ggsn = NULL; int allow_any_apn = 0; out_apn_str[0] = '\0'; if (TLVP_PRESENT(tp, GSM48_IE_GSM_APN)) { if (TLVP_LEN(tp, GSM48_IE_GSM_APN) >= GSM_APN_LENGTH - 1) { LOGMMCTXP(LOGL_ERROR, mmctx, "APN IE too long\n"); *gsm_cause = GSM_CAUSE_INV_MAND_INFO; return NULL; } osmo_apn_to_str(req_apn_str, TLVP_VAL(tp, GSM48_IE_GSM_APN), TLVP_LEN(tp, GSM48_IE_GSM_APN)); if (strcmp(req_apn_str, "*") == 0) req_apn_str[0] = 0; } if (mmctx->subscr == NULL) allow_any_apn = 1; if (strlen(req_apn_str) == 0 && !allow_any_apn) { /* No specific APN requested, check for an APN that is both * granted and configured */ llist_for_each_entry(pdp, &mmctx->subscr->sgsn_data->pdp_list, list) { if (strcmp(pdp->apn_str, "*") == 0) { allow_any_apn = 1; selected_apn_str = ""; insert_extra(tp, mmctx->subscr->sgsn_data, pdp); continue; } if (!llist_empty(&sgsn_apn_ctxts)) { apn_ctx = sgsn_apn_ctx_match(req_apn_str, mmctx->imsi); /* Not configured */ if (apn_ctx == NULL) continue; } insert_extra(tp, mmctx->subscr->sgsn_data, pdp); selected_apn_str = pdp->apn_str; break; } } else if (!allow_any_apn) { /* Check whether the given APN is granted */ llist_for_each_entry(pdp, &mmctx->subscr->sgsn_data->pdp_list, list) { if (strcmp(pdp->apn_str, "*") == 0) { insert_extra(tp, mmctx->subscr->sgsn_data, pdp); selected_apn_str = req_apn_str; allow_any_apn = 1; continue; } if (strcasecmp(pdp->apn_str, req_apn_str) == 0) { insert_extra(tp, mmctx->subscr->sgsn_data, pdp); selected_apn_str = req_apn_str; break; } } } else if (strlen(req_apn_str) != 0) { /* Any APN is allowed */ selected_apn_str = req_apn_str; } else { /* Prefer the GGSN associated with the wildcard APN */ selected_apn_str = ""; } if (!allow_any_apn && selected_apn_str == NULL) { /* Access not granted */ LOGMMCTXP(LOGL_NOTICE, mmctx, "The requested APN '%s' is not allowed\n", req_apn_str); *gsm_cause = GSM_CAUSE_REQ_SERV_OPT_NOTSUB; return NULL; } /* copy the selected apn_str */ if (selected_apn_str) strcpy(out_apn_str, selected_apn_str); else out_apn_str[0] = '\0'; if (apn_ctx == NULL && selected_apn_str) apn_ctx = sgsn_apn_ctx_match(selected_apn_str, mmctx->imsi); if (apn_ctx != NULL) { ggsn = apn_ctx->ggsn; } else if (llist_empty(&sgsn_apn_ctxts)) { /* No configuration -> use GGSN 0 */ ggsn = sgsn_ggsn_ctx_by_id(0); } else if (allow_any_apn && (selected_apn_str == NULL || strlen(selected_apn_str) == 0)) { /* No APN given and no default configuration -> Use GGSN 0 */ ggsn = sgsn_ggsn_ctx_by_id(0); } else { /* No matching configuration found */ LOGMMCTXP(LOGL_NOTICE, mmctx, "The selected APN '%s' has not been configured\n", selected_apn_str); *gsm_cause = GSM_CAUSE_MISSING_APN; return NULL; } if (!ggsn) { LOGMMCTXP(LOGL_NOTICE, mmctx, "No static GGSN configured. Selected APN '%s'\n", selected_apn_str); return NULL; } LOGMMCTXP(LOGL_INFO, mmctx, "Found GGSN %d for APN '%s' (requested '%s')\n", ggsn->id, selected_apn_str ? selected_apn_str : "---", req_apn_str); return ggsn; } static void sgsn_llme_cleanup_free(struct gprs_llc_llme *llme) { struct sgsn_mm_ctx *mmctx = NULL; llist_for_each_entry(mmctx, &sgsn_mm_ctxts, list) { if (llme == mmctx->gb.llme) { gsm0408_gprs_access_cancelled(mmctx, SGSN_ERROR_CAUSE_NONE); return; } } /* No MM context found */ LOGP(DGPRS, LOGL_INFO, "Deleting orphaned LLME, TLLI 0x%08x\n", llme->tlli); gprs_llgmm_unassign(llme); } static void sgsn_llme_check_cb(void *data_) { struct gprs_llc_llme *llme, *llme_tmp; struct timespec now_tp; time_t now, age; time_t max_age = gprs_max_time_to_idle(); int rc; rc = clock_gettime(CLOCK_MONOTONIC, &now_tp); OSMO_ASSERT(rc >= 0); now = now_tp.tv_sec; LOGP(DGPRS, LOGL_DEBUG, "Checking for inactive LLMEs, time = %u\n", (unsigned)now); llist_for_each_entry_safe(llme, llme_tmp, &gprs_llc_llmes, list) { if (llme->age_timestamp == GPRS_LLME_RESET_AGE) llme->age_timestamp = now; age = now - llme->age_timestamp; if (age > max_age || age < 0) { LOGP(DGPRS, LOGL_INFO, "Inactivity timeout for TLLI 0x%08x, age %d\n", llme->tlli, (int)age); sgsn_llme_cleanup_free(llme); } } osmo_timer_schedule(&sgsn->llme_timer, GPRS_LLME_CHECK_TICK, 0); } void sgsn_inst_init() { osmo_timer_setup(&sgsn->llme_timer, sgsn_llme_check_cb, NULL); osmo_timer_schedule(&sgsn->llme_timer, GPRS_LLME_CHECK_TICK, 0); } osmo-sgsn-1.3.0/src/gprs/gprs_sndcp.c000066400000000000000000001066341327264017000175240ustar00rootroot00000000000000/* GPRS SNDCP protocol implementation as per 3GPP TS 04.65 */ /* (C) 2010 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEBUG_IP_PACKETS 0 /* 0=Disabled, 1=Enabled */ #if DEBUG_IP_PACKETS == 1 /* Calculate TCP/IP checksum */ static uint16_t calc_ip_csum(uint8_t *data, int len) { int i; uint32_t accumulator = 0; uint16_t *pointer = (uint16_t *) data; for (i = len; i > 1; i -= 2) { accumulator += *pointer; pointer++; } if (len % 2) accumulator += *pointer; accumulator = (accumulator & 0xffff) + ((accumulator >> 16) & 0xffff); accumulator += (accumulator >> 16) & 0xffff; return (~accumulator); } /* Calculate TCP/IP checksum */ static uint16_t calc_tcpip_csum(const void *ctx, uint8_t *packet, int len) { uint8_t *buf; uint16_t csum; buf = talloc_zero_size(ctx, len); memset(buf, 0, len); memcpy(buf, packet + 12, 8); buf[9] = packet[9]; buf[11] = (len - 20) & 0xFF; buf[10] = (len - 20) >> 8 & 0xFF; memcpy(buf + 12, packet + 20, len - 20); csum = calc_ip_csum(buf, len - 20 + 12); talloc_free(buf); return csum; } /* Show some ip packet details */ static void debug_ip_packet(uint8_t *data, int len, int dir, char *info) { uint8_t tcp_flags; char flags_debugmsg[256]; int len_short; static unsigned int packet_count = 0; static unsigned int tcp_csum_err_count = 0; static unsigned int ip_csum_err_count = 0; packet_count++; if (len > 80) len_short = 80; else len_short = len; if (dir) DEBUGP(DSNDCP, "%s: MS => SGSN: %s\n", info, osmo_hexdump_nospc(data, len_short)); else DEBUGP(DSNDCP, "%s: MS <= SGSN: %s\n", info, osmo_hexdump_nospc(data, len_short)); DEBUGP(DSNDCP, "%s: Length.: %d\n", info, len); DEBUGP(DSNDCP, "%s: NO.: %d\n", info, packet_count); if (len < 20) { DEBUGP(DSNDCP, "%s: Error: Short IP packet!\n", info); return; } if (calc_ip_csum(data, 20) != 0) { DEBUGP(DSNDCP, "%s: Bad IP-Header checksum!\n", info); ip_csum_err_count++; } else DEBUGP(DSNDCP, "%s: IP-Header checksum ok.\n", info); if (data[9] == 0x06) { if (len < 40) { DEBUGP(DSNDCP, "%s: Error: Short TCP packet!\n", info); return; } DEBUGP(DSNDCP, "%s: Protocol type: TCP\n", info); tcp_flags = data[33]; if (calc_tcpip_csum(NULL, data, len) != 0) { DEBUGP(DSNDCP, "%s: Bad TCP checksum!\n", info); tcp_csum_err_count++; } else DEBUGP(DSNDCP, "%s: TCP checksum ok.\n", info); memset(flags_debugmsg, 0, sizeof(flags_debugmsg)); if (tcp_flags & 1) strcat(flags_debugmsg, "FIN "); if (tcp_flags & 2) strcat(flags_debugmsg, "SYN "); if (tcp_flags & 4) strcat(flags_debugmsg, "RST "); if (tcp_flags & 8) strcat(flags_debugmsg, "PSH "); if (tcp_flags & 16) strcat(flags_debugmsg, "ACK "); if (tcp_flags & 32) strcat(flags_debugmsg, "URG "); DEBUGP(DSNDCP, "%s: FLAGS: %s\n", info, flags_debugmsg); } else if (data[9] == 0x11) { DEBUGP(DSNDCP, "%s: Protocol type: UDP\n", info); } else { DEBUGP(DSNDCP, "%s: Protocol type: (%02x)\n", info, data[9]); } DEBUGP(DSNDCP, "%s: IP-Header checksum errors: %d\n", info, ip_csum_err_count); DEBUGP(DSNDCP, "%s: TCP-Checksum errors: %d\n", info, tcp_csum_err_count); } #endif /* Chapter 7.2: SN-PDU Formats */ struct sndcp_common_hdr { /* octet 1 */ uint8_t nsapi:4; uint8_t more:1; uint8_t type:1; uint8_t first:1; uint8_t spare:1; } __attribute__((packed)); /* PCOMP / DCOMP only exist in first fragment */ struct sndcp_comp_hdr { /* octet 2 */ uint8_t pcomp:4; uint8_t dcomp:4; } __attribute__((packed)); struct sndcp_udata_hdr { /* octet 3 */ uint8_t npdu_high:4; uint8_t seg_nr:4; /* octet 4 */ uint8_t npdu_low; } __attribute__((packed)); static void *tall_sndcp_ctx; /* A fragment queue entry, containing one framgent of a N-PDU */ struct defrag_queue_entry { struct llist_head list; /* segment number of this fragment */ uint32_t seg_nr; /* length of the data area of this fragment */ uint32_t data_len; /* pointer to the data of this fragment */ uint8_t *data; }; LLIST_HEAD(gprs_sndcp_entities); /* Check if any compression parameters are set in the sgsn configuration */ static inline int any_pcomp_or_dcomp_active(struct sgsn_instance *sgsn) { if (sgsn->cfg.pcomp_rfc1144.active || sgsn->cfg.pcomp_rfc1144.passive || sgsn->cfg.dcomp_v42bis.active || sgsn->cfg.dcomp_v42bis.passive) return true; else return false; } /* Enqueue a fragment into the defragment queue */ static int defrag_enqueue(struct gprs_sndcp_entity *sne, uint8_t seg_nr, uint8_t *data, uint32_t data_len) { struct defrag_queue_entry *dqe; dqe = talloc_zero(tall_sndcp_ctx, struct defrag_queue_entry); if (!dqe) return -ENOMEM; dqe->data = talloc_zero_size(dqe, data_len); if (!dqe->data) { talloc_free(dqe); return -ENOMEM; } dqe->seg_nr = seg_nr; dqe->data_len = data_len; llist_add(&dqe->list, &sne->defrag.frag_list); if (seg_nr > sne->defrag.highest_seg) sne->defrag.highest_seg = seg_nr; sne->defrag.seg_have |= (1 << seg_nr); sne->defrag.tot_len += data_len; memcpy(dqe->data, data, data_len); return 0; } /* return if we have all segments of this N-PDU */ static int defrag_have_all_segments(struct gprs_sndcp_entity *sne) { uint32_t seg_needed = 0; unsigned int i; /* create a bitmask of needed segments */ for (i = 0; i <= sne->defrag.highest_seg; i++) seg_needed |= (1 << i); if (seg_needed == sne->defrag.seg_have) return 1; return 0; } static struct defrag_queue_entry *defrag_get_seg(struct gprs_sndcp_entity *sne, uint32_t seg_nr) { struct defrag_queue_entry *dqe; llist_for_each_entry(dqe, &sne->defrag.frag_list, list) { if (dqe->seg_nr == seg_nr) { llist_del(&dqe->list); return dqe; } } return NULL; } /* Perform actual defragmentation and create an output packet */ static int defrag_segments(struct gprs_sndcp_entity *sne) { struct msgb *msg; unsigned int seg_nr; uint8_t *npdu; int npdu_len; int rc; uint8_t *expnd = NULL; LOGP(DSNDCP, LOGL_DEBUG, "TLLI=0x%08x NSAPI=%u: Defragment output PDU %u " "num_seg=%u tot_len=%u\n", sne->lle->llme->tlli, sne->nsapi, sne->defrag.npdu, sne->defrag.highest_seg, sne->defrag.tot_len); msg = msgb_alloc_headroom(sne->defrag.tot_len+256, 128, "SNDCP Defrag"); if (!msg) return -ENOMEM; /* FIXME: message headers + identifiers */ npdu = msg->data; for (seg_nr = 0; seg_nr <= sne->defrag.highest_seg; seg_nr++) { struct defrag_queue_entry *dqe; uint8_t *data; dqe = defrag_get_seg(sne, seg_nr); if (!dqe) { LOGP(DSNDCP, LOGL_ERROR, "Segment %u missing\n", seg_nr); msgb_free(msg); return -EIO; } /* actually append the segment to the N-PDU */ data = msgb_put(msg, dqe->data_len); memcpy(data, dqe->data, dqe->data_len); /* release memory for the fragment queue entry */ talloc_free(dqe); } npdu_len = sne->defrag.tot_len; /* FIXME: cancel timer */ /* actually send the N-PDU to the SGSN core code, which then * hands it off to the correct GTP tunnel + GGSN via gtp_data_req() */ /* Decompress packet */ #if DEBUG_IP_PACKETS == 1 DEBUGP(DSNDCP, " \n"); DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n"); DEBUGP(DSNDCP, "===================================================\n"); #endif if (any_pcomp_or_dcomp_active(sgsn)) { expnd = talloc_zero_size(msg, npdu_len * MAX_DATADECOMPR_FAC + MAX_HDRDECOMPR_INCR); memcpy(expnd, npdu, npdu_len); /* Apply data decompression */ rc = gprs_sndcp_dcomp_expand(expnd, npdu_len, sne->defrag.dcomp, sne->defrag.data); if (rc < 0) { LOGP(DSNDCP, LOGL_ERROR, "Data decompression failed!\n"); talloc_free(expnd); return -EIO; } /* Apply header decompression */ rc = gprs_sndcp_pcomp_expand(expnd, rc, sne->defrag.pcomp, sne->defrag.proto); if (rc < 0) { LOGP(DSNDCP, LOGL_ERROR, "TCP/IP Header decompression failed!\n"); talloc_free(expnd); return -EIO; } /* Modify npu length, expnd is handed directly handed * over to gsn_rx_sndcp_ud_ind(), see below */ npdu_len = rc; } else expnd = npdu; #if DEBUG_IP_PACKETS == 1 debug_ip_packet(expnd, npdu_len, 1, "defrag_segments()"); DEBUGP(DSNDCP, "===================================================\n"); DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n"); DEBUGP(DSNDCP, " \n"); #endif /* Hand off packet to gtp */ rc = sgsn_rx_sndcp_ud_ind(&sne->ra_id, sne->lle->llme->tlli, sne->nsapi, msg, npdu_len, expnd); if (any_pcomp_or_dcomp_active(sgsn)) talloc_free(expnd); return rc; } static int defrag_input(struct gprs_sndcp_entity *sne, struct msgb *msg, uint8_t *hdr, unsigned int len) { struct sndcp_common_hdr *sch; struct sndcp_udata_hdr *suh; uint16_t npdu_num; uint8_t *data; int rc; sch = (struct sndcp_common_hdr *) hdr; if (sch->first) { suh = (struct sndcp_udata_hdr *) (hdr + 1 + sizeof(struct sndcp_common_hdr)); } else suh = (struct sndcp_udata_hdr *) (hdr + sizeof(struct sndcp_common_hdr)); data = (uint8_t *)suh + sizeof(struct sndcp_udata_hdr); npdu_num = (suh->npdu_high << 8) | suh->npdu_low; LOGP(DSNDCP, LOGL_DEBUG, "TLLI=0x%08x NSAPI=%u: Input PDU %u Segment %u " "Length %u %s %s\n", sne->lle->llme->tlli, sne->nsapi, npdu_num, suh->seg_nr, len, sch->first ? "F " : "", sch->more ? "M" : ""); if (sch->first) { /* first segment of a new packet. Discard all leftover fragments of * previous packet */ if (!llist_empty(&sne->defrag.frag_list)) { struct defrag_queue_entry *dqe, *dqe2; LOGP(DSNDCP, LOGL_INFO, "TLLI=0x%08x NSAPI=%u: Dropping " "SN-PDU %u due to insufficient segments (%04x)\n", sne->lle->llme->tlli, sne->nsapi, sne->defrag.npdu, sne->defrag.seg_have); llist_for_each_entry_safe(dqe, dqe2, &sne->defrag.frag_list, list) { llist_del(&dqe->list); talloc_free(dqe); } } /* store the currently de-fragmented PDU number */ sne->defrag.npdu = npdu_num; /* Re-set fragmentation state */ sne->defrag.no_more = sne->defrag.highest_seg = sne->defrag.seg_have = 0; sne->defrag.tot_len = 0; /* FIXME: (re)start timer */ } if (sne->defrag.npdu != npdu_num) { LOGP(DSNDCP, LOGL_INFO, "Segment for different SN-PDU " "(%u != %u)\n", npdu_num, sne->defrag.npdu); /* FIXME */ } /* FIXME: check if seg_nr already exists */ /* make sure to subtract length of SNDCP header from 'len' */ rc = defrag_enqueue(sne, suh->seg_nr, data, len - (data - hdr)); if (rc < 0) return rc; if (!sch->more) { /* this is suppsed to be the last segment of the N-PDU, but it * might well be not the last to arrive */ sne->defrag.no_more = 1; } if (sne->defrag.no_more) { /* we have already received the last segment before, let's check * if all the previous segments exist */ if (defrag_have_all_segments(sne)) return defrag_segments(sne); } return 0; } static struct gprs_sndcp_entity *gprs_sndcp_entity_by_lle(const struct gprs_llc_lle *lle, uint8_t nsapi) { struct gprs_sndcp_entity *sne; llist_for_each_entry(sne, &gprs_sndcp_entities, list) { if (sne->lle == lle && sne->nsapi == nsapi) return sne; } return NULL; } static struct gprs_sndcp_entity *gprs_sndcp_entity_alloc(struct gprs_llc_lle *lle, uint8_t nsapi) { struct gprs_sndcp_entity *sne; sne = talloc_zero(tall_sndcp_ctx, struct gprs_sndcp_entity); if (!sne) return NULL; sne->lle = lle; sne->nsapi = nsapi; sne->defrag.timer.data = sne; //sne->fqueue.timer.cb = FIXME; sne->rx_state = SNDCP_RX_S_FIRST; INIT_LLIST_HEAD(&sne->defrag.frag_list); llist_add(&sne->list, &gprs_sndcp_entities); return sne; } /* Entry point for the SNSM-ACTIVATE.indication */ int sndcp_sm_activate_ind(struct gprs_llc_lle *lle, uint8_t nsapi) { LOGP(DSNDCP, LOGL_INFO, "SNSM-ACTIVATE.ind (lle=%p TLLI=%08x, " "SAPI=%u, NSAPI=%u)\n", lle, lle->llme->tlli, lle->sapi, nsapi); if (gprs_sndcp_entity_by_lle(lle, nsapi)) { LOGP(DSNDCP, LOGL_ERROR, "Trying to ACTIVATE " "already-existing entity (TLLI=%08x, NSAPI=%u)\n", lle->llme->tlli, nsapi); return -EEXIST; } if (!gprs_sndcp_entity_alloc(lle, nsapi)) { LOGP(DSNDCP, LOGL_ERROR, "Out of memory during ACTIVATE\n"); return -ENOMEM; } return 0; } /* Entry point for the SNSM-DEACTIVATE.indication */ int sndcp_sm_deactivate_ind(struct gprs_llc_lle *lle, uint8_t nsapi) { struct gprs_sndcp_entity *sne; LOGP(DSNDCP, LOGL_INFO, "SNSM-DEACTIVATE.ind (lle=%p, TLLI=%08x, " "SAPI=%u, NSAPI=%u)\n", lle, lle->llme->tlli, lle->sapi, nsapi); sne = gprs_sndcp_entity_by_lle(lle, nsapi); if (!sne) { LOGP(DSNDCP, LOGL_ERROR, "SNSM-DEACTIVATE.ind for non-" "existing TLLI=%08x SAPI=%u NSAPI=%u\n", lle->llme->tlli, lle->sapi, nsapi); return -ENOENT; } llist_del(&sne->list); /* frag queue entries are hierarchically allocated, so no need to * free them explicitly here */ talloc_free(sne); return 0; } /* Fragmenter state */ struct sndcp_frag_state { uint8_t frag_nr; struct msgb *msg; /* original message */ uint8_t *next_byte; /* first byte of next fragment */ struct gprs_sndcp_entity *sne; void *mmcontext; }; /* returns '1' if there are more fragments to send, '0' if none */ static int sndcp_send_ud_frag(struct sndcp_frag_state *fs, uint8_t pcomp, uint8_t dcomp) { struct gprs_sndcp_entity *sne = fs->sne; struct gprs_llc_lle *lle = sne->lle; struct sndcp_common_hdr *sch; struct sndcp_comp_hdr *scomph; struct sndcp_udata_hdr *suh; struct msgb *fmsg; unsigned int max_payload_len; unsigned int len; uint8_t *data; int rc, more; fmsg = msgb_alloc_headroom(fs->sne->lle->params.n201_u+256, 128, "SNDCP Frag"); if (!fmsg) { msgb_free(fs->msg); return -ENOMEM; } /* make sure lower layers route the fragment like the original */ msgb_tlli(fmsg) = msgb_tlli(fs->msg); msgb_bvci(fmsg) = msgb_bvci(fs->msg); msgb_nsei(fmsg) = msgb_nsei(fs->msg); /* prepend common SNDCP header */ sch = (struct sndcp_common_hdr *) msgb_put(fmsg, sizeof(*sch)); sch->nsapi = sne->nsapi; /* Set FIRST bit if we are the first fragment in a series */ if (fs->frag_nr == 0) sch->first = 1; sch->type = 1; /* append the compression header for first fragment */ if (sch->first) { scomph = (struct sndcp_comp_hdr *) msgb_put(fmsg, sizeof(*scomph)); scomph->pcomp = pcomp; scomph->dcomp = dcomp; } /* append the user-data header */ suh = (struct sndcp_udata_hdr *) msgb_put(fmsg, sizeof(*suh)); suh->npdu_low = sne->tx_npdu_nr & 0xff; suh->npdu_high = (sne->tx_npdu_nr >> 8) & 0xf; suh->seg_nr = fs->frag_nr % 0xf; /* calculate remaining length to be sent */ len = (fs->msg->data + fs->msg->len) - fs->next_byte; /* how much payload can we actually send via LLC? */ max_payload_len = lle->params.n201_u - (sizeof(*sch) + sizeof(*suh)); if (sch->first) max_payload_len -= sizeof(*scomph); /* check if we're exceeding the max */ if (len > max_payload_len) len = max_payload_len; /* copy the actual fragment data into our fmsg */ data = msgb_put(fmsg, len); memcpy(data, fs->next_byte, len); /* Increment fragment number and data pointer to next fragment */ fs->frag_nr++; fs->next_byte += len; /* determine if we have more fragemnts to send */ if ((fs->msg->data + fs->msg->len) <= fs->next_byte) more = 0; else more = 1; /* set the MORE bit of the SNDCP header accordingly */ sch->more = more; rc = gprs_llc_tx_ui(fmsg, lle->sapi, 0, fs->mmcontext, true); /* abort in case of error, do not advance frag_nr / next_byte */ if (rc < 0) { msgb_free(fs->msg); return rc; } if (!more) { /* we've sent all fragments */ msgb_free(fs->msg); memset(fs, 0, sizeof(*fs)); /* increment NPDU number for next frame */ sne->tx_npdu_nr = (sne->tx_npdu_nr + 1) % 0xfff; return 0; } /* default: more fragments to send */ return 1; } /* Request transmission of a SN-PDU over specified LLC Entity + SAPI */ int sndcp_unitdata_req(struct msgb *msg, struct gprs_llc_lle *lle, uint8_t nsapi, void *mmcontext) { struct gprs_sndcp_entity *sne; struct sndcp_common_hdr *sch; struct sndcp_comp_hdr *scomph; struct sndcp_udata_hdr *suh; struct sndcp_frag_state fs; uint8_t pcomp = 0; uint8_t dcomp = 0; int rc; /* Identifiers from UP: (TLLI, SAPI) + (BVCI, NSEI) */ /* Compress packet */ #if DEBUG_IP_PACKETS == 1 DEBUGP(DSNDCP, " \n"); DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n"); DEBUGP(DSNDCP, "===================================================\n"); debug_ip_packet(msg->data, msg->len, 0, "sndcp_initdata_req()"); #endif if (any_pcomp_or_dcomp_active(sgsn)) { /* Apply header compression */ rc = gprs_sndcp_pcomp_compress(msg->data, msg->len, &pcomp, lle->llme->comp.proto, nsapi); if (rc < 0) { LOGP(DSNDCP, LOGL_ERROR, "TCP/IP Header compression failed!\n"); return -EIO; } /* Fixup pointer locations and sizes in message buffer to match * the new, compressed buffer size */ msgb_get(msg, msg->len); msgb_put(msg, rc); /* Apply data compression */ rc = gprs_sndcp_dcomp_compress(msg->data, msg->len, &dcomp, lle->llme->comp.data, nsapi); if (rc < 0) { LOGP(DSNDCP, LOGL_ERROR, "Data compression failed!\n"); return -EIO; } /* Fixup pointer locations and sizes in message buffer to match * the new, compressed buffer size */ msgb_get(msg, msg->len); msgb_put(msg, rc); } #if DEBUG_IP_PACKETS == 1 DEBUGP(DSNDCP, "===================================================\n"); DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n"); DEBUGP(DSNDCP, " \n"); #endif sne = gprs_sndcp_entity_by_lle(lle, nsapi); if (!sne) { LOGP(DSNDCP, LOGL_ERROR, "Cannot find SNDCP Entity\n"); msgb_free(msg); return -EIO; } /* Check if we need to fragment this N-PDU into multiple SN-PDUs */ if (msg->len > lle->params.n201_u - (sizeof(*sch) + sizeof(*suh) + sizeof(*scomph))) { /* initialize the fragmenter state */ fs.msg = msg; fs.frag_nr = 0; fs.next_byte = msg->data; fs.sne = sne; fs.mmcontext = mmcontext; /* call function to generate and send fragments until all * of the N-PDU has been sent */ while (1) { int rc = sndcp_send_ud_frag(&fs,pcomp,dcomp); if (rc == 0) return 0; if (rc < 0) return rc; } /* not reached */ return 0; } /* this is the non-fragmenting case where we only build 1 SN-PDU */ /* prepend the user-data header */ suh = (struct sndcp_udata_hdr *) msgb_push(msg, sizeof(*suh)); suh->npdu_low = sne->tx_npdu_nr & 0xff; suh->npdu_high = (sne->tx_npdu_nr >> 8) & 0xf; suh->seg_nr = 0; sne->tx_npdu_nr = (sne->tx_npdu_nr + 1) % 0xfff; scomph = (struct sndcp_comp_hdr *) msgb_push(msg, sizeof(*scomph)); scomph->pcomp = pcomp; scomph->dcomp = dcomp; /* prepend common SNDCP header */ sch = (struct sndcp_common_hdr *) msgb_push(msg, sizeof(*sch)); sch->first = 1; sch->type = 1; sch->nsapi = nsapi; return gprs_llc_tx_ui(msg, lle->sapi, 0, mmcontext, true); } /* Section 5.1.2.17 LL-UNITDATA.ind */ int sndcp_llunitdata_ind(struct msgb *msg, struct gprs_llc_lle *lle, uint8_t *hdr, uint16_t len) { struct gprs_sndcp_entity *sne; struct sndcp_common_hdr *sch = (struct sndcp_common_hdr *)hdr; struct sndcp_comp_hdr *scomph = NULL; struct sndcp_udata_hdr *suh; uint8_t *npdu; uint16_t npdu_num __attribute__((unused)); int npdu_len; int rc; uint8_t *expnd = NULL; sch = (struct sndcp_common_hdr *) hdr; if (sch->first) { scomph = (struct sndcp_comp_hdr *) (hdr + 1); suh = (struct sndcp_udata_hdr *) (hdr + 1 + sizeof(struct sndcp_common_hdr)); } else suh = (struct sndcp_udata_hdr *) (hdr + sizeof(struct sndcp_common_hdr)); if (sch->type == 0) { LOGP(DSNDCP, LOGL_ERROR, "SN-DATA PDU at unitdata_ind() function\n"); return -EINVAL; } if (len < sizeof(*sch) + sizeof(*suh)) { LOGP(DSNDCP, LOGL_ERROR, "SN-UNITDATA PDU too short (%u)\n", len); return -EIO; } sne = gprs_sndcp_entity_by_lle(lle, sch->nsapi); if (!sne) { LOGP(DSNDCP, LOGL_ERROR, "Message for non-existing SNDCP Entity " "(lle=%p, TLLI=%08x, SAPI=%u, NSAPI=%u)\n", lle, lle->llme->tlli, lle->sapi, sch->nsapi); return -EIO; } /* FIXME: move this RA_ID up to the LLME or even higher */ bssgp_parse_cell_id(&sne->ra_id, msgb_bcid(msg)); if (scomph) { sne->defrag.pcomp = scomph->pcomp; sne->defrag.dcomp = scomph->dcomp; sne->defrag.proto = lle->llme->comp.proto; sne->defrag.data = lle->llme->comp.data; } /* any non-first segment is by definition something to defragment * as is any segment that tells us there are more segments */ if (!sch->first || sch->more) return defrag_input(sne, msg, hdr, len); npdu_num = (suh->npdu_high << 8) | suh->npdu_low; npdu = (uint8_t *)suh + sizeof(*suh); npdu_len = (msg->data + msg->len) - npdu - 3; /* -3 'removes' the FCS */ if (npdu_len <= 0) { LOGP(DSNDCP, LOGL_ERROR, "Short SNDCP N-PDU: %d\n", npdu_len); return -EIO; } /* actually send the N-PDU to the SGSN core code, which then * hands it off to the correct GTP tunnel + GGSN via gtp_data_req() */ /* Decompress packet */ #if DEBUG_IP_PACKETS == 1 DEBUGP(DSNDCP, " \n"); DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n"); DEBUGP(DSNDCP, "===================================================\n"); #endif if (any_pcomp_or_dcomp_active(sgsn)) { expnd = talloc_zero_size(msg, npdu_len * MAX_DATADECOMPR_FAC + MAX_HDRDECOMPR_INCR); memcpy(expnd, npdu, npdu_len); /* Apply data decompression */ rc = gprs_sndcp_dcomp_expand(expnd, npdu_len, sne->defrag.dcomp, sne->defrag.data); if (rc < 0) { LOGP(DSNDCP, LOGL_ERROR, "Data decompression failed!\n"); talloc_free(expnd); return -EIO; } /* Apply header decompression */ rc = gprs_sndcp_pcomp_expand(expnd, rc, sne->defrag.pcomp, sne->defrag.proto); if (rc < 0) { LOGP(DSNDCP, LOGL_ERROR, "TCP/IP Header decompression failed!\n"); talloc_free(expnd); return -EIO; } /* Modify npu length, expnd is handed directly handed * over to gsn_rx_sndcp_ud_ind(), see below */ npdu_len = rc; } else expnd = npdu; #if DEBUG_IP_PACKETS == 1 debug_ip_packet(expnd, npdu_len, 1, "sndcp_llunitdata_ind()"); DEBUGP(DSNDCP, "===================================================\n"); DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n"); DEBUGP(DSNDCP, " \n"); #endif /* Hand off packet to gtp */ rc = sgsn_rx_sndcp_ud_ind(&sne->ra_id, lle->llme->tlli, sne->nsapi, msg, npdu_len, expnd); if (any_pcomp_or_dcomp_active(sgsn)) talloc_free(expnd); return rc; } #if 0 /* Section 5.1.2.1 LL-RESET.ind */ static int sndcp_ll_reset_ind(struct gprs_sndcp_entity *se) { /* treat all outstanding SNDCP-LLC request type primitives as not sent */ /* reset all SNDCP XID parameters to default values */ LOGP(DSNDCP, LOGL_NOTICE, "not implemented.\n"); return 0; } static int sndcp_ll_status_ind() { /* inform the SM sub-layer by means of SNSM-STATUS.req */ LOGP(DSNDCP, LOGL_NOTICE, "not implemented.\n"); return 0; } static struct sndcp_state_list {{ uint32_t states; unsigned int type; int (*rout)(struct gprs_sndcp_entity *se, struct msgb *msg); } sndcp_state_list[] = { { ALL_STATES, LL_RESET_IND, sndcp_ll_reset_ind }, { ALL_STATES, LL_ESTABLISH_IND, sndcp_ll_est_ind }, { SBIT(SNDCP_S_EST_RQD), LL_ESTABLISH_RESP, sndcp_ll_est_ind }, { SBIT(SNDCP_S_EST_RQD), LL_ESTABLISH_CONF, sndcp_ll_est_conf }, { SBIT(SNDCP_S_ }; static int sndcp_rx_llc_prim() { case LL_ESTABLISH_REQ: case LL_RELEASE_REQ: case LL_XID_REQ: case LL_DATA_REQ: LL_UNITDATA_REQ, /* TLLI, SN-PDU, Ref, QoS, Radio Prio, Ciph */ switch (prim) { case LL_RESET_IND: case LL_ESTABLISH_IND: case LL_ESTABLISH_RESP: case LL_ESTABLISH_CONF: case LL_RELEASE_IND: case LL_RELEASE_CONF: case LL_XID_IND: case LL_XID_RESP: case LL_XID_CONF: case LL_DATA_IND: case LL_DATA_CONF: case LL_UNITDATA_IND: case LL_STATUS_IND: } } #endif /* Generate SNDCP-XID message */ static int gprs_llc_gen_sndcp_xid(uint8_t *bytes, int bytes_len, uint8_t nsapi) { int entity = 0; LLIST_HEAD(comp_fields); struct gprs_sndcp_pcomp_rfc1144_params rfc1144_params; struct gprs_sndcp_comp_field rfc1144_comp_field; struct gprs_sndcp_dcomp_v42bis_params v42bis_params; struct gprs_sndcp_comp_field v42bis_comp_field; memset(&rfc1144_comp_field, 0, sizeof(struct gprs_sndcp_comp_field)); memset(&v42bis_comp_field, 0, sizeof(struct gprs_sndcp_comp_field)); /* Setup rfc1144 */ if (sgsn->cfg.pcomp_rfc1144.active) { rfc1144_params.nsapi[0] = nsapi; rfc1144_params.nsapi_len = 1; rfc1144_params.s01 = sgsn->cfg.pcomp_rfc1144.s01; rfc1144_comp_field.p = 1; rfc1144_comp_field.entity = entity; rfc1144_comp_field.algo = RFC_1144; rfc1144_comp_field.comp[RFC1144_PCOMP1] = 1; rfc1144_comp_field.comp[RFC1144_PCOMP2] = 2; rfc1144_comp_field.comp_len = RFC1144_PCOMP_NUM; rfc1144_comp_field.rfc1144_params = &rfc1144_params; entity++; llist_add(&rfc1144_comp_field.list, &comp_fields); } /* Setup V.42bis */ if (sgsn->cfg.dcomp_v42bis.active) { v42bis_params.nsapi[0] = nsapi; v42bis_params.nsapi_len = 1; v42bis_params.p0 = sgsn->cfg.dcomp_v42bis.p0; v42bis_params.p1 = sgsn->cfg.dcomp_v42bis.p1; v42bis_params.p2 = sgsn->cfg.dcomp_v42bis.p2; v42bis_comp_field.p = 1; v42bis_comp_field.entity = entity; v42bis_comp_field.algo = V42BIS; v42bis_comp_field.comp[V42BIS_DCOMP1] = 1; v42bis_comp_field.comp_len = V42BIS_DCOMP_NUM; v42bis_comp_field.v42bis_params = &v42bis_params; entity++; llist_add(&v42bis_comp_field.list, &comp_fields); } /* Do not attempt to compile anything if there is no data in the list */ if (llist_empty(&comp_fields)) return 0; /* Compile bytestream */ return gprs_sndcp_compile_xid(bytes, bytes_len, &comp_fields, DEFAULT_SNDCP_VERSION); } /* Set of SNDCP-XID bnegotiation (See also: TS 144 065, * Section 6.8 XID parameter negotiation) */ int sndcp_sn_xid_req(struct gprs_llc_lle *lle, uint8_t nsapi) { /* Note: The specification requires the SNDCP-User to set of an * SNDCP xid request. See also 3GPP TS 44.065, 6.8 XID parameter * negotiation, Figure 11: SNDCP XID negotiation procedure. In * our case the SNDCP-User is sgsn_libgtp.c, which calls * sndcp_sn_xid_req directly. */ uint8_t l3params[1024]; int xid_len; struct gprs_llc_xid_field xid_field_request; /* Wipe off all compression entities and their states to * get rid of possible leftovers from a previous session */ gprs_sndcp_comp_free(lle->llme->comp.proto); gprs_sndcp_comp_free(lle->llme->comp.data); lle->llme->comp.proto = gprs_sndcp_comp_alloc(lle->llme); lle->llme->comp.data = gprs_sndcp_comp_alloc(lle->llme); talloc_free(lle->llme->xid); lle->llme->xid = NULL; /* Generate compression parameter bytestream */ xid_len = gprs_llc_gen_sndcp_xid(l3params, sizeof(l3params), nsapi); /* Send XID with the SNDCP-XID bytetsream included */ if (xid_len > 0) { xid_field_request.type = GPRS_LLC_XID_T_L3_PAR; xid_field_request.data = l3params; xid_field_request.data_len = xid_len; return gprs_ll_xid_req(lle, &xid_field_request); } /* When bytestream can not be generated, proceed without SNDCP-XID */ return gprs_ll_xid_req(lle, NULL); } /* Handle header compression entites */ static int handle_pcomp_entities(struct gprs_sndcp_comp_field *comp_field, struct gprs_llc_lle *lle) { /* Note: This functions also transforms the comp_field into its * echo form (strips comp values, resets propose bit etc...) * the processed comp_fields can then be sent back as XID- * Response without further modification. */ /* Delete propose bit */ comp_field->p = 0; /* Process proposed parameters */ switch (comp_field->algo) { case RFC_1144: if (sgsn->cfg.pcomp_rfc1144.passive && comp_field->rfc1144_params->nsapi_len > 0) { DEBUGP(DSNDCP, "Accepting RFC1144 header compression...\n"); gprs_sndcp_comp_add(lle->llme, lle->llme->comp.proto, comp_field); } else { DEBUGP(DSNDCP, "Rejecting RFC1144 header compression...\n"); gprs_sndcp_comp_delete(lle->llme->comp.proto, comp_field->entity); comp_field->rfc1144_params->nsapi_len = 0; } break; case RFC_2507: /* RFC 2507 is not yet supported, * so we set applicable nsapis to zero */ DEBUGP(DSNDCP, "Rejecting RFC2507 header compression...\n"); comp_field->rfc2507_params->nsapi_len = 0; gprs_sndcp_comp_delete(lle->llme->comp.proto, comp_field->entity); break; case ROHC: /* ROHC is not yet supported, * so we set applicable nsapis to zero */ DEBUGP(DSNDCP, "Rejecting ROHC header compression...\n"); comp_field->rohc_params->nsapi_len = 0; gprs_sndcp_comp_delete(lle->llme->comp.proto, comp_field->entity); break; } return 0; } /* Hanle data compression entites */ static int handle_dcomp_entities(struct gprs_sndcp_comp_field *comp_field, struct gprs_llc_lle *lle) { /* See note in handle_pcomp_entities() */ /* Delete propose bit */ comp_field->p = 0; /* Process proposed parameters */ switch (comp_field->algo) { case V42BIS: if (sgsn->cfg.dcomp_v42bis.passive && comp_field->v42bis_params->nsapi_len > 0) { DEBUGP(DSNDCP, "Accepting V.42bis data compression...\n"); gprs_sndcp_comp_add(lle->llme, lle->llme->comp.data, comp_field); } else { LOGP(DSNDCP, LOGL_DEBUG, "Rejecting V.42bis data compression...\n"); gprs_sndcp_comp_delete(lle->llme->comp.data, comp_field->entity); comp_field->v42bis_params->nsapi_len = 0; } break; case V44: /* V44 is not yet supported, * so we set applicable nsapis to zero */ DEBUGP(DSNDCP, "Rejecting V.44 data compression...\n"); comp_field->v44_params->nsapi_len = 0; gprs_sndcp_comp_delete(lle->llme->comp.data, comp_field->entity); break; } return 0; } /* Process SNDCP-XID indication * (See also: TS 144 065, Section 6.8 XID parameter negotiation) */ int sndcp_sn_xid_ind(struct gprs_llc_xid_field *xid_field_indication, struct gprs_llc_xid_field *xid_field_response, struct gprs_llc_lle *lle) { /* Note: This function computes the SNDCP-XID response that is sent * back to the ms when a ms originated XID is received. The * Input XID fields are directly processed and the result is directly * handed back. */ int rc; int compclass; int version; struct llist_head *comp_fields; struct gprs_sndcp_comp_field *comp_field; OSMO_ASSERT(xid_field_indication); OSMO_ASSERT(xid_field_response); OSMO_ASSERT(lle); /* Parse SNDCP-CID XID-Field */ comp_fields = gprs_sndcp_parse_xid(&version, lle->llme, xid_field_indication->data, xid_field_indication->data_len, NULL); if (!comp_fields) return -EINVAL; /* Handle compression entites */ DEBUGP(DSNDCP, "SNDCP-XID-IND (ms):\n"); gprs_sndcp_dump_comp_fields(comp_fields, LOGL_DEBUG); llist_for_each_entry(comp_field, comp_fields, list) { compclass = gprs_sndcp_get_compression_class(comp_field); if (compclass == SNDCP_XID_PROTOCOL_COMPRESSION) rc = handle_pcomp_entities(comp_field, lle); else if (compclass == SNDCP_XID_DATA_COMPRESSION) rc = handle_dcomp_entities(comp_field, lle); else { gprs_sndcp_comp_delete(lle->llme->comp.proto, comp_field->entity); gprs_sndcp_comp_delete(lle->llme->comp.data, comp_field->entity); rc = 0; } if (rc < 0) { talloc_free(comp_fields); return -EINVAL; } } DEBUGP(DSNDCP, "SNDCP-XID-RES (sgsn):\n"); gprs_sndcp_dump_comp_fields(comp_fields, LOGL_DEBUG); /* Reserve some memory to store the modified SNDCP-XID bytes */ xid_field_response->data = talloc_zero_size(lle->llme, xid_field_indication->data_len); /* Set Type flag for response */ xid_field_response->type = GPRS_LLC_XID_T_L3_PAR; /* Compile modified SNDCP-XID bytes */ rc = gprs_sndcp_compile_xid(xid_field_response->data, xid_field_indication->data_len, comp_fields, 0); if (rc > 0) xid_field_response->data_len = rc; else { talloc_free(xid_field_response->data); xid_field_response->data = NULL; xid_field_response->data_len = 0; return -EINVAL; } talloc_free(comp_fields); return 0; } /* Process SNDCP-XID indication * (See also: TS 144 065, Section 6.8 XID parameter negotiation) */ int sndcp_sn_xid_conf(struct gprs_llc_xid_field *xid_field_conf, struct gprs_llc_xid_field *xid_field_request, struct gprs_llc_lle *lle) { /* Note: This function handles an incomming SNDCP-XID confirmiation. * Since the confirmation fields may lack important parameters we * will reconstruct these missing fields using the original request * we have sent. After that we will create (or delete) the * compression entites */ struct llist_head *comp_fields_req; struct llist_head *comp_fields_conf; struct gprs_sndcp_comp_field *comp_field; int rc; int compclass; /* We need both, the confirmation that is sent back by the ms, * and the original request we have sent. If one of this is missing * we can not process the confirmation, the caller must check if * request and confirmation fields are available. */ OSMO_ASSERT(xid_field_conf); OSMO_ASSERT(xid_field_request); /* Parse SNDCP-CID XID-Field */ comp_fields_req = gprs_sndcp_parse_xid(NULL, lle->llme, xid_field_request->data, xid_field_request->data_len, NULL); if (!comp_fields_req) return -EINVAL; DEBUGP(DSNDCP, "SNDCP-XID-REQ (sgsn):\n"); gprs_sndcp_dump_comp_fields(comp_fields_req, LOGL_DEBUG); /* Parse SNDCP-CID XID-Field */ comp_fields_conf = gprs_sndcp_parse_xid(NULL, lle->llme, xid_field_conf->data, xid_field_conf->data_len, comp_fields_req); if (!comp_fields_conf) return -EINVAL; DEBUGP(DSNDCP, "SNDCP-XID-CONF (ms):\n"); gprs_sndcp_dump_comp_fields(comp_fields_conf, LOGL_DEBUG); /* Handle compression entites */ llist_for_each_entry(comp_field, comp_fields_conf, list) { compclass = gprs_sndcp_get_compression_class(comp_field); if (compclass == SNDCP_XID_PROTOCOL_COMPRESSION) rc = handle_pcomp_entities(comp_field, lle); else if (compclass == SNDCP_XID_DATA_COMPRESSION) rc = handle_dcomp_entities(comp_field, lle); else { gprs_sndcp_comp_delete(lle->llme->comp.proto, comp_field->entity); gprs_sndcp_comp_delete(lle->llme->comp.data, comp_field->entity); rc = 0; } if (rc < 0) { talloc_free(comp_fields_req); talloc_free(comp_fields_conf); return -EINVAL; } } talloc_free(comp_fields_req); talloc_free(comp_fields_conf); return 0; } osmo-sgsn-1.3.0/src/gprs/gprs_sndcp_comp.c000066400000000000000000000231361327264017000205350ustar00rootroot00000000000000/* GPRS SNDCP header compression entity management tools */ /* (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 #include #include #include #include #include #include /* Create a new compression entity from a XID-Field */ static struct gprs_sndcp_comp *gprs_sndcp_comp_create(const void *ctx, const struct gprs_sndcp_comp_field *comp_field) { struct gprs_sndcp_comp *comp_entity; comp_entity = talloc_zero(ctx, struct gprs_sndcp_comp); /* Copy relevant information from the SNDCP-XID field */ comp_entity->entity = comp_field->entity; comp_entity->comp_len = comp_field->comp_len; memcpy(comp_entity->comp, comp_field->comp, sizeof(comp_entity->comp)); if (comp_field->rfc1144_params) { comp_entity->nsapi_len = comp_field->rfc1144_params->nsapi_len; memcpy(comp_entity->nsapi, comp_field->rfc1144_params->nsapi, sizeof(comp_entity->nsapi)); } else if (comp_field->rfc2507_params) { comp_entity->nsapi_len = comp_field->rfc2507_params->nsapi_len; memcpy(comp_entity->nsapi, comp_field->rfc2507_params->nsapi, sizeof(comp_entity->nsapi)); } else if (comp_field->rohc_params) { comp_entity->nsapi_len = comp_field->rohc_params->nsapi_len; memcpy(comp_entity->nsapi, comp_field->rohc_params->nsapi, sizeof(comp_entity->nsapi)); } else if (comp_field->v42bis_params) { comp_entity->nsapi_len = comp_field->v42bis_params->nsapi_len; memcpy(comp_entity->nsapi, comp_field->v42bis_params->nsapi, sizeof(comp_entity->nsapi)); } else if (comp_field->v44_params) { comp_entity->nsapi_len = comp_field->v44_params->nsapi_len; memcpy(comp_entity->nsapi, comp_field->v44_params->nsapi, sizeof(comp_entity->nsapi)); } else { /* The caller is expected to check carefully if the all * data fields required for compression entity creation * are present. Otherwise we blow an assertion here */ OSMO_ASSERT(false); } comp_entity->algo = comp_field->algo; /* Check if an NSAPI is selected, if not, it does not make sense * to create the compression entity, since the caller should * have checked the presence of the NSAPI, we blow an assertion * in case of missing NSAPIs */ OSMO_ASSERT(comp_entity->nsapi_len > 0); /* Determine of which class our compression entity will be * (Protocol or Data compresson ?) */ comp_entity->compclass = gprs_sndcp_get_compression_class(comp_field); OSMO_ASSERT(comp_entity->compclass != -1); /* Create an algorithm specific compression context */ if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) { if (gprs_sndcp_pcomp_init(ctx, comp_entity, comp_field) != 0) { talloc_free(comp_entity); comp_entity = NULL; } } else { if (gprs_sndcp_dcomp_init(ctx, comp_entity, comp_field) != 0) { talloc_free(comp_entity); comp_entity = NULL; } } /* Bail on failure */ if (comp_entity == NULL) { LOGP(DSNDCP, LOGL_ERROR, "Compression entity creation failed!\n"); return NULL; } /* Display info message */ if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) { LOGP(DSNDCP, LOGL_INFO, "New header compression entity (%d) created.\n", comp_entity->entity); } else { LOGP(DSNDCP, LOGL_INFO, "New data compression entity (%d) created.\n", comp_entity->entity); } return comp_entity; } /* Allocate a compression enitiy list */ struct llist_head *gprs_sndcp_comp_alloc(const void *ctx) { struct llist_head *lh; lh = talloc_zero(ctx, struct llist_head); INIT_LLIST_HEAD(lh); return lh; } /* Free a compression entitiy list */ void gprs_sndcp_comp_free(struct llist_head *comp_entities) { struct gprs_sndcp_comp *comp_entity; /* We expect the caller to take care of allocating a * compression entity list properly. Attempting to * free a non existing list clearly points out * a malfunction. */ OSMO_ASSERT(comp_entities); llist_for_each_entry(comp_entity, comp_entities, list) { /* Free compression entity */ if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) { LOGP(DSNDCP, LOGL_INFO, "Deleting header compression entity %d ...\n", comp_entity->entity); gprs_sndcp_pcomp_term(comp_entity); } else { LOGP(DSNDCP, LOGL_INFO, "Deleting data compression entity %d ...\n", comp_entity->entity); gprs_sndcp_dcomp_term(comp_entity); } } talloc_free(comp_entities); } /* Delete a compression entity */ void gprs_sndcp_comp_delete(struct llist_head *comp_entities, unsigned int entity) { struct gprs_sndcp_comp *comp_entity; struct gprs_sndcp_comp *comp_entity_to_delete = NULL; OSMO_ASSERT(comp_entities); llist_for_each_entry(comp_entity, comp_entities, list) { if (comp_entity->entity == entity) { comp_entity_to_delete = comp_entity; break; } } if (!comp_entity_to_delete) return; if (comp_entity_to_delete->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) { LOGP(DSNDCP, LOGL_INFO, "Deleting header compression entity %d ...\n", comp_entity_to_delete->entity); gprs_sndcp_pcomp_term(comp_entity_to_delete); } else { LOGP(DSNDCP, LOGL_INFO, "Deleting data compression entity %d ...\n", comp_entity_to_delete->entity); } /* Delete compression entity */ llist_del(&comp_entity_to_delete->list); talloc_free(comp_entity_to_delete); } /* Create and Add a new compression entity * (returns a pointer to the compression entity that has just been created) */ struct gprs_sndcp_comp *gprs_sndcp_comp_add(const void *ctx, struct llist_head *comp_entities, const struct gprs_sndcp_comp_field *comp_field) { struct gprs_sndcp_comp *comp_entity; OSMO_ASSERT(comp_entities); OSMO_ASSERT(comp_field); /* Just to be sure, if the entity is already in * the list it will be deleted now */ gprs_sndcp_comp_delete(comp_entities, comp_field->entity); /* Create and add a new entity to the list */ comp_entity = gprs_sndcp_comp_create(ctx, comp_field); if (!comp_entity) return NULL; llist_add(&comp_entity->list, comp_entities); return comp_entity; } /* Find which compression entity handles the specified pcomp/dcomp */ struct gprs_sndcp_comp *gprs_sndcp_comp_by_comp(const struct llist_head *comp_entities, uint8_t comp) { struct gprs_sndcp_comp *comp_entity; int i; OSMO_ASSERT(comp_entities); llist_for_each_entry(comp_entity, comp_entities, list) { for (i = 0; i < comp_entity->comp_len; i++) { if (comp_entity->comp[i] == comp) return comp_entity; } } LOGP(DSNDCP, LOGL_ERROR, "Could not find a matching compression entity for given pcomp/dcomp value %d.\n", comp); return NULL; } /* Find which compression entity handles the specified nsapi */ struct gprs_sndcp_comp *gprs_sndcp_comp_by_nsapi(const struct llist_head *comp_entities, uint8_t nsapi) { struct gprs_sndcp_comp *comp_entity; int i; OSMO_ASSERT(comp_entities); llist_for_each_entry(comp_entity, comp_entities, list) { for (i = 0; i < comp_entity->nsapi_len; i++) { if (comp_entity->nsapi[i] == nsapi) return comp_entity; } } return NULL; } /* Find a comp_index for a given pcomp/dcomp value */ uint8_t gprs_sndcp_comp_get_idx(const struct gprs_sndcp_comp *comp_entity, uint8_t comp) { /* Note: This function returns a normalized version of the comp value, * which matches up with the position of the comp field. Since comp=0 * is reserved for "no compression", the index value starts counting * at one. The return value is the PCOMPn/DCOMPn value one can find * in the Specification (see e.g. 3GPP TS 44.065, 6.5.3.2, Table 7) */ int i; OSMO_ASSERT(comp_entity); /* A pcomp/dcomp value of zero is reserved for "no comproession", * So we just bail and return zero in this case */ if (comp == 0) return 0; /* Look in the pcomp/dcomp list for the index */ for (i = 0; i < comp_entity->comp_len; i++) { if (comp_entity->comp[i] == comp) return i + 1; } LOGP(DSNDCP, LOGL_ERROR, "Could not find a matching comp_index for given pcomp/dcomp value %d\n", comp); return 0; } /* Find a pcomp/dcomp value for a given comp_index */ uint8_t gprs_sndcp_comp_get_comp(const struct gprs_sndcp_comp *comp_entity, uint8_t comp_index) { OSMO_ASSERT(comp_entity); /* A comp_index of zero translates to zero right away. */ if (comp_index == 0) return 0; if (comp_index > comp_entity->comp_len) { LOGP(DSNDCP, LOGL_ERROR, "Could not find a matching pcomp/dcomp value for given comp_index value %d.\n", comp_index); return 0; } /* Look in the pcomp/dcomp list for the comp_index, see * note in gprs_sndcp_comp_get_idx() */ return comp_entity->comp[comp_index - 1]; } osmo-sgsn-1.3.0/src/gprs/gprs_sndcp_dcomp.c000066400000000000000000000242251327264017000207010ustar00rootroot00000000000000/* GPRS SNDCP data compression handler */ /* (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 #include #include #include #include #include #include #include #include #include #include #include /* A struct to capture the output data of compressor and decompressor */ struct v42bis_output_buffer { uint8_t *buf; uint8_t *buf_pointer; int len; }; /* Handler to capture the output data from the compressor */ void tx_v42bis_frame_handler(void *user_data, const uint8_t *pkt, int len) { struct v42bis_output_buffer *output_buffer = (struct v42bis_output_buffer *)user_data; memcpy(output_buffer->buf_pointer, pkt, len); output_buffer->buf_pointer += len; output_buffer->len += len; return; } /* Handler to capture the output data from the decompressor */ void rx_v42bis_data_handler(void *user_data, const uint8_t *buf, int len) { struct v42bis_output_buffer *output_buffer = (struct v42bis_output_buffer *)user_data; memcpy(output_buffer->buf_pointer, buf, len); output_buffer->buf_pointer += len; output_buffer->len += len; return; } /* Initalize data compression */ int gprs_sndcp_dcomp_init(const void *ctx, struct gprs_sndcp_comp *comp_entity, const struct gprs_sndcp_comp_field *comp_field) { /* Note: This function is automatically called from * gprs_sndcp_comp.c when a new data compression * entity is created by gprs_sndcp.c */ OSMO_ASSERT(comp_entity); OSMO_ASSERT(comp_field); if (comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION && comp_entity->algo == V42BIS) { OSMO_ASSERT(comp_field->v42bis_params); comp_entity->state = v42bis_init(ctx, NULL, comp_field->v42bis_params->p0, comp_field->v42bis_params->p1, comp_field->v42bis_params->p2, &tx_v42bis_frame_handler, NULL, V42BIS_MAX_OUTPUT_LENGTH, &rx_v42bis_data_handler, NULL, V42BIS_MAX_OUTPUT_LENGTH); LOGP(DSNDCP, LOGL_INFO, "V.42bis data compression initalized.\n"); return 0; } /* Just in case someone tries to initalize an unknown or unsupported * data compresson. Since everything is checked during the SNDCP * negotiation process, this should never happen! */ OSMO_ASSERT(false); } /* Terminate data compression */ void gprs_sndcp_dcomp_term(struct gprs_sndcp_comp *comp_entity) { /* Note: This function is automatically called from * gprs_sndcp_comp.c when a data compression * entity is deleted by gprs_sndcp.c */ OSMO_ASSERT(comp_entity); if (comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION && comp_entity->algo == V42BIS) { if (comp_entity->state) { v42bis_free((v42bis_state_t *) comp_entity->state); comp_entity->state = NULL; } LOGP(DSNDCP, LOGL_INFO, "V.42bis data compression terminated.\n"); return; } /* Just in case someone tries to terminate an unknown or unsupported * data compresson. Since everything is checked during the SNDCP * negotiation process, this should never happen! */ OSMO_ASSERT(false); } /* Perform a full reset of the V.42bis compression state */ static void v42bis_reset(v42bis_state_t *comp) { /* This function performs a complete reset of the V.42bis compression * state by reinitalizing the state withe the previously negotiated * parameters. */ int p0, p1, p2; p0 = comp->decompress.v42bis_parm_p0 | comp->compress.v42bis_parm_p0; p1 = comp->decompress.v42bis_parm_n2; p2 = comp->decompress.v42bis_parm_n7; DEBUGP(DSNDCP, "Resetting compression state: %p, p0=%d, p1=%d, p2=%d\n", comp, p0, p1, p2); v42bis_init(NULL, comp, p0, p1, p2, &tx_v42bis_frame_handler, NULL, V42BIS_MAX_OUTPUT_LENGTH, &rx_v42bis_data_handler, NULL, V42BIS_MAX_OUTPUT_LENGTH); } /* Compress a packet using V.42bis data compression */ static int v42bis_compress_unitdata(uint8_t *pcomp_index, uint8_t *data, unsigned int len, v42bis_state_t *comp) { /* Note: This implementation may only be used to compress SN_UNITDATA * packets, since it resets the compression state for each NPDU. */ uint8_t *data_o; int rc; int skip = 0; struct v42bis_output_buffer compressed_data; /* Don't bother with short packets */ if (len < MIN_COMPR_PAYLOAD) skip = 1; /* Skip if compression is not enabled for TX direction */ if (!comp->compress.v42bis_parm_p0) skip = 1; /* Skip compression */ if (skip) { *pcomp_index = 0; return len; } /* Reset V.42bis compression state */ v42bis_reset(comp); /* Run compressor */ data_o = talloc_zero_size(comp, len * MAX_DATADECOMPR_FAC); compressed_data.buf = data_o; compressed_data.buf_pointer = data_o; compressed_data.len = 0; comp->compress.user_data = (&compressed_data); rc = v42bis_compress(comp, data, len); if (rc < 0) { LOGP(DSNDCP, LOGL_ERROR, "Data compression failed, skipping...\n"); skip = 1; } rc = v42bis_compress_flush(comp); if (rc < 0) { LOGP(DSNDCP, LOGL_ERROR, "Data compression failed, skipping...\n"); skip = 1; } /* The compressor might yield negative compression gain, in * this case, we just decide to send the packat as normal, * uncompressed payload => skip compresssion */ if (compressed_data.len >= len) { LOGP(DSNDCP, LOGL_ERROR, "Data compression ineffective, skipping...\n"); skip = 1; } /* Skip compression */ if (skip) { *pcomp_index = 0; talloc_free(data_o); return len; } *pcomp_index = 1; memcpy(data, data_o, compressed_data.len); talloc_free(data_o); return compressed_data.len; } /* Expand a packet using V.42bis data compression */ static int v42bis_expand_unitdata(uint8_t *data, unsigned int len, uint8_t pcomp_index, v42bis_state_t *comp) { /* Note: This implementation may only be used to compress SN_UNITDATA * packets, since it resets the compression state for each NPDU. */ int rc; struct v42bis_output_buffer uncompressed_data; uint8_t *data_i; /* Skip when the packet is marked as uncompressed */ if (pcomp_index == 0) { return len; } /* Reset V.42bis compression state */ v42bis_reset(comp); /* Decompress packet */ data_i = talloc_zero_size(comp, len); memcpy(data_i, data, len); uncompressed_data.buf = data; uncompressed_data.buf_pointer = data; uncompressed_data.len = 0; comp->decompress.user_data = (&uncompressed_data); rc = v42bis_decompress(comp, data_i, len); talloc_free(data_i); if (rc < 0) return -EINVAL; rc = v42bis_decompress_flush(comp); if (rc < 0) return -EINVAL; return uncompressed_data.len; } /* Expand packet */ int gprs_sndcp_dcomp_expand(uint8_t *data, unsigned int len, uint8_t pcomp, const struct llist_head *comp_entities) { int rc; uint8_t pcomp_index = 0; struct gprs_sndcp_comp *comp_entity; OSMO_ASSERT(data); OSMO_ASSERT(comp_entities); LOGP(DSNDCP, LOGL_DEBUG, "Data compression entity list: comp_entities=%p\n", comp_entities); LOGP(DSNDCP, LOGL_DEBUG, "Data compression mode: dcomp=%d\n", pcomp); /* Skip on pcomp=0 */ if (pcomp == 0) { return len; } /* Find out which compression entity handles the data */ comp_entity = gprs_sndcp_comp_by_comp(comp_entities, pcomp); /* Skip compression if no suitable compression entity can be found */ if (!comp_entity) { return len; } /* Note: Only data compression entities may appear in * data compression context */ OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION); /* Note: Currently V42BIS is the only compression method we * support, so the only allowed algorithm is V42BIS */ OSMO_ASSERT(comp_entity->algo == V42BIS); /* Find pcomp_index */ pcomp_index = gprs_sndcp_comp_get_idx(comp_entity, pcomp); /* Run decompression algo */ rc = v42bis_expand_unitdata(data, len, pcomp_index, comp_entity->state); LOGP(DSNDCP, LOGL_DEBUG, "Data expansion done, old length=%d, new length=%d, entity=%p\n", len, rc, comp_entity); return rc; } /* Compress packet */ int gprs_sndcp_dcomp_compress(uint8_t *data, unsigned int len, uint8_t *pcomp, const struct llist_head *comp_entities, uint8_t nsapi) { int rc; uint8_t pcomp_index = 0; struct gprs_sndcp_comp *comp_entity; OSMO_ASSERT(data); OSMO_ASSERT(pcomp); OSMO_ASSERT(comp_entities); LOGP(DSNDCP, LOGL_DEBUG, "Data compression entity list: comp_entities=%p\n", comp_entities); /* Find out which compression entity handles the data */ comp_entity = gprs_sndcp_comp_by_nsapi(comp_entities, nsapi); /* Skip compression if no suitable compression entity can be found */ if (!comp_entity) { *pcomp = 0; return len; } /* Note: Only data compression entities may appear in * data compression context */ OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION); /* Note: Currently V42BIS is the only compression method we * support, so the only allowed algorithm is V42BIS */ OSMO_ASSERT(comp_entity->algo == V42BIS); /* Run compression algo */ rc = v42bis_compress_unitdata(&pcomp_index, data, len, comp_entity->state); /* Find pcomp value */ *pcomp = gprs_sndcp_comp_get_comp(comp_entity, pcomp_index); LOGP(DSNDCP, LOGL_DEBUG, "Data compression mode: dcomp=%d\n", *pcomp); LOGP(DSNDCP, LOGL_DEBUG, "Data compression done, old length=%d, new length=%d, entity=%p\n", len, rc, comp_entity); return rc; } osmo-sgsn-1.3.0/src/gprs/gprs_sndcp_pcomp.c000066400000000000000000000202111327264017000207040ustar00rootroot00000000000000/* GPRS SNDCP header compression handler */ /* (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 #include #include #include #include #include #include #include #include #include #include /* Initalize header compression */ int gprs_sndcp_pcomp_init(const void *ctx, struct gprs_sndcp_comp *comp_entity, const struct gprs_sndcp_comp_field *comp_field) { /* Note: This function is automatically called from * gprs_sndcp_comp.c when a new header compression * entity is created by gprs_sndcp.c */ OSMO_ASSERT(comp_entity); OSMO_ASSERT(comp_field); if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION && comp_entity->algo == RFC_1144) { OSMO_ASSERT(comp_field->rfc1144_params); comp_entity->state = slhc_init(ctx, comp_field->rfc1144_params->s01 + 1, comp_field->rfc1144_params->s01 + 1); LOGP(DSNDCP, LOGL_INFO, "RFC1144 header compression initalized.\n"); return 0; } /* Just in case someone tries to initalize an unknown or unsupported * header compresson. Since everything is checked during the SNDCP * negotiation process, this should never happen! */ OSMO_ASSERT(false); } /* Terminate header compression */ void gprs_sndcp_pcomp_term(struct gprs_sndcp_comp *comp_entity) { /* Note: This function is automatically called from * gprs_sndcp_comp.c when a header compression * entity is deleted by gprs_sndcp.c */ OSMO_ASSERT(comp_entity); if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION && comp_entity->algo == RFC_1144) { if (comp_entity->state) { slhc_free((struct slcompress *)comp_entity->state); comp_entity->state = NULL; } LOGP(DSNDCP, LOGL_INFO, "RFC1144 header compression terminated.\n"); return; } /* Just in case someone tries to terminate an unknown or unsupported * data compresson. Since everything is checked during the SNDCP * negotiation process, this should never happen! */ OSMO_ASSERT(false); } /* Compress a packet using Van Jacobson RFC1144 header compression */ static int rfc1144_compress(uint8_t *pcomp_index, uint8_t *data, unsigned int len, struct slcompress *comp) { uint8_t *comp_ptr; int compr_len; uint8_t *data_o; /* Create a working copy of the incoming data */ data_o = talloc_zero_size(comp, len); memcpy(data_o, data, len); /* Run compressor */ compr_len = slhc_compress(comp, data, len, data_o, &comp_ptr, 0); /* Generate pcomp_index */ if (data_o[0] & SL_TYPE_COMPRESSED_TCP) { *pcomp_index = 2; data_o[0] &= ~SL_TYPE_COMPRESSED_TCP; memcpy(data, data_o, compr_len); } else if ((data_o[0] & SL_TYPE_UNCOMPRESSED_TCP) == SL_TYPE_UNCOMPRESSED_TCP) { *pcomp_index = 1; data_o[0] &= 0x4F; memcpy(data, data_o, compr_len); } else *pcomp_index = 0; talloc_free(data_o); return compr_len; } /* Expand a packet using Van Jacobson RFC1144 header compression */ static int rfc1144_expand(uint8_t *data, unsigned int len, uint8_t pcomp_index, struct slcompress *comp) { int data_decompressed_len; int type; /* Note: this function should never be called with pcomp_index=0, * since this condition is already filtered * out by gprs_sndcp_pcomp_expand() */ /* Determine the data type by the PCOMP index */ switch (pcomp_index) { case 0: type = SL_TYPE_IP; break; case 1: type = SL_TYPE_UNCOMPRESSED_TCP; break; case 2: type = SL_TYPE_COMPRESSED_TCP; break; default: LOGP(DSNDCP, LOGL_ERROR, "rfc1144_expand() Invalid pcomp_index value (%d) detected, assuming no compression!\n", pcomp_index); type = SL_TYPE_IP; break; } /* Restore the original version nibble on * marked uncompressed packets */ if (type == SL_TYPE_UNCOMPRESSED_TCP) { /* Just in case the phone tags uncompressed tcp-data * (normally this is handled by pcomp so there is * no need for tagging the data) */ data[0] &= 0x4F; data_decompressed_len = slhc_remember(comp, data, len); return data_decompressed_len; } /* Uncompress compressed packets */ else if (type == SL_TYPE_COMPRESSED_TCP) { data_decompressed_len = slhc_uncompress(comp, data, len); return data_decompressed_len; } /* Regular or unknown packets will not be touched */ return len; } /* Expand packet header */ int gprs_sndcp_pcomp_expand(uint8_t *data, unsigned int len, uint8_t pcomp, const struct llist_head *comp_entities) { int rc; uint8_t pcomp_index = 0; struct gprs_sndcp_comp *comp_entity; OSMO_ASSERT(data); OSMO_ASSERT(comp_entities); LOGP(DSNDCP, LOGL_DEBUG, "Header compression entity list: comp_entities=%p\n", comp_entities); LOGP(DSNDCP, LOGL_DEBUG, "Header compression mode: pcomp=%d\n", pcomp); /* Skip on pcomp=0 */ if (pcomp == 0) { return len; } /* Find out which compression entity handles the data */ comp_entity = gprs_sndcp_comp_by_comp(comp_entities, pcomp); /* Skip compression if no suitable compression entity can be found */ if (!comp_entity) { return len; } /* Note: Only protocol compression entities may appear in * protocol compression context */ OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION); /* Note: Currently RFC1144 is the only compression method we * support, so the only allowed algorithm is RFC1144 */ OSMO_ASSERT(comp_entity->algo == RFC_1144); /* Find pcomp_index */ pcomp_index = gprs_sndcp_comp_get_idx(comp_entity, pcomp); /* Run decompression algo */ rc = rfc1144_expand(data, len, pcomp_index, comp_entity->state); slhc_i_status(comp_entity->state); slhc_o_status(comp_entity->state); LOGP(DSNDCP, LOGL_DEBUG, "Header expansion done, old length=%d, new length=%d, entity=%p\n", len, rc, comp_entity); return rc; } /* Compress packet header */ int gprs_sndcp_pcomp_compress(uint8_t *data, unsigned int len, uint8_t *pcomp, const struct llist_head *comp_entities, uint8_t nsapi) { int rc; uint8_t pcomp_index = 0; struct gprs_sndcp_comp *comp_entity; OSMO_ASSERT(data); OSMO_ASSERT(pcomp); OSMO_ASSERT(comp_entities); LOGP(DSNDCP, LOGL_DEBUG, "Header compression entity list: comp_entities=%p\n", comp_entities); /* Find out which compression entity handles the data */ comp_entity = gprs_sndcp_comp_by_nsapi(comp_entities, nsapi); /* Skip compression if no suitable compression entity can be found */ if (!comp_entity) { *pcomp = 0; return len; } /* Note: Only protocol compression entities may appear in * protocol compression context */ OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION); /* Note: Currently RFC1144 is the only compression method we * support, so the only allowed algorithm is RFC1144 */ OSMO_ASSERT(comp_entity->algo == RFC_1144); /* Run compression algo */ rc = rfc1144_compress(&pcomp_index, data, len, comp_entity->state); slhc_i_status(comp_entity->state); slhc_o_status(comp_entity->state); /* Find pcomp value */ *pcomp = gprs_sndcp_comp_get_comp(comp_entity, pcomp_index); LOGP(DSNDCP, LOGL_DEBUG, "Header compression mode: pcomp=%d\n", *pcomp); LOGP(DSNDCP, LOGL_DEBUG, "Header compression done, old length=%d, new length=%d, entity=%p\n", len, rc, comp_entity); return rc; } osmo-sgsn-1.3.0/src/gprs/gprs_sndcp_vty.c000066400000000000000000000037711327264017000204240ustar00rootroot00000000000000/* VTY interface for our GPRS SNDCP implementation */ /* (C) 2010 by Harald Welte * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 void vty_dump_sne(struct vty *vty, struct gprs_sndcp_entity *sne) { vty_out(vty, " TLLI %08x SAPI=%u NSAPI=%u:%s", sne->lle->llme->tlli, sne->lle->sapi, sne->nsapi, VTY_NEWLINE); vty_out(vty, " Defrag: npdu=%u highest_seg=%u seg_have=0x%08x tot_len=%u%s", sne->defrag.npdu, sne->defrag.highest_seg, sne->defrag.seg_have, sne->defrag.tot_len, VTY_NEWLINE); } DEFUN(show_sndcp, show_sndcp_cmd, "show sndcp", SHOW_STR "Display information about the SNDCP protocol") { struct gprs_sndcp_entity *sne; vty_out(vty, "State of SNDCP Entities%s", VTY_NEWLINE); llist_for_each_entry(sne, &gprs_sndcp_entities, list) vty_dump_sne(vty, sne); return CMD_SUCCESS; } int gprs_sndcp_vty_init(void) { install_element_ve(&show_sndcp_cmd); return 0; } osmo-sgsn-1.3.0/src/gprs/gprs_sndcp_xid.c000066400000000000000000001365731327264017000203750ustar00rootroot00000000000000/* GPRS SNDCP XID field encoding/decoding as per 3GPP TS 44.065 */ /* (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 #include #include #include #include #include #include /* When the propose bit in an SNDCP-XID compression field is set to zero, * the algorithm identifier is stripped. The algoritm parameters are specific * for each algorithms. The following struct is used to pass the information * about the referenced algorithm to the parser. */ struct entity_algo_table { unsigned int entity; /* see also: 6.5.1.1.3 and 6.6.1.1.3 */ unsigned int algo; /* see also: 6.5.1.1.4 and 6.6.1.1.4 */ unsigned int compclass; /* Can be either SNDCP_XID_DATA_COMPRESSION or SNDCP_XID_PROTOCOL_COMPRESSION */ }; /* FUNCTIONS RELATED TO SNDCP-XID ENCODING */ /* Encode applicable sapis (works the same in all three compression schemes) */ static int encode_pcomp_applicable_sapis(uint8_t *dst, const uint8_t *nsapis, uint8_t nsapis_len) { /* NOTE: Buffer *dst needs offer at 2 bytes * of space to store the generation results */ uint16_t blob; uint8_t nsapi; int i; /* Bail if number of possible nsapis exceeds valid range * (Only 11 nsapis possible for PDP-Contexts) */ OSMO_ASSERT(nsapis_len <= 11); /* Encode applicable SAPIs */ blob = 0; for (i = 0; i < nsapis_len; i++) { nsapi = nsapis[i]; /* Only NSAPI 5 to 15 are applicable for user traffic (PDP- * contexts). Only for these NSAPIs SNDCP-XID parameters * can apply. See also 3GPP TS 44.065, 5.1 Service primitives */ OSMO_ASSERT(nsapi >= 5 && nsapi <= 15); blob |= (1 << nsapi); } /* Store result */ *dst = (blob >> 8) & 0xFF; dst++; *dst = blob & 0xFF; return 2; } /* Encode rfc1144 parameter field * (see also: 3GPP TS 44.065, 6.5.2.1, Table 5) */ static int encode_pcomp_rfc1144_params(uint8_t *dst, unsigned int dst_maxlen, const struct gprs_sndcp_pcomp_rfc1144_params *params) { /* NOTE: Buffer *dst should offer at least 3 bytes * of space to store the generation results */ int dst_counter = 0; int rc; OSMO_ASSERT(dst_maxlen >= 3); /* Zero out buffer */ memset(dst, 0, dst_maxlen); /* Encode applicable SAPIs */ rc = encode_pcomp_applicable_sapis(dst, params->nsapi, params->nsapi_len); dst += rc; dst_counter += rc; /* Encode s01 (see also: 3GPP TS 44.065, 6.5.2.1, Table 5) */ OSMO_ASSERT(params->s01 >= 0); OSMO_ASSERT(params->s01 <= 255); *dst = params->s01; dst++; dst_counter++; /* Return generated length */ return dst_counter; } /* * Encode rfc2507 parameter field * (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */ static int encode_pcomp_rfc2507_params(uint8_t *dst, unsigned int dst_maxlen, const struct gprs_sndcp_pcomp_rfc2507_params *params) { /* NOTE: Buffer *dst should offer at least 3 bytes * of space to store the generation results */ int dst_counter = 0; int rc; OSMO_ASSERT(dst_maxlen >= 9); /* Zero out buffer */ memset(dst, 0, dst_maxlen); /* Encode applicable SAPIs */ rc = encode_pcomp_applicable_sapis(dst, params->nsapi, params->nsapi_len); dst += rc; dst_counter += rc; /* Encode F_MAX_PERIOD (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */ OSMO_ASSERT(params->f_max_period >= 1); OSMO_ASSERT(params->f_max_period <= 65535); *dst = (params->f_max_period >> 8) & 0xFF; dst++; dst_counter++; *dst = (params->f_max_period) & 0xFF; dst++; dst_counter++; /* Encode F_MAX_TIME (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */ OSMO_ASSERT(params->f_max_time >= 1); OSMO_ASSERT(params->f_max_time <= 255); *dst = params->f_max_time; dst++; dst_counter++; /* Encode MAX_HEADER (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */ OSMO_ASSERT(params->max_header >= 60); OSMO_ASSERT(params->max_header <= 255); *dst = params->max_header; dst++; dst_counter++; /* Encode TCP_SPACE (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */ OSMO_ASSERT(params->tcp_space >= 3); OSMO_ASSERT(params->tcp_space <= 255); *dst = params->tcp_space; dst++; dst_counter++; /* Encode NON_TCP_SPACE (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */ OSMO_ASSERT(params->non_tcp_space >= 3); OSMO_ASSERT(params->non_tcp_space <= 65535); *dst = (params->non_tcp_space >> 8) & 0xFF; dst++; dst_counter++; *dst = (params->non_tcp_space) & 0xFF; dst++; dst_counter++; /* Return generated length */ return dst_counter; } /* Encode ROHC parameter field * (see also: 3GPP TS 44.065, 6.5.4.1, Table 10) */ static int encode_pcomp_rohc_params(uint8_t *dst, unsigned int dst_maxlen, const struct gprs_sndcp_pcomp_rohc_params *params) { /* NOTE: Buffer *dst should offer at least 36 * (2 * 16 Profiles + 2 * 3 Parameter) bytes * of memory space to store generation results */ int i; int dst_counter = 0; int rc; OSMO_ASSERT(dst_maxlen >= 38); /* Bail if number of ROHC profiles exceeds limit * (ROHC supports only a maximum of 16 different profiles) */ OSMO_ASSERT(params->profile_len <= 16); /* Zero out buffer */ memset(dst, 0, dst_maxlen); /* Encode applicable SAPIs */ rc = encode_pcomp_applicable_sapis(dst, params->nsapi, params->nsapi_len); dst += rc; dst_counter += rc; /* Encode MAX_CID (see also: 3GPP TS 44.065, 6.5.4.1, Table 10) */ OSMO_ASSERT(params->max_cid >= 0); OSMO_ASSERT(params->max_cid <= 16383); *dst = (params->max_cid >> 8) & 0xFF; dst++; *dst = params->max_cid & 0xFF; dst++; dst_counter += 2; /* Encode MAX_HEADER (see also: 3GPP TS 44.065, 6.5.4.1, Table 10) */ OSMO_ASSERT(params->max_header >= 60); OSMO_ASSERT(params->max_header <= 255); *dst = (params->max_header >> 8) & 0xFF; dst++; *dst = params->max_header & 0xFF; dst++; dst_counter += 2; /* Encode ROHC Profiles (see also: 3GPP TS 44.065, 6.5.4.1, Table 10) */ for (i = 0; i < params->profile_len; i++) { *dst = (params->profile[i] >> 8) & 0xFF; dst++; *dst = params->profile[i] & 0xFF; dst++; dst_counter += 2; } /* Return generated length */ return dst_counter; } /* Encode V.42bis parameter field * (see also: 3GPP TS 44.065, 6.6.2.1, Table 7a) */ static int encode_dcomp_v42bis_params(uint8_t *dst, unsigned int dst_maxlen, const struct gprs_sndcp_dcomp_v42bis_params *params) { /* NOTE: Buffer *dst should offer at least 6 bytes * of space to store the generation results */ int dst_counter = 0; int rc; OSMO_ASSERT(dst_maxlen >= 6); /* Zero out buffer */ memset(dst, 0, dst_maxlen); /* Encode applicable SAPIs */ rc = encode_pcomp_applicable_sapis(dst, params->nsapi, params->nsapi_len); dst += rc; dst_counter += rc; /* Encode P0 (see also: 3GPP TS 44.065, 6.6.2.1, Table 7a) */ OSMO_ASSERT(params->p0 >= 0); OSMO_ASSERT(params->p0 <= 3); *dst = params->p0 & 0x03; dst++; dst_counter++; /* Encode P1 (see also: 3GPP TS 44.065, 6.6.2.1, Table 7a) */ OSMO_ASSERT(params->p1 >= 512); OSMO_ASSERT(params->p1 <= 65535); *dst = (params->p1 >> 8) & 0xFF; dst++; *dst = params->p1 & 0xFF; dst++; dst_counter += 2; /* Encode P2 (see also: 3GPP TS 44.065, 6.6.2.1, Table 7a) */ OSMO_ASSERT(params->p2 >= 6); OSMO_ASSERT(params->p2 <= 250); *dst = params->p2; dst++; dst_counter++; /* Return generated length */ return dst_counter; } /* Encode V44 parameter field * (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ static int encode_dcomp_v44_params(uint8_t *dst, unsigned int dst_maxlen, const struct gprs_sndcp_dcomp_v44_params *params) { /* NOTE: Buffer *dst should offer at least 12 bytes * of space to store the generation results */ int dst_counter = 0; int rc; OSMO_ASSERT(dst_maxlen >= 12); /* Zero out buffer */ memset(dst, 0, dst_maxlen); /* Encode applicable SAPIs */ rc = encode_pcomp_applicable_sapis(dst, params->nsapi, params->nsapi_len); dst += rc; dst_counter += rc; /* Encode C0 (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ OSMO_ASSERT(params->c0 == 0x80 || params->c0 == 0xC0); *dst = params->c0 & 0xC0; dst++; dst_counter++; /* Encode P0 (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ OSMO_ASSERT(params->p0 >= 0); OSMO_ASSERT(params->p0 <= 3); *dst = params->p0 & 0x03; dst++; dst_counter++; /* Encode P1T (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ OSMO_ASSERT(params->p1t >= 256); OSMO_ASSERT(params->p1t <= 65535); *dst = (params->p1t >> 8) & 0xFF; dst++; *dst = params->p1t & 0xFF; dst++; dst_counter += 2; /* Encode P1R (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ OSMO_ASSERT(params->p1r >= 256); OSMO_ASSERT(params->p1r <= 65535); *dst = (params->p1r >> 8) & 0xFF; dst++; *dst = params->p1r & 0xFF; dst++; dst_counter += 2; /* Encode P3T (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ OSMO_ASSERT(params->p3t >= 0); OSMO_ASSERT(params->p3t <= 65535); OSMO_ASSERT(params->p3t >= 2 * params->p1t); *dst = (params->p3t >> 8) & 0xFF; dst++; *dst = params->p3t & 0xFF; dst++; dst_counter += 2; /* Encode P3R (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ OSMO_ASSERT(params->p3r >= 0); OSMO_ASSERT(params->p3r <= 65535); OSMO_ASSERT(params->p3r >= 2 * params->p1r); *dst = (params->p3r >> 8) & 0xFF; dst++; *dst = params->p3r & 0xFF; dst++; dst_counter += 2; /* Return generated length */ return dst_counter; } /* Encode data or protocol control information compression field * (see also: 3GPP TS 44.065, 6.6.1.1, Figure 9 and * 3GPP TS 44.065, 6.5.1.1, Figure 7) */ static int encode_comp_field(uint8_t *dst, unsigned int dst_maxlen, const struct gprs_sndcp_comp_field *comp_field) { int dst_counter = 0; int len; int expected_length; int i; uint8_t payload_bytes[256]; int payload_bytes_len = -1; /* If possible, try do encode payload bytes first */ if (comp_field->rfc1144_params) { payload_bytes_len = encode_pcomp_rfc1144_params(payload_bytes, sizeof(payload_bytes), comp_field->rfc1144_params); } else if (comp_field->rfc2507_params) { payload_bytes_len = encode_pcomp_rfc2507_params(payload_bytes, sizeof(payload_bytes), comp_field->rfc2507_params); } else if (comp_field->rohc_params) { payload_bytes_len = encode_pcomp_rohc_params(payload_bytes, sizeof(payload_bytes), comp_field->rohc_params); } else if (comp_field->v42bis_params) { payload_bytes_len = encode_dcomp_v42bis_params(payload_bytes, sizeof(payload_bytes), comp_field->v42bis_params); } else if (comp_field->v44_params) { payload_bytes_len = encode_dcomp_v44_params(payload_bytes, sizeof(payload_bytes), comp_field->v44_params); } else OSMO_ASSERT(false); /* Bail immediately if payload byte generation failed */ OSMO_ASSERT(payload_bytes_len >= 0); /* Bail if comp_len is out of bounds */ OSMO_ASSERT(comp_field->comp_len <= sizeof(comp_field->comp)); /* Calculate length field of the data block */ if (comp_field->p) { len = payload_bytes_len + ceil((double)(comp_field->comp_len) / 2.0); expected_length = len + 3; } else { len = payload_bytes_len; expected_length = len + 2; } /* Bail immediately if no sufficient memory space is supplied */ OSMO_ASSERT(dst_maxlen >= expected_length); /* Check if the entity number is within bounds */ OSMO_ASSERT(comp_field->entity <= 0x1f); /* Check if the algorithm number is within bounds */ OSMO_ASSERT(comp_field->algo >= 0 || comp_field->algo <= 0x1f); /* Zero out buffer */ memset(dst, 0, dst_maxlen); /* Encode Propose bit */ if (comp_field->p) *dst |= (1 << 7); /* Encode entity number */ *dst |= comp_field->entity & 0x1F; dst++; dst_counter++; /* Encode algorithm number */ if (comp_field->p) { *dst |= comp_field->algo & 0x1F; dst++; dst_counter++; } /* Encode length field */ *dst |= len & 0xFF; dst++; dst_counter++; /* Encode PCOMP/DCOMP values */ if (comp_field->p) { for (i = 0; i < comp_field->comp_len; i++) { /* Check if submitted PCOMP/DCOMP values are within bounds */ if (comp_field->comp[i] > 0x0F) return -EINVAL; if (i & 1) { *dst |= comp_field->comp[i] & 0x0F; dst++; dst_counter++; } else *dst |= (comp_field->comp[i] << 4) & 0xF0; } if (i & 1) { dst++; dst_counter++; } } /* Append payload bytes */ memcpy(dst, payload_bytes, payload_bytes_len); dst_counter += payload_bytes_len; /* Return generated length */ return dst_counter; } /* Find out to which compression class the specified comp-field belongs * (header compression or data compression?) */ int gprs_sndcp_get_compression_class(const struct gprs_sndcp_comp_field *comp_field) { OSMO_ASSERT(comp_field); if (comp_field->rfc1144_params) return SNDCP_XID_PROTOCOL_COMPRESSION; else if (comp_field->rfc2507_params) return SNDCP_XID_PROTOCOL_COMPRESSION; else if (comp_field->rohc_params) return SNDCP_XID_PROTOCOL_COMPRESSION; else if (comp_field->v42bis_params) return SNDCP_XID_DATA_COMPRESSION; else if (comp_field->v44_params) return SNDCP_XID_DATA_COMPRESSION; else return -EINVAL; } /* Convert all compression fields to bytstreams */ static int gprs_sndcp_pack_fields(const struct llist_head *comp_fields, uint8_t *dst, unsigned int dst_maxlen, int class) { struct gprs_sndcp_comp_field *comp_field; int byte_counter = 0; int rc; llist_for_each_entry_reverse(comp_field, comp_fields, list) { if (class == gprs_sndcp_get_compression_class(comp_field)) { rc = encode_comp_field(dst + byte_counter, dst_maxlen - byte_counter, comp_field); /* When input data is correct, there is * no reason for the encoder to fail! */ OSMO_ASSERT(rc >= 0); byte_counter += rc; } } /* Return generated length */ return byte_counter; } /* Transform a list with compression fields into an SNDCP-XID message (dst) */ int gprs_sndcp_compile_xid(uint8_t *dst, unsigned int dst_maxlen, const struct llist_head *comp_fields, int version) { int rc; int byte_counter = 0; uint8_t comp_bytes[512]; uint8_t xid_version_number[1]; OSMO_ASSERT(comp_fields); OSMO_ASSERT(dst); OSMO_ASSERT(dst_maxlen >= 2 + sizeof(xid_version_number)); /* Prepend header with version number */ if (version >= 0) { xid_version_number[0] = (uint8_t) (version & 0xff); dst = tlv_put(dst, SNDCP_XID_VERSION_NUMBER, sizeof(xid_version_number), xid_version_number); byte_counter += (sizeof(xid_version_number) + 2); } /* Stop if there is no compression fields supplied */ if (llist_empty(comp_fields)) return byte_counter; /* Add data compression fields */ rc = gprs_sndcp_pack_fields(comp_fields, comp_bytes, sizeof(comp_bytes), SNDCP_XID_DATA_COMPRESSION); OSMO_ASSERT(rc >= 0); if (rc > 0) { dst = tlv_put(dst, SNDCP_XID_DATA_COMPRESSION, rc, comp_bytes); byte_counter += rc + 2; } /* Add header compression fields */ rc = gprs_sndcp_pack_fields(comp_fields, comp_bytes, sizeof(comp_bytes), SNDCP_XID_PROTOCOL_COMPRESSION); OSMO_ASSERT(rc >= 0); if (rc > 0) { dst = tlv_put(dst, SNDCP_XID_PROTOCOL_COMPRESSION, rc, comp_bytes); byte_counter += rc + 2; } /* Return generated length */ return byte_counter; } /* FUNCTIONS RELATED TO SNDCP-XID DECODING */ /* Decode applicable sapis (works the same in all three compression schemes) */ static int decode_pcomp_applicable_sapis(uint8_t *nsapis, uint8_t *nsapis_len, const uint8_t *src, unsigned int src_len) { uint16_t blob; int i; int nsapi_len = 0; /* Exit immediately if no result can be stored */ if (!nsapis) return -EINVAL; /* Exit immediately if not enough input data is available */ if (src_len < 2) return -EINVAL; /* Read bitmask */ blob = *src; blob = (blob << 8) & 0xFF00; src++; blob |= (*src) & 0xFF; blob = (blob >> 5); /* Decode applicable SAPIs */ for (i = 0; i < 15; i++) { if ((blob >> i) & 1) { nsapis[nsapi_len] = i + 5; nsapi_len++; } } /* Return consumed length */ *nsapis_len = nsapi_len; return 2; } /* Decode 16 bit field */ static int decode_pcomp_16_bit_field(int *value_int, uint16_t * value_uint16, const uint8_t *src, unsigned int src_len, int value_min, int value_max) { uint16_t blob; /* Reset values to zero (just to be sure) */ if (value_int) *value_int = -1; if (value_uint16) *value_uint16 = 0; /* Exit if not enough src are available */ if (src_len < 2) return -EINVAL; /* Decode bit value */ blob = *src; blob = (blob << 8) & 0xFF00; src++; blob |= *src; /* Check if parsed value is within bounds */ if (blob < value_min) return -EINVAL; if (blob > value_max) return -EINVAL; /* Hand back results to the caller */ if (value_int) *value_int = blob; if (value_uint16) *value_uint16 = blob; /* Return consumed length */ return 2; } /* Decode 8 bit field */ static int decode_pcomp_8_bit_field(int *value_int, uint8_t *value_uint8, const uint8_t *src, unsigned int src_len, int value_min, int value_max) { uint8_t blob; /* Reset values to invalid (just to be sure) */ if (value_int) *value_int = -1; if (value_uint8) *value_uint8 = 0; /* Exit if not enough src are available */ if (src_len < 1) return -EINVAL; /* Decode bit value */ blob = *src; /* Check if parsed value is within bounds */ if (blob < value_min) return -EINVAL; if (blob > value_max) return -EINVAL; /* Hand back results to the caller */ if (value_int) *value_int = blob; if (value_uint8) *value_uint8 = blob; /* Return consumed length */ return 1; } /* Decode rfc1144 parameter field see also: 3GPP TS 44.065, 6.5.2.1, Table 5) */ static int decode_pcomp_rfc1144_params(struct gprs_sndcp_pcomp_rfc1144_params *params, const uint8_t *src, unsigned int src_len) { int rc; int byte_counter = 0; /* Mark all optional parameters invalid by default */ params->s01 = -1; /* Decode applicable SAPIs */ rc = decode_pcomp_applicable_sapis(params->nsapi, ¶ms->nsapi_len, src, src_len); if (rc > 0) { byte_counter += rc; src += rc; } else return byte_counter; /* Decode parameter S0 -1 * (see also: 3GPP TS 44.065, 6.5.2.1, Table 5) */ rc = decode_pcomp_8_bit_field(¶ms->s01, NULL, src, src_len - byte_counter, 0, 255); if (rc <= 0) return byte_counter; byte_counter += rc; src += rc; /* Return consumed length */ return byte_counter; } /* Decode rfc2507 parameter field * (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */ static int decode_pcomp_rfc2507_params(struct gprs_sndcp_pcomp_rfc2507_params *params, const uint8_t *src, unsigned int src_len) { int rc; int byte_counter = 0; /* Mark all optional parameters invalid by default */ params->f_max_period = -1; params->f_max_time = -1; params->max_header = -1; params->tcp_space = -1; params->non_tcp_space = -1; /* Decode applicable SAPIs */ rc = decode_pcomp_applicable_sapis(params->nsapi, ¶ms->nsapi_len, src, src_len); if (rc > 0) { byte_counter += rc; src += rc; } else return byte_counter; /* Decode F_MAX_PERIOD (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */ rc = decode_pcomp_16_bit_field(¶ms->f_max_period, NULL, src, src_len - byte_counter, 1, 65535); if (rc <= 0) return byte_counter; byte_counter += rc; src += rc; /* Decode F_MAX_TIME (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */ rc = decode_pcomp_8_bit_field(¶ms->f_max_time, NULL, src, src_len - byte_counter, 1, 255); if (rc <= 0) return byte_counter; byte_counter += rc; src += rc; /* Decode MAX_HEADER (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */ rc = decode_pcomp_8_bit_field(¶ms->max_header, NULL, src, src_len - byte_counter, 60, 255); if (rc <= 0) return byte_counter; byte_counter += rc; src += rc; /* Decode TCP_SPACE (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */ rc = decode_pcomp_8_bit_field(¶ms->tcp_space, NULL, src, src_len - byte_counter, 3, 255); if (rc <= 0) return byte_counter; byte_counter += rc; src += rc; /* Decode NON_TCP_SPACE (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */ rc = decode_pcomp_16_bit_field(¶ms->non_tcp_space, NULL, src, src_len - byte_counter, 3, 65535); if (rc <= 0) return byte_counter; byte_counter += rc; src += rc; /* Return consumed length */ return byte_counter; } /* Decode ROHC parameter field (see also: 3GPP TS 44.065, 6.5.4.1, Table 10) */ static int decode_pcomp_rohc_params(struct gprs_sndcp_pcomp_rohc_params *params, const uint8_t *src, unsigned int src_len) { int rc; int byte_counter = 0; int i; /* Mark all optional parameters invalid by default */ params->max_cid = -1; params->max_header = -1; /* Decode applicable SAPIs */ rc = decode_pcomp_applicable_sapis(params->nsapi, ¶ms->nsapi_len, src, src_len); if (rc <= 0) return byte_counter; byte_counter += rc; src += rc; /* Decode MAX_CID (see also: 3GPP TS 44.065, 6.5.4.1, Table 10) */ rc = decode_pcomp_16_bit_field(¶ms->max_cid, NULL, src, src_len - byte_counter, 0, 16383); if (rc <= 0) return byte_counter; byte_counter += rc; src += rc; /* Decode MAX_HEADER (see also: 3GPP TS 44.065, 6.5.4.1, Table 10) */ rc = decode_pcomp_16_bit_field(¶ms->max_header, NULL, src, src_len - byte_counter, 60, 255); if (rc <= 0) return byte_counter; byte_counter += rc; src += rc; /* Decode Profiles (see also: 3GPP TS 44.065, 6.5.4.1, Table 10) */ for (i = 0; i < 16; i++) { params->profile_len = 0; rc = decode_pcomp_16_bit_field(NULL, ¶ms->profile[i], src, src_len - byte_counter, 0, 65535); if (rc <= 0) return byte_counter; byte_counter += rc; src += rc; params->profile_len = i + 1; } /* Return consumed length */ return byte_counter; } /* Decode V.42bis parameter field * (see also: 3GPP TS 44.065, 6.6.2.1, Table 7a) */ static int decode_dcomp_v42bis_params(struct gprs_sndcp_dcomp_v42bis_params *params, const uint8_t *src, unsigned int src_len) { int rc; int byte_counter = 0; /* Mark all optional parameters invalid by default */ params->p0 = -1; params->p1 = -1; params->p2 = -1; /* Decode applicable SAPIs */ rc = decode_pcomp_applicable_sapis(params->nsapi, ¶ms->nsapi_len, src, src_len); if (rc > 0) { byte_counter += rc; src += rc; } else return byte_counter; /* Decode P0 (see also: 3GPP TS 44.065, 6.6.2.1, Table 7a) */ rc = decode_pcomp_8_bit_field(¶ms->p0, NULL, src, src_len - byte_counter, 0, 3); if (rc <= 0) return byte_counter; byte_counter += rc; src += rc; /* Decode P1 (see also: 3GPP TS 44.065, 6.6.2.1, Table 7a) */ rc = decode_pcomp_16_bit_field(¶ms->p1, NULL, src, src_len - byte_counter, 512, 65535); if (rc <= 0) return byte_counter; byte_counter += rc; src += rc; /* Decode P2 (see also: 3GPP TS 44.065, 6.6.2.1, Table 7a) */ rc = decode_pcomp_8_bit_field(¶ms->p2, NULL, src, src_len - byte_counter, 6, 250); if (rc <= 0) return byte_counter; byte_counter += rc; src += rc; /* Return consumed length */ return byte_counter; } /* Decode V44 parameter field (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ static int decode_dcomp_v44_params(struct gprs_sndcp_dcomp_v44_params *params, const uint8_t *src, unsigned int src_len) { int rc; int byte_counter = 0; /* Mark all optional parameters invalid by default */ params->c0 = -1; params->p0 = -1; params->p1t = -1; params->p1r = -1; params->p3t = -1; params->p3r = -1; /* Decode applicable SAPIs */ rc = decode_pcomp_applicable_sapis(params->nsapi, ¶ms->nsapi_len, src, src_len); if (rc > 0) { byte_counter += rc; src += rc; } else return byte_counter; /* Decode C0 (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ rc = decode_pcomp_8_bit_field(¶ms->c0, NULL, src, src_len - byte_counter, 0, 255); if (rc <= 0) return byte_counter; if ((params->c0 != 0x80) && (params->c0 != 0xC0)) return -EINVAL; byte_counter += rc; src += rc; /* Decode P0 (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ rc = decode_pcomp_8_bit_field(¶ms->p0, NULL, src, src_len - byte_counter, 0, 3); if (rc <= 0) return byte_counter; byte_counter += rc; src += rc; /* Decode P1T (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ rc = decode_pcomp_16_bit_field(¶ms->p1t, NULL, src, src_len - byte_counter, 265, 65535); if (rc <= 0) return byte_counter; byte_counter += rc; src += rc; /* Decode P1R (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ rc = decode_pcomp_16_bit_field(¶ms->p1r, NULL, src, src_len - byte_counter, 265, 65535); if (rc <= 0) return byte_counter; byte_counter += rc; src += rc; /* Decode P3T (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ rc = decode_pcomp_16_bit_field(¶ms->p3t, NULL, src, src_len - byte_counter, 265, 65535); if (rc <= 0) return byte_counter; if (params->p3t < 2 * params->p1t) return -EINVAL; byte_counter += rc; src += rc; /* Decode P3R (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ rc = decode_pcomp_16_bit_field(¶ms->p3r, NULL, src, src_len - byte_counter, 265, 65535); if (rc <= 0) return byte_counter; if (params->p3r < 2 * params->p1r) return -EINVAL; byte_counter += rc; src += rc; /* Return consumed length */ return byte_counter; } /* Lookup algorithm identfier by entity ID */ static int lookup_algorithm_identifier(int entity, const struct entity_algo_table *lt, unsigned int lt_len, int compclass) { int i; if (!lt) return -1; for (i = 0; i < lt_len; i++) { if ((lt[i].entity == entity) && (lt[i].compclass == compclass)) return lt[i].algo; } return -1; } /* Helper function for decode_comp_field(), decodes * numeric pcomp/dcomp values */ static int decode_comp_values(struct gprs_sndcp_comp_field *comp_field, const uint8_t *src, int compclass) { int src_counter = 0; int i; if (comp_field->p) { /* Determine the number of expected PCOMP/DCOMP values */ if (compclass == SNDCP_XID_PROTOCOL_COMPRESSION) { /* For protocol compression */ switch (comp_field->algo) { case RFC_1144: comp_field->comp_len = RFC1144_PCOMP_NUM; break; case RFC_2507: comp_field->comp_len = RFC2507_PCOMP_NUM; break; case ROHC: comp_field->comp_len = ROHC_PCOMP_NUM; break; /* Exit if the algorithem type encodes something unknown / unspecified */ default: return -EINVAL; } } else { /* For data compression */ switch (comp_field->algo) { case V42BIS: comp_field->comp_len = V42BIS_DCOMP_NUM; break; case V44: comp_field->comp_len = V44_DCOMP_NUM; break; /* Exit if the algorithem type encodes something unknown / unspecified */ default: return -EINVAL; } } for (i = 0; i < comp_field->comp_len; i++) { if (i & 1) { comp_field->comp[i] = (*src) & 0x0F; src++; src_counter++; } else comp_field->comp[i] = ((*src) >> 4) & 0x0F; } if (i & 1) { src++; src_counter++; } } return src_counter; } /* Helper function for decode_comp_field(), decodes the parameters * which are protocol compression specific */ static int decode_pcomp_params(struct gprs_sndcp_comp_field *comp_field, const uint8_t *src, int src_len) { int rc; switch (comp_field->algo) { case RFC_1144: comp_field->rfc1144_params = talloc_zero(comp_field, struct gprs_sndcp_pcomp_rfc1144_params); rc = decode_pcomp_rfc1144_params(comp_field->rfc1144_params, src, src_len); if (rc < 0) talloc_free(comp_field->rfc1144_params); break; case RFC_2507: comp_field->rfc2507_params = talloc_zero(comp_field, struct gprs_sndcp_pcomp_rfc2507_params); rc = decode_pcomp_rfc2507_params(comp_field->rfc2507_params, src, src_len); if (rc < 0) talloc_free(comp_field->rfc1144_params); break; case ROHC: comp_field->rohc_params = talloc_zero(comp_field, struct gprs_sndcp_pcomp_rohc_params); rc = decode_pcomp_rohc_params(comp_field->rohc_params, src, src_len); if (rc < 0) talloc_free(comp_field->rohc_params); break; /* If no suitable decoder is detected, leave the remaining bytes undecoded */ default: rc = src_len; } if (rc < 0) { comp_field->rfc1144_params = NULL; comp_field->rfc2507_params = NULL; comp_field->rohc_params = NULL; } return rc; } /* Helper function for decode_comp_field(), decodes the parameters * which are data compression specific */ static int decode_dcomp_params(struct gprs_sndcp_comp_field *comp_field, const uint8_t *src, int src_len) { int rc; switch (comp_field->algo) { case V42BIS: comp_field->v42bis_params = talloc_zero(comp_field, struct gprs_sndcp_dcomp_v42bis_params); rc = decode_dcomp_v42bis_params(comp_field->v42bis_params, src, src_len); if (rc < 0) talloc_free(comp_field->v42bis_params); break; case V44: comp_field->v44_params = talloc_zero(comp_field, struct gprs_sndcp_dcomp_v44_params); rc = decode_dcomp_v44_params(comp_field->v44_params, src, src_len); if (rc < 0) talloc_free(comp_field->v44_params); break; /* If no suitable decoder is detected, * leave the remaining bytes undecoded */ default: rc = src_len; } if (rc < 0) { comp_field->v42bis_params = NULL; comp_field->v44_params = NULL; } return rc; } /* Decode data or protocol control information compression field * (see also: 3GPP TS 44.065, 6.6.1.1, Figure 9 and * 3GPP TS 44.065, 6.5.1.1, Figure 7) */ static int decode_comp_field(struct gprs_sndcp_comp_field *comp_field, const uint8_t *src, unsigned int src_len, const struct entity_algo_table *lt, unsigned int lt_len, int compclass) { int src_counter = 0; unsigned int len; int rc; OSMO_ASSERT(comp_field); /* Exit immediately if it is clear that no parseable data is present */ if (src_len < 1 || !src) return -EINVAL; /* Zero out target struct */ memset(comp_field, 0, sizeof(struct gprs_sndcp_comp_field)); /* Decode Propose bit and Entity number */ if ((*src) & 0x80) comp_field->p = 1; comp_field->entity = (*src) & 0x1F; src_counter++; src++; /* Decode algorithm number (if present) */ if (comp_field->p) { comp_field->algo = (*src) & 0x1F; src_counter++; src++; } /* Alternatively take the information from the lookup table */ else comp_field->algo = lookup_algorithm_identifier(comp_field->entity, lt, lt_len, compclass); /* Decode length field */ len = *src; src_counter++; src++; /* Decode PCOMP/DCOMP values */ rc = decode_comp_values(comp_field, src, compclass); if (rc < 0) return -EINVAL; src_counter += rc; src += rc; len -= rc; /* Decode algorithm specific payload data */ if (compclass == SNDCP_XID_PROTOCOL_COMPRESSION) rc = decode_pcomp_params(comp_field, src, len); else if (compclass == SNDCP_XID_DATA_COMPRESSION) rc = decode_dcomp_params(comp_field, src, len); else return -EINVAL; if (rc >= 0) src_counter += rc; else return -EINVAL; /* Return consumed length */ return src_counter; } /* Helper function for gprs_sndcp_decode_xid() to decode XID blocks */ static int decode_xid_block(struct llist_head *comp_fields, uint8_t tag, uint16_t tag_len, const uint8_t *val, const struct entity_algo_table *lt, unsigned int lt_len) { struct gprs_sndcp_comp_field *comp_field; int byte_counter = 0; int comp_field_count = 0; int rc; byte_counter = 0; do { /* Bail if more than the maximum number of comp_fields is generated */ if (comp_field_count > MAX_ENTITIES * 2) { return -EINVAL; } /* Parse and add comp_field */ comp_field = talloc_zero(comp_fields, struct gprs_sndcp_comp_field); rc = decode_comp_field(comp_field, val + byte_counter, tag_len - byte_counter, lt, lt_len, tag); if (rc < 0) { talloc_free(comp_field); return -EINVAL; } byte_counter += rc; llist_add(&comp_field->list, comp_fields); comp_field_count++; } while (tag_len - byte_counter > 0); return byte_counter; } /* Transform an SNDCP-XID message (src) into a list of SNDCP-XID fields */ static int gprs_sndcp_decode_xid(int *version, struct llist_head *comp_fields, const uint8_t *src, unsigned int src_len, const struct entity_algo_table *lt, unsigned int lt_len) { int src_pos = 0; uint8_t tag; uint16_t tag_len; const uint8_t *val; int byte_counter = 0; int rc; int tlv_count = 0; /* Preset version value as invalid */ if (version) *version = -1; /* Valid TLV-Tag and types */ static const struct tlv_definition sndcp_xid_def = { .def = { [SNDCP_XID_VERSION_NUMBER] = {TLV_TYPE_TLV,}, [SNDCP_XID_DATA_COMPRESSION] = {TLV_TYPE_TLV,}, [SNDCP_XID_PROTOCOL_COMPRESSION] = {TLV_TYPE_TLV,}, }, }; /* Parse TLV-Encoded SNDCP-XID message and defer payload to the apporpiate sub-parser functions */ while (1) { /* Bail if an the maximum number of TLV fields * have been parsed */ if (tlv_count >= 3) { talloc_free(comp_fields); return -EINVAL; } /* Parse TLV field */ rc = tlv_parse_one(&tag, &tag_len, &val, &sndcp_xid_def, src + src_pos, src_len - src_pos); if (rc > 0) src_pos += rc; else { talloc_free(comp_fields); return -EINVAL; } /* Decode sndcp xid version number */ if (version && tag == SNDCP_XID_VERSION_NUMBER) *version = val[0]; /* Decode compression parameters */ if ((tag == SNDCP_XID_PROTOCOL_COMPRESSION) || (tag == SNDCP_XID_DATA_COMPRESSION)) { rc = decode_xid_block(comp_fields, tag, tag_len, val, lt, lt_len); if (rc < 0) { talloc_free(comp_fields); return -EINVAL; } else byte_counter += rc; } /* Stop when no further TLV elements can be expected */ if (src_len - src_pos <= 2) break; tlv_count++; } return 0; } /* Fill up lookutable from a list with comression entitiy fields */ static int gprs_sndcp_fill_table(struct entity_algo_table *lt, unsigned int lt_len, const struct llist_head *comp_fields) { struct gprs_sndcp_comp_field *comp_field; int i = 0; int rc; if (!comp_fields) return -EINVAL; if (!lt) return -EINVAL; memset(lt, 0, sizeof(*lt)); llist_for_each_entry(comp_field, comp_fields, list) { if (comp_field->algo >= 0) { lt[i].entity = comp_field->entity; lt[i].algo = comp_field->algo; rc = gprs_sndcp_get_compression_class(comp_field); if (rc < 0) { memset(lt, 0, sizeof(*lt)); return -EINVAL; } lt[i].compclass = rc; i++; } } return i; } /* Complete comp field params * (if a param (dst) is not valid, it will be copied from source (src) */ static int complete_comp_field_params(struct gprs_sndcp_comp_field *comp_field_dst, const struct gprs_sndcp_comp_field *comp_field_src) { if (comp_field_dst->algo < 0) return -EINVAL; if (comp_field_dst->rfc1144_params && comp_field_src->rfc1144_params) { if (comp_field_dst->rfc1144_params->s01 < 0) { comp_field_dst->rfc1144_params->s01 = comp_field_src->rfc1144_params->s01; } return 0; } if (comp_field_dst->rfc2507_params && comp_field_src->rfc2507_params) { if (comp_field_dst->rfc2507_params->f_max_period < 0) { comp_field_dst->rfc2507_params->f_max_period = comp_field_src->rfc2507_params->f_max_period; } if (comp_field_dst->rfc2507_params->f_max_time < 0) { comp_field_dst->rfc2507_params->f_max_time = comp_field_src->rfc2507_params->f_max_time; } if (comp_field_dst->rfc2507_params->max_header < 0) { comp_field_dst->rfc2507_params->max_header = comp_field_src->rfc2507_params->max_header; } if (comp_field_dst->rfc2507_params->tcp_space < 0) { comp_field_dst->rfc2507_params->tcp_space = comp_field_src->rfc2507_params->tcp_space; } if (comp_field_dst->rfc2507_params->non_tcp_space < 0) { comp_field_dst->rfc2507_params->non_tcp_space = comp_field_src->rfc2507_params->non_tcp_space; } return 0; } if (comp_field_dst->rohc_params && comp_field_src->rohc_params) { if (comp_field_dst->rohc_params->max_cid < 0) { comp_field_dst->rohc_params->max_cid = comp_field_src->rohc_params->max_cid; } if (comp_field_dst->rohc_params->max_header < 0) { comp_field_dst->rohc_params->max_header = comp_field_src->rohc_params->max_header; } if (comp_field_dst->rohc_params->profile_len > 0) { memcpy(comp_field_dst->rohc_params->profile, comp_field_src->rohc_params->profile, sizeof(comp_field_dst->rohc_params->profile)); comp_field_dst->rohc_params->profile_len = comp_field_src->rohc_params->profile_len; } return 0; } if (comp_field_dst->v42bis_params && comp_field_src->v42bis_params) { if (comp_field_dst->v42bis_params->p0 < 0) { comp_field_dst->v42bis_params->p0 = comp_field_src->v42bis_params->p0; } if (comp_field_dst->v42bis_params->p1 < 0) { comp_field_dst->v42bis_params->p1 = comp_field_src->v42bis_params->p1; } if (comp_field_dst->v42bis_params->p2 < 0) { comp_field_dst->v42bis_params->p2 = comp_field_src->v42bis_params->p2; } return 0; } if (comp_field_dst->v44_params && comp_field_src->v44_params) { if (comp_field_dst->v44_params->c0 < 0) { comp_field_dst->v44_params->c0 = comp_field_src->v44_params->c0; } if (comp_field_dst->v44_params->p0 < 0) { comp_field_dst->v44_params->p0 = comp_field_src->v44_params->p0; } if (comp_field_dst->v44_params->p1t < 0) { comp_field_dst->v44_params->p1t = comp_field_src->v44_params->p1t; } if (comp_field_dst->v44_params->p1r < 0) { comp_field_dst->v44_params->p1r = comp_field_src->v44_params->p1r; } if (comp_field_dst->v44_params->p3t < 0) { comp_field_dst->v44_params->p3t = comp_field_src->v44_params->p3t; } if (comp_field_dst->v44_params->p3r < 0) { comp_field_dst->v44_params->p3r = comp_field_src->v44_params->p3r; } return 0; } /* There should be at least exist one param set * in the destination struct, otherwise something * must be wrong! */ return -EINVAL; } /* Complete missing parameters in a comp_field */ static int gprs_sndcp_complete_comp_field(struct gprs_sndcp_comp_field *comp_field, const struct llist_head *comp_fields) { struct gprs_sndcp_comp_field *comp_field_src; int rc = 0; llist_for_each_entry(comp_field_src, comp_fields, list) { if (comp_field_src->entity == comp_field->entity) { /* Complete header fields */ if (comp_field_src->comp_len > 0) { memcpy(comp_field->comp, comp_field_src->comp, sizeof(comp_field_src->comp)); comp_field->comp_len = comp_field_src->comp_len; } /* Complete parameter fields */ rc = complete_comp_field_params(comp_field, comp_field_src); } } return rc; } /* Complete missing parameters of all comp_field in a list */ static int gprs_sndcp_complete_comp_fields(struct llist_head *comp_fields_incomplete, const struct llist_head *comp_fields) { struct gprs_sndcp_comp_field *comp_field_incomplete; int rc; llist_for_each_entry(comp_field_incomplete, comp_fields_incomplete, list) { rc = gprs_sndcp_complete_comp_field(comp_field_incomplete, comp_fields); if (rc < 0) return -EINVAL; } return 0; } /* Transform an SNDCP-XID message (src) into a list of SNDCP-XID fields */ struct llist_head *gprs_sndcp_parse_xid(int *version, const void *ctx, const uint8_t *src, unsigned int src_len, const struct llist_head *comp_fields_req) { int rc; int lt_len; struct llist_head *comp_fields; struct entity_algo_table lt[MAX_ENTITIES * 2]; /* In case of a zero length field, just exit */ if (src_len == 0) return NULL; /* We should go any further if we have a field length greater * zero and a null pointer as buffer! */ OSMO_ASSERT(src); comp_fields = talloc_zero(ctx, struct llist_head); INIT_LLIST_HEAD(comp_fields); if (comp_fields_req) { /* Generate lookup table */ lt_len = gprs_sndcp_fill_table(lt, MAX_ENTITIES * 2, comp_fields_req); if (lt_len < 0) { talloc_free(comp_fields); return NULL; } /* Parse SNDCP-CID XID-Field */ rc = gprs_sndcp_decode_xid(version, comp_fields, src, src_len, lt, lt_len); if (rc < 0) { talloc_free(comp_fields); return NULL; } rc = gprs_sndcp_complete_comp_fields(comp_fields, comp_fields_req); if (rc < 0) { talloc_free(comp_fields); return NULL; } } else { /* Parse SNDCP-CID XID-Field */ rc = gprs_sndcp_decode_xid(version, comp_fields, src, src_len, NULL, 0); if (rc < 0) { talloc_free(comp_fields); return NULL; } } return comp_fields; } /* Helper for gprs_sndcp_dump_comp_fields(), * dumps protocol compression parameters */ static void dump_pcomp_params(const struct gprs_sndcp_comp_field *comp_field, unsigned int logl) { int i; switch (comp_field->algo) { case RFC_1144: if (comp_field->rfc1144_params == NULL) { LOGP(DSNDCP, logl, " gprs_sndcp_pcomp_rfc1144_params=NULL\n"); break; } LOGP(DSNDCP, logl, " gprs_sndcp_pcomp_rfc1144_params {\n"); LOGP(DSNDCP, logl, " nsapi_len=%d;\n", comp_field->rfc1144_params->nsapi_len); if (comp_field->rfc1144_params->nsapi_len == 0) LOGP(DSNDCP, logl, " nsapi[] = NULL;\n"); for (i = 0; i < comp_field->rfc1144_params->nsapi_len; i++) { LOGP(DSNDCP, logl, " nsapi[%d]=%d;\n", i, comp_field->rfc1144_params->nsapi[i]); } LOGP(DSNDCP, logl, " s01=%d;\n", comp_field->rfc1144_params->s01); LOGP(DSNDCP, logl, " }\n"); break; case RFC_2507: if (comp_field->rfc2507_params == NULL) { LOGP(DSNDCP, logl, " gprs_sndcp_pcomp_rfc2507_params=NULL\n"); break; } LOGP(DSNDCP, logl, " gprs_sndcp_pcomp_rfc2507_params {\n"); LOGP(DSNDCP, logl, " nsapi_len=%d;\n", comp_field->rfc2507_params->nsapi_len); if (comp_field->rfc2507_params->nsapi_len == 0) LOGP(DSNDCP, logl, " nsapi[] = NULL;\n"); for (i = 0; i < comp_field->rfc2507_params->nsapi_len; i++) { LOGP(DSNDCP, logl, " nsapi[%d]=%d;\n", i, comp_field->rfc2507_params->nsapi[i]); } LOGP(DSNDCP, logl, " f_max_period=%d;\n", comp_field->rfc2507_params->f_max_period); LOGP(DSNDCP, logl, " f_max_time=%d;\n", comp_field->rfc2507_params->f_max_time); LOGP(DSNDCP, logl, " max_header=%d;\n", comp_field->rfc2507_params->max_header); LOGP(DSNDCP, logl, " tcp_space=%d;\n", comp_field->rfc2507_params->tcp_space); LOGP(DSNDCP, logl, " non_tcp_space=%d;\n", comp_field->rfc2507_params->non_tcp_space); LOGP(DSNDCP, logl, " }\n"); break; case ROHC: if (comp_field->rohc_params == NULL) { LOGP(DSNDCP, logl, " gprs_sndcp_pcomp_rohc_params=NULL\n"); break; } LOGP(DSNDCP, logl, " gprs_sndcp_pcomp_rohc_params {\n"); LOGP(DSNDCP, logl, " nsapi_len=%d;\n", comp_field->rohc_params->nsapi_len); if (comp_field->rohc_params->nsapi_len == 0) LOGP(DSNDCP, logl, " nsapi[] = NULL;\n"); for (i = 0; i < comp_field->rohc_params->nsapi_len; i++) { LOGP(DSNDCP, logl, " nsapi[%d]=%d;\n", i, comp_field->rohc_params->nsapi[i]); } LOGP(DSNDCP, logl, " max_cid=%d;\n", comp_field->rohc_params->max_cid); LOGP(DSNDCP, logl, " max_header=%d;\n", comp_field->rohc_params->max_header); LOGP(DSNDCP, logl, " profile_len=%d;\n", comp_field->rohc_params->profile_len); if (comp_field->rohc_params->profile_len == 0) LOGP(DSNDCP, logl, " profile[] = NULL;\n"); for (i = 0; i < comp_field->rohc_params->profile_len; i++) LOGP(DSNDCP, logl, " profile[%d]=%04x;\n", i, comp_field->rohc_params->profile[i]); LOGP(DSNDCP, logl, " }\n"); break; } } /* Helper for gprs_sndcp_dump_comp_fields(), * data protocol compression parameters */ static void dump_dcomp_params(const struct gprs_sndcp_comp_field *comp_field, unsigned int logl) { int i; switch (comp_field->algo) { case V42BIS: if (comp_field->v42bis_params == NULL) { LOGP(DSNDCP, logl, " gprs_sndcp_dcomp_v42bis_params=NULL\n"); break; } LOGP(DSNDCP, logl, " gprs_sndcp_dcomp_v42bis_params {\n"); LOGP(DSNDCP, logl, " nsapi_len=%d;\n", comp_field->v42bis_params->nsapi_len); if (comp_field->v42bis_params->nsapi_len == 0) LOGP(DSNDCP, logl, " nsapi[] = NULL;\n"); for (i = 0; i < comp_field->v42bis_params->nsapi_len; i++) LOGP(DSNDCP, logl, " nsapi[%d]=%d;\n", i, comp_field->v42bis_params->nsapi[i]); LOGP(DSNDCP, logl, " p0=%d;\n", comp_field->v42bis_params->p0); LOGP(DSNDCP, logl, " p1=%d;\n", comp_field->v42bis_params->p1); LOGP(DSNDCP, logl, " p2=%d;\n", comp_field->v42bis_params->p2); LOGP(DSNDCP, logl, " }\n"); break; case V44: if (comp_field->v44_params == NULL) { LOGP(DSNDCP, logl, " gprs_sndcp_dcomp_v44_params=NULL\n"); break; } LOGP(DSNDCP, logl, " gprs_sndcp_dcomp_v44_params {\n"); LOGP(DSNDCP, logl, " nsapi_len=%d;\n", comp_field->v44_params->nsapi_len); if (comp_field->v44_params->nsapi_len == 0) LOGP(DSNDCP, logl, " nsapi[] = NULL;\n"); for (i = 0; i < comp_field->v44_params->nsapi_len; i++) { LOGP(DSNDCP, logl, " nsapi[%d]=%d;\n", i, comp_field->v44_params->nsapi[i]); } LOGP(DSNDCP, logl, " c0=%d;\n", comp_field->v44_params->c0); LOGP(DSNDCP, logl, " p0=%d;\n", comp_field->v44_params->p0); LOGP(DSNDCP, logl, " p1t=%d;\n", comp_field->v44_params->p1t); LOGP(DSNDCP, logl, " p1r=%d;\n", comp_field->v44_params->p1r); LOGP(DSNDCP, logl, " p3t=%d;\n", comp_field->v44_params->p3t); LOGP(DSNDCP, logl, " p3r=%d;\n", comp_field->v44_params->p3r); LOGP(DSNDCP, logl, " }\n"); break; } } /* Dump a list with SNDCP-XID fields (Debug) */ void gprs_sndcp_dump_comp_fields(const struct llist_head *comp_fields, unsigned int logl) { struct gprs_sndcp_comp_field *comp_field; int i; int compclass; OSMO_ASSERT(comp_fields); llist_for_each_entry(comp_field, comp_fields, list) { LOGP(DSNDCP, logl, "SNDCP-XID:\n"); LOGP(DSNDCP, logl, "struct gprs_sndcp_comp_field {\n"); LOGP(DSNDCP, logl, " entity=%d;\n", comp_field->entity); LOGP(DSNDCP, logl, " algo=%d;\n", comp_field->algo); LOGP(DSNDCP, logl, " comp_len=%d;\n", comp_field->comp_len); if (comp_field->comp_len == 0) LOGP(DSNDCP, logl, " comp[] = NULL;\n"); for (i = 0; i < comp_field->comp_len; i++) { LOGP(DSNDCP, logl, " comp[%d]=%d;\n", i, comp_field->comp[i]); } compclass = gprs_sndcp_get_compression_class(comp_field); if (compclass == SNDCP_XID_PROTOCOL_COMPRESSION) { dump_pcomp_params(comp_field, logl); } else if (compclass == SNDCP_XID_DATA_COMPRESSION) { dump_dcomp_params(comp_field, logl); } LOGP(DSNDCP, logl, "}\n"); } } osmo-sgsn-1.3.0/src/gprs/gprs_subscriber.c000066400000000000000000000637001327264017000205540ustar00rootroot00000000000000/* MS subscriber data handling */ /* (C) 2014 by sysmocom s.f.m.c. GmbH * (C) 2015 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 #define SGSN_SUBSCR_MAX_RETRIES 3 #define SGSN_SUBSCR_RETRY_INTERVAL 10 #define LOGGSUPP(level, gsup, fmt, args...) \ LOGP(DGPRS, level, "GSUP(%s) " fmt, \ (gsup)->imsi, \ ## args) extern void *tall_bsc_ctx; LLIST_HEAD(_gprs_subscribers); struct llist_head * const gprs_subscribers = &_gprs_subscribers; static int gsup_read_cb(struct gsup_client *gsupc, struct msgb *msg); /* TODO: Some functions are specific to the SGSN, but this file is more general * (it has gprs_* name). Either move these functions elsewhere, split them and * move a part, or replace the gprs_ prefix by sgsn_. The applies to * gprs_subscr_init, gsup_read_cb, and gprs_subscr_tx_gsup_message. */ int gprs_subscr_init(struct sgsn_instance *sgi) { const char *addr_str; if (!sgi->cfg.gsup_server_addr.sin_addr.s_addr) return 0; addr_str = inet_ntoa(sgi->cfg.gsup_server_addr.sin_addr); sgi->gsup_client = gsup_client_create( "SGSN", addr_str, sgi->cfg.gsup_server_port, &gsup_read_cb, &sgi->cfg.oap); if (!sgi->gsup_client) return -1; return 1; } static int gsup_read_cb(struct gsup_client *gsupc, struct msgb *msg) { int rc; rc = gprs_subscr_rx_gsup_message(msg); msgb_free(msg); if (rc < 0) return -1; return rc; } int gprs_subscr_purge(struct gprs_subscr *subscr); static struct sgsn_subscriber_data *sgsn_subscriber_data_alloc(void *ctx) { struct sgsn_subscriber_data *sdata; int idx; sdata = talloc_zero(ctx, struct sgsn_subscriber_data); sdata->error_cause = SGSN_ERROR_CAUSE_NONE; for (idx = 0; idx < ARRAY_SIZE(sdata->auth_triplets); idx++) sdata->auth_triplets[idx].key_seq = GSM_KEY_SEQ_INVAL; INIT_LLIST_HEAD(&sdata->pdp_list); return sdata; } struct sgsn_subscriber_pdp_data* sgsn_subscriber_pdp_data_alloc( struct sgsn_subscriber_data *sdata) { struct sgsn_subscriber_pdp_data* pdata; pdata = talloc_zero(sdata, struct sgsn_subscriber_pdp_data); llist_add_tail(&pdata->list, &sdata->pdp_list); return pdata; } struct gprs_subscr *gprs_subscr_get_by_imsi(const char *imsi) { struct gprs_subscr *gsub; if (!imsi || !*imsi) return NULL; llist_for_each_entry(gsub, gprs_subscribers, entry) { if (!strcmp(gsub->imsi, imsi)) return gprs_subscr_get(gsub); } return NULL; } static struct gprs_subscr *gprs_subscr_alloc(void) { struct gprs_subscr *gsub; gsub = talloc_zero(tall_bsc_ctx, struct gprs_subscr); if (!gsub) return NULL; llist_add_tail(&gsub->entry, gprs_subscribers); gsub->use_count = 1; gsub->tmsi = GSM_RESERVED_TMSI; return gsub; } struct gprs_subscr *gprs_subscr_get_or_create(const char *imsi) { struct gprs_subscr *gsub; gsub = gprs_subscr_get_by_imsi(imsi); if (!gsub) { gsub = gprs_subscr_alloc(); if (!gsub) return NULL; osmo_strlcpy(gsub->imsi, imsi, sizeof(gsub->imsi)); } if (!gsub->sgsn_data) gsub->sgsn_data = sgsn_subscriber_data_alloc(gsub); return gsub; } void gprs_subscr_cleanup(struct gprs_subscr *subscr) { if (subscr->sgsn_data->mm) { gprs_subscr_put(subscr->sgsn_data->mm->subscr); subscr->sgsn_data->mm->subscr = NULL; subscr->sgsn_data->mm = NULL; } if (subscr->flags & GPRS_SUBSCRIBER_ENABLE_PURGE) { gprs_subscr_purge(subscr); subscr->flags &= ~GPRS_SUBSCRIBER_ENABLE_PURGE; } } void gprs_subscr_cancel(struct gprs_subscr *subscr) { subscr->authorized = 0; subscr->flags |= GPRS_SUBSCRIBER_CANCELLED; subscr->flags &= ~GPRS_SUBSCRIBER_ENABLE_PURGE; gprs_subscr_update(subscr); gprs_subscr_cleanup(subscr); } static int gprs_subscr_tx_gsup_message(struct gprs_subscr *subscr, struct osmo_gsup_message *gsup_msg) { struct msgb *msg = gsup_client_msgb_alloc(); if (strlen(gsup_msg->imsi) == 0 && subscr) osmo_strlcpy(gsup_msg->imsi, subscr->imsi, sizeof(gsup_msg->imsi)); gsup_msg->cn_domain = OSMO_GSUP_CN_DOMAIN_PS; osmo_gsup_encode(msg, gsup_msg); LOGGSUBSCRP(LOGL_INFO, subscr, "Sending GSUP, will send: %s\n", msgb_hexdump(msg)); if (!sgsn->gsup_client) { msgb_free(msg); return -ENOTSUP; } return gsup_client_send(sgsn->gsup_client, msg); } static int gprs_subscr_tx_gsup_error_reply(struct gprs_subscr *subscr, struct osmo_gsup_message *gsup_orig, enum gsm48_gmm_cause cause) { struct osmo_gsup_message gsup_reply = {0}; osmo_strlcpy(gsup_reply.imsi, gsup_orig->imsi, sizeof(gsup_reply.imsi)); gsup_reply.cause = cause; gsup_reply.message_type = OSMO_GSUP_TO_MSGT_ERROR(gsup_orig->message_type); return gprs_subscr_tx_gsup_message(subscr, &gsup_reply); } static int gprs_subscr_handle_gsup_auth_res(struct gprs_subscr *subscr, struct osmo_gsup_message *gsup_msg) { unsigned idx; struct sgsn_subscriber_data *sdata = subscr->sgsn_data; LOGGSUBSCRP(LOGL_INFO, subscr, "Got SendAuthenticationInfoResult, num_auth_vectors = %zu\n", gsup_msg->num_auth_vectors); if (gsup_msg->num_auth_vectors > 0) { memset(sdata->auth_triplets, 0, sizeof(sdata->auth_triplets)); for (idx = 0; idx < ARRAY_SIZE(sdata->auth_triplets); idx++) sdata->auth_triplets[idx].key_seq = GSM_KEY_SEQ_INVAL; } for (idx = 0; idx < gsup_msg->num_auth_vectors; idx++) { size_t key_seq = idx; LOGGSUBSCRP(LOGL_DEBUG, subscr, "Adding auth tuple, cksn = %zu\n", key_seq); if (key_seq >= ARRAY_SIZE(sdata->auth_triplets)) { LOGGSUBSCRP(LOGL_NOTICE, subscr, "Skipping auth triplet with invalid cksn %zu\n", key_seq); continue; } sdata->auth_triplets[key_seq].vec = gsup_msg->auth_vectors[idx]; sdata->auth_triplets[key_seq].key_seq = key_seq; } sdata->auth_triplets_updated = 1; sdata->error_cause = SGSN_ERROR_CAUSE_NONE; gprs_subscr_update_auth_info(subscr); return 0; } static int gprs_subscr_pdp_data_clear(struct gprs_subscr *subscr) { struct sgsn_subscriber_pdp_data *pdp, *pdp2; int count = 0; llist_for_each_entry_safe(pdp, pdp2, &subscr->sgsn_data->pdp_list, list) { llist_del(&pdp->list); talloc_free(pdp); count += 1; } return count; } static struct sgsn_subscriber_pdp_data *gprs_subscr_pdp_data_get_by_id( struct gprs_subscr *subscr, unsigned context_id) { struct sgsn_subscriber_pdp_data *pdp; llist_for_each_entry(pdp, &subscr->sgsn_data->pdp_list, list) { if (pdp->context_id == context_id) return pdp; } return NULL; } static void gprs_subscr_gsup_insert_data(struct gprs_subscr *subscr, struct osmo_gsup_message *gsup_msg) { struct sgsn_subscriber_data *sdata = subscr->sgsn_data; unsigned idx; int rc; if (gsup_msg->msisdn_enc) { if (gsup_msg->msisdn_enc_len > sizeof(sdata->msisdn)) { LOGP(DGPRS, LOGL_ERROR, "MSISDN too long (%zu)\n", gsup_msg->msisdn_enc_len); sdata->msisdn_len = 0; } else { memcpy(sdata->msisdn, gsup_msg->msisdn_enc, gsup_msg->msisdn_enc_len); sdata->msisdn_len = gsup_msg->msisdn_enc_len; } } if (gsup_msg->hlr_enc) { if (gsup_msg->hlr_enc_len > sizeof(sdata->hlr)) { LOGP(DGPRS, LOGL_ERROR, "HLR-Number too long (%zu)\n", gsup_msg->hlr_enc_len); sdata->hlr_len = 0; } else { memcpy(sdata->hlr, gsup_msg->hlr_enc, gsup_msg->hlr_enc_len); sdata->hlr_len = gsup_msg->hlr_enc_len; } } if (gsup_msg->pdp_charg_enc && gsup_msg->pdp_charg_enc_len >= sizeof(sdata->pdp_charg)) { memcpy(&sdata->pdp_charg, gsup_msg->pdp_charg_enc, sizeof(sdata->pdp_charg)); sdata->has_pdp_charg = 1; } else { sdata->has_pdp_charg = 0; } if (gsup_msg->pdp_info_compl) { rc = gprs_subscr_pdp_data_clear(subscr); if (rc > 0) LOGP(DGPRS, LOGL_INFO, "Cleared existing PDP info\n"); } for (idx = 0; idx < gsup_msg->num_pdp_infos; idx++) { struct osmo_gsup_pdp_info *pdp_info = &gsup_msg->pdp_infos[idx]; size_t ctx_id = pdp_info->context_id; struct sgsn_subscriber_pdp_data *pdp_data; if (pdp_info->apn_enc_len >= sizeof(pdp_data->apn_str)-1) { LOGGSUBSCRP(LOGL_ERROR, subscr, "APN too long, context id = %zu, APN = %s\n", ctx_id, osmo_hexdump(pdp_info->apn_enc, pdp_info->apn_enc_len)); continue; } if (pdp_info->qos_enc_len > sizeof(pdp_data->qos_subscribed)) { LOGGSUBSCRP(LOGL_ERROR, subscr, "QoS info too long (%zu)\n", pdp_info->qos_enc_len); continue; } LOGGSUBSCRP(LOGL_INFO, subscr, "Will set PDP info, context id = %zu, APN = %s\n", ctx_id, osmo_hexdump(pdp_info->apn_enc, pdp_info->apn_enc_len)); /* Set PDP info [ctx_id] */ pdp_data = gprs_subscr_pdp_data_get_by_id(subscr, ctx_id); if (!pdp_data) { pdp_data = sgsn_subscriber_pdp_data_alloc(subscr->sgsn_data); pdp_data->context_id = ctx_id; } OSMO_ASSERT(pdp_data != NULL); pdp_data->pdp_type = pdp_info->pdp_type; osmo_apn_to_str(pdp_data->apn_str, pdp_info->apn_enc, pdp_info->apn_enc_len); memcpy(pdp_data->qos_subscribed, pdp_info->qos_enc, pdp_info->qos_enc_len); pdp_data->qos_subscribed_len = pdp_info->qos_enc_len; if (pdp_info->pdp_charg_enc && pdp_info->pdp_charg_enc_len >= sizeof(pdp_data->pdp_charg)) { memcpy(&pdp_data->pdp_charg, pdp_info->pdp_charg_enc, sizeof(pdp_data->pdp_charg)); pdp_data->has_pdp_charg = 1; } else { pdp_data->has_pdp_charg = 0; } } } static int gprs_subscr_handle_gsup_upd_loc_res(struct gprs_subscr *subscr, struct osmo_gsup_message *gsup_msg) { /* contrary to MAP, we allow piggy-backing subscriber data onto * the UPDATE LOCATION RESULT, and don't mandate the use of a * separate nested INSERT SUBSCRIBER DATA transaction */ gprs_subscr_gsup_insert_data(subscr, gsup_msg); subscr->authorized = 1; subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE; subscr->flags |= GPRS_SUBSCRIBER_ENABLE_PURGE; gprs_subscr_update(subscr); return 0; } static int gprs_subscr_handle_gsup_dsd_req(struct gprs_subscr *subscr, struct osmo_gsup_message *gsup_msg) { struct osmo_gsup_message gsup_reply = {0}; if (gsup_msg->cn_domain != OSMO_GSUP_CN_DOMAIN_PS) { LOGGSUBSCRP(LOGL_ERROR, subscr, "Rx GSUP message %s not supported for CS\n", osmo_gsup_message_type_name(gsup_msg->message_type)); gsup_reply.cause = GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL; gsup_reply.message_type = OSMO_GSUP_MSGT_DELETE_DATA_ERROR; } else { gsm0408_gprs_access_cancelled(subscr->sgsn_data->mm, GMM_CAUSE_GPRS_NOTALLOWED); gsup_reply.message_type = OSMO_GSUP_MSGT_DELETE_DATA_RESULT; } return gprs_subscr_tx_gsup_message(subscr, &gsup_reply); } static int gprs_subscr_handle_gsup_isd_req(struct gprs_subscr *subscr, struct osmo_gsup_message *gsup_msg) { struct osmo_gsup_message gsup_reply = {0}; gprs_subscr_gsup_insert_data(subscr, gsup_msg); subscr->authorized = 1; subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE; subscr->flags |= GPRS_SUBSCRIBER_ENABLE_PURGE; gprs_subscr_update(subscr); gsup_reply.message_type = OSMO_GSUP_MSGT_INSERT_DATA_RESULT; return gprs_subscr_tx_gsup_message(subscr, &gsup_reply); } static int check_cause(int cause) { switch (cause) { case GMM_CAUSE_IMSI_UNKNOWN ... GMM_CAUSE_ILLEGAL_ME: case GMM_CAUSE_GPRS_NOTALLOWED ... GMM_CAUSE_NO_GPRS_PLMN: return EACCES; case GMM_CAUSE_MSC_TEMP_NOTREACH ... GMM_CAUSE_CONGESTION: return EHOSTUNREACH; case GMM_CAUSE_SEM_INCORR_MSG ... GMM_CAUSE_PROTO_ERR_UNSPEC: default: return EINVAL; } } static int gprs_subscr_handle_gsup_auth_err(struct gprs_subscr *subscr, struct osmo_gsup_message *gsup_msg) { unsigned idx; struct sgsn_subscriber_data *sdata = subscr->sgsn_data; int cause_err; cause_err = check_cause(gsup_msg->cause); LOGGSUBSCRP(LOGL_DEBUG, subscr, "Send authentication info has failed with cause %d, " "handled as: %s\n", gsup_msg->cause, strerror(cause_err)); switch (cause_err) { case EACCES: LOGGSUBSCRP(LOGL_NOTICE, subscr, "GPRS send auth info req failed, access denied, " "GMM cause = '%s' (%d)\n", get_value_string(gsm48_gmm_cause_names, gsup_msg->cause), gsup_msg->cause); /* Clear auth tuples */ memset(sdata->auth_triplets, 0, sizeof(sdata->auth_triplets)); for (idx = 0; idx < ARRAY_SIZE(sdata->auth_triplets); idx++) sdata->auth_triplets[idx].key_seq = GSM_KEY_SEQ_INVAL; subscr->authorized = 0; sdata->error_cause = gsup_msg->cause; gprs_subscr_update_auth_info(subscr); break; case EHOSTUNREACH: LOGGSUBSCRP(LOGL_NOTICE, subscr, "GPRS send auth info req failed, GMM cause = '%s' (%d)\n", get_value_string(gsm48_gmm_cause_names, gsup_msg->cause), gsup_msg->cause); sdata->error_cause = gsup_msg->cause; gprs_subscr_update_auth_info(subscr); break; default: case EINVAL: LOGGSUBSCRP(LOGL_ERROR, subscr, "GSUP protocol remote error, GMM cause = '%s' (%d)\n", get_value_string(gsm48_gmm_cause_names, gsup_msg->cause), gsup_msg->cause); break; } return -gsup_msg->cause; } static int gprs_subscr_handle_gsup_upd_loc_err(struct gprs_subscr *subscr, struct osmo_gsup_message *gsup_msg) { int cause_err; cause_err = check_cause(gsup_msg->cause); LOGGSUBSCRP(LOGL_DEBUG, subscr, "Update location has failed with cause %d, handled as: %s\n", gsup_msg->cause, strerror(cause_err)); switch (cause_err) { case EACCES: LOGGSUBSCRP(LOGL_NOTICE, subscr, "GPRS update location failed, access denied, " "GMM cause = '%s' (%d)\n", get_value_string(gsm48_gmm_cause_names, gsup_msg->cause), gsup_msg->cause); subscr->authorized = 0; subscr->sgsn_data->error_cause = gsup_msg->cause; gprs_subscr_update_auth_info(subscr); break; case EHOSTUNREACH: LOGGSUBSCRP(LOGL_NOTICE, subscr, "GPRS update location failed, GMM cause = '%s' (%d)\n", get_value_string(gsm48_gmm_cause_names, gsup_msg->cause), gsup_msg->cause); subscr->sgsn_data->error_cause = gsup_msg->cause; gprs_subscr_update_auth_info(subscr); break; default: case EINVAL: LOGGSUBSCRP(LOGL_ERROR, subscr, "GSUP protocol remote error, GMM cause = '%s' (%d)\n", get_value_string(gsm48_gmm_cause_names, gsup_msg->cause), gsup_msg->cause); break; } return -gsup_msg->cause; } static int gprs_subscr_handle_gsup_purge_no_subscr( struct osmo_gsup_message *gsup_msg) { if (OSMO_GSUP_IS_MSGT_ERROR(gsup_msg->message_type)) { LOGGSUPP(LOGL_NOTICE, gsup_msg, "Purge MS has failed with cause '%s' (%d)\n", get_value_string(gsm48_gmm_cause_names, gsup_msg->cause), gsup_msg->cause); return -gsup_msg->cause; } LOGGSUPP(LOGL_INFO, gsup_msg, "Completing purge MS\n"); return 0; } static int gprs_subscr_handle_gsup_purge_res(struct gprs_subscr *subscr, struct osmo_gsup_message *gsup_msg) { LOGGSUBSCRP(LOGL_INFO, subscr, "Completing purge MS\n"); /* Force silent cancellation */ subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE; gprs_subscr_cancel(subscr); return 0; } static int gprs_subscr_handle_gsup_purge_err(struct gprs_subscr *subscr, struct osmo_gsup_message *gsup_msg) { LOGGSUBSCRP(LOGL_NOTICE, subscr, "Purge MS has failed with cause '%s' (%d)\n", get_value_string(gsm48_gmm_cause_names, gsup_msg->cause), gsup_msg->cause); /* In GSM 09.02, 19.1.4.4, the text and the SDL diagram imply that * the subscriber data is not removed if the request has failed. On the * other hand, keeping the subscriber data in either error case * (subscriber unknown, syntactical message error, connection error) * doesn't seem to give any advantage, since the data will be restored * on the next Attach Request anyway. * This approach ensures, that the subscriber record will not stick if * an error happens. */ /* TODO: Check whether this behaviour is acceptable and either just * remove this TODO-notice or change the implementation to not delete * the subscriber data (eventually resetting the ENABLE_PURGE flag and * restarting the expiry timer based on the cause). * * Subscriber Unknown: cancel subscr * Temporary network problems: do nothing (handled by timer based retry) * Message problems (syntax, nyi, ...): cancel subscr (retry won't help) */ gprs_subscr_handle_gsup_purge_res(subscr, gsup_msg); return -gsup_msg->cause; } static int gprs_subscr_handle_loc_cancel_req(struct gprs_subscr *subscr, struct osmo_gsup_message *gsup_msg) { struct osmo_gsup_message gsup_reply = {0}; int is_update_procedure = !gsup_msg->cancel_type || gsup_msg->cancel_type == OSMO_GSUP_CANCEL_TYPE_UPDATE; LOGGSUBSCRP(LOGL_INFO, subscr, "Cancelling MS subscriber (%s)\n", is_update_procedure ? "update procedure" : "subscription withdraw"); gsup_reply.message_type = OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT; gprs_subscr_tx_gsup_message(subscr, &gsup_reply); if (is_update_procedure) subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE; else /* Since a withdraw cause is not specified, just abort the * current attachment. The following re-attachment should then * be rejected with a proper cause value. */ subscr->sgsn_data->error_cause = GMM_CAUSE_IMPL_DETACHED; gprs_subscr_cancel(subscr); return 0; } static int gprs_subscr_handle_unknown_imsi(struct osmo_gsup_message *gsup_msg) { if (OSMO_GSUP_IS_MSGT_REQUEST(gsup_msg->message_type)) { gprs_subscr_tx_gsup_error_reply(NULL, gsup_msg, GMM_CAUSE_IMSI_UNKNOWN); LOGP(DGPRS, LOGL_NOTICE, "Unknown IMSI %s, discarding GSUP request " "of type 0x%02x\n", gsup_msg->imsi, gsup_msg->message_type); } else if (OSMO_GSUP_IS_MSGT_ERROR(gsup_msg->message_type)) { LOGP(DGPRS, LOGL_NOTICE, "Unknown IMSI %s, discarding GSUP error " "of type 0x%02x, cause '%s' (%d)\n", gsup_msg->imsi, gsup_msg->message_type, get_value_string(gsm48_gmm_cause_names, gsup_msg->cause), gsup_msg->cause); } else { LOGP(DGPRS, LOGL_NOTICE, "Unknown IMSI %s, discarding GSUP response " "of type 0x%02x\n", gsup_msg->imsi, gsup_msg->message_type); } return -GMM_CAUSE_IMSI_UNKNOWN; } int gprs_subscr_rx_gsup_message(struct msgb *msg) { uint8_t *data = msgb_l2(msg); size_t data_len = msgb_l2len(msg); int rc = 0; struct osmo_gsup_message gsup_msg = {0}; struct gprs_subscr *subscr; rc = osmo_gsup_decode(data, data_len, &gsup_msg); if (rc < 0) { LOGP(DGPRS, LOGL_ERROR, "decoding GSUP message fails with error '%s' (%d)\n", get_value_string(gsm48_gmm_cause_names, -rc), -rc); return rc; } if (!gsup_msg.imsi[0]) { LOGP(DGPRS, LOGL_ERROR, "Missing IMSI in GSUP message\n"); if (OSMO_GSUP_IS_MSGT_REQUEST(gsup_msg.message_type)) gprs_subscr_tx_gsup_error_reply(NULL, &gsup_msg, GMM_CAUSE_INV_MAND_INFO); return -GMM_CAUSE_INV_MAND_INFO; } if (!gsup_msg.cause && OSMO_GSUP_IS_MSGT_ERROR(gsup_msg.message_type)) gsup_msg.cause = GMM_CAUSE_NET_FAIL; subscr = gprs_subscr_get_by_imsi(gsup_msg.imsi); if (!subscr) { switch (gsup_msg.message_type) { case OSMO_GSUP_MSGT_PURGE_MS_RESULT: case OSMO_GSUP_MSGT_PURGE_MS_ERROR: return gprs_subscr_handle_gsup_purge_no_subscr(&gsup_msg); default: return gprs_subscr_handle_unknown_imsi(&gsup_msg); } } LOGGSUBSCRP(LOGL_INFO, subscr, "Received GSUP message %s\n", osmo_gsup_message_type_name(gsup_msg.message_type)); switch (gsup_msg.message_type) { case OSMO_GSUP_MSGT_LOCATION_CANCEL_REQUEST: rc = gprs_subscr_handle_loc_cancel_req(subscr, &gsup_msg); break; case OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT: rc = gprs_subscr_handle_gsup_auth_res(subscr, &gsup_msg); break; case OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR: rc = gprs_subscr_handle_gsup_auth_err(subscr, &gsup_msg); break; case OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: rc = gprs_subscr_handle_gsup_upd_loc_res(subscr, &gsup_msg); break; case OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR: rc = gprs_subscr_handle_gsup_upd_loc_err(subscr, &gsup_msg); break; case OSMO_GSUP_MSGT_PURGE_MS_ERROR: rc = gprs_subscr_handle_gsup_purge_err(subscr, &gsup_msg); break; case OSMO_GSUP_MSGT_PURGE_MS_RESULT: rc = gprs_subscr_handle_gsup_purge_res(subscr, &gsup_msg); break; case OSMO_GSUP_MSGT_INSERT_DATA_REQUEST: rc = gprs_subscr_handle_gsup_isd_req(subscr, &gsup_msg); break; case OSMO_GSUP_MSGT_DELETE_DATA_REQUEST: rc = gprs_subscr_handle_gsup_dsd_req(subscr, &gsup_msg); break; default: LOGGSUBSCRP(LOGL_ERROR, subscr, "Rx GSUP message %s not valid at SGSN\n", osmo_gsup_message_type_name(gsup_msg.message_type)); if (OSMO_GSUP_IS_MSGT_REQUEST(gsup_msg.message_type)) gprs_subscr_tx_gsup_error_reply( subscr, &gsup_msg, GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL); rc = -GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL; break; }; gprs_subscr_put(subscr); return rc; } int gprs_subscr_purge(struct gprs_subscr *subscr) { struct sgsn_subscriber_data *sdata = subscr->sgsn_data; struct osmo_gsup_message gsup_msg = {0}; LOGGSUBSCRP(LOGL_INFO, subscr, "purging MS subscriber\n"); gsup_msg.message_type = OSMO_GSUP_MSGT_PURGE_MS_REQUEST; /* Provide the HLR number in case it is known */ gsup_msg.hlr_enc_len = sdata->hlr_len; gsup_msg.hlr_enc = sdata->hlr; return gprs_subscr_tx_gsup_message(subscr, &gsup_msg); } static int gprs_subscr_query_auth_info(struct gprs_subscr *subscr, const uint8_t *auts, const uint8_t *auts_rand) { struct osmo_gsup_message gsup_msg = {0}; /* Make sure we have a complete resync or clearly no resync. */ OSMO_ASSERT((auts != NULL) == (auts_rand != NULL)); LOGGSUBSCRP(LOGL_INFO, subscr, "requesting auth info%s\n", auts ? " with AUTS (UMTS Resynch)" : ""); gsup_msg.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST; gsup_msg.auts = auts; gsup_msg.rand = auts_rand; return gprs_subscr_tx_gsup_message(subscr, &gsup_msg); } int gprs_subscr_location_update(struct gprs_subscr *subscr) { struct osmo_gsup_message gsup_msg = {0}; LOGGSUBSCRP(LOGL_INFO, subscr, "subscriber data is not available\n"); gsup_msg.message_type = OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST; return gprs_subscr_tx_gsup_message(subscr, &gsup_msg); } void gprs_subscr_update(struct gprs_subscr *subscr) { LOGGSUBSCRP(LOGL_DEBUG, subscr, "Updating subscriber data\n"); subscr->flags &= ~GPRS_SUBSCRIBER_UPDATE_LOCATION_PENDING; subscr->flags &= ~GPRS_SUBSCRIBER_FIRST_CONTACT; if (subscr->sgsn_data->mm) sgsn_update_subscriber_data(subscr->sgsn_data->mm); } void gprs_subscr_update_auth_info(struct gprs_subscr *subscr) { LOGGSUBSCRP(LOGL_DEBUG, subscr, "Updating subscriber authentication info\n"); subscr->flags &= ~GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING; subscr->flags &= ~GPRS_SUBSCRIBER_FIRST_CONTACT; if (subscr->sgsn_data->mm) sgsn_update_subscriber_data(subscr->sgsn_data->mm); } struct gprs_subscr *gprs_subscr_get_or_create_by_mmctx(struct sgsn_mm_ctx *mmctx) { struct gprs_subscr *subscr = NULL; if (mmctx->subscr) return gprs_subscr_get(mmctx->subscr); if (mmctx->imsi[0]) subscr = gprs_subscr_get_by_imsi(mmctx->imsi); if (!subscr) { subscr = gprs_subscr_get_or_create(mmctx->imsi); subscr->flags |= GPRS_SUBSCRIBER_FIRST_CONTACT; subscr->flags &= ~GPRS_SUBSCRIBER_ENABLE_PURGE; } osmo_strlcpy(subscr->imei, mmctx->imei, sizeof(subscr->imei)); if (subscr->lac != mmctx->ra.lac) subscr->lac = mmctx->ra.lac; subscr->sgsn_data->mm = mmctx; mmctx->subscr = gprs_subscr_get(subscr); return subscr; } int gprs_subscr_request_update_location(struct sgsn_mm_ctx *mmctx) { struct gprs_subscr *subscr = NULL; int rc; LOGMMCTXP(LOGL_DEBUG, mmctx, "Requesting subscriber data update\n"); subscr = gprs_subscr_get_or_create_by_mmctx(mmctx); subscr->flags |= GPRS_SUBSCRIBER_UPDATE_LOCATION_PENDING; rc = gprs_subscr_location_update(subscr); gprs_subscr_put(subscr); return rc; } /*! \brief Send Update Auth Info request via GSUP, with or without resync. * \param[in] mmctx MM context to request authentication tuples for. * \param[in] auts 14 octet AUTS token for UMTS resync, or NULL. * \param[in] auts_rand 16 octet Random token for UMTS resync, or NULL. * In case of normal Authentication Info request, both \a auts and \a auts_rand * must be NULL. For resync, both must be non-NULL. */ int gprs_subscr_request_auth_info(struct sgsn_mm_ctx *mmctx, const uint8_t *auts, const uint8_t *auts_rand) { struct gprs_subscr *subscr = NULL; int rc; LOGMMCTXP(LOGL_DEBUG, mmctx, "Requesting subscriber authentication info\n"); subscr = gprs_subscr_get_or_create_by_mmctx(mmctx); subscr->flags |= GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING; rc = gprs_subscr_query_auth_info(subscr, auts, auts_rand); gprs_subscr_put(subscr); return rc; } static void gprs_subscr_free(struct gprs_subscr *gsub) { llist_del(&gsub->entry); talloc_free(gsub); } struct gprs_subscr *_gprs_subscr_get(struct gprs_subscr *gsub, const char *file, int line) { OSMO_ASSERT(gsub->use_count < INT_MAX); gsub->use_count++; LOGPSRC(DREF, LOGL_DEBUG, file, line, "subscr %s usage increases to: %d\n", gsub->imsi, gsub->use_count); return gsub; } struct gprs_subscr *_gprs_subscr_put(struct gprs_subscr *gsub, const char *file, int line) { gsub->use_count--; LOGPSRC(DREF, gsub->use_count >= 0? LOGL_DEBUG : LOGL_ERROR, file, line, "subscr %s usage decreases to: %d%s\n", gsub->imsi, gsub->use_count, gsub->keep_in_ram? ", keep-in-ram flag is set" : ""); if (gsub->use_count > 0) return gsub; if (gsub->keep_in_ram) return gsub; gprs_subscr_free(gsub); return NULL; } osmo-sgsn-1.3.0/src/gprs/gprs_utils.c000066400000000000000000000137671327264017000175610ustar00rootroot00000000000000/* GPRS utility functions */ /* (C) 2010 by Harald Welte * (C) 2010-2014 by On-Waves * (C) 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 . * */ #include #include #include #include #include #include #include /* FIXME: this needs to go to libosmocore/msgb.c */ struct msgb *gprs_msgb_copy(const struct msgb *msg, const char *name) { struct libgb_msgb_cb *old_cb, *new_cb; struct msgb *new_msg; new_msg = msgb_alloc(msg->data_len, name); if (!new_msg) return NULL; /* copy data */ memcpy(new_msg->_data, msg->_data, new_msg->data_len); /* copy header */ new_msg->len = msg->len; new_msg->data += msg->data - msg->_data; new_msg->head += msg->head - msg->_data; new_msg->tail += msg->tail - msg->_data; if (msg->l1h) new_msg->l1h = new_msg->_data + (msg->l1h - msg->_data); if (msg->l2h) new_msg->l2h = new_msg->_data + (msg->l2h - msg->_data); if (msg->l3h) new_msg->l3h = new_msg->_data + (msg->l3h - msg->_data); if (msg->l4h) new_msg->l4h = new_msg->_data + (msg->l4h - msg->_data); /* copy GB specific data */ old_cb = LIBGB_MSGB_CB(msg); new_cb = LIBGB_MSGB_CB(new_msg); if (old_cb->bssgph) new_cb->bssgph = new_msg->_data + (old_cb->bssgph - msg->_data); if (old_cb->llch) new_cb->llch = new_msg->_data + (old_cb->llch - msg->_data); /* bssgp_cell_id is a pointer into the old msgb, so we need to make * it a pointer into the new msgb */ if (old_cb->bssgp_cell_id) new_cb->bssgp_cell_id = new_msg->_data + (old_cb->bssgp_cell_id - msg->_data); new_cb->nsei = old_cb->nsei; new_cb->bvci = old_cb->bvci; new_cb->tlli = old_cb->tlli; return new_msg; } /* TODO: Move this to libosmocore/msgb.c */ int gprs_msgb_resize_area(struct msgb *msg, uint8_t *area, size_t old_size, size_t new_size) { int rc; uint8_t *rest = area + old_size; int rest_len = msg->len - old_size - (area - msg->data); int delta_size = (int)new_size - (int)old_size; if (delta_size == 0) return 0; if (delta_size > 0) { rc = msgb_trim(msg, msg->len + delta_size); if (rc < 0) return rc; } memmove(area + new_size, area + old_size, rest_len); if (msg->l1h >= rest) msg->l1h += delta_size; if (msg->l2h >= rest) msg->l2h += delta_size; if (msg->l3h >= rest) msg->l3h += delta_size; if (msg->l4h >= rest) msg->l4h += delta_size; if (delta_size < 0) msgb_trim(msg, msg->len + delta_size); return 0; } int gprs_str_to_apn(uint8_t *apn_enc, size_t max_len, const char *str) { uint8_t *last_len_field; int len; /* Can we even write the length field to the output? */ if (max_len == 0) return -1; /* Remember where we need to put the length once we know it */ last_len_field = apn_enc; len = 1; apn_enc += 1; while (str[0]) { if (len >= max_len) return -1; if (str[0] == '.') { *last_len_field = (apn_enc - last_len_field) - 1; last_len_field = apn_enc; } else { *apn_enc = str[0]; } apn_enc += 1; str += 1; len += 1; } *last_len_field = (apn_enc - last_len_field) - 1; return len; } /* GSM 04.08, 10.5.7.3 GPRS Timer */ int gprs_tmr_to_secs(uint8_t tmr) { switch (tmr & GPRS_TMR_UNIT_MASK) { case GPRS_TMR_2SECONDS: return 2 * (tmr & GPRS_TMR_FACT_MASK); default: case GPRS_TMR_MINUTE: return 60 * (tmr & GPRS_TMR_FACT_MASK); case GPRS_TMR_6MINUTE: return 360 * (tmr & GPRS_TMR_FACT_MASK); case GPRS_TMR_DEACTIVATED: return -1; } } /* This functions returns a tmr value such that * - f is monotonic * - f(s) <= s * - f(s) == s if a tmr exists with s = gprs_tmr_to_secs(tmr) * - the best possible resolution is used * where * f(s) = gprs_tmr_to_secs(gprs_secs_to_tmr_floor(s)) */ uint8_t gprs_secs_to_tmr_floor(int secs) { if (secs < 0) return GPRS_TMR_DEACTIVATED; if (secs < 2 * 32) return GPRS_TMR_2SECONDS | (secs / 2); if (secs < 60 * 2) /* Ensure monotonicity */ return GPRS_TMR_2SECONDS | GPRS_TMR_FACT_MASK; if (secs < 60 * 32) return GPRS_TMR_MINUTE | (secs / 60); if (secs < 360 * 6) /* Ensure monotonicity */ return GPRS_TMR_MINUTE | GPRS_TMR_FACT_MASK; if (secs < 360 * 32) return GPRS_TMR_6MINUTE | (secs / 360); return GPRS_TMR_6MINUTE | GPRS_TMR_FACT_MASK; } /* GSM 04.08, 10.5.1.4 */ int gprs_is_mi_tmsi(const uint8_t *value, size_t value_len) { if (value_len != GSM48_TMSI_LEN) return 0; if (!value || (value[0] & GSM_MI_TYPE_MASK) != GSM_MI_TYPE_TMSI) return 0; return 1; } /* GSM 04.08, 10.5.1.4 */ int gprs_is_mi_imsi(const uint8_t *value, size_t value_len) { if (value_len == 0) return 0; if (!value || (value[0] & GSM_MI_TYPE_MASK) != GSM_MI_TYPE_IMSI) return 0; return 1; } int gprs_parse_mi_tmsi(const uint8_t *value, size_t value_len, uint32_t *tmsi) { uint32_t tmsi_be; if (!gprs_is_mi_tmsi(value, value_len)) return 0; memcpy(&tmsi_be, value + 1, sizeof(tmsi_be)); *tmsi = ntohl(tmsi_be); return 1; } void gprs_parse_tmsi(const uint8_t *value, uint32_t *tmsi) { uint32_t tmsi_be; memcpy(&tmsi_be, value, sizeof(tmsi_be)); *tmsi = ntohl(tmsi_be); } int gprs_ra_id_equals(const struct gprs_ra_id *id1, const struct gprs_ra_id *id2) { return (id1->mcc == id2->mcc && !osmo_mnc_cmp(id1->mnc, id1->mnc_3_digits, id2->mnc, id2->mnc_3_digits) && id1->lac == id2->lac && id1->rac == id2->rac); } osmo-sgsn-1.3.0/src/gprs/gsup_client.c000066400000000000000000000212631327264017000176700ustar00rootroot00000000000000/* Generic Subscriber Update Protocol client */ /* (C) 2014-2016 by Sysmocom s.f.m.c. GmbH * All Rights Reserved * * Author: Jacob Erlbeck * 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 #include #include extern void *tall_bsc_ctx; static void start_test_procedure(struct gsup_client *gsupc); static void gsup_client_send_ping(struct gsup_client *gsupc) { struct msgb *msg = gsup_client_msgb_alloc(); msg->l2h = msgb_put(msg, 1); msg->l2h[0] = IPAC_MSGT_PING; ipa_msg_push_header(msg, IPAC_PROTO_IPACCESS); ipa_client_conn_send(gsupc->link, msg); } static int gsup_client_connect(struct gsup_client *gsupc) { int rc; if (gsupc->is_connected) return 0; if (osmo_timer_pending(&gsupc->connect_timer)) { LOGP(DLGSUP, LOGL_DEBUG, "GSUP connect: connect timer already running\n"); osmo_timer_del(&gsupc->connect_timer); } if (osmo_timer_pending(&gsupc->ping_timer)) { LOGP(DLGSUP, LOGL_DEBUG, "GSUP connect: ping timer already running\n"); osmo_timer_del(&gsupc->ping_timer); } if (ipa_client_conn_clear_queue(gsupc->link) > 0) LOGP(DLGSUP, LOGL_DEBUG, "GSUP connect: discarded stored messages\n"); rc = ipa_client_conn_open(gsupc->link); if (rc >= 0) { LOGP(DLGSUP, LOGL_NOTICE, "GSUP connecting to %s:%d\n", gsupc->link->addr, gsupc->link->port); return 0; } LOGP(DLGSUP, LOGL_ERROR, "GSUP failed to connect to %s:%d: %s\n", gsupc->link->addr, gsupc->link->port, strerror(-rc)); if (rc == -EBADF || rc == -ENOTSOCK || rc == -EAFNOSUPPORT || rc == -EINVAL) return rc; osmo_timer_schedule(&gsupc->connect_timer, GSUP_CLIENT_RECONNECT_INTERVAL, 0); LOGP(DLGSUP, LOGL_INFO, "Scheduled timer to retry GSUP connect to %s:%d\n", gsupc->link->addr, gsupc->link->port); return 0; } static void connect_timer_cb(void *gsupc_) { struct gsup_client *gsupc = gsupc_; if (gsupc->is_connected) return; gsup_client_connect(gsupc); } static void client_send(struct gsup_client *gsupc, int proto_ext, struct msgb *msg_tx) { ipa_prepend_header_ext(msg_tx, proto_ext); ipa_msg_push_header(msg_tx, IPAC_PROTO_OSMO); ipa_client_conn_send(gsupc->link, msg_tx); /* msg_tx is now queued and will be freed. */ } static void gsup_client_oap_register(struct gsup_client *gsupc) { struct msgb *msg_tx; int rc; rc = oap_client_register(&gsupc->oap_state, &msg_tx); if ((rc < 0) || (!msg_tx)) { LOGP(DLGSUP, LOGL_ERROR, "GSUP OAP set up, but cannot register.\n"); return; } client_send(gsupc, IPAC_PROTO_EXT_OAP, msg_tx); } static void gsup_client_updown_cb(struct ipa_client_conn *link, int up) { struct gsup_client *gsupc = link->data; LOGP(DLGSUP, LOGL_INFO, "GSUP link to %s:%d %s\n", link->addr, link->port, up ? "UP" : "DOWN"); gsupc->is_connected = up; if (up) { start_test_procedure(gsupc); if (gsupc->oap_state.state == OAP_INITIALIZED) gsup_client_oap_register(gsupc); osmo_timer_del(&gsupc->connect_timer); } else { osmo_timer_del(&gsupc->ping_timer); osmo_timer_schedule(&gsupc->connect_timer, GSUP_CLIENT_RECONNECT_INTERVAL, 0); } } static int gsup_client_oap_handle(struct gsup_client *gsupc, struct msgb *msg_rx) { int rc; struct msgb *msg_tx; /* If the oap_state is disabled, this will reject the messages. */ rc = oap_client_handle(&gsupc->oap_state, msg_rx, &msg_tx); msgb_free(msg_rx); if (rc < 0) return rc; if (msg_tx) client_send(gsupc, IPAC_PROTO_EXT_OAP, msg_tx); return 0; } static int gsup_client_read_cb(struct ipa_client_conn *link, struct msgb *msg) { struct ipaccess_head *hh = (struct ipaccess_head *) msg->data; struct ipaccess_head_ext *he = (struct ipaccess_head_ext *) msgb_l2(msg); struct gsup_client *gsupc = (struct gsup_client *)link->data; int rc; struct ipaccess_unit ipa_dev = { /* see gsup_client_create() on const vs non-const */ .unit_name = (char*)gsupc->unit_name, }; OSMO_ASSERT(ipa_dev.unit_name); msg->l2h = &hh->data[0]; rc = ipaccess_bts_handle_ccm(link, &ipa_dev, msg); if (rc < 0) { LOGP(DLGSUP, LOGL_NOTICE, "GSUP received an invalid IPA/CCM message from %s:%d\n", link->addr, link->port); /* Link has been closed */ gsupc->is_connected = 0; msgb_free(msg); return -1; } if (rc == 1) { uint8_t msg_type = *(msg->l2h); /* CCM message */ if (msg_type == IPAC_MSGT_PONG) { LOGP(DLGSUP, LOGL_DEBUG, "GSUP receiving PONG\n"); gsupc->got_ipa_pong = 1; } msgb_free(msg); return 0; } if (hh->proto != IPAC_PROTO_OSMO) goto invalid; if (!he || msgb_l2len(msg) < sizeof(*he)) goto invalid; msg->l2h = &he->data[0]; if (he->proto == IPAC_PROTO_EXT_GSUP) { OSMO_ASSERT(gsupc->read_cb != NULL); gsupc->read_cb(gsupc, msg); /* expecting read_cb() to free msg */ } else if (he->proto == IPAC_PROTO_EXT_OAP) { return gsup_client_oap_handle(gsupc, msg); /* gsup_client_oap_handle frees msg */ } else goto invalid; return 0; invalid: LOGP(DLGSUP, LOGL_NOTICE, "GSUP received an invalid IPA message from %s:%d, size = %d\n", link->addr, link->port, msgb_length(msg)); msgb_free(msg); return -1; } static void ping_timer_cb(void *gsupc_) { struct gsup_client *gsupc = gsupc_; LOGP(DLGSUP, LOGL_INFO, "GSUP ping callback (%s, %s PONG)\n", gsupc->is_connected ? "connected" : "not connected", gsupc->got_ipa_pong ? "got" : "didn't get"); if (gsupc->got_ipa_pong) { start_test_procedure(gsupc); return; } LOGP(DLGSUP, LOGL_NOTICE, "GSUP ping timed out, reconnecting\n"); ipa_client_conn_close(gsupc->link); gsupc->is_connected = 0; gsup_client_connect(gsupc); } static void start_test_procedure(struct gsup_client *gsupc) { osmo_timer_setup(&gsupc->ping_timer, ping_timer_cb, gsupc); gsupc->got_ipa_pong = 0; osmo_timer_schedule(&gsupc->ping_timer, GSUP_CLIENT_PING_INTERVAL, 0); LOGP(DLGSUP, LOGL_DEBUG, "GSUP sending PING\n"); gsup_client_send_ping(gsupc); } struct gsup_client *gsup_client_create(const char *unit_name, const char *ip_addr, unsigned int tcp_port, gsup_client_read_cb_t read_cb, struct oap_client_config *oapc_config) { struct gsup_client *gsupc; int rc; gsupc = talloc_zero(tall_bsc_ctx, struct gsup_client); OSMO_ASSERT(gsupc); /* struct ipaccess_unit has a non-const unit_name, so let's copy to be * able to have a non-const unit_name here as well. To not taint the * public gsup_client API, let's store it in a const char* anyway. */ gsupc->unit_name = talloc_strdup(gsupc, unit_name); OSMO_ASSERT(gsupc->unit_name); /* a NULL oapc_config will mark oap_state disabled. */ rc = oap_client_init(oapc_config, &gsupc->oap_state); if (rc != 0) goto failed; gsupc->link = ipa_client_conn_create(gsupc, /* no e1inp */ NULL, 0, ip_addr, tcp_port, gsup_client_updown_cb, gsup_client_read_cb, /* default write_cb */ NULL, gsupc); if (!gsupc->link) goto failed; osmo_timer_setup(&gsupc->connect_timer, connect_timer_cb, gsupc); rc = gsup_client_connect(gsupc); if (rc < 0) goto failed; gsupc->read_cb = read_cb; return gsupc; failed: gsup_client_destroy(gsupc); return NULL; } void gsup_client_destroy(struct gsup_client *gsupc) { osmo_timer_del(&gsupc->connect_timer); osmo_timer_del(&gsupc->ping_timer); if (gsupc->link) { ipa_client_conn_close(gsupc->link); ipa_client_conn_destroy(gsupc->link); gsupc->link = NULL; } talloc_free(gsupc); } int gsup_client_send(struct gsup_client *gsupc, struct msgb *msg) { if (!gsupc) { LOGP(DGPRS, LOGL_NOTICE, "No GSUP client, unable to " "send %s\n", msgb_hexdump(msg)); msgb_free(msg); return -ENOTCONN; } if (!gsupc->is_connected) { LOGP(DGPRS, LOGL_NOTICE, "GSUP not connected, unable to " "send %s\n", msgb_hexdump(msg)); msgb_free(msg); return -EAGAIN; } client_send(gsupc, IPAC_PROTO_EXT_GSUP, msg); return 0; } struct msgb *gsup_client_msgb_alloc(void) { return msgb_alloc_headroom(4000, 64, __func__); } osmo-sgsn-1.3.0/src/gprs/gtphub.c000066400000000000000000002324421327264017000166500ustar00rootroot00000000000000/* GTP Hub Implementation */ /* (C) 2015 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const int GTPH_GC_TICK_SECONDS = 1; void *osmo_gtphub_ctx; /* Convenience makro, note: only within this C file. */ #define LOG(level, fmt, args...) \ LOGP(DGTPHUB, level, fmt, ##args) #define ZERO_STRUCT(struct_pointer) memset(struct_pointer, '\0', \ sizeof(*(struct_pointer))) /* TODO move this to osmocom/core/select.h ? */ typedef int (*osmo_fd_cb_t)(struct osmo_fd *fd, unsigned int what); /* TODO move this to osmocom/core/linuxlist.h ? */ #define __llist_first(head) (((head)->next == (head)) ? NULL : (head)->next) #define llist_first(head, type, entry) \ llist_entry(__llist_first(head), type, entry) #define __llist_last(head) (((head)->next == (head)) ? NULL : (head)->prev) #define llist_last(head, type, entry) \ llist_entry(__llist_last(head), type, entry) /* TODO move GTP header stuff to openggsn/gtp/ ? See gtp_decaps*() */ enum gtp_rc { GTP_RC_UNKNOWN = 0, GTP_RC_TINY = 1, /* no IEs (like ping/pong) */ GTP_RC_PDU_C = 2, /* a real packet with IEs */ GTP_RC_PDU_U = 3, /* a real packet with User data */ GTP_RC_TOOSHORT = -1, GTP_RC_UNSUPPORTED_VERSION = -2, GTP_RC_INVALID_IE = -3, }; struct gtp_packet_desc { union gtp_packet *data; int data_len; int header_len; int version; uint8_t type; uint16_t seq; uint32_t header_tei_rx; uint32_t header_tei; int rc; /* enum gtp_rc */ unsigned int plane_idx; unsigned int side_idx; struct gtphub_tunnel *tun; time_t timestamp; union gtpie_member *ie[GTPIE_SIZE]; }; struct pending_delete { struct llist_head entry; struct expiring_item expiry_entry; struct gtphub_tunnel *tun; uint8_t teardown_ind; uint8_t nsapi; }; /* counters */ enum gtphub_counters_io { GTPH_CTR_PKTS_IN = 0, GTPH_CTR_PKTS_OUT, GTPH_CTR_BYTES_IN, GTPH_CTR_BYTES_OUT }; static const struct rate_ctr_desc gtphub_counters_io_desc[] = { { "packets.in", "Packets ( In)" }, { "packets.out", "Packets (Out)" }, { "bytes.in", "Bytes ( In)" }, { "bytes.out", "Bytes (Out)" }, }; static const struct rate_ctr_group_desc gtphub_ctrg_io_desc = { .group_name_prefix = "gtphub:bind", .group_description = "I/O Statistics", .num_ctr = ARRAY_SIZE(gtphub_counters_io_desc), .ctr_desc = gtphub_counters_io_desc, .class_id = OSMO_STATS_CLASS_GLOBAL, }; /* support */ static const char *gtp_type_str(uint8_t type) { switch (type) { case 1: return " (Echo Request)"; case 2: return " (Echo Response)"; case 16: return " (Create PDP Ctx Request)"; case 17: return " (Create PDP Ctx Response)"; case 18: return " (Update PDP Ctx Request)"; case 19: return " (Update PDP Ctx Response)"; case 20: return " (Delete PDP Ctx Request)"; case 21: return " (Delete PDP Ctx Response)"; case 255: return " (User Data)"; default: return ""; } } void gsn_addr_copy(struct gsn_addr *gsna, const struct gsn_addr *src) { *gsna = *src; } int gsn_addr_from_sockaddr(struct gsn_addr *gsna, uint16_t *port, const struct osmo_sockaddr *sa) { char addr_str[256]; char port_str[6]; if (osmo_sockaddr_to_strs(addr_str, sizeof(addr_str), port_str, sizeof(port_str), sa, (NI_NUMERICHOST | NI_NUMERICSERV)) != 0) { return -1; } if (port) *port = atoi(port_str); return gsn_addr_from_str(gsna, addr_str); } int gsn_addr_from_str(struct gsn_addr *gsna, const char *numeric_addr_str) { if ((!gsna) || (!numeric_addr_str)) return -1; int af = AF_INET; gsna->len = 4; const char *pos = numeric_addr_str; for (; *pos; pos++) { if (*pos == ':') { af = AF_INET6; gsna->len = 16; break; } } int rc = inet_pton(af, numeric_addr_str, gsna->buf); if (rc != 1) { LOG(LOGL_ERROR, "Cannot resolve numeric address: '%s'\n", numeric_addr_str); return -1; } return 0; } const char *gsn_addr_to_str(const struct gsn_addr *gsna) { static char buf[INET6_ADDRSTRLEN + 1]; return gsn_addr_to_strb(gsna, buf, sizeof(buf)); } const char *gsn_addr_to_strb(const struct gsn_addr *gsna, char *strbuf, int strbuf_len) { int af; switch (gsna->len) { case 4: af = AF_INET; break; case 16: af = AF_INET6; break; default: return NULL; } const char *r = inet_ntop(af, gsna->buf, strbuf, strbuf_len); if (!r) { LOG(LOGL_ERROR, "Cannot convert gsn_addr to string:" " %s: len=%d, buf=%s\n", strerror(errno), (int)gsna->len, osmo_hexdump(gsna->buf, sizeof(gsna->buf))); } return r; } int gsn_addr_same(const struct gsn_addr *a, const struct gsn_addr *b) { if (a == b) return 1; if ((!a) || (!b)) return 0; if (a->len != b->len) return 0; return (memcmp(a->buf, b->buf, a->len) == 0)? 1 : 0; } static int gsn_addr_get(struct gsn_addr *gsna, const struct gtp_packet_desc *p, int idx) { if (p->rc != GTP_RC_PDU_C) return -1; unsigned int len; /* gtpie.h fails to declare gtpie_gettlv()'s first arg as const. */ if (gtpie_gettlv((union gtpie_member**)p->ie, GTPIE_GSN_ADDR, idx, &len, gsna->buf, sizeof(gsna->buf)) != 0) return -1; gsna->len = len; return 0; } static int gsn_addr_put(const struct gsn_addr *gsna, struct gtp_packet_desc *p, int idx) { if (p->rc != GTP_RC_PDU_C) return -1; int ie_idx; ie_idx = gtpie_getie(p->ie, GTPIE_GSN_ADDR, idx); if (ie_idx < 0) return -1; struct gtpie_tlv *ie = &p->ie[ie_idx]->tlv; int ie_l = ntoh16(ie->l); if (ie_l != gsna->len) { LOG(LOGL_ERROR, "Not implemented:" " replace an IE address of different size:" " replace %d with %d\n", (int)ie_l, (int)gsna->len); return -1; } memcpy(ie->v, gsna->buf, (int)ie_l); return 0; } /* Validate GTP version 0 data; analogous to validate_gtp1_header(), see there. */ void validate_gtp0_header(struct gtp_packet_desc *p) { const struct gtp0_header *pheader = &(p->data->gtp0.h); p->rc = GTP_RC_UNKNOWN; p->header_len = 0; OSMO_ASSERT(p->data_len >= 1); OSMO_ASSERT(p->version == 0); if (p->data_len < GTP0_HEADER_SIZE) { LOG(LOGL_ERROR, "GTP0 packet too short: %d\n", p->data_len); p->rc = GTP_RC_TOOSHORT; return; } p->type = ntoh8(pheader->type); p->seq = ntoh16(pheader->seq); p->header_tei_rx = 0; /* TODO */ p->header_tei = p->header_tei_rx; if (p->data_len == GTP0_HEADER_SIZE) { p->rc = GTP_RC_TINY; p->header_len = GTP0_HEADER_SIZE; return; } /* Check packet length field versus length of packet */ if (p->data_len != (ntoh16(pheader->length) + GTP0_HEADER_SIZE)) { LOG(LOGL_ERROR, "GTP packet length field (%d + %d) does not" " match actual length (%d)\n", GTP0_HEADER_SIZE, (int)ntoh16(pheader->length), p->data_len); p->rc = GTP_RC_TOOSHORT; return; } LOG(LOGL_DEBUG, "GTP v0 TID = %" PRIu64 "\n", pheader->tid); p->header_len = GTP0_HEADER_SIZE; p->rc = GTP_RC_PDU_C; } /* Validate GTP version 1 data, and update p->rc with the result, as well as * p->header_len in case of a valid header. */ void validate_gtp1_header(struct gtp_packet_desc *p) { const struct gtp1_header_long *pheader = &(p->data->gtp1l.h); p->rc = GTP_RC_UNKNOWN; p->header_len = 0; OSMO_ASSERT(p->data_len >= 1); OSMO_ASSERT(p->version == 1); if ((p->data_len < GTP1_HEADER_SIZE_LONG) && (p->data_len != GTP1_HEADER_SIZE_SHORT)){ LOG(LOGL_ERROR, "GTP packet too short: %d\n", p->data_len); p->rc = GTP_RC_TOOSHORT; return; } p->type = ntoh8(pheader->type); p->header_tei_rx = ntoh32(pheader->tei); p->header_tei = p->header_tei_rx; p->seq = ntoh16(pheader->seq); LOG(LOGL_DEBUG, "| GTPv1\n"); LOG(LOGL_DEBUG, "| type = %" PRIu8 " 0x%02" PRIx8 "\n", p->type, p->type); LOG(LOGL_DEBUG, "| length = %" PRIu16 " 0x%04" PRIx16 "\n", ntoh16(pheader->length), ntoh16(pheader->length)); LOG(LOGL_DEBUG, "| TEI = %" PRIu32 " 0x%08" PRIx32 "\n", p->header_tei_rx, p->header_tei_rx); LOG(LOGL_DEBUG, "| seq = %" PRIu16 " 0x%04" PRIx16 "\n", p->seq, p->seq); LOG(LOGL_DEBUG, "| npdu = %" PRIu8 " 0x%02" PRIx8 "\n", pheader->npdu, pheader->npdu); LOG(LOGL_DEBUG, "| next = %" PRIu8 " 0x%02" PRIx8 "\n", pheader->next, pheader->next); if (p->data_len <= GTP1_HEADER_SIZE_LONG) { p->rc = GTP_RC_TINY; p->header_len = GTP1_HEADER_SIZE_SHORT; return; } /* Check packet length field versus length of packet */ int announced_len = ntoh16(pheader->length) + GTP1_HEADER_SIZE_SHORT; if (p->data_len != announced_len) { LOG(LOGL_ERROR, "GTP packet length field (%d + %d) does not" " match actual length (%d)\n", GTP1_HEADER_SIZE_SHORT, (int)ntoh16(pheader->length), p->data_len); p->rc = GTP_RC_TOOSHORT; return; } p->rc = GTP_RC_PDU_C; p->header_len = GTP1_HEADER_SIZE_LONG; } /* Examine whether p->data of size p->data_len has a valid GTP header. Set * p->version, p->rc and p->header_len. On error, p->rc <= 0 (see enum * gtp_rc). p->data must point at a buffer with p->data_len set. */ void validate_gtp_header(struct gtp_packet_desc *p) { p->rc = GTP_RC_UNKNOWN; /* Need at least 1 byte in order to check version */ if (p->data_len < 1) { LOG(LOGL_ERROR, "Discarding packet - too small: %d\n", p->data_len); p->rc = GTP_RC_TOOSHORT; return; } p->version = p->data->flags >> 5; switch (p->version) { case 0: validate_gtp0_header(p); break; case 1: validate_gtp1_header(p); break; default: LOG(LOGL_ERROR, "Unsupported GTP version: %d\n", p->version); p->rc = GTP_RC_UNSUPPORTED_VERSION; break; } } /* Return the value of the i'th IMSI IEI by copying to *imsi. * The first IEI is reached by passing i = 0. * imsi must point at allocated space of (at least) 8 bytes. * Return 1 on success, or 0 if not found. */ static int get_ie_imsi(union gtpie_member *ie[], int i, uint8_t *imsi) { return gtpie_gettv0(ie, GTPIE_IMSI, i, imsi, 8) == 0; } /* Analogous to get_ie_imsi(). nsapi must point at a single uint8_t. */ static int get_ie_nsapi(union gtpie_member *ie[], int i, uint8_t *nsapi) { return gtpie_gettv1(ie, GTPIE_NSAPI, i, nsapi) == 0; } static char imsi_digit_to_char(uint8_t nibble) { nibble &= 0x0f; if (nibble > 9) return (nibble == 0x0f) ? '\0' : '?'; return '0' + nibble; } /* Return a human readable IMSI string, in a static buffer. * imsi must point at 8 octets of IMSI IE encoded IMSI data. */ static int imsi_to_str(uint8_t *imsi, const char **imsi_str) { static char str[17]; int i; for (i = 0; i < 8; i++) { char c; c = imsi_digit_to_char(imsi[i]); if (c == '?') return -1; str[2*i] = c; c = imsi_digit_to_char(imsi[i] >> 4); if (c == '?') return -1; str[2*i + 1] = c; } str[16] = '\0'; *imsi_str = str; return 1; } /* Return 0 if not present, 1 if present and decoded successfully, -1 if * present but cannot be decoded. */ static int get_ie_imsi_str(union gtpie_member *ie[], int i, const char **imsi_str) { uint8_t imsi_buf[8]; if (!get_ie_imsi(ie, i, imsi_buf)) return 0; return imsi_to_str(imsi_buf, imsi_str); } /* Return 0 if not present, 1 if present and decoded successfully, -1 if * present but cannot be decoded. */ static int get_ie_apn_str(union gtpie_member *ie[], const char **apn_str) { static char apn_buf[GSM_APN_LENGTH]; unsigned int len; if (gtpie_gettlv(ie, GTPIE_APN, 0, &len, apn_buf, sizeof(apn_buf)) != 0) return 0; if (len < 2) { LOG(LOGL_ERROR, "APN IE: invalid length: %d\n", (int)len); return -1; } if (len > (sizeof(apn_buf) - 1)) len = sizeof(apn_buf) - 1; apn_buf[len] = '\0'; *apn_str = osmo_apn_to_str(apn_buf, (uint8_t*)apn_buf, len); if (!(*apn_str)) { LOG(LOGL_ERROR, "APN IE: present but cannot be decoded: %s\n", osmo_hexdump((uint8_t*)apn_buf, len)); return -1; } return 1; } /* Validate header, and index information elements. Write decoded packet * information to *res. res->data will point at the given data buffer. On * error, p->rc is set <= 0 (see enum gtp_rc). */ static void gtp_decode(const uint8_t *data, int data_len, unsigned int from_side_idx, unsigned int from_plane_idx, struct gtp_packet_desc *res, time_t now) { ZERO_STRUCT(res); res->data = (union gtp_packet*)data; res->data_len = data_len; res->side_idx = from_side_idx; res->plane_idx = from_plane_idx; res->timestamp = now; validate_gtp_header(res); if (res->rc <= 0) return; LOG(LOGL_DEBUG, "Valid GTP header (v%d)\n", res->version); if (from_plane_idx == GTPH_PLANE_USER) { res->rc = GTP_RC_PDU_U; return; } if (res->rc != GTP_RC_PDU_C) { LOG(LOGL_DEBUG, "no IEs in this GTP packet\n"); return; } if (gtpie_decaps(res->ie, res->version, (void*)(data + res->header_len), res->data_len - res->header_len) != 0) { res->rc = GTP_RC_INVALID_IE; LOG(LOGL_ERROR, "INVALID: cannot decode IEs." " Dropping GTP packet%s.\n", gtp_type_str(res->type) ); return; } #if 1 /* TODO if () (waiting for a commit from jerlbeck) */ int i; for (i = 0; i < 10; i++) { const char *imsi; if (get_ie_imsi_str(res->ie, i, &imsi) < 1) break; LOG(LOGL_DEBUG, "| IMSI %s\n", imsi); } for (i = 0; i < 10; i++) { uint8_t nsapi; if (!get_ie_nsapi(res->ie, i, &nsapi)) break; LOG(LOGL_DEBUG, "| NSAPI %d\n", (int)nsapi); } for (i = 0; i < 2; i++) { struct gsn_addr addr; if (gsn_addr_get(&addr, res, i) == 0) LOG(LOGL_DEBUG, "| addr %s\n", gsn_addr_to_str(&addr)); } for (i = 0; i < 10; i++) { uint32_t tei; if (gtpie_gettv4(res->ie, GTPIE_TEI_DI, i, &tei) != 0) break; LOG(LOGL_DEBUG, "| TEI DI (USER) %" PRIu32 " 0x%08" PRIx32 "\n", tei, tei); } for (i = 0; i < 10; i++) { uint32_t tei; if (gtpie_gettv4(res->ie, GTPIE_TEI_C, i, &tei) != 0) break; LOG(LOGL_DEBUG, "| TEI (CTRL) %" PRIu32 " 0x%08" PRIx32 "\n", tei, tei); } #endif } /* expiry */ void expiry_init(struct expiry *exq, int expiry_in_seconds) { ZERO_STRUCT(exq); exq->expiry_in_seconds = expiry_in_seconds; INIT_LLIST_HEAD(&exq->items); } void expiry_add(struct expiry *exq, struct expiring_item *item, time_t now) { item->expiry = now + exq->expiry_in_seconds; OSMO_ASSERT(llist_empty(&exq->items) || (item->expiry >= llist_last(&exq->items, struct expiring_item, entry)->expiry)); /* Add/move to the tail to always sort by expiry, ascending. */ llist_del(&item->entry); llist_add_tail(&item->entry, &exq->items); } int expiry_tick(struct expiry *exq, time_t now) { int expired = 0; struct expiring_item *m, *n; llist_for_each_entry_safe(m, n, &exq->items, entry) { if (m->expiry <= now) { expiring_item_del(m); expired ++; } else { /* The items are added sorted by expiry. So when we hit * an unexpired entry, only more unexpired ones will * follow. */ break; } } return expired; } void expiry_clear(struct expiry *exq) { struct expiring_item *m, *n; llist_for_each_entry_safe(m, n, &exq->items, entry) { expiring_item_del(m); } } void expiring_item_init(struct expiring_item *item) { ZERO_STRUCT(item); INIT_LLIST_HEAD(&item->entry); } void expiring_item_del(struct expiring_item *item) { OSMO_ASSERT(item); llist_del(&item->entry); INIT_LLIST_HEAD(&item->entry); if (item->del_cb) { /* avoid loops */ del_cb_t del_cb = item->del_cb; item->del_cb = 0; (del_cb)(item); } } /* nr_map, nr_pool */ void nr_pool_init(struct nr_pool *pool, nr_t nr_min, nr_t nr_max) { *pool = (struct nr_pool){ .nr_min = nr_min, .nr_max = nr_max, .last_nr = nr_max }; } nr_t nr_pool_next(struct nr_pool *pool) { if (pool->last_nr >= pool->nr_max) pool->last_nr = pool->nr_min; else pool->last_nr ++; return pool->last_nr; } void nr_map_init(struct nr_map *map, struct nr_pool *pool, struct expiry *exq) { ZERO_STRUCT(map); map->pool = pool; map->add_items_to_expiry = exq; INIT_LLIST_HEAD(&map->mappings); } void nr_mapping_init(struct nr_mapping *m) { ZERO_STRUCT(m); INIT_LLIST_HEAD(&m->entry); expiring_item_init(&m->expiry_entry); } void nr_map_add(struct nr_map *map, struct nr_mapping *mapping, time_t now) { /* Generate a mapped number */ mapping->repl = nr_pool_next(map->pool); /* Add to the tail to always yield a list sorted by expiry, in * ascending order. */ llist_add_tail(&mapping->entry, &map->mappings); nr_map_refresh(map, mapping, now); } void nr_map_refresh(struct nr_map *map, struct nr_mapping *mapping, time_t now) { if (!map->add_items_to_expiry) return; expiry_add(map->add_items_to_expiry, &mapping->expiry_entry, now); } void nr_map_clear(struct nr_map *map) { struct nr_mapping *m; struct nr_mapping *n; llist_for_each_entry_safe(m, n, &map->mappings, entry) { nr_mapping_del(m); } } int nr_map_empty(const struct nr_map *map) { return llist_empty(&map->mappings); } struct nr_mapping *nr_map_get(const struct nr_map *map, void *origin, nr_t nr_orig) { struct nr_mapping *mapping; llist_for_each_entry(mapping, &map->mappings, entry) { if ((mapping->origin == origin) && (mapping->orig == nr_orig)) return mapping; } /* Not found. */ return NULL; } struct nr_mapping *nr_map_get_inv(const struct nr_map *map, nr_t nr_repl) { struct nr_mapping *mapping; llist_for_each_entry(mapping, &map->mappings, entry) { if (mapping->repl == nr_repl) { return mapping; } } /* Not found. */ return NULL; } void nr_mapping_del(struct nr_mapping *mapping) { OSMO_ASSERT(mapping); llist_del(&mapping->entry); INIT_LLIST_HEAD(&mapping->entry); expiring_item_del(&mapping->expiry_entry); } /* gtphub */ const char* const gtphub_plane_idx_names[GTPH_PLANE_N] = { "CTRL", "USER", }; const uint16_t gtphub_plane_idx_default_port[GTPH_PLANE_N] = { 2123, 2152, }; const char* const gtphub_side_idx_names[GTPH_SIDE_N] = { "SGSN", "GGSN", }; time_t gtphub_now(void) { struct timespec now_tp; OSMO_ASSERT(clock_gettime(CLOCK_MONOTONIC, &now_tp) >= 0); return now_tp.tv_sec; } /* Remove a gtphub_peer from its list and free it. */ static void gtphub_peer_del(struct gtphub_peer *peer) { OSMO_ASSERT(llist_empty(&peer->addresses)); nr_map_clear(&peer->seq_map); llist_del(&peer->entry); talloc_free(peer); } static void gtphub_peer_addr_del(struct gtphub_peer_addr *pa) { OSMO_ASSERT(llist_empty(&pa->ports)); llist_del(&pa->entry); talloc_free(pa); } static void gtphub_peer_port_del(struct gtphub_peer_port *pp) { OSMO_ASSERT(pp->ref_count == 0); llist_del(&pp->entry); rate_ctr_group_free(pp->counters_io); talloc_free(pp); } /* From the information in the gtp_packet_desc, return the address of a GGSN. * Return -1 on error. */ static int gtphub_resolve_ggsn(struct gtphub *hub, struct gtp_packet_desc *p, struct gtphub_peer_port **pp); /* See gtphub_ext.c (wrapped by unit test) */ struct gtphub_peer_port *gtphub_resolve_ggsn_addr(struct gtphub *hub, const char *imsi_str, const char *apn_ni_str); int gtphub_ares_init(struct gtphub *hub); static void gtphub_zero(struct gtphub *hub) { ZERO_STRUCT(hub); INIT_LLIST_HEAD(&hub->ggsn_lookups); INIT_LLIST_HEAD(&hub->resolved_ggsns); } static int gtphub_sock_init(struct osmo_fd *ofd, const struct gtphub_cfg_addr *addr, osmo_fd_cb_t cb, void *data, int ofd_id) { if (!addr->addr_str) { LOG(LOGL_FATAL, "Cannot bind: empty address.\n"); return -1; } if (!addr->port) { LOG(LOGL_FATAL, "Cannot bind: zero port not permitted.\n"); return -1; } ofd->when = BSC_FD_READ; ofd->cb = cb; ofd->data = data; ofd->priv_nr = ofd_id; int rc; rc = osmo_sock_init_ofd(ofd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, addr->addr_str, addr->port, OSMO_SOCK_F_BIND); if (rc < 1) { LOG(LOGL_FATAL, "Cannot bind to %s port %d (rc %d)\n", addr->addr_str, (int)addr->port, rc); return -1; } return 0; } static void gtphub_sock_close(struct osmo_fd *ofd) { close(ofd->fd); osmo_fd_unregister(ofd); ofd->cb = NULL; } static void gtphub_bind_init(struct gtphub_bind *b, uint32_t idx) { ZERO_STRUCT(b); INIT_LLIST_HEAD(&b->peers); b->counters_io = rate_ctr_group_alloc(osmo_gtphub_ctx, >phub_ctrg_io_desc, idx); OSMO_ASSERT(b->counters_io); } static int gtphub_bind_start(struct gtphub_bind *b, const struct gtphub_cfg_bind *cfg, osmo_fd_cb_t cb, void *cb_data, unsigned int ofd_id) { LOG(LOGL_DEBUG, "Starting bind %s\n", b->label); if (gsn_addr_from_str(&b->local_addr, cfg->bind.addr_str) != 0) { LOG(LOGL_FATAL, "Invalid bind address for %s: %s\n", b->label, cfg->bind.addr_str); return -1; } if (gtphub_sock_init(&b->ofd, &cfg->bind, cb, cb_data, ofd_id) != 0) { LOG(LOGL_FATAL, "Cannot bind for %s: %s\n", b->label, cfg->bind.addr_str); return -1; } b->local_port = cfg->bind.port; return 0; } static void gtphub_bind_free(struct gtphub_bind *b) { OSMO_ASSERT(llist_empty(&b->peers)); if (b->counters_io) rate_ctr_group_free(b->counters_io); } static void gtphub_bind_stop(struct gtphub_bind *b) { gtphub_sock_close(&b->ofd); gtphub_bind_free(b); } /* Recv datagram from from->fd, write sender's address to *from_addr. * Return the number of bytes read, zero on error. */ static int gtphub_read(const struct osmo_fd *from, struct osmo_sockaddr *from_addr, uint8_t *buf, size_t buf_len) { OSMO_ASSERT(from_addr); /* recvfrom requires the available length set in *from_addr_len. */ from_addr->l = sizeof(from_addr->a); errno = 0; ssize_t received = recvfrom(from->fd, buf, buf_len, 0, (struct sockaddr*)&from_addr->a, &from_addr->l); /* TODO use recvmsg and get a MSG_TRUNC flag to make sure the message * is not truncated. Then maybe reduce buf's size. */ if (received <= 0) { LOG((errno == EAGAIN? LOGL_DEBUG : LOGL_ERROR), "error: %s\n", strerror(errno)); return 0; } LOG(LOGL_DEBUG, "Received %d bytes from %s: %s%s\n", (int)received, osmo_sockaddr_to_str(from_addr), osmo_hexdump(buf, received > 1000? 1000 : received), received > 1000 ? "..." : ""); return received; } static inline void gtphub_port_ref_count_inc(struct gtphub_peer_port *pp) { OSMO_ASSERT(pp); OSMO_ASSERT(pp->ref_count < UINT_MAX); pp->ref_count++; } static inline void gtphub_port_ref_count_dec(struct gtphub_peer_port *pp) { OSMO_ASSERT(pp); OSMO_ASSERT(pp->ref_count > 0); pp->ref_count--; } static inline void set_seq(struct gtp_packet_desc *p, uint16_t seq) { OSMO_ASSERT(p->version == 1); p->data->gtp1l.h.seq = hton16(seq); p->seq = seq; } static inline void set_tei(struct gtp_packet_desc *p, uint32_t tei) { OSMO_ASSERT(p->version == 1); p->data->gtp1l.h.tei = hton32(tei); p->header_tei = tei; } static void gtphub_mapping_del_cb(struct expiring_item *expi); static struct nr_mapping *gtphub_mapping_new() { struct nr_mapping *nrm; nrm = talloc_zero(osmo_gtphub_ctx, struct nr_mapping); OSMO_ASSERT(nrm); nr_mapping_init(nrm); nrm->expiry_entry.del_cb = gtphub_mapping_del_cb; return nrm; } #define APPEND(args...) \ l = snprintf(pos, left, args); \ pos += l; \ left -= l static const char *gtphub_tunnel_side_str(struct gtphub_tunnel *tun, int side_idx) { static char buf[256]; char *pos = buf; int left = sizeof(buf); int l; struct gtphub_tunnel_endpoint *c, *u; c = &tun->endpoint[side_idx][GTPH_PLANE_CTRL]; u = &tun->endpoint[side_idx][GTPH_PLANE_USER]; /* print both only if they differ. */ if (!c->peer) { APPEND("(uninitialized)"); } else { APPEND("%s", gsn_addr_to_str(&c->peer->peer_addr->addr)); } if (!u->peer) { if (c->peer) { APPEND("/(uninitialized)"); } } else if ((!c->peer) || (!gsn_addr_same(&u->peer->peer_addr->addr, &c->peer->peer_addr->addr))) { APPEND("/%s", gsn_addr_to_str(&u->peer->peer_addr->addr)); } APPEND(" (TEI C=%x U=%x)", c->tei_orig, u->tei_orig); return buf; } const char *gtphub_tunnel_str(struct gtphub_tunnel *tun) { static char buf[512]; char *pos = buf; int left = sizeof(buf); int l; if (!tun) return "null-tunnel"; APPEND("TEI=%x: ", tun->tei_repl); APPEND("%s", gtphub_tunnel_side_str(tun, GTPH_SIDE_SGSN)); APPEND(" <-> %s", gtphub_tunnel_side_str(tun, GTPH_SIDE_GGSN)); return buf; } #undef APPEND void gtphub_tunnel_endpoint_set_peer(struct gtphub_tunnel_endpoint *te, struct gtphub_peer_port *pp) { if (te->peer) gtphub_port_ref_count_dec(te->peer); te->peer = pp; if (te->peer) gtphub_port_ref_count_inc(te->peer); } int gtphub_tunnel_complete(struct gtphub_tunnel *tun) { if (!tun) return 0; if (!tun->tei_repl) return 0; int side_idx; int plane_idx; for_each_side_and_plane(side_idx, plane_idx) { struct gtphub_tunnel_endpoint *te = &tun->endpoint[side_idx][plane_idx]; if (!(te->peer && te->tei_orig)) return 0; } return 1; } static void gtphub_tunnel_del_cb(struct expiring_item *expi) { struct gtphub_tunnel *tun = container_of(expi, struct gtphub_tunnel, expiry_entry); LOG(LOGL_DEBUG, "expired: %s\n", gtphub_tunnel_str(tun)); llist_del(&tun->entry); INIT_LLIST_HEAD(&tun->entry); /* mark unused */ expi->del_cb = 0; /* avoid recursion loops */ expiring_item_del(&tun->expiry_entry); /* usually already done, but make sure. */ int side_idx; int plane_idx; for_each_side_and_plane(side_idx, plane_idx) { struct gtphub_tunnel_endpoint *te = &tun->endpoint[side_idx][plane_idx]; /* clear ref count */ gtphub_tunnel_endpoint_set_peer(te, NULL); rate_ctr_group_free(te->counters_io); } talloc_free(tun); } #define CTR_IDX(s, p, a, b) (a + s + (p + b) * 2) /* rate counter index for tunnels: [3; 6] */ #define CTR_IDX_TUN(s, p) CTR_IDX(s, p, 1, 1) /* rate counter index for hubs: [7; 10] */ #define CTR_IDX_HUB(s, p) CTR_IDX(s, p, 3, 2) static struct gtphub_tunnel *gtphub_tunnel_new() { struct gtphub_tunnel *tun; tun = talloc_zero(osmo_gtphub_ctx, struct gtphub_tunnel); OSMO_ASSERT(tun); INIT_LLIST_HEAD(&tun->entry); expiring_item_init(&tun->expiry_entry); int side_idx, plane_idx; for_each_side_and_plane(side_idx, plane_idx) { struct gtphub_tunnel_endpoint *te = &tun->endpoint[side_idx][plane_idx]; te->counters_io = rate_ctr_group_alloc(osmo_gtphub_ctx, >phub_ctrg_io_desc, CTR_IDX_TUN(side_idx, plane_idx)); if (!te->counters_io) return NULL; } tun->expiry_entry.del_cb = gtphub_tunnel_del_cb; return tun; } static const char *gtphub_peer_strb(struct gtphub_peer *peer, char *buf, int buflen) { if (llist_empty(&peer->addresses)) return "(addressless)"; struct gtphub_peer_addr *a = llist_first(&peer->addresses, struct gtphub_peer_addr, entry); return gsn_addr_to_strb(&a->addr, buf, buflen); } static const char *gtphub_port_strb(struct gtphub_peer_port *port, char *buf, int buflen) { if (!port) return "(null port)"; snprintf(buf, buflen, "%s port %d", gsn_addr_to_str(&port->peer_addr->addr), (int)port->port); return buf; } const char *gtphub_peer_str(struct gtphub_peer *peer) { static char buf[256]; return gtphub_peer_strb(peer, buf, sizeof(buf)); } const char *gtphub_port_str(struct gtphub_peer_port *port) { static char buf[256]; return gtphub_port_strb(port, buf, sizeof(buf)); } static const char *gtphub_port_str2(struct gtphub_peer_port *port) { static char buf[256]; return gtphub_port_strb(port, buf, sizeof(buf)); } static void gtphub_mapping_del_cb(struct expiring_item *expi) { expi->del_cb = 0; /* avoid recursion loops */ expiring_item_del(expi); /* usually already done, but make sure. */ struct nr_mapping *nrm = container_of(expi, struct nr_mapping, expiry_entry); llist_del(&nrm->entry); INIT_LLIST_HEAD(&nrm->entry); /* mark unused */ /* Just for log */ struct gtphub_peer_port *from = nrm->origin; OSMO_ASSERT(from); LOG(LOGL_DEBUG, "expired: %d: nr mapping from %s: %u->%u\n", (int)nrm->expiry_entry.expiry, gtphub_port_str(from), (unsigned int)nrm->orig, (unsigned int)nrm->repl); gtphub_port_ref_count_dec(from); talloc_free(nrm); } static struct nr_mapping *gtphub_mapping_have(struct nr_map *map, struct gtphub_peer_port *from, nr_t orig_nr, time_t now) { struct nr_mapping *nrm; nrm = nr_map_get(map, from, orig_nr); if (!nrm) { nrm = gtphub_mapping_new(); nrm->orig = orig_nr; nrm->origin = from; nr_map_add(map, nrm, now); gtphub_port_ref_count_inc(from); LOG(LOGL_DEBUG, "peer %s: sequence map %d --> %d\n", gtphub_port_str(from), (int)(nrm->orig), (int)(nrm->repl)); } else { nr_map_refresh(map, nrm, now); } OSMO_ASSERT(nrm); return nrm; } static void gtphub_map_seq(struct gtp_packet_desc *p, struct gtphub_peer_port *from_port, struct gtphub_peer_port *to_port) { /* Store a mapping in to_peer's map, so when we later receive a GTP * packet back from to_peer, the seq nr can be unmapped back to its * origin (from_peer here). */ struct nr_mapping *nrm; nrm = gtphub_mapping_have(&to_port->peer_addr->peer->seq_map, from_port, p->seq, p->timestamp); /* Change the GTP packet to yield the new, mapped seq nr */ set_seq(p, nrm->repl); } static struct gtphub_peer_port *gtphub_unmap_seq(struct gtp_packet_desc *p, struct gtphub_peer_port *responding_port) { OSMO_ASSERT(p->version == 1); struct nr_mapping *nrm = nr_map_get_inv(&responding_port->peer_addr->peer->seq_map, p->seq); if (!nrm) return NULL; LOG(LOGL_DEBUG, "peer %p: sequence unmap %d <-- %d\n", nrm->origin, (int)(nrm->orig), (int)(nrm->repl)); set_seq(p, nrm->orig); return nrm->origin; } static int gtphub_check_mapped_tei(struct gtphub_tunnel *new_tun, struct gtphub_tunnel *iterated_tun, uint32_t *tei_min, uint32_t *tei_max) { if (!new_tun->tei_repl || !iterated_tun->tei_repl) return 1; *tei_min = (*tei_min < iterated_tun->tei_repl)? *tei_min : iterated_tun->tei_repl; *tei_max = (*tei_max > iterated_tun->tei_repl)? *tei_max : iterated_tun->tei_repl; if (new_tun->tei_repl != iterated_tun->tei_repl) return 1; /* new_tun->tei_repl is already taken. Try to find one out of the known * range. */ LOG(LOGL_DEBUG, "TEI replacement %d already taken.\n", new_tun->tei_repl); if ((*tei_max) < 0xffffffff) { (*tei_max)++; new_tun->tei_repl = *tei_max; LOG(LOGL_DEBUG, "Using TEI %d instead.\n", new_tun->tei_repl); return 1; } else if ((*tei_min) > 1) { (*tei_min)--; new_tun->tei_repl = *tei_min; LOG(LOGL_DEBUG, "Using TEI %d instead.\n", new_tun->tei_repl); return 1; } /* None seems to be available. */ return 0; } static int gtphub_check_reused_teis(struct gtphub *hub, struct gtphub_tunnel *new_tun) { uint32_t tei_min = 0xffffffff; uint32_t tei_max = 0; int side_idx; int plane_idx; struct gtphub_tunnel_endpoint *te; struct gtphub_tunnel_endpoint *te2; struct gtphub_tunnel *tun, *ntun; llist_for_each_entry_safe(tun, ntun, &hub->tunnels, entry) { if (tun == new_tun) continue; /* Check whether the GSN sent a TEI that it is reusing from a * previous tunnel. */ int tun_continue = 0; for_each_side(side_idx) { for_each_plane(plane_idx) { te = &tun->endpoint[side_idx][plane_idx]; te2 = &new_tun->endpoint[side_idx][plane_idx]; if ((te->tei_orig == 0) || (te->tei_orig != te2->tei_orig) || (!te->peer) || (!te2->peer) || !gsn_addr_same(&te->peer->peer_addr->addr, &te2->peer->peer_addr->addr)) continue; /* The peer is reusing a TEI that I believe to * be part of another tunnel. The other tunnel * must be stale, then. */ LOG(LOGL_NOTICE, "Expiring tunnel due to reused TEI:" " %s peer %s sent %s TEI %x," " previously used by tunnel %s...\n", gtphub_side_idx_names[side_idx], gtphub_port_str(te->peer), gtphub_plane_idx_names[plane_idx], te->tei_orig, gtphub_tunnel_str(tun)); LOG(LOGL_NOTICE, "...while establishing tunnel %s\n", gtphub_tunnel_str(new_tun)); expiring_item_del(&tun->expiry_entry); /* continue to find more matches. There shouldn't be * any, but let's make sure. However, tun is deleted, * so we need to skip to the next tunnel. */ tun_continue = 1; break; } if (tun_continue) break; } if (tun_continue) continue; /* Check whether the mapped TEI is already used by another * tunnel. */ if (!gtphub_check_mapped_tei(new_tun, tun, &tei_min, &tei_max)) { LOG(LOGL_ERROR, "No mapped TEI is readily available." " Searching for holes between occupied" " TEIs not implemented."); return 0; } } return 1; } static void gtphub_tunnel_refresh(struct gtphub *hub, struct gtphub_tunnel *tun, time_t now) { expiry_add(&hub->expire_slowly, &tun->expiry_entry, now); } static struct gtphub_tunnel_endpoint *gtphub_unmap_tei(struct gtphub *hub, struct gtp_packet_desc *p, struct gtphub_peer_port *from, struct gtphub_tunnel **unmapped_from_tun) { OSMO_ASSERT(from); int other_side = other_side_idx(p->side_idx); struct gtphub_tunnel *tun; llist_for_each_entry(tun, &hub->tunnels, entry) { struct gtphub_tunnel_endpoint *te_from = &tun->endpoint[p->side_idx][p->plane_idx]; struct gtphub_tunnel_endpoint *te_to = &tun->endpoint[other_side][p->plane_idx]; if ((tun->tei_repl == p->header_tei_rx) && te_from->peer && gsn_addr_same(&te_from->peer->peer_addr->addr, &from->peer_addr->addr)) { gtphub_tunnel_refresh(hub, tun, p->timestamp); if (unmapped_from_tun) *unmapped_from_tun = tun; return te_to; } } if (unmapped_from_tun) *unmapped_from_tun = NULL; return NULL; } static void gtphub_map_restart_counter(struct gtphub *hub, struct gtp_packet_desc *p) { if (p->rc != GTP_RC_PDU_C) return; int ie_idx; ie_idx = gtpie_getie(p->ie, GTPIE_RECOVERY, 0); if (ie_idx < 0) return; /* Always send gtphub's own restart counter */ p->ie[ie_idx]->tv1.v = hton8(hub->restart_counter); } static int gtphub_unmap_header_tei(struct gtphub_peer_port **to_port_p, struct gtphub_tunnel **unmapped_from_tun, struct gtphub *hub, struct gtp_packet_desc *p, struct gtphub_peer_port *from_port) { OSMO_ASSERT(p->version == 1); *to_port_p = NULL; if (unmapped_from_tun) *unmapped_from_tun = NULL; /* If the header's TEI is zero, no PDP context has been established * yet. If nonzero, a mapping should actually already exist for this * TEI, since it must have been announced in a PDP context creation. */ if (!p->header_tei_rx) return 0; /* to_peer has previously announced a TEI, which was stored and * mapped in a tunnel struct. */ struct gtphub_tunnel_endpoint *to; to = gtphub_unmap_tei(hub, p, from_port, unmapped_from_tun); if (!to) { LOG(LOGL_ERROR, "Received unknown TEI %" PRIx32 " from %s\n", p->header_tei_rx, gtphub_port_str(from_port)); return -1; } if (unmapped_from_tun) { OSMO_ASSERT(*unmapped_from_tun); LOG(LOGL_DEBUG, "Unmapped TEI coming from: %s\n", gtphub_tunnel_str(*unmapped_from_tun)); } uint32_t unmapped_tei = to->tei_orig; set_tei(p, unmapped_tei); /* May be NULL for an invalidated tunnel. */ *to_port_p = to->peer; return 0; } static int gtphub_handle_create_pdp_ctx(struct gtphub *hub, struct gtp_packet_desc *p, struct gtphub_peer_port *from_ctrl, struct gtphub_peer_port *to_ctrl) { int plane_idx; osmo_static_assert((GTPH_PLANE_CTRL == 0) && (GTPH_PLANE_USER == 1), plane_nrs_match_GSN_addr_IE_indices); struct gtphub_tunnel *tun = p->tun; if (p->type == GTP_CREATE_PDP_REQ) { if (p->side_idx != GTPH_SIDE_SGSN) { LOG(LOGL_ERROR, "Wrong side: Create PDP Context" " Request from the GGSN side: %s", gtphub_port_str(from_ctrl)); return -1; } if (tun) { LOG(LOGL_ERROR, "Not implemented: Received" " Create PDP Context Request for an already" " established tunnel:" " from %s, tunnel %s\n", gtphub_port_str(from_ctrl), gtphub_tunnel_str(p->tun)); return -1; } /* A new tunnel. */ tun = gtphub_tunnel_new(); if (!tun) { LOG(LOGL_ERROR, "Failed to allocate new tunnel %s <-> %s\n", gtphub_port_str(from_ctrl), gtphub_port_str(to_ctrl)); return -1; } p->tun = tun; /* Create TEI mapping */ tun->tei_repl = nr_pool_next(&hub->tei_pool); llist_add(&tun->entry, &hub->tunnels); gtphub_tunnel_refresh(hub, tun, p->timestamp); /* The endpoint peers on this side (SGSN) will be set from IEs * below. Also set the GGSN Ctrl endpoint, for logging. */ gtphub_tunnel_endpoint_set_peer(&tun->endpoint[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL], to_ctrl); } else if (p->type == GTP_CREATE_PDP_RSP) { if (p->side_idx != GTPH_SIDE_GGSN) { LOG(LOGL_ERROR, "Wrong side: Create PDP Context" " Response from the SGSN side: %s", gtphub_port_str(from_ctrl)); return -1; } /* The tunnel should already have been resolved from the header * TEI and be available in tun (== p->tun). Just fill in the * GSN Addresses below.*/ OSMO_ASSERT(tun); OSMO_ASSERT(tun->tei_repl == p->header_tei_rx); OSMO_ASSERT(to_ctrl); } uint8_t ie_type[] = { GTPIE_TEI_C, GTPIE_TEI_DI }; int ie_mandatory = (p->type == GTP_CREATE_PDP_REQ); unsigned int side_idx = p->side_idx; for_each_plane(plane_idx) { int rc; struct gsn_addr use_addr; uint16_t use_port; uint32_t tei_from_ie; int ie_idx; /* Fetch GSN Address and TEI from IEs. As ensured by above * static asserts, plane_idx corresponds to the GSN Address IE * index (the first one = 0 = ctrl, second one = 1 = user). */ rc = gsn_addr_get(&use_addr, p, plane_idx); if (rc) { LOG(LOGL_ERROR, "Cannot read %s GSN Address IE\n", gtphub_plane_idx_names[plane_idx]); return -1; } LOG(LOGL_DEBUG, "Read %s GSN addr %s (%d)\n", gtphub_plane_idx_names[plane_idx], gsn_addr_to_str(&use_addr), use_addr.len); ie_idx = gtpie_getie(p->ie, ie_type[plane_idx], 0); if (ie_idx < 0) { if (ie_mandatory) { LOG(LOGL_ERROR, "Create PDP Context message invalid:" " missing IE %d\n", (int)ie_type[plane_idx]); return -1; } tei_from_ie = 0; } else tei_from_ie = ntoh32(p->ie[ie_idx]->tv4.v); /* Make sure an entry for this peer address with default port * exists. * * Exception: if sgsn_use_sender is set, instead use the * sender's address and port for Ctrl -- the User port is not * known until the first User packet arrives. * * Note: doing this here is just an optimization, because * gtphub_handle_buf() has code to replace the tunnel * endpoints' addresses with the sender (needed for User * plane). We could just ignore sgsn_use_sender here. But if we * set up a default port here and replace it in * gtphub_handle_buf(), we'd be creating a peer port just to * expire it right away. */ if (hub->sgsn_use_sender && (side_idx == GTPH_SIDE_SGSN)) { int rc = gsn_addr_from_sockaddr(&use_addr, &use_port, &from_ctrl->sa); if (rc < 0) LOG(LOGL_ERROR, "%s(): failed to obtain GSN address\n", __func__); } else { use_port = gtphub_plane_idx_default_port[plane_idx]; } struct gtphub_peer_port *peer_from_ie; peer_from_ie = gtphub_port_have(hub, &hub->to_gsns[side_idx][plane_idx], &use_addr, use_port); gtphub_tunnel_endpoint_set_peer(&tun->endpoint[side_idx][plane_idx], peer_from_ie); if (!tei_from_ie && !tun->endpoint[side_idx][plane_idx].tei_orig) { LOG(LOGL_ERROR, "Create PDP Context message omits %s TEI, but" " no TEI has been announced for this tunnel: %s\n", gtphub_plane_idx_names[plane_idx], gtphub_tunnel_str(tun)); return -1; } if (tei_from_ie) { /* Replace TEI in GTP packet IE */ tun->endpoint[side_idx][plane_idx].tei_orig = tei_from_ie; p->ie[ie_idx]->tv4.v = hton32(tun->tei_repl); if (!gtphub_check_reused_teis(hub, tun)) { /* It's highly unlikely that all TEIs are * taken. But the code looking for an unused * TEI is, at the time of writing this comment, * not able to find gaps in the TEI space. To * explicitly alert the user of this problem, * rather abort than carry on. */ LOG(LOGL_FATAL, "TEI range exhausted. Cannot create TEI mapping, aborting.\n"); abort(); } } /* Replace the GSN address to reflect gtphub. */ rc = gsn_addr_put(&hub->to_gsns[other_side_idx(side_idx)][plane_idx].local_addr, p, plane_idx); if (rc) { LOG(LOGL_ERROR, "Cannot write %s GSN Address IE\n", gtphub_plane_idx_names[plane_idx]); return -1; } } if (p->type == GTP_CREATE_PDP_REQ) { LOG(LOGL_DEBUG, "New tunnel, first half: %s\n", gtphub_tunnel_str(tun)); } else if (p->type == GTP_CREATE_PDP_RSP) { LOG(LOGL_DEBUG, "New tunnel: %s\n", gtphub_tunnel_str(tun)); } return 0; } static void pending_delete_del_cb(struct expiring_item *expi) { struct pending_delete *pd; pd = container_of(expi, struct pending_delete, expiry_entry); llist_del(&pd->entry); INIT_LLIST_HEAD(&pd->entry); pd->expiry_entry.del_cb = 0; expiring_item_del(&pd->expiry_entry); talloc_free(pd); } static struct pending_delete *pending_delete_new(void) { struct pending_delete *pd = talloc_zero(osmo_gtphub_ctx, struct pending_delete); INIT_LLIST_HEAD(&pd->entry); expiring_item_init(&pd->expiry_entry); pd->expiry_entry.del_cb = pending_delete_del_cb; return pd; } static int gtphub_handle_delete_pdp_ctx(struct gtphub *hub, struct gtp_packet_desc *p, struct gtphub_peer_port *from_ctrl, struct gtphub_peer_port *to_ctrl) { struct gtphub_tunnel *known_tun = p->tun; if (p->type == GTP_DELETE_PDP_REQ) { if (!known_tun) { LOG(LOGL_ERROR, "Cannot find tunnel for Delete PDP Context Request.\n"); return -1; } /* Store the Delete Request until a successful Response is seen. */ uint8_t teardown_ind; uint8_t nsapi; if (gtpie_gettv1(p->ie, GTPIE_TEARDOWN, 0, &teardown_ind) != 0) { LOG(LOGL_ERROR, "Missing Teardown Ind IE in Delete PDP Context Request.\n"); return -1; } if (gtpie_gettv1(p->ie, GTPIE_NSAPI, 0, &nsapi) != 0) { LOG(LOGL_ERROR, "Missing NSAPI IE in Delete PDP Context Request.\n"); return -1; } struct pending_delete *pd = NULL; struct pending_delete *pdi = NULL; llist_for_each_entry(pdi, &hub->pending_deletes, entry) { if ((pdi->tun == known_tun) && (pdi->teardown_ind == teardown_ind) && (pdi->nsapi == nsapi)) { pd = pdi; break; } } if (!pd) { pd = pending_delete_new(); pd->tun = known_tun; pd->teardown_ind = teardown_ind; pd->nsapi = nsapi; LOG(LOGL_DEBUG, "Tunnel delete pending: %s\n", gtphub_tunnel_str(known_tun)); llist_add(&pd->entry, &hub->pending_deletes); } /* Add or refresh timeout. */ expiry_add(&hub->expire_quickly, &pd->expiry_entry, p->timestamp); /* If a pending_delete should expire before the response to * indicate success comes in, the responding peer will have the * tunnel deactivated, while the requesting peer gets no reply * and keeps the tunnel. The hope is that the requesting peer * will try again and get a useful response. */ } else if (p->type == GTP_DELETE_PDP_RSP) { /* Find the Delete Request for this Response. */ struct pending_delete *pd = NULL; struct pending_delete *pdi; llist_for_each_entry(pdi, &hub->pending_deletes, entry) { if (known_tun == pdi->tun) { pd = pdi; break; } } if (!pd) { LOG(LOGL_ERROR, "Delete PDP Context Response:" " Cannot find matching request."); /* If we delete the tunnel now, anyone can send a * Delete response to kill tunnels at will. */ return -1; } /* TODO handle teardown_ind and nsapi */ expiring_item_del(&pd->expiry_entry); uint8_t cause; if (gtpie_gettv1(p->ie, GTPIE_CAUSE, 0, &cause) != 0) { LOG(LOGL_ERROR, "Delete PDP Context Response:" " Missing Cause IE."); /* If we delete the tunnel now, at least one of the * peers may still think it is active. */ return -1; } if (cause != GTPCAUSE_ACC_REQ) { LOG(LOGL_NOTICE, "Delete PDP Context Response indicates failure;" "for %s\n", gtphub_tunnel_str(known_tun)); return -1; } LOG(LOGL_DEBUG, "Delete PDP Context: removing tunnel %s\n", gtphub_tunnel_str(known_tun)); p->tun = NULL; expiring_item_del(&known_tun->expiry_entry); } return 0; } static int gtphub_handle_update_pdp_ctx(struct gtphub *hub, struct gtp_packet_desc *p, struct gtphub_peer_port *from_ctrl, struct gtphub_peer_port *to_ctrl) { /* TODO */ return 0; } /* Read GSN address IEs from p, and make sure these peer addresses exist in * bind[plane_idx] with default ports, in their respective planes (both Ctrl * and User). Map TEIs announced in IEs, and write mapped TEIs in-place into * the packet p. */ static int gtphub_handle_pdp_ctx(struct gtphub *hub, struct gtp_packet_desc *p, struct gtphub_peer_port *from_ctrl, struct gtphub_peer_port *to_ctrl) { OSMO_ASSERT(p->plane_idx == GTPH_PLANE_CTRL); switch (p->type) { case GTP_CREATE_PDP_REQ: case GTP_CREATE_PDP_RSP: return gtphub_handle_create_pdp_ctx(hub, p, from_ctrl, to_ctrl); case GTP_DELETE_PDP_REQ: case GTP_DELETE_PDP_RSP: return gtphub_handle_delete_pdp_ctx(hub, p, from_ctrl, to_ctrl); case GTP_UPDATE_PDP_REQ: case GTP_UPDATE_PDP_RSP: return gtphub_handle_update_pdp_ctx(hub, p, from_ctrl, to_ctrl); default: /* Nothing to do for this message type. */ return 0; } } static int gtphub_send_del_pdp_ctx(struct gtphub *hub, struct gtphub_tunnel *tun, int to_side) { static uint8_t del_ctx_msg[16] = { 0x32, /* GTP v1 flags */ GTP_DELETE_PDP_REQ, 0x00, 0x08, /* Length in network byte order */ 0x00, 0x00, 0x00, 0x00, /* TEI to be replaced */ 0, 0, /* Seq, to be replaced */ 0, 0, /* no extensions */ 0x13, 0xff, /* 19: Teardown ind = 1 */ 0x14, 0 /* 20: NSAPI = 0 */ }; uint32_t *tei = (uint32_t*)&del_ctx_msg[4]; uint16_t *seq = (uint16_t*)&del_ctx_msg[8]; struct gtphub_tunnel_endpoint *te = &tun->endpoint[to_side][GTPH_PLANE_CTRL]; if (! te->peer) return 0; *tei = hton32(te->tei_orig); *seq = hton16(nr_pool_next(&te->peer->peer_addr->peer->seq_pool)); struct gtphub_bind *to_bind = &hub->to_gsns[to_side][GTPH_PLANE_CTRL]; int rc = gtphub_write(&to_bind->ofd, &te->peer->sa, del_ctx_msg, sizeof(del_ctx_msg)); if (rc != 0) { LOG(LOGL_ERROR, "Failed to send out-of-band Delete PDP Context Request to %s\n", gtphub_port_str(te->peer)); } return rc; } /* Tell all peers on the other end of tunnels that PDP contexts are void. */ static void gtphub_restarted(struct gtphub *hub, struct gtp_packet_desc *p, struct gtphub_peer_port *pp) { LOG(LOGL_NOTICE, "Peer has restarted: %s\n", gtphub_port_str(pp)); int deleted_count = 0; struct gtphub_tunnel *tun; llist_for_each_entry(tun, &hub->tunnels, entry) { int side_idx; for_each_side(side_idx) { struct gtphub_tunnel_endpoint *te = &tun->endpoint[side_idx][GTPH_PLANE_CTRL]; struct gtphub_tunnel_endpoint *te2 = &tun->endpoint[other_side_idx(side_idx)][GTPH_PLANE_CTRL]; if ((!te->peer) || (!te2->tei_orig) || (pp->peer_addr->peer != te->peer->peer_addr->peer)) continue; LOG(LOGL_DEBUG, "Deleting tunnel due to peer restart: %s\n", gtphub_tunnel_str(tun)); deleted_count ++; /* Send a Delete PDP Context Request to the * peer on the other side, remember the pending * delete and wait for the response to delete * the tunnel. Clear this side of the tunnel to * make sure it isn't used. * * Should the delete message send fail, or if no * response is received, this tunnel will expire. If * its TEIs come up in a new PDP Context Request, it * will be removed. If messages for this tunnel should * come in (from the not restarted side), they will be * dropped because the tunnel is rendered unusable. */ gtphub_send_del_pdp_ctx(hub, tun, other_side_idx(side_idx)); gtphub_tunnel_endpoint_set_peer(&tun->endpoint[side_idx][GTPH_PLANE_CTRL], NULL); gtphub_tunnel_endpoint_set_peer(&tun->endpoint[side_idx][GTPH_PLANE_USER], NULL); } } if (deleted_count) LOG(LOGL_NOTICE, "Deleting %d tunnels due to restart of: %s\n", deleted_count, gtphub_port_str(pp)); } static int get_restart_count(struct gtp_packet_desc *p) { int ie_idx; ie_idx = gtpie_getie(p->ie, GTPIE_RECOVERY, 0); if (ie_idx < 0) return -1; return ntoh8(p->ie[ie_idx]->tv1.v); } static void gtphub_check_restart_counter(struct gtphub *hub, struct gtp_packet_desc *p, struct gtphub_peer_port *from) { /* If the peer is sending a Recovery IE (7.7.11) with a restart counter * that doesn't match the peer's previously sent restart counter, clear * that peer and cancel PDP contexts. */ int restart = get_restart_count(p); if ((restart < 0) || (restart > 255)) return; if ((from->last_restart_count >= 0) && (from->last_restart_count <= 255)) { if (from->last_restart_count != restart) { gtphub_restarted(hub, p, from); } } from->last_restart_count = restart; } static int from_sgsns_read_cb(struct osmo_fd *from_sgsns_ofd, unsigned int what) { unsigned int plane_idx = from_sgsns_ofd->priv_nr; OSMO_ASSERT(plane_idx < GTPH_PLANE_N); LOG(LOGL_DEBUG, "=== reading from SGSN (%s)\n", gtphub_plane_idx_names[plane_idx]); if (!(what & BSC_FD_READ)) return 0; struct gtphub *hub = from_sgsns_ofd->data; static uint8_t buf[4096]; struct osmo_sockaddr from_addr; struct osmo_sockaddr to_addr; struct osmo_fd *to_ofd; int len; uint8_t *reply_buf; len = gtphub_read(from_sgsns_ofd, &from_addr, buf, sizeof(buf)); if (len < 1) return 0; len = gtphub_handle_buf(hub, GTPH_SIDE_SGSN, plane_idx, &from_addr, buf, len, gtphub_now(), &reply_buf, &to_ofd, &to_addr); if (len < 1) return 0; return gtphub_write(to_ofd, &to_addr, reply_buf, len); } static int from_ggsns_read_cb(struct osmo_fd *from_ggsns_ofd, unsigned int what) { unsigned int plane_idx = from_ggsns_ofd->priv_nr; OSMO_ASSERT(plane_idx < GTPH_PLANE_N); LOG(LOGL_DEBUG, "=== reading from GGSN (%s)\n", gtphub_plane_idx_names[plane_idx]); if (!(what & BSC_FD_READ)) return 0; struct gtphub *hub = from_ggsns_ofd->data; static uint8_t buf[4096]; struct osmo_sockaddr from_addr; struct osmo_sockaddr to_addr; struct osmo_fd *to_ofd; int len; uint8_t *reply_buf; len = gtphub_read(from_ggsns_ofd, &from_addr, buf, sizeof(buf)); if (len < 1) return 0; len = gtphub_handle_buf(hub, GTPH_SIDE_GGSN, plane_idx, &from_addr, buf, len, gtphub_now(), &reply_buf, &to_ofd, &to_addr); if (len < 1) return 0; return gtphub_write(to_ofd, &to_addr, reply_buf, len); } static int gtphub_unmap(struct gtphub *hub, struct gtp_packet_desc *p, struct gtphub_peer_port *from, struct gtphub_peer_port *to_proxy, struct gtphub_peer_port **final_unmapped, struct gtphub_peer_port **unmapped_from_seq) { /* Always (try to) unmap sequence and TEI numbers, which need to be * replaced in the packet. Either way, give precedence to the proxy, if * configured. */ if (unmapped_from_seq) *unmapped_from_seq = NULL; if (final_unmapped) *final_unmapped = NULL; p->tun = NULL; struct gtphub_peer_port *from_seq = NULL; struct gtphub_peer_port *from_tei = NULL; struct gtphub_peer_port *unmapped = NULL; from_seq = gtphub_unmap_seq(p, from); if (gtphub_unmap_header_tei(&from_tei, &p->tun, hub, p, from) < 0) return -1; struct gtphub_peer *from_peer = from->peer_addr->peer; if (from_seq && from_tei && (from_seq != from_tei)) { LOG(LOGL_DEBUG, "Seq unmap and TEI unmap yield two different peers." " Using seq unmap." " (from %s %s: seq %d yields %s, tei %u yields %s)\n", gtphub_plane_idx_names[p->plane_idx], gtphub_peer_str(from_peer), (int)p->seq, gtphub_port_str(from_seq), (unsigned int)p->header_tei_rx, gtphub_port_str2(from_tei) ); } unmapped = (from_seq? from_seq : from_tei); if (unmapped && to_proxy && (unmapped != to_proxy)) { LOG(LOGL_NOTICE, "Unmap yields a different peer than the configured proxy." " Using proxy." " unmapped: %s proxy: %s\n", gtphub_port_str(unmapped), gtphub_port_str2(to_proxy) ); } unmapped = (to_proxy? to_proxy : unmapped); if (!unmapped) { /* Return no error, but returned pointers are all NULL. */ return 0; } if (unmapped_from_seq) *unmapped_from_seq = from_seq; if (final_unmapped) *final_unmapped = unmapped; return 0; } static int gsn_addr_to_sockaddr(struct gsn_addr *src, uint16_t port, struct osmo_sockaddr *dst) { return osmo_sockaddr_init_udp(dst, gsn_addr_to_str(src), port); } /* If p is an Echo request, replace p's data with the matching response and * return 1. If p is no Echo request, return 0, or -1 if an invalid packet is * detected. */ static int gtphub_handle_echo_req(struct gtphub *hub, struct gtp_packet_desc *p, uint8_t **reply_buf) { if (p->type != GTP_ECHO_REQ) return 0; static uint8_t echo_response_data[14] = { 0x32, /* GTP v1 flags */ GTP_ECHO_RSP, 0x00, 14 - 8, /* Length in network byte order */ 0x00, 0x00, 0x00, 0x00, /* Zero TEI */ 0, 0, /* Seq, to be replaced */ 0, 0, /* no extensions */ 0x0e, /* Recovery IE */ 0 /* Restart counter, to be replaced */ }; uint16_t *seq = (uint16_t*)&echo_response_data[8]; uint8_t *recovery = &echo_response_data[13]; *seq = hton16(p->seq); *recovery = hub->restart_counter; *reply_buf = echo_response_data; return sizeof(echo_response_data); } struct gtphub_peer_port *gtphub_known_addr_have_port(const struct gtphub_bind *bind, const struct osmo_sockaddr *addr); /* Parse buffer as GTP packet, replace elements in-place and return the ofd and * address to forward to. Return a pointer to the osmo_fd, but copy the * sockaddr to *to_addr. The reason for this is that the sockaddr may expire at * any moment, while the osmo_fd is guaranteed to persist. Return the number of * bytes to forward, 0 or less on failure. */ int gtphub_handle_buf(struct gtphub *hub, unsigned int side_idx, unsigned int plane_idx, const struct osmo_sockaddr *from_addr, uint8_t *buf, size_t received, time_t now, uint8_t **reply_buf, struct osmo_fd **to_ofd, struct osmo_sockaddr *to_addr) { struct gtphub_bind *from_bind = &hub->to_gsns[side_idx][plane_idx]; struct gtphub_bind *to_bind = &hub->to_gsns[other_side_idx(side_idx)][plane_idx]; rate_ctr_add(&from_bind->counters_io->ctr[GTPH_CTR_BYTES_IN], received); struct gtp_packet_desc p; gtp_decode(buf, received, side_idx, plane_idx, &p, now); LOG(LOGL_DEBUG, "%s rx %s from %s %s%s\n", (side_idx == GTPH_SIDE_GGSN)? "<-" : "->", gtphub_plane_idx_names[plane_idx], gtphub_side_idx_names[side_idx], osmo_sockaddr_to_str(from_addr), gtp_type_str(p.type)); if (p.rc <= 0) { LOG(LOGL_ERROR, "INVALID: dropping GTP packet%s from %s %s %s\n", gtp_type_str(p.type), gtphub_side_idx_names[side_idx], gtphub_plane_idx_names[plane_idx], osmo_sockaddr_to_str(from_addr)); return -1; } rate_ctr_inc(&from_bind->counters_io->ctr[GTPH_CTR_PKTS_IN]); int reply_len; reply_len = gtphub_handle_echo_req(hub, &p, reply_buf); if (reply_len > 0) { /* It was an echo. Nothing left to do. */ osmo_sockaddr_copy(to_addr, from_addr); *to_ofd = &from_bind->ofd; rate_ctr_inc(&from_bind->counters_io->ctr[GTPH_CTR_PKTS_OUT]); rate_ctr_add(&from_bind->counters_io->ctr[GTPH_CTR_BYTES_OUT], reply_len); LOG(LOGL_DEBUG, "%s Echo response to %s: %d bytes to %s\n", (side_idx == GTPH_SIDE_GGSN)? "-->" : "<--", gtphub_side_idx_names[side_idx], (int)reply_len, osmo_sockaddr_to_str(to_addr)); return reply_len; } if (reply_len < 0) return -1; *to_ofd = &to_bind->ofd; /* If a proxy is configured, check that it's indeed that proxy talking * to us. A proxy is a forced 1:1 connection, e.g. to another gtphub, * so no-one else is allowed to talk to us from that side. */ struct gtphub_peer_port *from_peer = hub->proxy[side_idx][plane_idx]; if (from_peer) { if (osmo_sockaddr_cmp(&from_peer->sa, from_addr) != 0) { LOG(LOGL_ERROR, "Rejecting: %s proxy configured, but GTP packet" " received on %s bind is from another sender:" " proxy: %s sender: %s\n", gtphub_side_idx_names[side_idx], gtphub_side_idx_names[side_idx], gtphub_port_str(from_peer), osmo_sockaddr_to_str(from_addr)); return -1; } } if (!from_peer) { /* Find or create a peer with a matching address. The sender's * port may in fact differ. */ from_peer = gtphub_known_addr_have_port(from_bind, from_addr); } /* If any PDP context has been created, we already have an entry for * this GSN. If we don't have an entry, a GGSN has nothing to tell us * about, while an SGSN may initiate a PDP context. */ if (!from_peer) { if (side_idx == GTPH_SIDE_GGSN) { LOG(LOGL_ERROR, "Dropping packet%s: unknown GGSN peer: %s\n", gtp_type_str(p.type), osmo_sockaddr_to_str(from_addr)); return -1; } else { /* SGSN */ /* A new peer. If this is on the Ctrl plane, an SGSN * may make first contact without being known yet, so * create the peer struct for the current sender. */ if (plane_idx != GTPH_PLANE_CTRL) { LOG(LOGL_ERROR, "Dropping packet%s: User plane peer was not" "announced by PDP Context: %s\n", gtp_type_str(p.type), osmo_sockaddr_to_str(from_addr)); return -1; } struct gsn_addr from_gsna; uint16_t from_port; if (gsn_addr_from_sockaddr(&from_gsna, &from_port, from_addr) != 0) return -1; from_peer = gtphub_port_have(hub, from_bind, &from_gsna, from_port); } } if (!from_peer) { /* This could theoretically happen for invalid address data or * somesuch. */ LOG(LOGL_ERROR, "Dropping packet%s: invalid %s peer: %s\n", gtp_type_str(p.type), gtphub_side_idx_names[side_idx], osmo_sockaddr_to_str(from_addr)); return -1; } rate_ctr_add(&from_peer->counters_io->ctr[GTPH_CTR_BYTES_IN], received); rate_ctr_inc(&from_peer->counters_io->ctr[GTPH_CTR_PKTS_IN]); LOG(LOGL_DEBUG, "from %s peer: %s\n", gtphub_side_idx_names[side_idx], gtphub_port_str(from_peer)); gtphub_check_restart_counter(hub, &p, from_peer); gtphub_map_restart_counter(hub, &p); struct gtphub_peer_port *to_peer_from_seq; struct gtphub_peer_port *to_peer; if (gtphub_unmap(hub, &p, from_peer, hub->proxy[other_side_idx(side_idx)][plane_idx], &to_peer, &to_peer_from_seq) != 0) { return -1; } if (p.tun) { struct gtphub_tunnel_endpoint *te = &p.tun->endpoint[p.side_idx][p.plane_idx]; rate_ctr_add(&te->counters_io->ctr[GTPH_CTR_BYTES_IN], received); rate_ctr_inc(&te->counters_io->ctr[GTPH_CTR_PKTS_IN]); } if ((!to_peer) && (side_idx == GTPH_SIDE_SGSN)) { if (gtphub_resolve_ggsn(hub, &p, &to_peer) < 0) return -1; } if (!to_peer && p.tun && p.type == GTP_DELETE_PDP_RSP) { /* It's a delete confirmation for a tunnel that is partly * invalid, probably marked unsuable due to a restarted peer. * Remove the tunnel and be happy without forwarding. */ expiring_item_del(&p.tun->expiry_entry); p.tun = NULL; return 0; } if (!to_peer) { LOG(LOGL_ERROR, "No %s to send to. Dropping packet%s" " (type=%" PRIu8 ", header-TEI=%" PRIx32 ", seq=%" PRIx16 ").\n", gtphub_side_idx_names[other_side_idx(side_idx)], gtp_type_str(p.type), p.type, p.header_tei_rx, p.seq ); return -1; } if (plane_idx == GTPH_PLANE_CTRL) { /* This may be a Create PDP Context response. If it is, there * are other addresses in the GTP message to set up apart from * the sender. */ if (gtphub_handle_pdp_ctx(hub, &p, from_peer, to_peer) != 0) return -1; } /* Either to_peer was resolved from an existing tunnel, * or a PDP Ctx and thus a tunnel has just been created, * or the tunnel has been deleted due to this message. */ OSMO_ASSERT(p.tun || (p.type == GTP_DELETE_PDP_RSP)); /* If the GGSN is replying to an SGSN request, the sequence nr has * already been unmapped above (to_peer_from_seq != NULL), and we need not * create a new mapping. */ if (!to_peer_from_seq) gtphub_map_seq(&p, from_peer, to_peer); osmo_sockaddr_copy(to_addr, &to_peer->sa); *reply_buf = (uint8_t*)p.data; if (received) { rate_ctr_inc(&to_bind->counters_io->ctr[GTPH_CTR_PKTS_OUT]); rate_ctr_add(&to_bind->counters_io->ctr[GTPH_CTR_BYTES_OUT], received); rate_ctr_inc(&to_peer->counters_io->ctr[GTPH_CTR_PKTS_OUT]); rate_ctr_add(&to_peer->counters_io->ctr[GTPH_CTR_BYTES_OUT], received); } if (p.tun) { struct gtphub_tunnel_endpoint *te = &p.tun->endpoint[other_side_idx(p.side_idx)][p.plane_idx]; rate_ctr_inc(&te->counters_io->ctr[GTPH_CTR_PKTS_OUT]); rate_ctr_add(&te->counters_io->ctr[GTPH_CTR_BYTES_OUT], received); } LOG(LOGL_DEBUG, "%s Forward to %s:" " header-TEI %" PRIx32", seq %" PRIx16", %d bytes to %s\n", (side_idx == GTPH_SIDE_SGSN)? "-->" : "<--", gtphub_side_idx_names[other_side_idx(side_idx)], p.header_tei, p.seq, (int)received, osmo_sockaddr_to_str(to_addr)); return received; } static void resolved_gssn_del_cb(struct expiring_item *expi) { struct gtphub_resolved_ggsn *ggsn; ggsn = container_of(expi, struct gtphub_resolved_ggsn, expiry_entry); gtphub_port_ref_count_dec(ggsn->peer); llist_del(&ggsn->entry); ggsn->expiry_entry.del_cb = 0; expiring_item_del(&ggsn->expiry_entry); talloc_free(ggsn); } void gtphub_resolved_ggsn(struct gtphub *hub, const char *apn_oi_str, struct gsn_addr *resolved_addr, time_t now) { struct gtphub_peer_port *pp; struct gtphub_resolved_ggsn *ggsn; LOG(LOGL_DEBUG, "Resolved GGSN callback: %s %s\n", apn_oi_str, osmo_hexdump((unsigned char*)resolved_addr, sizeof(*resolved_addr))); pp = gtphub_port_have(hub, &hub->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL], resolved_addr, 2123); if (!pp) { LOG(LOGL_ERROR, "Internal: Cannot create/find peer '%s'\n", gsn_addr_to_str(resolved_addr)); return; } ggsn = talloc_zero(osmo_gtphub_ctx, struct gtphub_resolved_ggsn); OSMO_ASSERT(ggsn); INIT_LLIST_HEAD(&ggsn->entry); expiring_item_init(&ggsn->expiry_entry); ggsn->peer = pp; gtphub_port_ref_count_inc(pp); osmo_strlcpy(ggsn->apn_oi_str, apn_oi_str, sizeof(ggsn->apn_oi_str)); ggsn->expiry_entry.del_cb = resolved_gssn_del_cb; expiry_add(&hub->expire_slowly, &ggsn->expiry_entry, now); llist_add(&ggsn->entry, &hub->resolved_ggsns); } static int gtphub_gc_peer_port(struct gtphub_peer_port *pp) { return pp->ref_count == 0; } static int gtphub_gc_peer_addr(struct gtphub_peer_addr *pa) { struct gtphub_peer_port *pp, *npp; llist_for_each_entry_safe(pp, npp, &pa->ports, entry) { if (gtphub_gc_peer_port(pp)) { LOG(LOGL_DEBUG, "expired: peer %s\n", gtphub_port_str(pp)); gtphub_peer_port_del(pp); } } return llist_empty(&pa->ports); } static int gtphub_gc_peer(struct gtphub_peer *p) { struct gtphub_peer_addr *pa, *npa; llist_for_each_entry_safe(pa, npa, &p->addresses, entry) { if (gtphub_gc_peer_addr(pa)) { gtphub_peer_addr_del(pa); } } /* Note that there's a ref_count in each gtphub_peer_port instance * listed within p->addresses, referenced by TEI mappings from * hub->tei_map. As long as those don't expire, this peer will stay. */ return llist_empty(&p->addresses) && nr_map_empty(&p->seq_map); } static void gtphub_gc_bind(struct gtphub_bind *b) { struct gtphub_peer *p, *n; llist_for_each_entry_safe(p, n, &b->peers, entry) { if (gtphub_gc_peer(p)) { gtphub_peer_del(p); } } } void gtphub_gc(struct gtphub *hub, time_t now) { int expired; expired = expiry_tick(&hub->expire_quickly, now); expired += expiry_tick(&hub->expire_slowly, now); /* ... */ if (expired) { int s, p; for_each_side_and_plane(s, p) { gtphub_gc_bind(&hub->to_gsns[s][p]); } } } static void gtphub_gc_cb(void *data) { struct gtphub *hub = data; gtphub_gc(hub, gtphub_now()); osmo_timer_schedule(&hub->gc_timer, GTPH_GC_TICK_SECONDS, 0); } static void gtphub_gc_start(struct gtphub *hub) { osmo_timer_setup(&hub->gc_timer, gtphub_gc_cb, hub); osmo_timer_schedule(&hub->gc_timer, GTPH_GC_TICK_SECONDS, 0); } /* called by unit tests */ void gtphub_init(struct gtphub *hub) { gtphub_zero(hub); INIT_LLIST_HEAD(&hub->tunnels); INIT_LLIST_HEAD(&hub->pending_deletes); expiry_init(&hub->expire_quickly, GTPH_EXPIRE_QUICKLY_SECS); expiry_init(&hub->expire_slowly, GTPH_EXPIRE_SLOWLY_MINUTES * 60); nr_pool_init(&hub->tei_pool, 1, 0xffffffff); int side_idx; int plane_idx; for_each_side_and_plane(side_idx, plane_idx) { gtphub_bind_init(&hub->to_gsns[side_idx][plane_idx], CTR_IDX_HUB(side_idx, plane_idx)); } hub->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].label = "SGSN Ctrl"; hub->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].label = "GGSN Ctrl"; hub->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_USER].label = "SGSN User"; hub->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_USER].label = "GGSN User"; } /* For the test suite, this is kept separate from gtphub_stop(), which also * closes sockets. The test suite avoids using sockets and would cause * segfaults when trying to close uninitialized ofds. */ void gtphub_free(struct gtphub *hub) { /* By expiring all mappings, a garbage collection should free * everything else. A gtphub_bind_free() will assert that everything is * indeed empty. */ expiry_clear(&hub->expire_quickly); expiry_clear(&hub->expire_slowly); int side_idx; int plane_idx; for_each_side_and_plane(side_idx, plane_idx) { gtphub_gc_bind(&hub->to_gsns[side_idx][plane_idx]); gtphub_bind_free(&hub->to_gsns[side_idx][plane_idx]); } } void gtphub_stop(struct gtphub *hub) { int side_idx; int plane_idx; for_each_side_and_plane(side_idx, plane_idx) { gtphub_bind_stop(&hub->to_gsns[side_idx][plane_idx]); } gtphub_free(hub); } static int gtphub_make_proxy(struct gtphub *hub, struct gtphub_peer_port **pp, struct gtphub_bind *bind, const struct gtphub_cfg_addr *addr) { if (!addr->addr_str) return 0; struct gsn_addr gsna; if (gsn_addr_from_str(&gsna, addr->addr_str) != 0) return -1; *pp = gtphub_port_have(hub, bind, &gsna, addr->port); /* This is *the* proxy. Make sure it is never expired. */ gtphub_port_ref_count_inc(*pp); return 0; } int gtphub_start(struct gtphub *hub, struct gtphub_cfg *cfg, uint8_t restart_counter) { gtphub_init(hub); hub->restart_counter = restart_counter; hub->sgsn_use_sender = cfg->sgsn_use_sender? 1 : 0; /* If a Ctrl plane proxy is configured, ares will never be used. */ if (!cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].addr_str) { if (gtphub_ares_init(hub) != 0) { LOG(LOGL_FATAL, "Failed to initialize ares\n"); return -1; } } int side_idx; int plane_idx; for_each_side_and_plane(side_idx, plane_idx) { int rc; rc = gtphub_bind_start(&hub->to_gsns[side_idx][plane_idx], &cfg->to_gsns[side_idx][plane_idx], (side_idx == GTPH_SIDE_SGSN) ? from_sgsns_read_cb : from_ggsns_read_cb, hub, plane_idx); if (rc) { LOG(LOGL_FATAL, "Failed to bind for %ss (%s)\n", gtphub_side_idx_names[side_idx], gtphub_plane_idx_names[plane_idx]); return rc; } } for_each_side_and_plane(side_idx, plane_idx) { if (gtphub_make_proxy(hub, &hub->proxy[side_idx][plane_idx], &hub->to_gsns[side_idx][plane_idx], &cfg->proxy[side_idx][plane_idx]) != 0) { LOG(LOGL_FATAL, "Cannot configure %s proxy" " %s port %d.\n", gtphub_side_idx_names[side_idx], cfg->proxy[side_idx][plane_idx].addr_str, (int)cfg->proxy[side_idx][plane_idx].port); return -1; } } for_each_side_and_plane(side_idx, plane_idx) { if (hub->proxy[side_idx][plane_idx]) LOG(LOGL_NOTICE, "Using %s %s proxy %s\n", gtphub_side_idx_names[side_idx], gtphub_plane_idx_names[plane_idx], gtphub_port_str(hub->proxy[side_idx][plane_idx])); } if (hub->sgsn_use_sender) LOG(LOGL_NOTICE, "Using sender address and port for SGSN instead of GSN Addr IE and default ports.\n"); gtphub_gc_start(hub); return 0; } static struct gtphub_peer_addr *gtphub_peer_find_addr(const struct gtphub_peer *peer, const struct gsn_addr *addr) { struct gtphub_peer_addr *a; llist_for_each_entry(a, &peer->addresses, entry) { if (gsn_addr_same(&a->addr, addr)) return a; } return NULL; } static struct gtphub_peer_port *gtphub_addr_find_port(const struct gtphub_peer_addr *a, uint16_t port) { OSMO_ASSERT(port); struct gtphub_peer_port *pp; llist_for_each_entry(pp, &a->ports, entry) { if (pp->port == port) return pp; } return NULL; } static struct gtphub_peer_addr *gtphub_addr_find(const struct gtphub_bind *bind, const struct gsn_addr *addr) { struct gtphub_peer *peer; llist_for_each_entry(peer, &bind->peers, entry) { struct gtphub_peer_addr *a = gtphub_peer_find_addr(peer, addr); if (a) return a; } return NULL; } static struct gtphub_peer_port *gtphub_port_find(const struct gtphub_bind *bind, const struct gsn_addr *addr, uint16_t port) { struct gtphub_peer_addr *a = gtphub_addr_find(bind, addr); if (!a) return NULL; return gtphub_addr_find_port(a, port); } struct gtphub_peer_port *gtphub_port_find_sa(const struct gtphub_bind *bind, const struct osmo_sockaddr *addr) { struct gsn_addr gsna; uint16_t port; if (gsn_addr_from_sockaddr(&gsna, &port, addr) != 0) return NULL; return gtphub_port_find(bind, &gsna, port); } static struct gtphub_peer *gtphub_peer_new(struct gtphub *hub, struct gtphub_bind *bind) { struct gtphub_peer *peer = talloc_zero(osmo_gtphub_ctx, struct gtphub_peer); OSMO_ASSERT(peer); INIT_LLIST_HEAD(&peer->addresses); nr_pool_init(&peer->seq_pool, 0, 0xffff); nr_map_init(&peer->seq_map, &peer->seq_pool, &hub->expire_quickly); /* TODO use something random to pick the initial sequence nr. 0x6d31 produces the ASCII character sequence 'm1', currently used in gtphub_nc_test.sh. */ peer->seq_pool.last_nr = 0x6d31 - 1; llist_add(&peer->entry, &bind->peers); return peer; } static struct gtphub_peer_addr *gtphub_peer_add_addr(struct gtphub_peer *peer, const struct gsn_addr *addr) { struct gtphub_peer_addr *a; a = talloc_zero(osmo_gtphub_ctx, struct gtphub_peer_addr); OSMO_ASSERT(a); a->peer = peer; gsn_addr_copy(&a->addr, addr); INIT_LLIST_HEAD(&a->ports); llist_add(&a->entry, &peer->addresses); return a; } static struct gtphub_peer_addr *gtphub_addr_have(struct gtphub *hub, struct gtphub_bind *bind, const struct gsn_addr *addr) { struct gtphub_peer_addr *a = gtphub_addr_find(bind, addr); if (a) return a; /* If we haven't found an address, that means we need to create an * entirely new peer for the new address. More addresses may be added * to this peer later, but not via this function. */ struct gtphub_peer *peer = gtphub_peer_new(hub, bind); a = gtphub_peer_add_addr(peer, addr); LOG(LOGL_DEBUG, "New peer address: %s %s\n", bind->label, gsn_addr_to_str(&a->addr)); return a; } static struct gtphub_peer_port *gtphub_addr_add_port(struct gtphub_peer_addr *a, uint16_t port) { struct gtphub_peer_port *pp; pp = talloc_zero(osmo_gtphub_ctx, struct gtphub_peer_port); OSMO_ASSERT(pp); pp->peer_addr = a; pp->port = port; pp->last_restart_count = -1; if (gsn_addr_to_sockaddr(&a->addr, port, &pp->sa) != 0) { talloc_free(pp); return NULL; } pp->counters_io = rate_ctr_group_alloc(osmo_gtphub_ctx, >phub_ctrg_io_desc, port); if (!pp->counters_io) { LOG(LOGL_ERROR, "Failed to allocate rate counters for %s:%u\n", gsn_addr_to_str(&a->addr), port); talloc_free(pp); return NULL; } llist_add(&pp->entry, &a->ports); LOG(LOGL_DEBUG, "New peer port: %s port %d\n", gsn_addr_to_str(&a->addr), (int)port); return pp; } struct gtphub_peer_port *gtphub_port_have(struct gtphub *hub, struct gtphub_bind *bind, const struct gsn_addr *addr, uint16_t port) { struct gtphub_peer_addr *a = gtphub_addr_have(hub, bind, addr); struct gtphub_peer_port *pp = gtphub_addr_find_port(a, port); if (pp) return pp; return gtphub_addr_add_port(a, port); } /* Find a GGSN peer with a matching address. If the address is known but the * port not, create a new port for that peer address. */ struct gtphub_peer_port *gtphub_known_addr_have_port(const struct gtphub_bind *bind, const struct osmo_sockaddr *addr) { struct gtphub_peer_addr *pa; struct gtphub_peer_port *pp; struct gsn_addr gsna; uint16_t port; int rc = gsn_addr_from_sockaddr(&gsna, &port, addr); if (rc < 0) LOG(LOGL_ERROR, "%s(): failed to obtain GSN address\n", __func__); pa = gtphub_addr_find(bind, &gsna); if (!pa) return NULL; pp = gtphub_addr_find_port(pa, port); if (!pp) pp = gtphub_addr_add_port(pa, port); return pp; } /* Return 0 if the message in p is not applicable for GGSN resolution, -1 if * resolution should be possible but failed, and 1 if resolution was * successful. *pp will be set to NULL if <1 is returned. */ static int gtphub_resolve_ggsn(struct gtphub *hub, struct gtp_packet_desc *p, struct gtphub_peer_port **pp) { *pp = NULL; /* TODO determine from message type whether IEs should be present? */ int rc; const char *imsi_str; rc = get_ie_imsi_str(p->ie, 0, &imsi_str); if (rc < 1) return rc; OSMO_ASSERT(imsi_str); const char *apn_str; rc = get_ie_apn_str(p->ie, &apn_str); if (rc < 1) return rc; OSMO_ASSERT(apn_str); *pp = gtphub_resolve_ggsn_addr(hub, imsi_str, apn_str); return (*pp)? 1 : -1; } /* TODO move to osmocom/core/socket.c ? */ /* use this in osmo_sock_init() to remove dup. */ /* Internal: call getaddrinfo for osmo_sockaddr_init(). The caller is required to call freeaddrinfo(*result), iff zero is returned. */ static int _osmo_getaddrinfo(struct addrinfo **result, uint16_t family, uint16_t type, uint8_t proto, const char *host, uint16_t port) { struct addrinfo hints; char portbuf[16]; sprintf(portbuf, "%u", port); memset(&hints, '\0', sizeof(struct addrinfo)); hints.ai_family = family; if (type == SOCK_RAW) { /* Workaround for glibc, that returns EAI_SERVICE (-8) if * SOCK_RAW and IPPROTO_GRE is used. */ hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; } else { hints.ai_socktype = type; hints.ai_protocol = proto; } return getaddrinfo(host, portbuf, &hints, result); } /* TODO move to osmocom/core/socket.c ? */ int osmo_sockaddr_init(struct osmo_sockaddr *addr, uint16_t family, uint16_t type, uint8_t proto, const char *host, uint16_t port) { struct addrinfo *res; int rc; rc = _osmo_getaddrinfo(&res, family, type, proto, host, port); if (rc != 0) { LOG(LOGL_ERROR, "getaddrinfo returned error %d\n", (int)rc); return -EINVAL; } OSMO_ASSERT(res->ai_addrlen <= sizeof(addr->a)); memcpy(&addr->a, res->ai_addr, res->ai_addrlen); addr->l = res->ai_addrlen; freeaddrinfo(res); return 0; } int osmo_sockaddr_to_strs(char *addr_str, size_t addr_str_len, char *port_str, size_t port_str_len, const struct osmo_sockaddr *addr, int flags) { int rc; if ((addr->l < 1) || (addr->l > sizeof(addr->a))) { LOGP(DGTPHUB, LOGL_ERROR, "Invalid address size: %d\n", addr->l); return -1; } if (addr->l > sizeof(addr->a)) { LOGP(DGTPHUB, LOGL_ERROR, "Invalid address: too long: %d\n", addr->l); return -1; } rc = getnameinfo((struct sockaddr*)&addr->a, addr->l, addr_str, addr_str_len, port_str, port_str_len, flags); if (rc) LOGP(DGTPHUB, LOGL_ERROR, "Invalid address: %s: %s\n", gai_strerror(rc), osmo_hexdump((uint8_t*)&addr->a, addr->l)); return rc; } const char *osmo_sockaddr_to_strb(const struct osmo_sockaddr *addr, char *buf, size_t buf_len) { const int portbuf_len = 6; OSMO_ASSERT(buf_len > portbuf_len); char *portbuf = buf + buf_len - portbuf_len; buf_len -= portbuf_len; if (osmo_sockaddr_to_strs(buf, buf_len, portbuf, portbuf_len, addr, NI_NUMERICHOST | NI_NUMERICSERV)) return NULL; char *pos = buf + strnlen(buf, buf_len-1); size_t len = buf_len - (pos - buf); snprintf(pos, len, " port %s", portbuf); buf[buf_len-1] = '\0'; return buf; } const char *osmo_sockaddr_to_str(const struct osmo_sockaddr *addr) { static char buf[256]; const char *result = osmo_sockaddr_to_strb(addr, buf, sizeof(buf)); if (! result) return "(invalid)"; return result; } int osmo_sockaddr_cmp(const struct osmo_sockaddr *a, const struct osmo_sockaddr *b) { if (a == b) return 0; if (!a) return -1; if (!b) return 1; if (a->l != b->l) { /* Lengths are not the same, but determine the order. Will * anyone ever sort a list by osmo_sockaddr though...? */ int cmp = memcmp(&a->a, &b->a, (a->l < b->l)? a->l : b->l); if (cmp == 0) { if (a->l < b->l) return -1; else return 1; } return cmp; } return memcmp(&a->a, &b->a, a->l); } void osmo_sockaddr_copy(struct osmo_sockaddr *dst, const struct osmo_sockaddr *src) { OSMO_ASSERT(src->l <= sizeof(dst->a)); memcpy(&dst->a, &src->a, src->l); dst->l = src->l; } osmo-sgsn-1.3.0/src/gprs/gtphub_ares.c000066400000000000000000000142771327264017000176660ustar00rootroot00000000000000/* GTP Hub Implementation */ /* (C) 2015 by sysmocom s.f.m.c. GmbH * All Rights Reserved * * gtphub_ares.c. * * This file is kept separate so that these functions can be wrapped for * gtphub_test.c. When a function and its callers are in the same compilational * unit, the wrappability may be optimized away. * * 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 /* TODO split GRX ares from sgsn into a separate struct and allow use without * globals. */ #include extern struct sgsn_instance *sgsn; struct sgsn_instance sgsn_inst = { 0 }; struct sgsn_instance *sgsn = &sgsn_inst; extern void *osmo_gtphub_ctx; int gtphub_ares_init(struct gtphub *hub) { return sgsn_ares_init(sgsn); } struct ggsn_lookup { struct llist_head entry; struct expiring_item expiry_entry; struct gtphub *hub; char imsi_str[GSM23003_IMSI_MAX_DIGITS+1]; char apn_ni_str[GSM_APN_LENGTH]; char apn_oi_str[GSM_APN_LENGTH]; int have_3dig_mnc; }; static int start_ares_query(struct ggsn_lookup *lookup); static void ggsn_lookup_cb(void *arg, int status, int timeouts, struct hostent *hostent) { struct ggsn_lookup *lookup = arg; LOGP(DGTPHUB, LOGL_NOTICE, "ggsn_lookup_cb(%p / %p)", lookup, &lookup->expiry_entry); if (status != ARES_SUCCESS) { LOGP(DGTPHUB, LOGL_ERROR, "DNS query failed.\n"); /* Need to try with three digits now */ if (!lookup->have_3dig_mnc) { lookup->have_3dig_mnc = 1; if (start_ares_query(lookup) == 0) return; } LOGP(DGTPHUB, LOGL_ERROR, "Failed to resolve GGSN. (%p)\n", lookup); goto remove_from_queue; } struct gsn_addr resolved_addr; if (hostent->h_length > sizeof(resolved_addr.buf)) { LOGP(DGTPHUB, LOGL_ERROR, "Addr size too large: %d > %d\n", (int)hostent->h_length, (int)sizeof(resolved_addr.buf)); goto remove_from_queue; } /* Get the first addr from the list */ char *addr0 = hostent->h_addr_list[0]; if (!addr0) { LOGP(DGTPHUB, LOGL_ERROR, "No host address.\n"); goto remove_from_queue; } memcpy(resolved_addr.buf, addr0, hostent->h_length); resolved_addr.len = hostent->h_length; LOGP(DGTPHUB, LOGL_NOTICE, "resolved addr %s\n", osmo_hexdump((unsigned char*)&resolved_addr, sizeof(resolved_addr))); gtphub_resolved_ggsn(lookup->hub, lookup->apn_oi_str, &resolved_addr, gtphub_now()); remove_from_queue: LOGP(DGTPHUB, LOGL_ERROR, "Removing GGSN lookup. (%p / %p)\n", lookup, &lookup->expiry_entry); expiring_item_del(&lookup->expiry_entry); } static void make_addr_str(struct ggsn_lookup *lookup) { char *apn_oi_str; apn_oi_str = osmo_apn_qualify_from_imsi(lookup->imsi_str, lookup->apn_ni_str, lookup->have_3dig_mnc); osmo_strlcpy(lookup->apn_oi_str, apn_oi_str, sizeof(lookup->apn_oi_str)); } static int start_ares_query(struct ggsn_lookup *lookup) { LOGP(DGTPHUB, LOGL_DEBUG, "Going to query %s (%p / %p)\n", lookup->apn_oi_str, lookup, &lookup->expiry_entry); int rc = sgsn_ares_query(sgsn, lookup->apn_oi_str, ggsn_lookup_cb, lookup); if (rc != 0) LOGP(DGTPHUB, LOGL_ERROR, "Failed to start ares query.\n"); return rc; } static void ggsn_lookup_del_cb(struct expiring_item *expi) { struct ggsn_lookup *lookup; lookup = container_of(expi, struct ggsn_lookup, expiry_entry); LOGP(DGTPHUB, LOGL_NOTICE, "ggsn_lookup_del_cb(%p / %p)\n", lookup, expi); lookup->expiry_entry.del_cb = 0; expiring_item_del(expi); llist_del(&lookup->entry); talloc_free(lookup); } struct gtphub_peer_port *gtphub_resolve_ggsn_addr(struct gtphub *hub, const char *imsi_str, const char *apn_ni_str) { OSMO_ASSERT(imsi_str); OSMO_ASSERT(apn_ni_str); struct ggsn_lookup *lookup = talloc_zero(osmo_gtphub_ctx, struct ggsn_lookup); OSMO_ASSERT(lookup); LOGP(DGTPHUB, LOGL_DEBUG, "Request to resolve IMSI" " '%s' with APN-NI '%s' (%p / %p)\n", imsi_str, apn_ni_str, lookup, &lookup->expiry_entry); expiring_item_init(&lookup->expiry_entry); lookup->hub = hub; osmo_strlcpy(lookup->imsi_str, imsi_str, sizeof(lookup->imsi_str)); osmo_strlcpy(lookup->apn_ni_str, apn_ni_str, sizeof(lookup->apn_ni_str)); make_addr_str(lookup); struct ggsn_lookup *active; llist_for_each_entry(active, &hub->ggsn_lookups, entry) { if (strncmp(active->apn_oi_str, lookup->apn_oi_str, sizeof(lookup->apn_oi_str)) == 0) { LOGP(DGTPHUB, LOGL_DEBUG, "Query already pending for %s\n", lookup->apn_oi_str); /* A query already pending. Just tip our hat. */ return NULL; } } struct gtphub_resolved_ggsn *resolved; llist_for_each_entry(resolved, &hub->resolved_ggsns, entry) { if (strncmp(resolved->apn_oi_str, lookup->apn_oi_str, sizeof(lookup->apn_oi_str)) == 0) { LOGP(DGTPHUB, LOGL_DEBUG, "GGSN resolved from cache: %s -> %s\n", lookup->apn_oi_str, gtphub_port_str(resolved->peer)); return resolved->peer; } } /* Kick off a resolution, but so far return nothing. The hope is that * the peer will resend the request (a couple of times), and by then * the GGSN will be resolved. */ LOGP(DGTPHUB, LOGL_DEBUG, "Sending out DNS query for %s..." " (Returning failure, hoping for a retry once resolution" " has concluded)\n", lookup->apn_oi_str); llist_add(&lookup->entry, &hub->ggsn_lookups); lookup->expiry_entry.del_cb = ggsn_lookup_del_cb; expiry_add(&hub->expire_quickly, &lookup->expiry_entry, gtphub_now()); start_ares_query(lookup); return NULL; } osmo-sgsn-1.3.0/src/gprs/gtphub_main.c000066400000000000000000000215211327264017000176460ustar00rootroot00000000000000/* GTP Hub main program */ /* (C) 2015 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 #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../bscconfig.h" #if BUILD_IU #include #endif extern void *osmo_gtphub_ctx; void *tall_bsc_ctx; const char *gtphub_copyright = "Copyright (C) 2015 sysmocom s.f.m.c GmbH \r\n" "License AGPLv3+: GNU AGPL version 2 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"; static struct log_info_cat gtphub_categories[] = { [DGTPHUB] = { .name = "DGTPHUB", .description = "GTP Hub", .color = "\033[1;33m", .enabled = 1, .loglevel = LOGL_INFO, }, }; int gtphub_log_filter_fn(const struct log_context *ctx, struct log_target *tar) { return 0; } static const struct log_info gtphub_log_info = { .filter_fn = gtphub_log_filter_fn, .cat = gtphub_categories, .num_cat = ARRAY_SIZE(gtphub_categories), }; void log_cfg(struct gtphub_cfg *cfg) { int side_idx, plane_idx; for_each_side_and_plane(side_idx, plane_idx) { struct gtphub_cfg_addr *a; a = &cfg->to_gsns[side_idx][plane_idx].bind; LOGP(DGTPHUB, LOGL_NOTICE, "to-%ss bind, %s: %s port %d\n", gtphub_side_idx_names[side_idx], gtphub_plane_idx_names[plane_idx], a->addr_str, a->port); } } static void signal_handler(int signal) { fprintf(stdout, "signal %d received\n", signal); switch (signal) { case SIGINT: case SIGTERM: osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL); sleep(1); 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: case SIGUSR2: talloc_report_full(osmo_gtphub_ctx, stderr); break; default: break; } } #if BUILD_IU int gtphub_vty_go_parent(struct vty *vty) { switch (vty->node) { default: osmo_ss7_vty_go_parent(vty); } return vty->node; } #endif int gtphub_vty_is_config_node(struct vty *vty, int node) { /* Check if libosmo-sccp declares the node in * question as config node */ #if BUILD_IU if (osmo_ss7_is_config_node(vty, node)) return 1; #endif switch (node) { /* add items that are not config */ case CONFIG_NODE: return 0; default: return 1; } } static struct vty_app_info vty_info = { .name = "OsmoGTPhub", .version = PACKAGE_VERSION, #if BUILD_IU .go_parent_cb = gtphub_vty_go_parent, #endif .is_config_node = gtphub_vty_is_config_node, }; struct cmdline_cfg { const char *config_file; const char *restart_counter_file; int daemonize; }; static uint8_t next_restart_count(const char *path) { int umask_was = umask(022); uint8_t counter = 0; FILE *f = fopen(path, "r"); if (f) { int rc = fscanf(f, "%hhu", &counter); if (rc != 1) goto failed_to_read; char c; while (fread(&c, 1, 1, f) > 0) { switch (c) { case ' ': case '\t': case '\n': case '\r': break; default: goto failed_to_read; } } fclose(f); } counter ++; f = fopen(path, "w"); if (!f) goto failed_to_write; if (fprintf(f, "%" PRIu8 "\n", counter) < 2) goto failed_to_write; if (fclose(f)) { f = NULL; goto failed_to_write; } umask(umask_was); LOGP(DGTPHUB, LOGL_NOTICE, "Restarted with counter %hhu\n", counter); return counter; failed_to_read: fclose(f); umask(umask_was); LOGP(DGTPHUB, LOGL_FATAL, "Restart counter file cannot be parsed:" " %s\n", path); exit(1); failed_to_write: if (f) fclose(f); umask(umask_was); LOGP(DGTPHUB, LOGL_FATAL, "Restart counter file cannot be written:" " %s\n", path); exit(1); } static void print_help(struct cmdline_cfg *ccfg) { printf("gtphub commandline options\n"); printf(" -h,--help This text.\n"); printf(" -D,--daemonize Fork the process into a background daemon.\n"); printf(" -d,--debug Enable Debugging for this category.\n"); printf(" Pass '-d list' to get a category listing.\n"); printf(" -s,--disable-color\n"); printf(" -c,--config-file The config file to use [%s].\n", ccfg->config_file); printf(" -e,--log-level Set a global log level.\n"); printf(" -r,--restart-file File for counting restarts [%s].\n", ccfg->restart_counter_file); } static void list_categories(void) { printf("Avaliable debug categories:\n"); int i; for (i = 0; i < gtphub_log_info.num_cat; ++i) { if (!gtphub_log_info.cat[i].name) continue; printf("%s\n", gtphub_log_info.cat[i].name); } } static void handle_options(struct cmdline_cfg *ccfg, 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'}, {"log-level", 1, 0, 'e'}, {"restart-file", 1, 0, 'r'}, {NULL, 0, 0, 0} }; c = getopt_long(argc, argv, "hd:Dc:sTe:r:", long_options, &option_index); if (c == -1) { if (optind < argc) { LOGP(DGTPHUB, LOGL_FATAL, "Excess commandline arguments ('%s').\n", argv[optind]); exit(2); } break; } switch (c) { case 'h': //print_usage(); print_help(ccfg); exit(0); case 's': log_set_use_color(osmo_stderr_target, 0); break; case 'd': if (strcmp("list", optarg) == 0) { list_categories(); exit(0); } else log_parse_category_mask(osmo_stderr_target, optarg); break; case 'D': ccfg->daemonize = 1; break; case 'c': ccfg->config_file = optarg; 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 'r': ccfg->restart_counter_file = optarg; break; default: LOGP(DGTPHUB, LOGL_FATAL, "Invalid command line argument, abort.\n"); exit(1); break; } } } int main(int argc, char **argv) { int rc; struct cmdline_cfg _ccfg; struct cmdline_cfg *ccfg = &_ccfg; memset(ccfg, '\0', sizeof(*ccfg)); ccfg->config_file = "./gtphub.conf"; ccfg->restart_counter_file = "./gtphub_restart_count"; struct gtphub_cfg _cfg; struct gtphub_cfg *cfg = &_cfg; memset(cfg, '\0', sizeof(*cfg)); struct gtphub _hub; struct gtphub *hub = &_hub; osmo_gtphub_ctx = talloc_named_const(NULL, 0, "osmo_gtphub"); msgb_talloc_ctx_init(osmo_gtphub_ctx, 0); vty_info.tall_ctx = osmo_gtphub_ctx; signal(SIGINT, &signal_handler); signal(SIGTERM, &signal_handler); signal(SIGABRT, &signal_handler); signal(SIGUSR1, &signal_handler); signal(SIGUSR2, &signal_handler); osmo_init_ignore_signals(); osmo_init_logging2(osmo_gtphub_ctx, >phub_log_info); vty_info.copyright = gtphub_copyright; vty_init(&vty_info); logging_vty_add_cmds(NULL); osmo_talloc_vty_add_cmds(); gtphub_vty_init(hub, cfg); rate_ctr_init(osmo_gtphub_ctx); handle_options(ccfg, argc, argv); rc = gtphub_cfg_read(cfg, ccfg->config_file); if (rc < 0) { LOGP(DGTPHUB, LOGL_FATAL, "Cannot parse config file '%s'\n", ccfg->config_file); exit(2); } /* start telnet after reading config for vty_get_bind_addr() */ rc = telnet_init_dynif(osmo_gtphub_ctx, 0, vty_get_bind_addr(), OSMO_VTY_PORT_GTPHUB); if (rc < 0) exit(1); if (gtphub_start(hub, cfg, next_restart_count(ccfg->restart_counter_file)) != 0) return -1; log_cfg(cfg); if (ccfg->daemonize) { rc = osmo_daemonize(); if (rc < 0) { LOGP(DGTPHUB, LOGL_FATAL, "Error during daemonize"); exit(1); } } while (1) { rc = osmo_select_main(0); if (rc < 0) exit(3); } /* not reached */ exit(0); } osmo-sgsn-1.3.0/src/gprs/gtphub_sock.c000066400000000000000000000035331327264017000176640ustar00rootroot00000000000000/* GTP Hub Implementation */ /* (C) 2015 by sysmocom s.f.m.c. GmbH * All Rights Reserved * * gtphub_sock.c. * * This file is kept separate so that these functions can be wrapped for * gtphub_test.c. When a function and its callers are in the same compilational * unit, the wrappability may be optimized away. * * 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 /* Convenience makro, note: only within this C file. */ #define LOG(level, fmt, args...) \ LOGP(DGTPHUB, level, fmt, ##args) int gtphub_write(const struct osmo_fd *to, const struct osmo_sockaddr *to_addr, const uint8_t *buf, size_t buf_len) { errno = 0; ssize_t sent = sendto(to->fd, buf, buf_len, 0, (struct sockaddr*)&to_addr->a, to_addr->l); LOG(LOGL_DEBUG, "to %s\n", osmo_sockaddr_to_str(to_addr)); if (sent == -1) { LOG(LOGL_ERROR, "error: %s\n", strerror(errno)); return -EINVAL; } if (sent != buf_len) LOG(LOGL_ERROR, "sent(%d) != data_len(%d)\n", (int)sent, (int)buf_len); else LOG(LOGL_DEBUG, "Sent %d: %s%s\n", (int)sent, osmo_hexdump(buf, sent > 1000? 1000 : sent), sent > 1000 ? "..." : ""); return 0; } osmo-sgsn-1.3.0/src/gprs/gtphub_vty.c000066400000000000000000000430611327264017000175470ustar00rootroot00000000000000/* (C) 2015 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 #include #include #include #include #include /* TODO split GRX ares from sgsn into a separate struct and allow use without * globals. */ #include extern struct sgsn_instance *sgsn; extern void *tall_bsc_ctx; static struct gtphub *g_hub = 0; static struct gtphub_cfg *g_cfg = 0; static struct cmd_node gtphub_node = { GTPHUB_NODE, "%s(config-gtphub)# ", 1, }; #define GTPH_DEFAULT_CONTROL_PORT 2123 #define GTPH_DEFAULT_USER_PORT 2152 static void write_addrs(struct vty *vty, const char *name, struct gtphub_cfg_addr *c, struct gtphub_cfg_addr *u) { if ((c->port == GTPH_DEFAULT_CONTROL_PORT) && (u->port == GTPH_DEFAULT_USER_PORT) && (strcmp(c->addr_str, u->addr_str) == 0)) { /* Default port numbers and same IP address: write "short" * variant. */ vty_out(vty, " %s %s%s", name, c->addr_str, VTY_NEWLINE); return; } vty_out(vty, " %s ctrl %s %d user %s %d%s", name, c->addr_str, (int)c->port, u->addr_str, (int)u->port, VTY_NEWLINE); struct ares_addr_node *server; for (server = sgsn->ares_servers; server; server = server->next) vty_out(vty, " grx-dns-add %s%s", inet_ntoa(server->addr.addr4), VTY_NEWLINE); } static int config_write_gtphub(struct vty *vty) { vty_out(vty, "gtphub%s", VTY_NEWLINE); write_addrs(vty, "bind-to-sgsns", &g_cfg->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].bind, &g_cfg->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_USER].bind); write_addrs(vty, "bind-to-ggsns", &g_cfg->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].bind, &g_cfg->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_USER].bind); if (g_cfg->sgsn_use_sender) { vty_out(vty, "sgsn-use-sender%s", VTY_NEWLINE); } if (g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].addr_str) { write_addrs(vty, "sgsn-proxy", &g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL], &g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_USER]); } if (g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].addr_str) { write_addrs(vty, "ggsn-proxy", &g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL], &g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_USER]); } return CMD_SUCCESS; } DEFUN(cfg_gtphub, cfg_gtphub_cmd, "gtphub", "Configure the GTP hub\n") { vty->node = GTPHUB_NODE; return CMD_SUCCESS; } #define BIND_ARGS "ctrl ADDR <0-65535> user ADDR <0-65535>" #define BIND_DOCS \ "Set GTP-C bind\n" \ "GTP-C local IP address (v4 or v6)\n" \ "GTP-C local port\n" \ "Set GTP-U bind\n" \ "GTP-U local IP address (v4 or v6)\n" \ "GTP-U local port\n" DEFUN(cfg_gtphub_bind_to_sgsns_short, cfg_gtphub_bind_to_sgsns_short_cmd, "bind-to-sgsns ADDR", "GTP Hub Parameters\n" "Set the local bind address to listen for SGSNs, for both GTP-C and GTP-U\n" "Local IP address (v4 or v6)\n" ) { int i; for_each_plane(i) g_cfg->to_gsns[GTPH_SIDE_SGSN][i].bind.addr_str = talloc_strdup(tall_vty_ctx, argv[0]); g_cfg->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].bind.port = GTPH_DEFAULT_CONTROL_PORT; g_cfg->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_USER].bind.port = GTPH_DEFAULT_USER_PORT; return CMD_SUCCESS; } DEFUN(cfg_gtphub_bind_to_ggsns_short, cfg_gtphub_bind_to_ggsns_short_cmd, "bind-to-ggsns ADDR", "GTP Hub Parameters\n" "Set the local bind address to listen for GGSNs, for both GTP-C and GTP-U\n" "Local IP address (v4 or v6)\n" ) { int i; for_each_plane(i) g_cfg->to_gsns[GTPH_SIDE_GGSN][i].bind.addr_str = talloc_strdup(tall_vty_ctx, argv[0]); g_cfg->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].bind.port = GTPH_DEFAULT_CONTROL_PORT; g_cfg->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_USER].bind.port = GTPH_DEFAULT_USER_PORT; return CMD_SUCCESS; } static int handle_binds(struct gtphub_cfg_bind *b, const char **argv) { b[GTPH_PLANE_CTRL].bind.addr_str = talloc_strdup(tall_vty_ctx, argv[0]); b[GTPH_PLANE_CTRL].bind.port = atoi(argv[1]); b[GTPH_PLANE_USER].bind.addr_str = talloc_strdup(tall_vty_ctx, argv[2]); b[GTPH_PLANE_USER].bind.port = atoi(argv[3]); return CMD_SUCCESS; } DEFUN(cfg_gtphub_bind_to_sgsns, cfg_gtphub_bind_to_sgsns_cmd, "bind-to-sgsns " BIND_ARGS, "GTP Hub Parameters\n" "Set the local bind addresses and ports to listen for SGSNs\n" BIND_DOCS ) { return handle_binds(g_cfg->to_gsns[GTPH_SIDE_SGSN], argv); } DEFUN(cfg_gtphub_bind_to_ggsns, cfg_gtphub_bind_to_ggsns_cmd, "bind-to-ggsns " BIND_ARGS, "GTP Hub Parameters\n" "Set the local bind addresses and ports to listen for GGSNs\n" BIND_DOCS ) { return handle_binds(g_cfg->to_gsns[GTPH_SIDE_GGSN], argv); } DEFUN(cfg_gtphub_ggsn_proxy_short, cfg_gtphub_ggsn_proxy_short_cmd, "ggsn-proxy ADDR", "GTP Hub Parameters\n" "Redirect all GGSN bound traffic to default ports on this address (another gtphub)\n" "Remote IP address (v4 or v6)\n" ) { g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].addr_str = talloc_strdup(tall_vty_ctx, argv[0]); g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].port = GTPH_DEFAULT_CONTROL_PORT; g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_USER].addr_str = talloc_strdup(tall_vty_ctx, argv[0]); g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_USER].port = GTPH_DEFAULT_USER_PORT; return CMD_SUCCESS; } DEFUN(cfg_gtphub_ggsn_proxy, cfg_gtphub_ggsn_proxy_cmd, "ggsn-proxy " BIND_ARGS, "GTP Hub Parameters\n" "Redirect all GGSN bound traffic to these addresses and ports (another gtphub)\n" BIND_DOCS ) { g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].addr_str = talloc_strdup(tall_vty_ctx, argv[0]); g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].port = atoi(argv[1]); g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_USER].addr_str = talloc_strdup(tall_vty_ctx, argv[2]); g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_USER].port = atoi(argv[3]); return CMD_SUCCESS; } DEFUN(cfg_gtphub_sgsn_proxy_short, cfg_gtphub_sgsn_proxy_short_cmd, "sgsn-proxy ADDR", "GTP Hub Parameters\n" "Redirect all SGSN bound traffic to default ports on this address (another gtphub)\n" "Remote IP address (v4 or v6)\n" ) { g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].addr_str = talloc_strdup(tall_vty_ctx, argv[0]); g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].port = GTPH_DEFAULT_CONTROL_PORT; g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_USER].addr_str = talloc_strdup(tall_vty_ctx, argv[0]); g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_USER].port = GTPH_DEFAULT_USER_PORT; return CMD_SUCCESS; } DEFUN(cfg_gtphub_sgsn_proxy, cfg_gtphub_sgsn_proxy_cmd, "sgsn-proxy " BIND_ARGS, "GTP Hub Parameters\n" "Redirect all SGSN bound traffic to these addresses and ports (another gtphub)\n" BIND_DOCS ) { g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].addr_str = talloc_strdup(tall_vty_ctx, argv[0]); g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].port = atoi(argv[1]); g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_USER].addr_str = talloc_strdup(tall_vty_ctx, argv[2]); g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_USER].port = atoi(argv[3]); return CMD_SUCCESS; } #define SGSN_USE_SENDER_STR \ "Ignore SGSN's Address IEs, use sender address and port (useful over NAT)\n" DEFUN(cfg_gtphub_sgsn_use_sender, cfg_gtphub_sgsn_use_sender_cmd, "sgsn-use-sender", SGSN_USE_SENDER_STR) { g_cfg->sgsn_use_sender = 1; return CMD_SUCCESS; } DEFUN(cfg_gtphub_no_sgsn_use_sender, cfg_gtphub_no_sgsn_use_sender_cmd, "no sgsn-use-sender", NO_STR SGSN_USE_SENDER_STR) { g_cfg->sgsn_use_sender = 0; return CMD_SUCCESS; } /* Copied from sgsn_vty.h */ DEFUN(cfg_grx_ggsn, cfg_grx_ggsn_cmd, "grx-dns-add A.B.C.D", "Add DNS server\nIPv4 address\n") { struct ares_addr_node *node = talloc_zero(tall_bsc_ctx, struct ares_addr_node); node->family = AF_INET; inet_aton(argv[0], &node->addr.addr4); node->next = sgsn->ares_servers; sgsn->ares_servers = node; return CMD_SUCCESS; } static void show_bind_stats_all(struct vty *vty) { int plane_idx; for_each_plane(plane_idx) { vty_out(vty, "- %s Plane:%s", gtphub_plane_idx_names[plane_idx], VTY_NEWLINE); int side_idx; for_each_side(side_idx) { struct gtphub_bind *b = &g_hub->to_gsns[side_idx][plane_idx]; vty_out(vty, " - local addr to/from %ss: %s port %d%s", gtphub_side_idx_names[side_idx], gsn_addr_to_str(&b->local_addr), (int)b->local_port, VTY_NEWLINE); vty_out_rate_ctr_group(vty, " ", b->counters_io); } } } static void show_tunnel_stats(struct vty *vty, struct gtphub_tunnel *tun) { int plane_idx; for_each_plane(plane_idx) { vty_out(vty, "- %s Plane:%s", gtphub_plane_idx_names[plane_idx], VTY_NEWLINE); int side_idx; for_each_side(side_idx) { struct gtphub_tunnel_endpoint *te = &tun->endpoint[side_idx][plane_idx]; vty_out(vty, " - to/from %s:%s", gtphub_side_idx_names[side_idx], VTY_NEWLINE); vty_out_rate_ctr_group(vty, " ", te->counters_io); } } } static void show_peer_summary(struct vty *vty, const char *prefix, int side_idx, int plane_idx, struct gtphub_peer *p, int with_io_stats) { struct gtphub_peer_addr *pa; int p2l = strlen(prefix) + 4 + 1; char prefix2[p2l]; memset(prefix2, ' ', p2l - 1); prefix2[p2l - 1] = '\0'; if (with_io_stats) { llist_for_each_entry(pa, &p->addresses, entry) { vty_out(vty, "%s- %s %s %s%s", prefix, gtphub_side_idx_names[side_idx], gtphub_plane_idx_names[plane_idx], gsn_addr_to_str(&pa->addr), VTY_NEWLINE); struct gtphub_peer_port *pp; llist_for_each_entry(pp, &pa->ports, entry) { vty_out(vty, "%s Port %" PRIu16 "%s", prefix, pp->port, VTY_NEWLINE); vty_out_rate_ctr_group(vty, prefix2, pp->counters_io); } } } else { llist_for_each_entry(pa, &p->addresses, entry) { vty_out(vty, "%s- %s %s %s", prefix, gtphub_side_idx_names[side_idx], gtphub_plane_idx_names[plane_idx], gsn_addr_to_str(&pa->addr)); struct gtphub_peer_port *pp; llist_for_each_entry(pp, &pa->ports, entry) { vty_out(vty, ":%" PRIu16, pp->port); } vty_out(vty, VTY_NEWLINE); } } } static void show_peers_summary(struct vty *vty) { int side_idx; int plane_idx; int count[GTPH_SIDE_N][GTPH_PLANE_N] = {{0}}; for_each_side(side_idx) { for_each_plane(plane_idx) { struct gtphub_peer *p; llist_for_each_entry(p, &g_hub->to_gsns[side_idx][plane_idx].peers, entry) { count[side_idx][plane_idx] ++; } } } vty_out(vty, "Peers Count:%s", VTY_NEWLINE); for_each_side_and_plane(side_idx, plane_idx) { vty_out(vty, " %s %s peers: %d%s", gtphub_side_idx_names[side_idx], gtphub_plane_idx_names[plane_idx], count[side_idx][plane_idx], VTY_NEWLINE); } } static void show_peers_all(struct vty *vty, int with_io_stats) { int side_idx; int plane_idx; int count[GTPH_SIDE_N][GTPH_PLANE_N] = {{0}}; vty_out(vty, "All Peers%s%s", with_io_stats? " with I/O stats" : "", VTY_NEWLINE); for_each_side(side_idx) { vty_out(vty, "- %s%s", gtphub_side_idx_names[side_idx], VTY_NEWLINE); for_each_plane(plane_idx) { struct gtphub_peer *p; llist_for_each_entry(p, &g_hub->to_gsns[side_idx][plane_idx].peers, entry) { count[side_idx][plane_idx] ++; show_peer_summary(vty, " ", side_idx, plane_idx, p, with_io_stats); } } } for_each_side_and_plane(side_idx, plane_idx) { vty_out(vty, "%s %s peers: %d%s", gtphub_side_idx_names[side_idx], gtphub_plane_idx_names[plane_idx], count[side_idx][plane_idx], VTY_NEWLINE); } } static void show_tunnels_summary(struct vty *vty) { time_t now = gtphub_now(); const int w = 36; int max_expiry = g_hub->expire_slowly.expiry_in_seconds; float seconds_per_step = ((float)max_expiry) / w; /* Print TEI mapping expiry in an ASCII histogram, like: TEI map summary Legend: '_'=0 '.'<=1% ':'<=2% '|'<=10% '#'>10% (10.0 m/step) CTRL: 30 mappings, valid for 360m[# :. | . : . ]1m USER: 30 mappings, valid for 360m[# :. | . : . ]1m 4 TEI mappings in total, last expiry in 359.4 min */ vty_out(vty, "Tunnels summary%s" " Legend: ' '=0 '.'<=1%% ':'<=2%% '|'<=10%% '#'>10%% (%.1f m/step)%s", VTY_NEWLINE, seconds_per_step / 60., VTY_NEWLINE); int last_expiry = 0; unsigned int count = 0; int histogram[w]; memset(histogram, 0, sizeof(histogram)); struct gtphub_tunnel *t; llist_for_each_entry(t, &g_hub->tunnels, entry) { count ++; int expiry = t->expiry_entry.expiry - now; last_expiry = (last_expiry > expiry) ? last_expiry : expiry; int hi = ((float)expiry) / seconds_per_step; if (hi < 0) hi = 0; if (hi > (w - 1)) hi = w - 1; histogram[hi] ++; } vty_out(vty, " %u tunnels, valid for %dm[", count, max_expiry / 60); int i; for (i = w - 1; i >= 0; i--) { char c; int val = histogram[i]; int percent = 100. * val / count; if (!val) c = ' '; else if (percent <= 1) c = '.'; else if (percent <= 2) c = ':'; else if (percent <= 10) c = '|'; else c = '#'; vty_out(vty, "%c", c); } vty_out(vty, "]1m%s", VTY_NEWLINE); vty_out(vty, " last expiry in %.1f min%s", ((float)last_expiry) / 60., VTY_NEWLINE); } static void show_tunnels_all(struct vty *vty, int with_io_stats) { time_t now = gtphub_now(); vty_out(vty, "All tunnels%s:%s" "Legend: TEI=: SGSN <-> GGSN (expiry in minutes), with each:%s" " [/] (TEI C= U=)%s", with_io_stats? "with I/O stats" : "", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); unsigned int count = 0; unsigned int incomplete = 0; struct gtphub_tunnel *tun; llist_for_each_entry(tun, &g_hub->tunnels, entry) { vty_out(vty, "%s (expiry in %dm)%s", gtphub_tunnel_str(tun), (int)((tun->expiry_entry.expiry - now) / 60), VTY_NEWLINE); count ++; if (!gtphub_tunnel_complete(tun)) incomplete ++; if (with_io_stats) show_tunnel_stats(vty, tun); } vty_out(vty, "Total: %u tunnels (of which %u incomplete)%s", count, incomplete, VTY_NEWLINE); } #define SHOW_GTPHUB_STRS SHOW_STR "Show info on running GTP hub\n" #define SHOW_GTPHUB_PEERS_STRS SHOW_GTPHUB_STRS "Active peers\n" #define SHOW_GTPHUB_TUNS_STRS SHOW_GTPHUB_STRS "Active tunnels\n" DEFUN(show_gtphub_peers_summary, show_gtphub_peers_summary_cmd, "show gtphub peers summary", SHOW_GTPHUB_PEERS_STRS "Summary of all peers\n") { show_peers_summary(vty); return CMD_SUCCESS; } DEFUN(show_gtphub_peers_list, show_gtphub_peers_list_cmd, "show gtphub peers list", SHOW_GTPHUB_PEERS_STRS "List all peers\n") { show_peers_all(vty, 0); return CMD_SUCCESS; } DEFUN(show_gtphub_peers_stats, show_gtphub_peers_stats_cmd, "show gtphub peers stats", SHOW_GTPHUB_PEERS_STRS "List all peers with I/O stats\n") { show_peers_all(vty, 1); return CMD_SUCCESS; } DEFUN(show_gtphub_tunnels_summary, show_gtphub_tunnels_summary_cmd, "show gtphub tunnels summary", SHOW_GTPHUB_TUNS_STRS "Summary of all tunnels\n") { show_tunnels_summary(vty); return CMD_SUCCESS; } DEFUN(show_gtphub_tunnels_list, show_gtphub_tunnels_list_cmd, "show gtphub tunnels list", SHOW_GTPHUB_TUNS_STRS "List all tunnels\n") { show_tunnels_all(vty, 0); return CMD_SUCCESS; } DEFUN(show_gtphub_tunnels_stats, show_gtphub_tunnels_stats_cmd, "show gtphub tunnels stats", SHOW_GTPHUB_TUNS_STRS "List all tunnels with I/O stats\n") { show_tunnels_all(vty, 1); return CMD_SUCCESS; } DEFUN(show_gtphub, show_gtphub_cmd, "show gtphub all", SHOW_GTPHUB_STRS "Summarize everything about the GTP hub\n") { show_bind_stats_all(vty); show_peers_summary(vty); show_tunnels_summary(vty); return CMD_SUCCESS; } int gtphub_vty_init(struct gtphub *global_hub, struct gtphub_cfg *global_cfg) { g_hub = global_hub; g_cfg = global_cfg; install_element_ve(&show_gtphub_cmd); install_element_ve(&show_gtphub_peers_summary_cmd); install_element_ve(&show_gtphub_peers_list_cmd); install_element_ve(&show_gtphub_peers_stats_cmd); install_element_ve(&show_gtphub_tunnels_summary_cmd); install_element_ve(&show_gtphub_tunnels_list_cmd); install_element_ve(&show_gtphub_tunnels_stats_cmd); install_element(CONFIG_NODE, &cfg_gtphub_cmd); install_node(>phub_node, config_write_gtphub); install_element(GTPHUB_NODE, &cfg_gtphub_bind_to_sgsns_short_cmd); install_element(GTPHUB_NODE, &cfg_gtphub_bind_to_sgsns_cmd); install_element(GTPHUB_NODE, &cfg_gtphub_bind_to_ggsns_short_cmd); install_element(GTPHUB_NODE, &cfg_gtphub_bind_to_ggsns_cmd); install_element(GTPHUB_NODE, &cfg_gtphub_ggsn_proxy_short_cmd); install_element(GTPHUB_NODE, &cfg_gtphub_ggsn_proxy_cmd); install_element(GTPHUB_NODE, &cfg_gtphub_sgsn_proxy_short_cmd); install_element(GTPHUB_NODE, &cfg_gtphub_sgsn_proxy_cmd); install_element(GTPHUB_NODE, &cfg_gtphub_sgsn_use_sender_cmd); install_element(GTPHUB_NODE, &cfg_gtphub_no_sgsn_use_sender_cmd); install_element(GTPHUB_NODE, &cfg_grx_ggsn_cmd); return 0; } int gtphub_cfg_read(struct gtphub_cfg *cfg, const char *config_file) { int rc; rc = vty_read_config_file(config_file, NULL); if (rc < 0) { fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file); return rc; } return 0; } osmo-sgsn-1.3.0/src/gprs/oap_client.c000066400000000000000000000171111327264017000174660ustar00rootroot00000000000000/* Osmocom Authentication Protocol API */ /* (C) 2015 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 #include int oap_client_init(struct oap_client_config *config, struct oap_client_state *state) { OSMO_ASSERT(state->state == OAP_UNINITIALIZED); if (!config) goto disable; if (config->client_id == 0) goto disable; if (config->secret_k_present == 0) { LOGP(DLOAP, LOGL_NOTICE, "OAP: client ID set, but secret K missing.\n"); goto disable; } if (config->secret_opc_present == 0) { LOGP(DLOAP, LOGL_NOTICE, "OAP: client ID set, but secret OPC missing.\n"); goto disable; } state->client_id = config->client_id; memcpy(state->secret_k, config->secret_k, sizeof(state->secret_k)); memcpy(state->secret_opc, config->secret_opc, sizeof(state->secret_opc)); state->state = OAP_INITIALIZED; return 0; disable: state->state = OAP_DISABLED; return 0; } /* From the given state and received RAND and AUTN octets, validate the * server's authenticity and formulate the matching milenage reply octets in * *tx_xres. The state is not modified. * On success, and if tx_res is not NULL, exactly 8 octets will be written to * *tx_res. If not NULL, tx_res must point at allocated memory of at least 8 * octets. The caller will want to send XRES back to the server in a challenge * response message and update the state. * Return 0 on success; -1 if OAP is disabled; -2 if rx_random and rx_autn fail * the authentication check; -3 for any other errors. */ static int oap_evaluate_challenge(const struct oap_client_state *state, const uint8_t *rx_random, const uint8_t *rx_autn, uint8_t *tx_xres) { struct osmo_auth_vector vec; struct osmo_sub_auth_data auth = { .type = OSMO_AUTH_TYPE_UMTS, .algo = OSMO_AUTH_ALG_MILENAGE, }; osmo_static_assert(sizeof(((struct osmo_sub_auth_data*)0)->u.umts.k) == sizeof(state->secret_k), _secret_k_size_match); osmo_static_assert(sizeof(((struct osmo_sub_auth_data*)0)->u.umts.opc) == sizeof(state->secret_opc), _secret_opc_size_match); switch (state->state) { case OAP_UNINITIALIZED: case OAP_DISABLED: return -1; default: break; } memcpy(auth.u.umts.k, state->secret_k, sizeof(auth.u.umts.k)); memcpy(auth.u.umts.opc, state->secret_opc, sizeof(auth.u.umts.opc)); memset(auth.u.umts.amf, '\0', sizeof(auth.u.umts.amf)); auth.u.umts.sqn = 41; /* TODO use incrementing sequence nr */ memset(&vec, 0, sizeof(vec)); osmo_auth_gen_vec(&vec, &auth, rx_random); if (vec.res_len != 8) { LOGP(DLOAP, LOGL_ERROR, "OAP: Expected XRES to be 8 octets, got %d\n", vec.res_len); return -3; } if (osmo_constant_time_cmp(vec.autn, rx_autn, sizeof(vec.autn)) != 0) { LOGP(DLOAP, LOGL_ERROR, "OAP: AUTN mismatch!\n"); LOGP(DLOAP, LOGL_INFO, "OAP: AUTN from server: %s\n", osmo_hexdump_nospc(rx_autn, sizeof(vec.autn))); LOGP(DLOAP, LOGL_INFO, "OAP: AUTN expected: %s\n", osmo_hexdump_nospc(vec.autn, sizeof(vec.autn))); return -2; } if (tx_xres != NULL) memcpy(tx_xres, vec.res, 8); return 0; } struct msgb *oap_client_encoded(const struct osmo_oap_message *oap_msg) { struct msgb *msg = msgb_alloc_headroom(1000, 64, __func__); OSMO_ASSERT(msg); osmo_oap_encode(msg, oap_msg); return msg; } /* Create a new msgb containing an OAP registration message. * On error, return NULL. */ static struct msgb* oap_msg_register(uint16_t client_id) { struct osmo_oap_message oap_msg = {0}; if (client_id < 1) { LOGP(DLOAP, LOGL_ERROR, "OAP: Invalid client ID: %d\n", client_id); return NULL; } oap_msg.message_type = OAP_MSGT_REGISTER_REQUEST; oap_msg.client_id = client_id; return oap_client_encoded(&oap_msg); } int oap_client_register(struct oap_client_state *state, struct msgb **msg_tx) { *msg_tx = oap_msg_register(state->client_id); if (!(*msg_tx)) return -1; state->state = OAP_REQUESTED_CHALLENGE; return 0; } /* Create a new msgb containing an OAP challenge response message. * xres must point at 8 octets to return as challenge response. * On error, return NULL. */ static struct msgb* oap_msg_challenge_response(uint8_t *xres) { struct osmo_oap_message oap_reply = {0}; oap_reply.message_type = OAP_MSGT_CHALLENGE_RESULT; memcpy(oap_reply.xres, xres, sizeof(oap_reply.xres)); oap_reply.xres_present = 1; return oap_client_encoded(&oap_reply); } static int handle_challenge(struct oap_client_state *state, struct osmo_oap_message *oap_rx, struct msgb **msg_tx) { int rc; uint8_t xres[8]; if (!(oap_rx->rand_present && oap_rx->autn_present)) { LOGP(DLOAP, LOGL_ERROR, "OAP challenge incomplete (rand_present: %d, autn_present: %d)\n", oap_rx->rand_present, oap_rx->autn_present); rc = -2; goto failure; } rc = oap_evaluate_challenge(state, oap_rx->rand, oap_rx->autn, xres); if (rc < 0) goto failure; *msg_tx = oap_msg_challenge_response(xres); if ((*msg_tx) == NULL) { rc = -1; goto failure; } state->state = OAP_SENT_CHALLENGE_RESULT; return 0; failure: OSMO_ASSERT(rc < 0); state->state = OAP_INITIALIZED; return rc; } int oap_client_handle(struct oap_client_state *state, const struct msgb *msg_rx, struct msgb **msg_tx) { uint8_t *data = msgb_l2(msg_rx); size_t data_len = msgb_l2len(msg_rx); struct osmo_oap_message oap_msg = {0}; int rc = 0; *msg_tx = NULL; OSMO_ASSERT(data); rc = osmo_oap_decode(&oap_msg, data, data_len); if (rc < 0) { LOGP(DLOAP, LOGL_ERROR, "Decoding OAP message failed with error '%s' (%d)\n", get_value_string(gsm48_gmm_cause_names, -rc), -rc); return -10; } switch (state->state) { case OAP_UNINITIALIZED: LOGP(DLOAP, LOGL_ERROR, "Received OAP message %d, but the OAP client is" " not initialized\n", oap_msg.message_type); return -ENOTCONN; case OAP_DISABLED: LOGP(DLOAP, LOGL_ERROR, "Received OAP message %d, but the OAP client is" " disabled\n", oap_msg.message_type); return -ENOTCONN; default: break; } switch (oap_msg.message_type) { case OAP_MSGT_CHALLENGE_REQUEST: return handle_challenge(state, &oap_msg, msg_tx); case OAP_MSGT_REGISTER_RESULT: /* successfully registered */ state->state = OAP_REGISTERED; break; case OAP_MSGT_REGISTER_ERROR: LOGP(DLOAP, LOGL_ERROR, "OAP registration failed\n"); state->state = OAP_INITIALIZED; if (state->registration_failures < 3) { state->registration_failures ++; return oap_client_register(state, msg_tx); } return -11; case OAP_MSGT_REGISTER_REQUEST: case OAP_MSGT_CHALLENGE_RESULT: LOGP(DLOAP, LOGL_ERROR, "Received invalid OAP message type for OAP client side: %d\n", (int)oap_msg.message_type); return -12; default: LOGP(DLOAP, LOGL_ERROR, "Unknown OAP message type: %d\n", (int)oap_msg.message_type); return -13; } return 0; } osmo-sgsn-1.3.0/src/gprs/sgsn_ares.c000066400000000000000000000110051327264017000173310ustar00rootroot00000000000000/* C-ARES DNS resolver integration */ /* * (C) 2015 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 extern void *tall_bsc_ctx; struct cares_event_fd { struct llist_head head; struct osmo_fd fd; }; struct cares_cb_data { ares_host_callback cb; void *data; }; static void osmo_ares_reschedule(struct sgsn_instance *sgsn); static void ares_cb(void *_arg, int status, int timeouts, struct hostent *hostent) { struct cares_cb_data *arg = _arg; arg->cb(arg->data, status, timeouts, hostent); osmo_ares_reschedule(sgsn); talloc_free(arg); } static int ares_osmo_fd_cb(struct osmo_fd *fd, unsigned int what) { LOGP(DGPRS, LOGL_DEBUG, "C-ares fd(%d) ready(%d)\n", fd->fd, what); ares_process_fd(sgsn->ares_channel, (what & BSC_FD_READ) ? fd->fd : ARES_SOCKET_BAD, (what & BSC_FD_WRITE) ? fd->fd : ARES_SOCKET_BAD); osmo_ares_reschedule(sgsn); return 0; } static void ares_timeout_cb(void *data) { struct sgsn_instance *sgsn = data; LOGP(DGPRS, LOGL_DEBUG, "C-ares triggering timeout\n"); ares_process_fd(sgsn->ares_channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD); osmo_ares_reschedule(sgsn); } static void osmo_ares_reschedule(struct sgsn_instance *sgsn) { struct timeval *timeout, tv; osmo_timer_del(&sgsn->ares_timer); timeout = ares_timeout(sgsn->ares_channel, NULL, &tv); if (timeout) { LOGP(DGPRS, LOGL_DEBUG, "C-ares scheduling timeout %llu.%llu\n", (unsigned long long) tv.tv_sec, (unsigned long long) tv.tv_usec); osmo_timer_setup(&sgsn->ares_timer, ares_timeout_cb, sgsn); osmo_timer_schedule(&sgsn->ares_timer, tv.tv_sec, tv.tv_usec); } } static void setup_ares_osmo_fd(void *data, int fd, int read, int write) { struct cares_event_fd *ufd, *tmp; /* delete the entry */ if (read == 0 && write == 0) { llist_for_each_entry_safe(ufd, tmp, &sgsn->ares_fds, head) { if (ufd->fd.fd != fd) continue; LOGP(DGPRS, LOGL_DEBUG, "Removing C-ares watched fd (%d)\n", fd); osmo_fd_unregister(&ufd->fd); llist_del(&ufd->head); talloc_free(ufd); return; } } /* Search for the fd or create a new one */ llist_for_each_entry(ufd, &sgsn->ares_fds, head) { if (ufd->fd.fd != fd) continue; LOGP(DGPRS, LOGL_DEBUG, "Updating C-ares fd (%d)\n", fd); goto update_fd; } LOGP(DGPRS, LOGL_DEBUG, "Registering C-ares fd (%d)\n", fd); ufd = talloc_zero(tall_bsc_ctx, struct cares_event_fd); ufd->fd.fd = fd; ufd->fd.cb = ares_osmo_fd_cb; ufd->fd.data = data; if (osmo_fd_register(&ufd->fd) != 0) LOGP(DGPRS, LOGL_ERROR, "Failed to register C-ares fd (%d)\n", fd); llist_add(&ufd->head, &sgsn->ares_fds); update_fd: if (read) ufd->fd.when |= BSC_FD_READ; else ufd->fd.when &= ~BSC_FD_READ; if (write) ufd->fd.when |= BSC_FD_WRITE; else ufd->fd.when &= ~BSC_FD_WRITE; osmo_ares_reschedule(sgsn); } int sgsn_ares_query(struct sgsn_instance *sgsn, const char *name, ares_host_callback cb, void *data) { struct cares_cb_data *cb_data; cb_data = talloc_zero(tall_bsc_ctx, struct cares_cb_data); cb_data->cb = cb; cb_data->data = data; ares_gethostbyname(sgsn->ares_channel, name, AF_INET, ares_cb, cb_data); osmo_ares_reschedule(sgsn); return 0; } int sgsn_ares_init(struct sgsn_instance *sgsn) { struct ares_options options; int optmask; int rc; INIT_LLIST_HEAD(&sgsn->ares_fds); memset(&options, 0, sizeof(options)); options.sock_state_cb = setup_ares_osmo_fd; options.sock_state_cb_data = sgsn; optmask = ARES_OPT_FLAGS | ARES_OPT_SOCK_STATE_CB | ARES_OPT_DOMAINS; if (sgsn->ares_servers) optmask |= ARES_OPT_SERVERS; ares_library_init(ARES_LIB_INIT_ALL); rc = ares_init_options(&sgsn->ares_channel, &options, optmask); if (rc != ARES_SUCCESS) return rc; if (sgsn->ares_servers) rc = ares_set_servers(sgsn->ares_channel, sgsn->ares_servers); return rc; } osmo_static_assert(ARES_SUCCESS == 0, ares_success_zero); osmo-sgsn-1.3.0/src/gprs/sgsn_auth.c000066400000000000000000000177621327264017000173600ustar00rootroot00000000000000/* MS authorization and subscriber data handling */ /* (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 const struct value_string auth_state_names[] = { { SGSN_AUTH_ACCEPTED, "accepted"}, { SGSN_AUTH_REJECTED, "rejected"}, { SGSN_AUTH_UNKNOWN, "unknown"}, { SGSN_AUTH_AUTHENTICATE, "authenticate" }, { SGSN_AUTH_UMTS_RESYNC, "UMTS-resync" }, { 0, NULL } }; const struct value_string *sgsn_auth_state_names = auth_state_names; void sgsn_auth_init(void) { INIT_LLIST_HEAD(&sgsn->cfg.imsi_acl); } /* temporary IMSI ACL hack */ struct imsi_acl_entry *sgsn_acl_lookup(const char *imsi, struct sgsn_config *cfg) { struct imsi_acl_entry *acl; llist_for_each_entry(acl, &cfg->imsi_acl, list) { if (!strcmp(imsi, acl->imsi)) return acl; } return NULL; } int sgsn_acl_add(const char *imsi, struct sgsn_config *cfg) { struct imsi_acl_entry *acl; if (sgsn_acl_lookup(imsi, cfg)) return -EEXIST; acl = talloc_zero(NULL, struct imsi_acl_entry); if (!acl) return -ENOMEM; osmo_strlcpy(acl->imsi, imsi, sizeof(acl->imsi)); llist_add(&acl->list, &cfg->imsi_acl); return 0; } int sgsn_acl_del(const char *imsi, struct sgsn_config *cfg) { struct imsi_acl_entry *acl; acl = sgsn_acl_lookup(imsi, cfg); if (!acl) return -ENODEV; llist_del(&acl->list); talloc_free(acl); return 0; } enum sgsn_auth_state sgsn_auth_state(struct sgsn_mm_ctx *mmctx) { char mccmnc[16]; int check_net = 0; int check_acl = 0; OSMO_ASSERT(mmctx); switch (sgsn->cfg.auth_policy) { case SGSN_AUTH_POLICY_OPEN: return SGSN_AUTH_ACCEPTED; case SGSN_AUTH_POLICY_CLOSED: check_net = 1; check_acl = 1; break; case SGSN_AUTH_POLICY_ACL_ONLY: check_acl = 1; break; case SGSN_AUTH_POLICY_REMOTE: if (!mmctx->subscr) return mmctx->auth_state; if (mmctx->subscr->flags & GPRS_SUBSCRIBER_UPDATE_PENDING_MASK) return mmctx->auth_state; if (sgsn->cfg.require_authentication && (!sgsn_mm_ctx_is_authenticated(mmctx) || mmctx->subscr->sgsn_data->auth_triplets_updated)) return SGSN_AUTH_AUTHENTICATE; if (mmctx->subscr->authorized) return SGSN_AUTH_ACCEPTED; return SGSN_AUTH_REJECTED; } if (!strlen(mmctx->imsi)) { LOGMMCTXP(LOGL_NOTICE, mmctx, "Missing IMSI, authorization state not known\n"); return SGSN_AUTH_UNKNOWN; } if (check_net) { /* We simply assume that the IMSI exists, as long as it is part * of 'our' network */ snprintf(mccmnc, sizeof(mccmnc), "%s%s", osmo_mcc_name(mmctx->ra.mcc), osmo_mnc_name(mmctx->ra.mnc, mmctx->ra.mnc_3_digits)); if (strncmp(mccmnc, mmctx->imsi, mmctx->ra.mnc_3_digits ? 6 : 5) == 0) return SGSN_AUTH_ACCEPTED; } if (check_acl && sgsn_acl_lookup(mmctx->imsi, &sgsn->cfg)) return SGSN_AUTH_ACCEPTED; return SGSN_AUTH_REJECTED; } /* * This function is directly called by e.g. the GMM layer. It returns either * after calling sgsn_auth_update directly or after triggering an asynchronous * procedure which will call sgsn_auth_update later on. */ int sgsn_auth_request(struct sgsn_mm_ctx *mmctx) { struct gprs_subscr *subscr; struct gsm_auth_tuple *at; int need_update_location; int rc; LOGMMCTXP(LOGL_DEBUG, mmctx, "Requesting authorization\n"); if (sgsn->cfg.auth_policy != SGSN_AUTH_POLICY_REMOTE) { sgsn_auth_update(mmctx); return 0; } need_update_location = sgsn->cfg.require_update_location && (mmctx->subscr == NULL || mmctx->pending_req == GSM48_MT_GMM_ATTACH_REQ); /* This has the side effect of registering the subscr with the mmctx */ subscr = gprs_subscr_get_or_create_by_mmctx(mmctx); gprs_subscr_put(subscr); OSMO_ASSERT(mmctx->subscr != NULL); if (sgsn->cfg.require_authentication && !sgsn_mm_ctx_is_authenticated(mmctx)) { /* Find next tuple */ at = sgsn_auth_get_tuple(mmctx, mmctx->auth_triplet.key_seq); if (!at) { /* No valid tuple found, request fresh ones */ mmctx->auth_triplet.key_seq = GSM_KEY_SEQ_INVAL; LOGMMCTXP(LOGL_INFO, mmctx, "Requesting authentication tuples\n"); rc = gprs_subscr_request_auth_info(mmctx, NULL, NULL); if (rc >= 0) return 0; return rc; } mmctx->auth_triplet = *at; } else if (need_update_location) { LOGMMCTXP(LOGL_INFO, mmctx, "Missing information, requesting subscriber data\n"); rc = gprs_subscr_request_update_location(mmctx); if (rc >= 0) return 0; return rc; } sgsn_auth_update(mmctx); return 0; } void sgsn_auth_update(struct sgsn_mm_ctx *mmctx) { enum sgsn_auth_state auth_state; struct gprs_subscr *subscr = mmctx->subscr; struct gsm_auth_tuple *at; int gmm_cause; auth_state = sgsn_auth_state(mmctx); LOGMMCTXP(LOGL_DEBUG, mmctx, "Updating authorization (%s -> %s)\n", get_value_string(sgsn_auth_state_names, mmctx->auth_state), get_value_string(sgsn_auth_state_names, auth_state)); if (auth_state == SGSN_AUTH_UNKNOWN && subscr && !(subscr->flags & GPRS_SUBSCRIBER_UPDATE_PENDING_MASK)) { /* Reject requests if gprs_subscr_request_update_location fails */ LOGMMCTXP(LOGL_ERROR, mmctx, "Missing information, authorization not possible\n"); auth_state = SGSN_AUTH_REJECTED; } if (auth_state == SGSN_AUTH_AUTHENTICATE && mmctx->auth_triplet.key_seq == GSM_KEY_SEQ_INVAL) { /* The current tuple is not valid, but we are possibly called * because new auth tuples have been received */ at = sgsn_auth_get_tuple(mmctx, mmctx->auth_triplet.key_seq); if (!at) { LOGMMCTXP(LOGL_ERROR, mmctx, "Missing auth tuples, authorization not possible\n"); auth_state = SGSN_AUTH_REJECTED; } else { mmctx->auth_triplet = *at; } } if (mmctx->auth_state == auth_state) return; LOGMMCTXP(LOGL_INFO, mmctx, "Got authorization update: state %s -> %s\n", get_value_string(sgsn_auth_state_names, mmctx->auth_state), get_value_string(sgsn_auth_state_names, auth_state)); mmctx->auth_state = auth_state; switch (auth_state) { case SGSN_AUTH_AUTHENTICATE: if (subscr) subscr->sgsn_data->auth_triplets_updated = 0; gsm0408_gprs_authenticate(mmctx); break; case SGSN_AUTH_ACCEPTED: gsm0408_gprs_access_granted(mmctx); break; case SGSN_AUTH_REJECTED: gmm_cause = subscr ? subscr->sgsn_data->error_cause : SGSN_ERROR_CAUSE_NONE; if (subscr && (subscr->flags & GPRS_SUBSCRIBER_CANCELLED) != 0) gsm0408_gprs_access_cancelled(mmctx, gmm_cause); else gsm0408_gprs_access_denied(mmctx, gmm_cause); break; default: break; } } struct gsm_auth_tuple *sgsn_auth_get_tuple(struct sgsn_mm_ctx *mmctx, unsigned key_seq) { unsigned count; unsigned idx; struct gsm_auth_tuple *at = NULL; struct sgsn_subscriber_data *sdata; if (!mmctx->subscr) return NULL; if (key_seq == GSM_KEY_SEQ_INVAL) /* Start with 0 after increment module array size */ idx = ARRAY_SIZE(sdata->auth_triplets) - 1; else idx = key_seq; sdata = mmctx->subscr->sgsn_data; /* Find next tuple */ for (count = ARRAY_SIZE(sdata->auth_triplets); count > 0; count--) { idx = (idx + 1) % ARRAY_SIZE(sdata->auth_triplets); if (sdata->auth_triplets[idx].key_seq == GSM_KEY_SEQ_INVAL) continue; if (sdata->auth_triplets[idx].use_count == 0) { at = &sdata->auth_triplets[idx]; at->use_count = 1; return at; } } return NULL; } osmo-sgsn-1.3.0/src/gprs/sgsn_cdr.c000066400000000000000000000170141327264017000171550ustar00rootroot00000000000000/* GPRS SGSN CDR dumper */ /* (C) 2015 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 /* TODO...avoid going through a global */ extern struct sgsn_instance *sgsn; extern struct ctrl_handle *g_ctrlh; /** * The CDR module will generate an entry like: * * IMSI, # Subscriber IMSI * IMEI, # Subscriber IMEI * MSISDN, # Subscriber MISDN * Charging_Timestamp, # Event start Time * Charging_UTC, # Time zone of event start time * Duration, # Session DURATION * Cell_Id, # CELL_ID * Location_Area, # LAC * GGSN_ADDR, # GGSN_ADDR * SGSN_ADDR, # SGSN_ADDR * APNI, # APNI * PDP_ADDR, # PDP_ADDR * VOL_IN, # VOL_IN in Bytes * VOL_OUT, # VOL_OUT in Bytes * CAUSE_FOR_TERM, # CAUSE_FOR_TERM */ static void send_cdr_trap(char *value) { if (ctrl_cmd_send_trap(g_ctrlh, "cdr-v1", value) < 0) LOGP(DGPRS, LOGL_ERROR, "Failed to create and send TRAP cdr-v1\n"); } static void maybe_print_header(FILE *cdr_file) { if (ftell(cdr_file) != 0) return; fprintf(cdr_file, "timestamp,imsi,imei,msisdn,cell_id,lac,hlr,event,pdp_duration,ggsn_addr,sgsn_addr,apni,eua_addr,vol_in,vol_out,charging_id\n"); } static int cdr_snprintf_mm(char *buf, size_t size, const char *ev, struct sgsn_mm_ctx *mmctx) { struct tm tm; struct timeval tv; int ret; gettimeofday(&tv, NULL); gmtime_r(&tv.tv_sec, &tm); ret = snprintf(buf, size, "%04d%02d%02d%02d%02d%02d%03d,%s,%s,%s,%d,%d,%s,%s", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, (int)(tv.tv_usec / 1000), mmctx->imsi, mmctx->imei, mmctx->msisdn, mmctx->gb.cell_id, mmctx->ra.lac, mmctx->hlr, ev); return ret; } static void cdr_log_mm(struct sgsn_instance *inst, const char *ev, struct sgsn_mm_ctx *mmctx) { FILE *cdr_file; char buf[1024]; if (!inst->cfg.cdr.filename && !inst->cfg.cdr.trap) return; cdr_snprintf_mm(buf, sizeof(buf), ev, mmctx); if (inst->cfg.cdr.trap) send_cdr_trap(buf); if (inst->cfg.cdr.filename) { cdr_file = fopen(inst->cfg.cdr.filename, "a"); if (!cdr_file) { LOGP(DGPRS, LOGL_ERROR, "Failed to open %s\n", inst->cfg.cdr.filename); return; } maybe_print_header(cdr_file); fprintf(cdr_file, "%s\n", buf); fclose(cdr_file); } } static void extract_eua(struct ul66_t *eua, char *eua_addr) { if (eua->l < 2) return; /* there is no addr for ETSI/PPP */ if ((eua->v[0] & 0x0F) != 1) { strcpy(eua_addr, "ETSI"); return; } if (eua->v[1] == 0x21 && eua->l == 6) inet_ntop(AF_INET, &eua->v[2], eua_addr, INET_ADDRSTRLEN); else if (eua->v[1] == 0x57 && eua->l == 18) inet_ntop(AF_INET6, &eua->v[2], eua_addr, INET6_ADDRSTRLEN); else { /* e.g. both IPv4 and IPv6 */ strcpy(eua_addr, "Unknown address"); } } static int cdr_snprintf_pdp(char *buf, size_t size, const char *ev, struct sgsn_pdp_ctx *pdp) { char apni[(pdp->lib ? pdp->lib->apn_use.l : 0) + 1]; char ggsn_addr[INET_ADDRSTRLEN + 1]; char sgsn_addr[INET_ADDRSTRLEN + 1]; char eua_addr[INET6_ADDRSTRLEN + 1]; struct tm tm; struct timeval tv; time_t duration; struct timespec tp; int ret; memset(apni, 0, sizeof(apni)); memset(ggsn_addr, 0, sizeof(ggsn_addr)); memset(eua_addr, 0, sizeof(eua_addr)); if (pdp->lib) { osmo_apn_to_str(apni, pdp->lib->apn_use.v, pdp->lib->apn_use.l); inet_ntop(AF_INET, &pdp->lib->hisaddr0.s_addr, ggsn_addr, sizeof(ggsn_addr)); extract_eua(&pdp->lib->eua, eua_addr); } if (pdp->ggsn) inet_ntop(AF_INET, &pdp->ggsn->gsn->gsnc.s_addr, sgsn_addr, sizeof(sgsn_addr)); clock_gettime(CLOCK_MONOTONIC, &tp); gettimeofday(&tv, NULL); /* convert the timestamp to UTC */ gmtime_r(&tv.tv_sec, &tm); /* Check the duration of the PDP context */ duration = tp.tv_sec - pdp->cdr_start.tv_sec; ret = snprintf(buf, size, "%04d%02d%02d%02d%02d%02d%03d,%s,%s,%s,%d,%d,%s,%s,%ld,%s,%s,%s,%s,%" PRIu64 ",%" PRIu64 ",%u", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, (int)(tv.tv_usec / 1000), pdp->mm ? pdp->mm->imsi : "N/A", pdp->mm ? pdp->mm->imei : "N/A", pdp->mm ? pdp->mm->msisdn : "N/A", pdp->mm ? pdp->mm->gb.cell_id : -1, pdp->mm ? pdp->mm->ra.lac : -1, pdp->mm ? pdp->mm->hlr : "N/A", ev, (unsigned long ) duration, ggsn_addr, sgsn_addr, apni, eua_addr, pdp->cdr_bytes_in, pdp->cdr_bytes_out, pdp->cdr_charging_id); return ret; } static void cdr_log_pdp(struct sgsn_instance *inst, const char *ev, struct sgsn_pdp_ctx *pdp) { FILE *cdr_file; char buf[1024]; if (!inst->cfg.cdr.filename && !inst->cfg.cdr.trap) return; cdr_snprintf_pdp(buf, sizeof(buf), ev, pdp); if (inst->cfg.cdr.trap) send_cdr_trap(buf); if (inst->cfg.cdr.filename) { cdr_file = fopen(inst->cfg.cdr.filename, "a"); if (!cdr_file) { LOGP(DGPRS, LOGL_ERROR, "Failed to open %s\n", inst->cfg.cdr.filename); return; } maybe_print_header(cdr_file); fprintf(cdr_file, "%s\n", buf); fclose(cdr_file); } } static void cdr_pdp_timeout(void *_data) { struct sgsn_pdp_ctx *pdp = _data; cdr_log_pdp(sgsn, "pdp-periodic", pdp); osmo_timer_schedule(&pdp->cdr_timer, sgsn->cfg.cdr.interval, 0); } static int handle_sgsn_sig(unsigned int subsys, unsigned int signal, void *handler_data, void *_signal_data) { struct sgsn_signal_data *signal_data = _signal_data; struct sgsn_instance *inst = handler_data; if (subsys != SS_SGSN) return 0; switch (signal) { case S_SGSN_ATTACH: cdr_log_mm(inst, "attach", signal_data->mm); break; case S_SGSN_UPDATE: cdr_log_mm(inst, "update", signal_data->mm); break; case S_SGSN_DETACH: cdr_log_mm(inst, "detach", signal_data->mm); break; case S_SGSN_MM_FREE: cdr_log_mm(inst, "free", signal_data->mm); break; case S_SGSN_PDP_ACT: clock_gettime(CLOCK_MONOTONIC, &signal_data->pdp->cdr_start); signal_data->pdp->cdr_charging_id = signal_data->pdp->lib->cid; cdr_log_pdp(inst, "pdp-act", signal_data->pdp); osmo_timer_setup(&signal_data->pdp->cdr_timer, cdr_pdp_timeout, signal_data->pdp); osmo_timer_schedule(&signal_data->pdp->cdr_timer, inst->cfg.cdr.interval, 0); break; case S_SGSN_PDP_DEACT: cdr_log_pdp(inst, "pdp-deact", signal_data->pdp); osmo_timer_del(&signal_data->pdp->cdr_timer); break; case S_SGSN_PDP_TERMINATE: cdr_log_pdp(inst, "pdp-terminate", signal_data->pdp); osmo_timer_del(&signal_data->pdp->cdr_timer); break; case S_SGSN_PDP_FREE: cdr_log_pdp(inst, "pdp-free", signal_data->pdp); osmo_timer_del(&signal_data->pdp->cdr_timer); break; } return 0; } int sgsn_cdr_init(struct sgsn_instance *sgsn) { /* register for CDR related events */ sgsn->cfg.cdr.interval = 10 * 60; osmo_signal_register_handler(SS_SGSN, handle_sgsn_sig, sgsn); return 0; } osmo-sgsn-1.3.0/src/gprs/sgsn_ctrl.c000066400000000000000000000036361327264017000173560ustar00rootroot00000000000000/* Control Interface Implementation for the SGSN */ /* * (C) 2014 by Holger Hans Peter Freyther * (C) 2014 by sysmocom s.f.m.c. GmbH * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include extern vector ctrl_node_vec; static int get_subscriber_list(struct ctrl_cmd *cmd, void *d) { struct sgsn_mm_ctx *mm; cmd->reply = talloc_strdup(cmd, ""); llist_for_each_entry(mm, &sgsn_mm_ctxts, list) { char *addr = NULL; struct sgsn_pdp_ctx *pdp; if (strlen(mm->imsi) == 0) continue; llist_for_each_entry(pdp, &mm->pdp_list, list) addr = gprs_pdpaddr2str(pdp->lib->eua.v, pdp->lib->eua.l); cmd->reply = talloc_asprintf_append( cmd->reply, "%s,%s\n", mm->imsi, addr ? addr : ""); } return CTRL_CMD_REPLY; } CTRL_CMD_DEFINE_RO(subscriber_list, "subscriber-list-active-v1"); int sgsn_ctrl_cmds_install(void) { int rc = 0; rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_subscriber_list); return rc; } struct ctrl_handle *sgsn_controlif_setup(struct gsm_network *net, const char *bind_addr, uint16_t port) { return ctrl_interface_setup_dynip(net, bind_addr, port, NULL); } osmo-sgsn-1.3.0/src/gprs/sgsn_libgtp.c000066400000000000000000000633011327264017000176660ustar00rootroot00000000000000/* GPRS SGSN integration with libgtp of OpenGGSN */ /* libgtp implements the GPRS Tunelling Protocol GTP per TS 09.60 / 29.060 */ /* (C) 2010 by Harald Welte * (C) 2010 by On-Waves * (C) 2015 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 "bscconfig.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef BUILD_IU #include #include #endif #include #include /* TS 23.003: The MSISDN shall take the dummy MSISDN value composed of * 15 digits set to 0 (encoded as an E.164 international number) when * the MSISDN is not available in messages in which the presence of the * MSISDN parameter */ static const uint8_t dummy_msisdn[] = { 0x91, /* No extension, international, E.164 */ 0, 0, 0, 0, 0, 0, 0, /* 14 digits of zeroes */ 0xF0 /* 15th digit of zero + padding */ }; const struct value_string gtp_cause_strs[] = { { GTPCAUSE_REQ_IMSI, "Request IMSI" }, { GTPCAUSE_REQ_IMEI, "Request IMEI" }, { GTPCAUSE_REQ_IMSI_IMEI, "Request IMSI and IMEI" }, { GTPCAUSE_NO_ID_NEEDED, "No identity needed" }, { GTPCAUSE_MS_REFUSES_X, "MS refuses" }, { GTPCAUSE_MS_NOT_RESP_X, "MS is not GPRS responding" }, { GTPCAUSE_ACC_REQ, "Request accepted" }, { GTPCAUSE_NON_EXIST, "Non-existent" }, { GTPCAUSE_INVALID_MESSAGE, "Invalid message format" }, { GTPCAUSE_IMSI_NOT_KNOWN, "IMSI not known" }, { GTPCAUSE_MS_DETACHED, "MS is GPRS detached" }, { GTPCAUSE_MS_NOT_RESP, "MS is not GPRS responding" }, { GTPCAUSE_MS_REFUSES, "MS refuses" }, { GTPCAUSE_NO_RESOURCES, "No resources available" }, { GTPCAUSE_NOT_SUPPORTED, "Service not supported" }, { GTPCAUSE_MAN_IE_INCORRECT, "Mandatory IE incorrect" }, { GTPCAUSE_MAN_IE_MISSING, "Mandatory IE missing" }, { GTPCAUSE_OPT_IE_INCORRECT, "Optional IE incorrect" }, { GTPCAUSE_SYS_FAIL, "System failure" }, { GTPCAUSE_ROAMING_REST, "Roaming restrictions" }, { GTPCAUSE_PTIMSI_MISMATCH, "P-TMSI Signature mismatch" }, { GTPCAUSE_CONN_SUSP, "GPRS connection suspended" }, { GTPCAUSE_AUTH_FAIL, "Authentication failure" }, { GTPCAUSE_USER_AUTH_FAIL, "User authentication failed" }, { GTPCAUSE_CONTEXT_NOT_FOUND, "Context not found" }, { GTPCAUSE_ADDR_OCCUPIED, "All dynamic PDP addresses occupied" }, { GTPCAUSE_NO_MEMORY, "No memory is available" }, { GTPCAUSE_RELOC_FAIL, "Relocation failure" }, { GTPCAUSE_UNKNOWN_MAN_EXTHEADER, "Unknown mandatory ext. header" }, { GTPCAUSE_SEM_ERR_TFT, "Semantic error in TFT operation" }, { GTPCAUSE_SYN_ERR_TFT, "Syntactic error in TFT operation" }, { GTPCAUSE_SEM_ERR_FILTER, "Semantic errors in packet filter" }, { GTPCAUSE_SYN_ERR_FILTER, "Syntactic errors in packet filter" }, { GTPCAUSE_MISSING_APN, "Missing or unknown APN" }, { GTPCAUSE_UNKNOWN_PDP, "Unknown PDP address or PDP type" }, { 0, NULL } }; /* Generate the GTP IMSI IE according to 09.60 Section 7.9.2 */ static uint64_t imsi_str2gtp(char *str) { uint64_t imsi64 = 0; unsigned int n; unsigned int imsi_len = strlen(str); if (imsi_len > 16) { LOGP(DGPRS, LOGL_NOTICE, "IMSI length > 16 not supported!\n"); return 0; } for (n = 0; n < 16; n++) { uint64_t val; if (n < imsi_len) val = (str[n]-'0') & 0xf; else val = 0xf; imsi64 |= (val << (n*4)); } return imsi64; } /* generate a PDP context based on the IE's from the 04.08 message, * and send the GTP create pdp context request to the GGSN */ struct sgsn_pdp_ctx *sgsn_create_pdp_ctx(struct sgsn_ggsn_ctx *ggsn, struct sgsn_mm_ctx *mmctx, uint16_t nsapi, struct tlv_parsed *tp) { struct gprs_ra_id raid; struct sgsn_pdp_ctx *pctx; struct pdp_t *pdp; uint64_t imsi_ui64; size_t qos_len; const uint8_t *qos; int rc; pctx = sgsn_pdp_ctx_alloc(mmctx, nsapi); if (!pctx) { LOGP(DGPRS, LOGL_ERROR, "Couldn't allocate PDP Ctx\n"); return NULL; } imsi_ui64 = imsi_str2gtp(mmctx->imsi); rc = pdp_newpdp(&pdp, imsi_ui64, nsapi, NULL); if (rc) { LOGP(DGPRS, LOGL_ERROR, "Out of libgtp PDP Contexts\n"); return NULL; } pdp->priv = pctx; pctx->lib = pdp; pctx->ggsn = ggsn; //pdp->peer = /* sockaddr_in of GGSN (receive) */ //pdp->ipif = /* not used by library */ pdp->version = ggsn->gtp_version; pdp->hisaddr0 = ggsn->remote_addr; pdp->hisaddr1 = ggsn->remote_addr; //pdp->cch_pdp = 512; /* Charging Flat Rate */ /* MS provided APN, subscription was verified by the caller */ pdp->selmode = 0xFC | 0x00; /* IMSI, TEID/TEIC, FLLU/FLLC, TID, NSAPI set in pdp_newpdp */ LOGPDPCTXP(LOGL_NOTICE, pctx, "Create PDP Context\n"); /* Put the MSISDN in case we have it */ if (mmctx->subscr && mmctx->subscr->sgsn_data->msisdn_len) { pdp->msisdn.l = mmctx->subscr->sgsn_data->msisdn_len; if (pdp->msisdn.l > sizeof(pdp->msisdn.v)) pdp->msisdn.l = sizeof(pdp->msisdn.v); memcpy(pdp->msisdn.v, mmctx->subscr->sgsn_data->msisdn, pdp->msisdn.l); } else { /* use the dummy 15-digits-zero MSISDN value */ pdp->msisdn.l = sizeof(dummy_msisdn); memcpy(pdp->msisdn.v, dummy_msisdn, pdp->msisdn.l); } /* End User Address from GMM requested PDP address */ pdp->eua.l = TLVP_LEN(tp, OSMO_IE_GSM_REQ_PDP_ADDR); if (pdp->eua.l > sizeof(pdp->eua.v)) pdp->eua.l = sizeof(pdp->eua.v); memcpy(pdp->eua.v, TLVP_VAL(tp, OSMO_IE_GSM_REQ_PDP_ADDR), pdp->eua.l); /* Highest 4 bits of first byte need to be set to 1, otherwise * the IE is identical with the 04.08 PDP Address IE */ pdp->eua.v[0] |= 0xf0; /* APN name from GMM */ pdp->apn_use.l = TLVP_LEN(tp, GSM48_IE_GSM_APN); if (pdp->apn_use.l > sizeof(pdp->apn_use.v)) pdp->apn_use.l = sizeof(pdp->apn_use.v); memcpy(pdp->apn_use.v, TLVP_VAL(tp, GSM48_IE_GSM_APN), pdp->apn_use.l); /* Protocol Configuration Options from GMM */ pdp->pco_req.l = TLVP_LEN(tp, GSM48_IE_GSM_PROTO_CONF_OPT); if (pdp->pco_req.l > sizeof(pdp->pco_req.v)) pdp->pco_req.l = sizeof(pdp->pco_req.v); memcpy(pdp->pco_req.v, TLVP_VAL(tp, GSM48_IE_GSM_PROTO_CONF_OPT), pdp->pco_req.l); /* QoS options from GMM or remote */ if (TLVP_LEN(tp, OSMO_IE_GSM_SUB_QOS) > 0) { qos_len = TLVP_LEN(tp, OSMO_IE_GSM_SUB_QOS); qos = TLVP_VAL(tp, OSMO_IE_GSM_SUB_QOS); } else { qos_len = TLVP_LEN(tp, OSMO_IE_GSM_REQ_QOS); qos = TLVP_VAL(tp, OSMO_IE_GSM_REQ_QOS); } if (qos_len <= 3) { pdp->qos_req.l = qos_len + 1; if (pdp->qos_req.l > sizeof(pdp->qos_req.v)) pdp->qos_req.l = sizeof(pdp->qos_req.v); pdp->qos_req.v[0] = 0; /* Allocation/Retention policy */ memcpy(&pdp->qos_req.v[1], qos, pdp->qos_req.l - 1); } else { pdp->qos_req.l = qos_len; if (pdp->qos_req.l > sizeof(pdp->qos_req.v)) pdp->qos_req.l = sizeof(pdp->qos_req.v); memcpy(pdp->qos_req.v, qos, pdp->qos_req.l); } /* charging characteristics if present */ if (TLVP_LEN(tp, OSMO_IE_GSM_CHARG_CHAR) >= sizeof(pdp->cch_pdp)) pdp->cch_pdp = tlvp_val16be(tp, OSMO_IE_GSM_CHARG_CHAR); /* SGSN address for control plane */ pdp->gsnlc.l = sizeof(sgsn->cfg.gtp_listenaddr.sin_addr); memcpy(pdp->gsnlc.v, &sgsn->cfg.gtp_listenaddr.sin_addr, sizeof(sgsn->cfg.gtp_listenaddr.sin_addr)); /* SGSN address for user plane * Default to the control plane addr for now. If we are connected to a * hnbgw via IuPS we'll need to send a PDP context update with the * correct IP address after the RAB Assignment is complete */ pdp->gsnlu.l = sizeof(sgsn->cfg.gtp_listenaddr.sin_addr); memcpy(pdp->gsnlu.v, &sgsn->cfg.gtp_listenaddr.sin_addr, sizeof(sgsn->cfg.gtp_listenaddr.sin_addr)); /* Encode RAT Type according to TS 29.060 7.7.50 */ pdp->rattype.l = 1; if (mmctx->ran_type == MM_CTX_T_UTRAN_Iu) pdp->rattype.v[0] = 1; else pdp->rattype.v[0] = 2; pdp->rattype_given = 1; /* Include RAI and ULI all the time */ pdp->rai_given = 1; pdp->rai.l = 6; /* Routing Area Identifier with LAC and RAC fixed values, as * requested in 29.006 7.3.1 */ raid = mmctx->ra; raid.lac = 0xFFFE; raid.rac = 0xFF; gsm48_encode_ra((struct gsm48_ra_id *)pdp->rai.v, &raid); /* Encode User Location Information accordint to TS 29.060 7.7.51 */ pdp->userloc_given = 1; pdp->userloc.l = 8; switch (mmctx->ran_type) { case MM_CTX_T_GERAN_Gb: case MM_CTX_T_GERAN_Iu: pdp->rattype.v[0] = 2; /* User Location Information */ pdp->userloc_given = 1; pdp->userloc.l = 8; pdp->userloc.v[0] = 0; /* CGI for GERAN */ bssgp_create_cell_id(&pdp->userloc.v[1], &mmctx->ra, mmctx->gb.cell_id); break; case MM_CTX_T_UTRAN_Iu: pdp->userloc.v[0] = 1; /* SAI for UTRAN */ /* SAI is like CGI but with SAC instead of CID, so we can abuse this function */ bssgp_create_cell_id(&pdp->userloc.v[1], &mmctx->ra, mmctx->iu.sac); break; } /* include the IMEI(SV) */ pdp->imeisv_given = 1; gsm48_encode_bcd_number(&pdp->imeisv.v[0], 8, 0, mmctx->imei); pdp->imeisv.l = pdp->imeisv.v[0]; memmove(&pdp->imeisv.v[0], &pdp->imeisv.v[1], 8); /* change pdp state to 'requested' */ pctx->state = PDP_STATE_CR_REQ; rc = gtp_create_context_req(ggsn->gsn, pdp, pctx); /* FIXME */ return pctx; } /* SGSN wants to delete a PDP context */ int sgsn_delete_pdp_ctx(struct sgsn_pdp_ctx *pctx) { LOGPDPCTXP(LOGL_ERROR, pctx, "Delete PDP Context\n"); /* FIXME: decide if we need teardown or not ! */ return gtp_delete_context_req(pctx->ggsn->gsn, pctx->lib, pctx, 1); } struct cause_map { uint8_t cause_in; uint8_t cause_out; }; static uint8_t cause_map(const struct cause_map *map, uint8_t in, uint8_t deflt) { const struct cause_map *m; for (m = map; m->cause_in && m->cause_out; m++) { if (m->cause_in == in) return m->cause_out; } return deflt; } /* how do we map from gtp cause to SM cause */ static const struct cause_map gtp2sm_cause_map[] = { { GTPCAUSE_NO_RESOURCES, GSM_CAUSE_INSUFF_RSRC }, { GTPCAUSE_NOT_SUPPORTED, GSM_CAUSE_SERV_OPT_NOTSUPP }, { GTPCAUSE_MAN_IE_INCORRECT, GSM_CAUSE_INV_MAND_INFO }, { GTPCAUSE_MAN_IE_MISSING, GSM_CAUSE_INV_MAND_INFO }, { GTPCAUSE_OPT_IE_INCORRECT, GSM_CAUSE_PROTO_ERR_UNSPEC }, { GTPCAUSE_SYS_FAIL, GSM_CAUSE_NET_FAIL }, { GTPCAUSE_ROAMING_REST, GSM_CAUSE_REQ_SERV_OPT_NOTSUB }, { GTPCAUSE_PTIMSI_MISMATCH, GSM_CAUSE_PROTO_ERR_UNSPEC }, { GTPCAUSE_CONN_SUSP, GSM_CAUSE_PROTO_ERR_UNSPEC }, { GTPCAUSE_AUTH_FAIL, GSM_CAUSE_AUTH_FAILED }, { GTPCAUSE_USER_AUTH_FAIL, GSM_CAUSE_ACT_REJ_GGSN }, { GTPCAUSE_CONTEXT_NOT_FOUND, GSM_CAUSE_PROTO_ERR_UNSPEC }, { GTPCAUSE_ADDR_OCCUPIED, GSM_CAUSE_INSUFF_RSRC }, { GTPCAUSE_NO_MEMORY, GSM_CAUSE_INSUFF_RSRC }, { GTPCAUSE_RELOC_FAIL, GSM_CAUSE_PROTO_ERR_UNSPEC }, { GTPCAUSE_UNKNOWN_MAN_EXTHEADER, GSM_CAUSE_PROTO_ERR_UNSPEC }, { GTPCAUSE_MISSING_APN, GSM_CAUSE_MISSING_APN }, { GTPCAUSE_UNKNOWN_PDP, GSM_CAUSE_UNKNOWN_PDP }, { 0, 0 } }; static int send_act_pdp_cont_acc(struct sgsn_pdp_ctx *pctx) { struct sgsn_signal_data sig_data; int rc; struct gprs_llc_lle *lle; /* Inform others about it */ memset(&sig_data, 0, sizeof(sig_data)); sig_data.pdp = pctx; osmo_signal_dispatch(SS_SGSN, S_SGSN_PDP_ACT, &sig_data); /* Send PDP CTX ACT to MS */ rc = gsm48_tx_gsm_act_pdp_acc(pctx); if (rc < 0) return rc; if (pctx->mm->ran_type == MM_CTX_T_GERAN_Gb) { /* Send SNDCP XID to MS */ lle = &pctx->mm->gb.llme->lle[pctx->sapi]; rc = sndcp_sn_xid_req(lle,pctx->nsapi); if (rc < 0) return rc; } return 0; } /* The GGSN has confirmed the creation of a PDP Context */ static int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause) { struct sgsn_pdp_ctx *pctx = cbp; uint8_t reject_cause; LOGPDPCTXP(LOGL_INFO, pctx, "Received CREATE PDP CTX CONF, cause=%d(%s)\n", cause, get_value_string(gtp_cause_strs, cause)); if (!pctx->mm) { LOGP(DGPRS, LOGL_INFO, "No MM context, aborting CREATE PDP CTX CONF\n"); return -EIO; } /* Check for cause value if it was really successful */ if (cause < 0) { LOGP(DGPRS, LOGL_NOTICE, "Create PDP ctx req timed out\n"); if (pdp && pdp->version == 1) { pdp->version = 0; gtp_create_context_req(sgsn->gsn, pdp, cbp); return 0; } else { reject_cause = GSM_CAUSE_NET_FAIL; goto reject; } } /* Check for cause value if it was really successful */ if (cause != GTPCAUSE_ACC_REQ) { reject_cause = cause_map(gtp2sm_cause_map, cause, GSM_CAUSE_ACT_REJ_GGSN); goto reject; } if (pctx->mm->ran_type == MM_CTX_T_GERAN_Gb) { /* Activate the SNDCP layer */ sndcp_sm_activate_ind(&pctx->mm->gb.llme->lle[pctx->sapi], pctx->nsapi); return send_act_pdp_cont_acc(pctx); } else if (pctx->mm->ran_type == MM_CTX_T_UTRAN_Iu) { #ifdef BUILD_IU /* Activate a radio bearer */ iu_rab_act_ps(pdp->nsapi, pctx); return 0; #else return -ENOTSUP; #endif } LOGP(DGPRS, LOGL_ERROR, "Unknown ran_type %d\n", pctx->mm->ran_type); reject_cause = GSM_CAUSE_PROTO_ERR_UNSPEC; reject: /* * In case of a timeout pdp will be NULL but we have a valid pointer * in pctx->lib. For other rejects pctx->lib and pdp might be the * same. */ pctx->state = PDP_STATE_NONE; if (pctx->lib && pctx->lib != pdp) pdp_freepdp(pctx->lib); pctx->lib = NULL; if (pdp) pdp_freepdp(pdp); /* Send PDP CTX ACT REJ to MS */ gsm48_tx_gsm_act_pdp_rej(pctx->mm, pctx->ti, reject_cause, 0, NULL); sgsn_pdp_ctx_free(pctx); return EOF; } void sgsn_pdp_upd_gtp_u(struct sgsn_pdp_ctx *pdp, void *addr, size_t alen) { pdp->lib->gsnlu.l = alen; memcpy(pdp->lib->gsnlu.v, addr, alen); gtp_update_context(pdp->ggsn->gsn, pdp->lib, pdp, &pdp->lib->hisaddr0); } #ifdef BUILD_IU /* Callback for RAB assignment response */ int sgsn_ranap_rab_ass_resp(struct sgsn_mm_ctx *ctx, RANAP_RAB_SetupOrModifiedItemIEs_t *setup_ies) { uint8_t rab_id; bool require_pdp_update = false; struct sgsn_pdp_ctx *pdp = NULL; RANAP_RAB_SetupOrModifiedItem_t *item = &setup_ies->raB_SetupOrModifiedItem; rab_id = item->rAB_ID.buf[0]; pdp = sgsn_pdp_ctx_by_nsapi(ctx, rab_id); if (!pdp) { LOGP(DRANAP, LOGL_ERROR, "RAB Assignment Response for unknown RAB/NSAPI=%u\n", rab_id); return -1; } if (item->transportLayerAddress) { LOGPC(DRANAP, LOGL_INFO, " Setup: (%u/%s)", rab_id, osmo_hexdump(item->transportLayerAddress->buf, item->transportLayerAddress->size)); switch (item->transportLayerAddress->size) { case 7: /* It must be IPv4 inside a X213 NSAP */ memcpy(pdp->lib->gsnlu.v, &item->transportLayerAddress->buf[3], 4); break; case 4: /* It must be a raw IPv4 address */ memcpy(pdp->lib->gsnlu.v, item->transportLayerAddress->buf, 4); break; case 16: /* TODO: It must be a raw IPv6 address */ case 19: /* TODO: It must be IPv6 inside a X213 NSAP */ default: LOGP(DRANAP, LOGL_ERROR, "RAB Assignment Resp: Unknown " "transport layer address size %u\n", item->transportLayerAddress->size); return -1; } require_pdp_update = true; } /* The TEI on the RNC side might have changed, too */ if (item->iuTransportAssociation && item->iuTransportAssociation->present == RANAP_IuTransportAssociation_PR_gTP_TEI && item->iuTransportAssociation->choice.gTP_TEI.buf && item->iuTransportAssociation->choice.gTP_TEI.size >= 4) { uint32_t tei = osmo_load32be(item->iuTransportAssociation->choice.gTP_TEI.buf); LOGP(DRANAP, LOGL_DEBUG, "Updating TEID on RNC side from 0x%08x to 0x%08x\n", pdp->lib->teid_own, tei); pdp->lib->teid_own = tei; require_pdp_update = true; } if (require_pdp_update) gtp_update_context(pdp->ggsn->gsn, pdp->lib, pdp, &pdp->lib->hisaddr0); if (pdp->state != PDP_STATE_CR_CONF) { send_act_pdp_cont_acc(pdp); pdp->state = PDP_STATE_CR_CONF; } return 0; } #endif /* Confirmation of a PDP Context Delete */ static int delete_pdp_conf(struct pdp_t *pdp, void *cbp, int cause) { struct sgsn_signal_data sig_data; struct sgsn_pdp_ctx *pctx = cbp; int rc = 0; LOGPDPCTXP(LOGL_INFO, pctx, "Received DELETE PDP CTX CONF, cause=%d(%s)\n", cause, get_value_string(gtp_cause_strs, cause)); memset(&sig_data, 0, sizeof(sig_data)); sig_data.pdp = pctx; osmo_signal_dispatch(SS_SGSN, S_SGSN_PDP_DEACT, &sig_data); if (pctx->mm) { if (pctx->mm->ran_type == MM_CTX_T_GERAN_Gb) { /* Deactivate the SNDCP layer */ sndcp_sm_deactivate_ind(&pctx->mm->gb.llme->lle[pctx->sapi], pctx->nsapi); } else { #ifdef BUILD_IU /* Deactivate radio bearer */ ranap_iu_rab_deact(pctx->mm->iu.ue_ctx, 1); #else return -ENOTSUP; #endif } /* Confirm deactivation of PDP context to MS */ rc = gsm48_tx_gsm_deact_pdp_acc(pctx); } else { LOGPDPCTXP(LOGL_NOTICE, pctx, "Not deactivating SNDCP layer since the MM context " "is not available\n"); } /* unlink the now non-existing library handle from the pdp * context */ pctx->lib = NULL; sgsn_pdp_ctx_free(pctx); return rc; } /* Confirmation of an GTP ECHO request */ static int echo_conf(struct pdp_t *pdp, void *cbp, int recovery) { if (recovery < 0) { LOGP(DGPRS, LOGL_NOTICE, "GTP Echo Request timed out\n"); /* FIXME: if version == 1, retry with version 0 */ } else { DEBUGP(DGPRS, "GTP Rx Echo Response\n"); } return 0; } /* Any message received by GGSN contains a recovery IE */ static int cb_recovery(struct sockaddr_in *peer, uint8_t recovery) { struct sgsn_ggsn_ctx *ggsn; ggsn = sgsn_ggsn_ctx_by_addr(&peer->sin_addr); if (!ggsn) { LOGP(DGPRS, LOGL_NOTICE, "Received Recovery IE for unknown GGSN\n"); return -EINVAL; } if (ggsn->remote_restart_ctr == -1) { /* First received ECHO RESPONSE, note the restart ctr */ ggsn->remote_restart_ctr = recovery; } else if (ggsn->remote_restart_ctr != recovery) { /* counter has changed (GGSN restart): release all PDP */ LOGP(DGPRS, LOGL_NOTICE, "GGSN recovery (%u->%u), " "releasing all PDP contexts\n", ggsn->remote_restart_ctr, recovery); ggsn->remote_restart_ctr = recovery; drop_all_pdp_for_ggsn(ggsn); } return 0; } /* libgtp callback for confirmations */ static int cb_conf(int type, int cause, struct pdp_t *pdp, void *cbp) { DEBUGP(DGPRS, "libgtp cb_conf(type=%d, cause=%d, pdp=%p, cbp=%p)\n", type, cause, pdp, cbp); if (cause == EOF) LOGP(DGPRS, LOGL_ERROR, "libgtp EOF (type=%u, pdp=%p, cbp=%p)\n", type, pdp, cbp); switch (type) { case GTP_ECHO_REQ: /* libgtp hands us the RECOVERY number instead of a cause */ return echo_conf(pdp, cbp, cause); case GTP_CREATE_PDP_REQ: return create_pdp_conf(pdp, cbp, cause); case GTP_DELETE_PDP_REQ: return delete_pdp_conf(pdp, cbp, cause); default: break; } return 0; } /* Called whenever a PDP context is deleted for any reason */ static int cb_delete_context(struct pdp_t *pdp) { LOGPDPX(DGPRS, LOGL_INFO, pdp, "Context was deleted\n"); return 0; } /* Called when we receive a Version Not Supported message */ static int cb_unsup_ind(struct sockaddr_in *peer) { LOGP(DGPRS, LOGL_INFO, "GTP Version not supported Indication " "from %s:%u\n", inet_ntoa(peer->sin_addr), ntohs(peer->sin_port)); return 0; } /* Called when we receive a Supported Ext Headers Notification */ static int cb_extheader_ind(struct sockaddr_in *peer) { LOGP(DGPRS, LOGL_INFO, "GTP Supported Ext Headers Notification " "from %s:%u\n", inet_ntoa(peer->sin_addr), ntohs(peer->sin_port)); return 0; } /* Called whenever we receive a DATA packet */ static int cb_data_ind(struct pdp_t *lib, void *packet, unsigned int len) { struct bssgp_paging_info pinfo; struct sgsn_pdp_ctx *pdp; struct sgsn_mm_ctx *mm; struct msgb *msg; uint8_t *ud; pdp = lib->priv; if (!pdp) { LOGP(DGPRS, LOGL_NOTICE, "GTP DATA IND from GGSN for unknown PDP\n"); return -EIO; } mm = pdp->mm; if (!mm) { LOGP(DGPRS, LOGL_ERROR, "PDP context (address=%u) without MM context!\n", pdp->address); return -EIO; } DEBUGP(DGPRS, "GTP DATA IND from GGSN for %s, length=%u\n", mm->imsi, len); if (mm->ran_type == MM_CTX_T_UTRAN_Iu) { #ifdef BUILD_IU /* Ignore the packet for now and page the UE to get the RAB * reestablished */ ranap_iu_page_ps(mm->imsi, &mm->p_tmsi, mm->ra.lac, mm->ra.rac); return 0; #else return -ENOTSUP; #endif } msg = msgb_alloc_headroom(len+256, 128, "GTP->SNDCP"); ud = msgb_put(msg, len); memcpy(ud, packet, len); msgb_tlli(msg) = mm->gb.tlli; msgb_bvci(msg) = mm->gb.bvci; msgb_nsei(msg) = mm->gb.nsei; switch (mm->gmm_state) { case GMM_REGISTERED_SUSPENDED: /* initiate PS PAGING procedure */ memset(&pinfo, 0, sizeof(pinfo)); pinfo.mode = BSSGP_PAGING_PS; pinfo.scope = BSSGP_PAGING_BVCI; pinfo.bvci = mm->gb.bvci; pinfo.imsi = mm->imsi; pinfo.ptmsi = &mm->p_tmsi; pinfo.drx_params = mm->drx_parms; pinfo.qos[0] = 0; // FIXME bssgp_tx_paging(mm->gb.nsei, 0, &pinfo); rate_ctr_inc(&mm->ctrg->ctr[GMM_CTR_PAGING_PS]); /* FIXME: queue the packet we received from GTP */ break; case GMM_REGISTERED_NORMAL: break; default: LOGP(DGPRS, LOGL_ERROR, "GTP DATA IND for TLLI %08X in state " "%u\n", mm->gb.tlli, mm->gmm_state); msgb_free(msg); return -1; } rate_ctr_inc(&pdp->ctrg->ctr[PDP_CTR_PKTS_UDATA_OUT]); rate_ctr_add(&pdp->ctrg->ctr[PDP_CTR_BYTES_UDATA_OUT], len); rate_ctr_inc(&mm->ctrg->ctr[GMM_CTR_PKTS_UDATA_OUT]); rate_ctr_add(&mm->ctrg->ctr[GMM_CTR_BYTES_UDATA_OUT], len); /* It is easier to have a global count */ pdp->cdr_bytes_out += len; return sndcp_unitdata_req(msg, &mm->gb.llme->lle[pdp->sapi], pdp->nsapi, mm); } /* Called by SNDCP when it has received/re-assembled a N-PDU */ int sgsn_rx_sndcp_ud_ind(struct gprs_ra_id *ra_id, int32_t tlli, uint8_t nsapi, struct msgb *msg, uint32_t npdu_len, uint8_t *npdu) { struct sgsn_mm_ctx *mmctx; struct sgsn_pdp_ctx *pdp; /* look-up the MM context for this message */ mmctx = sgsn_mm_ctx_by_tlli(tlli, ra_id); if (!mmctx) { LOGP(DGPRS, LOGL_ERROR, "Cannot find MM CTX for TLLI %08x\n", tlli); return -EIO; } /* look-up the PDP context for this message */ pdp = sgsn_pdp_ctx_by_nsapi(mmctx, nsapi); if (!pdp) { LOGP(DGPRS, LOGL_ERROR, "Cannot find PDP CTX for " "TLLI=%08x, NSAPI=%u\n", tlli, nsapi); return -EIO; } if (!pdp->lib) { LOGP(DGPRS, LOGL_ERROR, "PDP CTX without libgtp\n"); return -EIO; } rate_ctr_inc(&pdp->ctrg->ctr[PDP_CTR_PKTS_UDATA_IN]); rate_ctr_add(&pdp->ctrg->ctr[PDP_CTR_BYTES_UDATA_IN], npdu_len); rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PKTS_UDATA_IN]); rate_ctr_add(&mmctx->ctrg->ctr[GMM_CTR_BYTES_UDATA_IN], npdu_len); /* It is easier to have a global count */ pdp->cdr_bytes_in += npdu_len; return gtp_data_req(pdp->ggsn->gsn, pdp->lib, npdu, npdu_len); } /* libgtp select loop integration */ static int sgsn_gtp_fd_cb(struct osmo_fd *fd, unsigned int what) { struct sgsn_instance *sgi = fd->data; int rc; if (!(what & BSC_FD_READ)) return 0; switch (fd->priv_nr) { case 0: rc = gtp_decaps0(sgi->gsn); break; case 1: rc = gtp_decaps1c(sgi->gsn); break; case 2: rc = gtp_decaps1u(sgi->gsn); break; default: rc = -EINVAL; break; } return rc; } static void sgsn_gtp_tmr_start(struct sgsn_instance *sgi) { struct timeval next; /* Retrieve next retransmission as struct timeval */ gtp_retranstimeout(sgi->gsn, &next); /* re-schedule the timer */ osmo_timer_schedule(&sgi->gtp_timer, next.tv_sec, next.tv_usec/1000); } /* timer callback for libgtp retransmissions and ping */ static void sgsn_gtp_tmr_cb(void *data) { struct sgsn_instance *sgi = data; /* Do all the retransmissions as needed */ gtp_retrans(sgi->gsn); sgsn_gtp_tmr_start(sgi); } int sgsn_gtp_init(struct sgsn_instance *sgi) { int rc; struct gsn_t *gsn; rc = gtp_new(&sgi->gsn, sgi->cfg.gtp_statedir, &sgi->cfg.gtp_listenaddr.sin_addr, GTP_MODE_SGSN); if (rc) { LOGP(DGPRS, LOGL_ERROR, "Failed to create GTP: %d\n", rc); return rc; } LOGP(DGPRS, LOGL_NOTICE, "Created GTP on %s\n", inet_ntoa(sgi->cfg.gtp_listenaddr.sin_addr)); gsn = sgi->gsn; if (gsn->mode != GTP_MODE_SGSN) return -EINVAL; sgi->gtp_fd0.fd = gsn->fd0; sgi->gtp_fd0.priv_nr = 0; sgi->gtp_fd0.data = sgi; sgi->gtp_fd0.when = BSC_FD_READ; sgi->gtp_fd0.cb = sgsn_gtp_fd_cb; rc = osmo_fd_register(&sgi->gtp_fd0); if (rc < 0) return rc; sgi->gtp_fd1c.fd = gsn->fd1c; sgi->gtp_fd1c.priv_nr = 1; sgi->gtp_fd1c.data = sgi; sgi->gtp_fd1c.when = BSC_FD_READ; sgi->gtp_fd1c.cb = sgsn_gtp_fd_cb; rc = osmo_fd_register(&sgi->gtp_fd1c); if (rc < 0) { osmo_fd_unregister(&sgi->gtp_fd0); return rc; } sgi->gtp_fd1u.fd = gsn->fd1u; sgi->gtp_fd1u.priv_nr = 2; sgi->gtp_fd1u.data = sgi; sgi->gtp_fd1u.when = BSC_FD_READ; sgi->gtp_fd1u.cb = sgsn_gtp_fd_cb; rc = osmo_fd_register(&sgi->gtp_fd1u); if (rc < 0) { osmo_fd_unregister(&sgi->gtp_fd0); osmo_fd_unregister(&sgi->gtp_fd1c); return rc; } /* Start GTP re-transmission timer */ osmo_timer_setup(&sgi->gtp_timer, sgsn_gtp_tmr_cb, sgi); sgsn_gtp_tmr_start(sgi); /* Register callbackcs with libgtp */ gtp_set_cb_delete_context(gsn, cb_delete_context); gtp_set_cb_conf(gsn, cb_conf); gtp_set_cb_recovery(gsn, cb_recovery); gtp_set_cb_data_ind(gsn, cb_data_ind); gtp_set_cb_unsup_ind(gsn, cb_unsup_ind); gtp_set_cb_extheader_ind(gsn, cb_extheader_ind); return 0; } osmo-sgsn-1.3.0/src/gprs/sgsn_main.c000066400000000000000000000317341327264017000173360ustar00rootroot00000000000000/* GPRS SGSN Implementation */ /* (C) 2010 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 . * */ #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" #if BUILD_IU #include #include #include #endif #define _GNU_SOURCE #include void *tall_bsc_ctx; struct ctrl_handle *g_ctrlh; struct gprs_ns_inst *sgsn_nsi; static int daemonize = 0; const char *openbsc_copyright = "Copyright (C) 2010 Harald Welte and On-Waves\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"; #define CONFIG_FILE_DEFAULT "osmo-sgsn.cfg" #define CONFIG_FILE_LEGACY "osmo_sgsn.cfg" static struct sgsn_instance sgsn_inst = { .config_file = NULL, .cfg = { .gtp_statedir = "./", .auth_policy = SGSN_AUTH_POLICY_CLOSED, .gsup_server_port = OSMO_GSUP_PORT, }, }; struct sgsn_instance *sgsn = &sgsn_inst; /* call-back function for the NS protocol */ static int sgsn_ns_cb(enum gprs_ns_evt event, struct gprs_nsvc *nsvc, struct msgb *msg, uint16_t bvci) { int rc = 0; switch (event) { case GPRS_NS_EVT_UNIT_DATA: /* hand the message into the BSSGP implementation */ rc = bssgp_rcvmsg(msg); break; default: LOGP(DGPRS, LOGL_ERROR, "SGSN: Unknown event %u from NS\n", event); if (msg) msgb_free(msg); rc = -EIO; break; } return rc; } /* call-back function for the BSSGP protocol */ int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx) { struct osmo_bssgp_prim *bp; bp = container_of(oph, struct osmo_bssgp_prim, oph); switch (oph->sap) { case SAP_BSSGP_LL: switch (oph->primitive) { case PRIM_BSSGP_UL_UD: return gprs_llc_rcvmsg(oph->msg, bp->tp); } break; case SAP_BSSGP_GMM: switch (oph->primitive) { case PRIM_BSSGP_GMM_SUSPEND: return gprs_gmm_rx_suspend(bp->ra_id, bp->tlli); case PRIM_BSSGP_GMM_RESUME: return gprs_gmm_rx_resume(bp->ra_id, bp->tlli, bp->u.resume.suspend_ref); } break; case SAP_BSSGP_NM: break; } return 0; } static void signal_handler(int signal) { fprintf(stdout, "signal %u received\n", signal); switch (signal) { case SIGINT: case SIGTERM: osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL); sleep(1); 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: talloc_report_full(tall_vty_ctx, stderr); break; default: break; } } /* NSI that BSSGP uses when transmitting on NS */ extern struct gprs_ns_inst *bssgp_nsi; int sgsn_vty_is_config_node(struct vty *vty, int node) { /* So far the SGSN has no nested nodes that need parent node * declaration, except for the ss7 vty nodes. */ switch (node) { case SGSN_NODE: return 1; default: #if BUILD_IU return osmo_ss7_is_config_node(vty, node); #else return 0; #endif } } int sgsn_vty_go_parent(struct vty *vty) { /* So far the SGSN has no nested nodes that need parent node * declaration, except for the ss7 vty nodes. */ #if BUILD_IU return osmo_ss7_vty_go_parent(vty); #else vty->node = CONFIG_NODE; vty->index = NULL; return 0; #endif } static struct vty_app_info vty_info = { .name = "OsmoSGSN", .version = PACKAGE_VERSION, .go_parent_cb = sgsn_vty_go_parent, .is_config_node = sgsn_vty_is_config_node, }; static void print_help(void) { printf("Some useful help...\n"); printf(" -h --help\tthis text\n"); printf(" -D --daemonize\tFork the process into a background daemon\n"); printf(" -d option --debug\tenable Debugging\n"); printf(" -s --disable-color\n"); printf(" -c --config-file\tThe config file to use [%s]\n", CONFIG_FILE_DEFAULT); printf(" -e --log-level number\tSet a global log level\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' }, {"log-level", 1, 0, 'e'}, {NULL, 0, 0, 0} }; c = getopt_long(argc, argv, "hd:Dc:sTVe:", 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': sgsn_inst.config_file = strdup(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; default: /* ignore */ break; } } } /* default categories */ static struct log_info_cat gprs_categories[] = { [DMM] = { .name = "DMM", .description = "Layer3 Mobility Management (MM)", .color = "\033[1;33m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [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, }, [DREF] = { .name = "DREF", .description = "Reference Counting", .enabled = 0, .loglevel = LOGL_NOTICE, }, [DGPRS] = { .name = "DGPRS", .description = "GPRS Packet Service", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DNS] = { .name = "DNS", .description = "GPRS Network Service (NS)", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DBSSGP] = { .name = "DBSSGP", .description = "GPRS BSS Gateway Protocol (BSSGP)", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DLLC] = { .name = "DLLC", .description = "GPRS Logical Link Control Protocol (LLC)", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DSNDCP] = { .name = "DSNDCP", .description = "GPRS Sub-Network Dependent Control Protocol (SNDCP)", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DRANAP] = { .name = "DRANAP", .description = "RAN Application Part (RANAP)", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DSUA] = { .name = "DSUA", .description = "SCCP User Adaptation (SUA)", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DSLHC] = { .name = "DSLHC", .description = "RFC1144 TCP/IP Header compression (SLHC)", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DV42BIS] = { .name = "DV42BIS", .description = "V.42bis data compression (SNDCP)", .enabled = 1, .loglevel = LOGL_NOTICE, } }; static const struct log_info gprs_log_info = { .filter_fn = gprs_log_filter_fn, .cat = gprs_categories, .num_cat = ARRAY_SIZE(gprs_categories), }; #if BUILD_IU int sgsn_ranap_iu_event(struct ranap_ue_conn_ctx *ctx, enum ranap_iu_event_type type, void *data); #endif static bool file_exists(const char *path) { struct stat sb; return stat(path, &sb) ? false : true; } int main(int argc, char **argv) { int rc; #if BUILD_IU struct osmo_sccp_instance *sccp; #endif srand(time(NULL)); tall_bsc_ctx = talloc_named_const(NULL, 0, "osmo_sgsn"); msgb_talloc_ctx_init(tall_bsc_ctx, 0); vty_info.tall_ctx = tall_bsc_ctx; signal(SIGINT, &signal_handler); signal(SIGTERM, &signal_handler); signal(SIGABRT, &signal_handler); signal(SIGUSR1, &signal_handler); signal(SIGUSR2, &signal_handler); osmo_init_ignore_signals(); osmo_init_logging2(tall_bsc_ctx, &gprs_log_info); osmo_stats_init(tall_bsc_ctx); vty_info.copyright = openbsc_copyright; vty_init(&vty_info); logging_vty_add_cmds(NULL); osmo_talloc_vty_add_cmds(); osmo_stats_vty_add_cmds(&gprs_log_info); sgsn_vty_init(&sgsn_inst.cfg); ctrl_vty_init(tall_bsc_ctx); #if BUILD_IU osmo_ss7_init(); osmo_ss7_vty_init_asp(tall_bsc_ctx); #endif handle_options(argc, argv); /* Backwards compatibility: for years, the default config file name was * osmo_sgsn.cfg. All other Osmocom programs use osmo-*.cfg with a * dash. To be able to use the new config file name without breaking * previous setups that might rely on the legacy default config file * name, we need to look for the old config file if no -c option was * passed AND no file exists with the new default file name. */ if (!sgsn_inst.config_file) { /* No -c option was passed */ if (file_exists(CONFIG_FILE_LEGACY) && !file_exists(CONFIG_FILE_DEFAULT)) sgsn_inst.config_file = CONFIG_FILE_LEGACY; else sgsn_inst.config_file = CONFIG_FILE_DEFAULT; } rate_ctr_init(tall_bsc_ctx); gprs_ns_set_log_ss(DNS); bssgp_set_log_ss(DBSSGP); sgsn_nsi = gprs_ns_instantiate(&sgsn_ns_cb, tall_bsc_ctx); if (!sgsn_nsi) { LOGP(DGPRS, LOGL_ERROR, "Unable to instantiate NS\n"); exit(1); } bssgp_nsi = sgsn_inst.cfg.nsi = sgsn_nsi; gprs_llc_init("/usr/local/lib/osmocom/crypt/"); sgsn_rate_ctr_init(); sgsn_inst_init(); gprs_ns_vty_init(bssgp_nsi); bssgp_vty_init(); gprs_llc_vty_init(); gprs_sndcp_vty_init(); sgsn_auth_init(); sgsn_cdr_init(&sgsn_inst); /* FIXME: register signal handler for SS_L_NS */ rc = sgsn_parse_config(sgsn_inst.config_file); if (rc < 0) { LOGP(DGPRS, LOGL_FATAL, "Error in config file\n"); exit(2); } /* start telnet after reading config for vty_get_bind_addr() */ rc = telnet_init_dynif(tall_bsc_ctx, NULL, vty_get_bind_addr(), OSMO_VTY_PORT_SGSN); if (rc < 0) exit(1); /* start control interface after reading config for * ctrl_vty_get_bind_addr() */ g_ctrlh = sgsn_controlif_setup(NULL, ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_SGSN); if (!g_ctrlh) { LOGP(DGPRS, LOGL_ERROR, "Failed to create CTRL interface.\n"); exit(1); } if (sgsn_ctrl_cmds_install() != 0) { LOGP(DGPRS, LOGL_ERROR, "Failed to install CTRL commands.\n"); exit(1); } rc = sgsn_gtp_init(&sgsn_inst); if (rc) { LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen on GTP socket\n"); exit(2); } else LOGP(DGPRS, LOGL_NOTICE, "libGTP v%s initialized\n", gtp_version()); rc = gprs_subscr_init(&sgsn_inst); if (rc < 0) { LOGP(DGPRS, LOGL_FATAL, "Cannot set up subscriber management\n"); exit(2); } rc = gprs_ns_nsip_listen(sgsn_nsi); if (rc < 0) { LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen on NSIP socket\n"); exit(2); } rc = gprs_ns_frgre_listen(sgsn_nsi); if (rc < 0) { LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen GRE " "socket. Do you have CAP_NET_RAW?\n"); exit(2); } if (sgsn->cfg.dynamic_lookup) { if (sgsn_ares_init(sgsn) != 0) { LOGP(DGPRS, LOGL_FATAL, "Failed to initialize c-ares(%d)\n", rc); exit(4); } } #if BUILD_IU /* Note that these are mostly defaults and can be overriden from the VTY */ sccp = osmo_sccp_simple_client(tall_bsc_ctx, "OsmoSGSN", (23 << 3) + 4, OSMO_SS7_ASP_PROT_M3UA, 0, NULL, 0, "127.0.0.1"); if (!sccp) { printf("Setting up SCCP client failed.\n"); return 8; } ranap_iu_init(tall_bsc_ctx, DRANAP, "OsmoSGSN-IuPS", sccp, gsm0408_gprs_rcvmsg_iu, sgsn_ranap_iu_event); #endif if (daemonize) { rc = osmo_daemonize(); if (rc < 0) { perror("Error during daemonize"); exit(1); } } while (1) { rc = osmo_select_main(0); if (rc < 0) exit(3); } /* not reached */ exit(0); } osmo-sgsn-1.3.0/src/gprs/sgsn_vty.c000066400000000000000000001162041327264017000172300ustar00rootroot00000000000000/* * (C) 2010-2016 by Harald Welte * (C) 2010 by On-Waves * (C) 2015 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 "../../bscconfig.h" #ifdef BUILD_IU #include #endif extern void *tall_bsc_ctx; static struct sgsn_config *g_cfg = NULL; const struct value_string sgsn_auth_pol_strs[] = { { SGSN_AUTH_POLICY_OPEN, "accept-all" }, { SGSN_AUTH_POLICY_CLOSED, "closed" }, { SGSN_AUTH_POLICY_ACL_ONLY, "acl-only" }, { SGSN_AUTH_POLICY_REMOTE, "remote" }, { 0, NULL } }; /* Section 11.2.2 / Table 11.3a GPRS Mobility management timers – MS side */ #define GSM0408_T3312_SECS (10*60) /* periodic RAU interval, default 54min */ /* Section 11.2.2 / Table 11.4 MM timers netwokr side */ #define GSM0408_T3322_SECS 6 /* DETACH_REQ -> DETACH_ACC */ #define GSM0408_T3350_SECS 6 /* waiting for ATT/RAU/TMSI COMPL */ #define GSM0408_T3360_SECS 6 /* waiting for AUTH/CIPH RESP */ #define GSM0408_T3370_SECS 6 /* waiting for ID RESP */ /* Section 11.2.2 / Table 11.4a MM timers network side */ #define GSM0408_T3313_SECS 30 /* waiting for paging response */ #define GSM0408_T3314_SECS 44 /* force to STBY on expiry, Ready timer */ #define GSM0408_T3316_SECS 44 /* Section 11.3 / Table 11.2d Timers of Session Management - network side */ #define GSM0408_T3385_SECS 8 /* wait for ACT PDP CTX REQ */ #define GSM0408_T3386_SECS 8 /* wait for MODIFY PDP CTX ACK */ #define GSM0408_T3395_SECS 8 /* wait for DEACT PDP CTX ACK */ #define GSM0408_T3397_SECS 8 /* wait for DEACT AA PDP CTX ACK */ #define DECLARE_TIMER(number, doc) \ DEFUN(cfg_sgsn_T##number, \ cfg_sgsn_T##number##_cmd, \ "timer t" #number " <0-65535>", \ "Configure GPRS Timers\n" \ doc "\nTimer Value in seconds\n") \ { \ int value = atoi(argv[0]); \ \ if (value < 0 || value > 65535) { \ vty_out(vty, "Timer value %s out of range.%s", \ argv[0], VTY_NEWLINE); \ return CMD_WARNING; \ } \ \ g_cfg->timers.T##number = value; \ return CMD_SUCCESS; \ } DECLARE_TIMER(3312, "Periodic RA Update timer (s)") DECLARE_TIMER(3322, "Detach request -> accept timer (s)") DECLARE_TIMER(3350, "Waiting for ATT/RAU/TMSI_COMPL timer (s)") DECLARE_TIMER(3360, "Waiting for AUTH/CIPH response timer (s)") DECLARE_TIMER(3370, "Waiting for IDENTITY response timer (s)") DECLARE_TIMER(3313, "Waiting for paging response timer (s)") DECLARE_TIMER(3314, "Force to STANDBY on expiry timer (s)") DECLARE_TIMER(3316, "AA-Ready timer (s)") DECLARE_TIMER(3385, "Wait for ACT PDP CTX REQ timer (s)") DECLARE_TIMER(3386, "Wait for MODIFY PDP CTX ACK timer (s)") DECLARE_TIMER(3395, "Wait for DEACT PDP CTX ACK timer (s)") DECLARE_TIMER(3397, "Wait for DEACT AA PDP CTX ACK timer (s)") char *gprs_pdpaddr2str(uint8_t *pdpa, uint8_t len) { static char str[INET6_ADDRSTRLEN + 10]; if (!pdpa || len < 2) return "none"; switch (pdpa[0] & 0x0f) { case PDP_TYPE_ORG_IETF: switch (pdpa[1]) { case PDP_TYPE_N_IETF_IPv4: if (len < 2 + 4) break; strcpy(str, "IPv4 "); inet_ntop(AF_INET, pdpa+2, str+5, sizeof(str)-5); return str; case PDP_TYPE_N_IETF_IPv6: if (len < 2 + 8) break; strcpy(str, "IPv6 "); inet_ntop(AF_INET6, pdpa+2, str+5, sizeof(str)-5); return str; default: break; } break; case PDP_TYPE_ORG_ETSI: if (pdpa[1] == PDP_TYPE_N_ETSI_PPP) return "PPP"; break; default: break; } return "invalid"; } static struct cmd_node sgsn_node = { SGSN_NODE, "%s(config-sgsn)# ", 1, }; static int config_write_sgsn(struct vty *vty) { struct sgsn_ggsn_ctx *gctx; struct imsi_acl_entry *acl; struct apn_ctx *actx; struct ares_addr_node *server; vty_out(vty, "sgsn%s", VTY_NEWLINE); vty_out(vty, " gtp local-ip %s%s", inet_ntoa(g_cfg->gtp_listenaddr.sin_addr), VTY_NEWLINE); llist_for_each_entry(gctx, &sgsn_ggsn_ctxts, list) { if (gctx->id == UINT32_MAX) continue; vty_out(vty, " ggsn %u remote-ip %s%s", gctx->id, inet_ntoa(gctx->remote_addr), VTY_NEWLINE); vty_out(vty, " ggsn %u gtp-version %u%s", gctx->id, gctx->gtp_version, VTY_NEWLINE); } if (sgsn->cfg.dynamic_lookup) vty_out(vty, " ggsn dynamic%s", VTY_NEWLINE); for (server = sgsn->ares_servers; server; server = server->next) vty_out(vty, " grx-dns-add %s%s", inet_ntoa(server->addr.addr4), VTY_NEWLINE); if (g_cfg->cipher != GPRS_ALGO_GEA0) vty_out(vty, " encryption %s%s", get_value_string(gprs_cipher_names, g_cfg->cipher), VTY_NEWLINE); if (g_cfg->gsup_server_addr.sin_addr.s_addr) vty_out(vty, " gsup remote-ip %s%s", inet_ntoa(g_cfg->gsup_server_addr.sin_addr), VTY_NEWLINE); if (g_cfg->gsup_server_port) vty_out(vty, " gsup remote-port %d%s", g_cfg->gsup_server_port, VTY_NEWLINE); vty_out(vty, " auth-policy %s%s", get_value_string(sgsn_auth_pol_strs, g_cfg->auth_policy), VTY_NEWLINE); vty_out(vty, " gsup oap-id %d%s", (int)g_cfg->oap.client_id, VTY_NEWLINE); if (g_cfg->oap.secret_k_present != 0) vty_out(vty, " gsup oap-k %s%s", osmo_hexdump_nospc(g_cfg->oap.secret_k, sizeof(g_cfg->oap.secret_k)), VTY_NEWLINE); if (g_cfg->oap.secret_opc_present != 0) vty_out(vty, " gsup oap-opc %s%s", osmo_hexdump_nospc(g_cfg->oap.secret_opc, sizeof(g_cfg->oap.secret_opc)), VTY_NEWLINE); llist_for_each_entry(acl, &g_cfg->imsi_acl, list) vty_out(vty, " imsi-acl add %s%s", acl->imsi, VTY_NEWLINE); if (llist_empty(&sgsn_apn_ctxts)) vty_out(vty, " ! apn * ggsn 0%s", VTY_NEWLINE); llist_for_each_entry(actx, &sgsn_apn_ctxts, list) { if (strlen(actx->imsi_prefix) > 0) vty_out(vty, " apn %s imsi-prefix %s ggsn %u%s", actx->name, actx->imsi_prefix, actx->ggsn->id, VTY_NEWLINE); else vty_out(vty, " apn %s ggsn %u%s", actx->name, actx->ggsn->id, VTY_NEWLINE); } if (g_cfg->cdr.filename) vty_out(vty, " cdr filename %s%s", g_cfg->cdr.filename, VTY_NEWLINE); else vty_out(vty, " no cdr filename%s", VTY_NEWLINE); if (g_cfg->cdr.trap) vty_out(vty, " cdr trap%s", VTY_NEWLINE); else vty_out(vty, " no cdr trap%s", VTY_NEWLINE); vty_out(vty, " cdr interval %d%s", g_cfg->cdr.interval, VTY_NEWLINE); vty_out(vty, " timer t3312 %d%s", g_cfg->timers.T3312, VTY_NEWLINE); vty_out(vty, " timer t3322 %d%s", g_cfg->timers.T3322, VTY_NEWLINE); vty_out(vty, " timer t3350 %d%s", g_cfg->timers.T3350, VTY_NEWLINE); vty_out(vty, " timer t3360 %d%s", g_cfg->timers.T3360, VTY_NEWLINE); vty_out(vty, " timer t3370 %d%s", g_cfg->timers.T3370, VTY_NEWLINE); vty_out(vty, " timer t3313 %d%s", g_cfg->timers.T3313, VTY_NEWLINE); vty_out(vty, " timer t3314 %d%s", g_cfg->timers.T3314, VTY_NEWLINE); vty_out(vty, " timer t3316 %d%s", g_cfg->timers.T3316, VTY_NEWLINE); vty_out(vty, " timer t3385 %d%s", g_cfg->timers.T3385, VTY_NEWLINE); vty_out(vty, " timer t3386 %d%s", g_cfg->timers.T3386, VTY_NEWLINE); vty_out(vty, " timer t3395 %d%s", g_cfg->timers.T3395, VTY_NEWLINE); vty_out(vty, " timer t3397 %d%s", g_cfg->timers.T3397, VTY_NEWLINE); if (g_cfg->pcomp_rfc1144.active) { vty_out(vty, " compression rfc1144 active slots %d%s", g_cfg->pcomp_rfc1144.s01 + 1, VTY_NEWLINE); } else if (g_cfg->pcomp_rfc1144.passive) { vty_out(vty, " compression rfc1144 passive%s", VTY_NEWLINE); } else vty_out(vty, " no compression rfc1144%s", VTY_NEWLINE); if (g_cfg->dcomp_v42bis.active && g_cfg->dcomp_v42bis.p0 == 1) { vty_out(vty, " compression v42bis active direction sgsn codewords %d strlen %d%s", g_cfg->dcomp_v42bis.p1, g_cfg->dcomp_v42bis.p2, VTY_NEWLINE); } else if (g_cfg->dcomp_v42bis.active && g_cfg->dcomp_v42bis.p0 == 2) { vty_out(vty, " compression v42bis active direction ms codewords %d strlen %d%s", g_cfg->dcomp_v42bis.p1, g_cfg->dcomp_v42bis.p2, VTY_NEWLINE); } else if (g_cfg->dcomp_v42bis.active && g_cfg->dcomp_v42bis.p0 == 3) { vty_out(vty, " compression v42bis active direction both codewords %d strlen %d%s", g_cfg->dcomp_v42bis.p1, g_cfg->dcomp_v42bis.p2, VTY_NEWLINE); } else if (g_cfg->dcomp_v42bis.passive) { vty_out(vty, " compression v42bis passive%s", VTY_NEWLINE); } else vty_out(vty, " no compression v42bis%s", VTY_NEWLINE); #ifdef BUILD_IU ranap_iu_vty_config_write(vty, " "); #endif return CMD_SUCCESS; } #define SGSN_STR "Configure the SGSN\n" #define GGSN_STR "Configure the GGSN information\n" DEFUN(cfg_sgsn, cfg_sgsn_cmd, "sgsn", SGSN_STR) { vty->node = SGSN_NODE; return CMD_SUCCESS; } DEFUN(cfg_sgsn_bind_addr, cfg_sgsn_bind_addr_cmd, "gtp local-ip A.B.C.D", "GTP Parameters\n" "Set the IP address for the local GTP bind for the Gp interface (towards the GGSNs)." " Note: in case you would like to run the GGSN on the same machine as the SGSN, you can not run" " both on the same IP address, since both sides are specified to use the same GTP port numbers" " (" OSMO_STRINGIFY_VAL(GTP1C_PORT) " and " OSMO_STRINGIFY_VAL(GTP1U_PORT) ")." " For example, you could use 127.0.0.1 for the SGSN and 127.0.0.2 for the GGSN in such" " situations.\n" "IPv4 Address\n") { inet_aton(argv[0], &g_cfg->gtp_listenaddr.sin_addr); return CMD_SUCCESS; } DEFUN(cfg_ggsn_remote_ip, cfg_ggsn_remote_ip_cmd, "ggsn <0-255> remote-ip A.B.C.D", GGSN_STR "GGSN Number\n" "Configure this static GGSN to use the specified remote IP address.\n" "IPv4 Address\n") { uint32_t id = atoi(argv[0]); struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(id); inet_aton(argv[1], &ggc->remote_addr); return CMD_SUCCESS; } #if 0 DEFUN(cfg_ggsn_remote_port, cfg_ggsn_remote_port_cmd, "ggsn <0-255> remote-port <0-65535>", "") { uint32_t id = atoi(argv[0]); struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(id); uint16_t port = atoi(argv[1]); } #endif DEFUN(cfg_ggsn_gtp_version, cfg_ggsn_gtp_version_cmd, "ggsn <0-255> gtp-version (0|1)", GGSN_STR "GGSN Number\n" "GTP Version\n" "Version 0\n" "Version 1\n") { uint32_t id = atoi(argv[0]); struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(id); if (atoi(argv[1])) ggc->gtp_version = 1; else ggc->gtp_version = 0; return CMD_SUCCESS; } DEFUN(cfg_ggsn_dynamic_lookup, cfg_ggsn_dynamic_lookup_cmd, "ggsn dynamic", GGSN_STR "Enable dynamic resolving of GGSNs based on DNS resolving the APN name like in a GRX-style setup." " Changing this setting requires a restart.\n") { sgsn->cfg.dynamic_lookup = 1; return CMD_SUCCESS; } DEFUN(cfg_grx_ggsn, cfg_grx_ggsn_cmd, "grx-dns-add A.B.C.D", "Use the specified IP address for DNS-resolving the AP names to GGSN IP addresses\n" "IPv4 address\n") { struct ares_addr_node *node = talloc_zero(tall_bsc_ctx, struct ares_addr_node); node->family = AF_INET; inet_aton(argv[0], &node->addr.addr4); node->next = sgsn->ares_servers; sgsn->ares_servers = node; return CMD_SUCCESS; } #define APN_STR "Configure the information per APN\n" #define APN_GW_STR "The APN gateway name optionally prefixed by '*' (wildcard)\n" static int add_apn_ggsn_mapping(struct vty *vty, const char *apn_str, const char *imsi_prefix, int ggsn_id) { struct apn_ctx *actx; struct sgsn_ggsn_ctx *ggsn; ggsn = sgsn_ggsn_ctx_by_id(ggsn_id); if (ggsn == NULL) { vty_out(vty, "%% a GGSN with id %d has not been defined%s", ggsn_id, VTY_NEWLINE); return CMD_WARNING; } actx = sgsn_apn_ctx_find_alloc(apn_str, imsi_prefix); if (!actx) { vty_out(vty, "%% unable to create APN context for %s/%s%s", apn_str, imsi_prefix, VTY_NEWLINE); return CMD_WARNING; } actx->ggsn = ggsn; return CMD_SUCCESS; } DEFUN(cfg_apn_ggsn, cfg_apn_ggsn_cmd, "apn APNAME ggsn <0-255>", APN_STR APN_GW_STR "Select the GGSN to use for the given APN gateway prefix\n" "The GGSN id") { return add_apn_ggsn_mapping(vty, argv[0], "", atoi(argv[1])); } DEFUN(cfg_apn_imsi_ggsn, cfg_apn_imsi_ggsn_cmd, "apn APNAME imsi-prefix IMSIPRE ggsn <0-255>", APN_STR APN_GW_STR "Select the GGSN to use for the given APN gateway prefix if and only if the IMSI matches the" " given prefix.\n" "An IMSI prefix\n" "Select the GGSN to use when APN gateway and IMSI prefix match\n" "The GGSN id") { return add_apn_ggsn_mapping(vty, argv[0], argv[1], atoi(argv[2])); } const struct value_string gprs_mm_st_strs[] = { { GMM_DEREGISTERED, "DEREGISTERED" }, { GMM_COMMON_PROC_INIT, "COMMON PROCEDURE (INIT)" }, { GMM_REGISTERED_NORMAL, "REGISTERED (NORMAL)" }, { GMM_REGISTERED_SUSPENDED, "REGISTERED (SUSPENDED)" }, { GMM_DEREGISTERED_INIT, "DEREGISTERED (INIT)" }, { 0, NULL } }; char *sgsn_gtp_ntoa(struct ul16_t *ul) { struct in_addr ia; if (gsna2in_addr(&ia, ul) != 0) return "UNKNOWN"; return inet_ntoa(ia); } static void vty_dump_pdp(struct vty *vty, const char *pfx, struct sgsn_pdp_ctx *pdp) { const char *imsi = pdp->mm ? pdp->mm->imsi : "(detaching)"; vty_out(vty, "%sPDP Context IMSI: %s, SAPI: %u, NSAPI: %u, TI: %u%s", pfx, imsi, pdp->sapi, pdp->nsapi, pdp->ti, VTY_NEWLINE); if (pdp->lib) { char apnbuf[APN_MAXLEN + 1]; vty_out(vty, "%s APN: %s%s", pfx, osmo_apn_to_str(apnbuf, pdp->lib->apn_use.v, pdp->lib->apn_use.l), VTY_NEWLINE); vty_out(vty, "%s PDP Address: %s%s", pfx, gprs_pdpaddr2str(pdp->lib->eua.v, pdp->lib->eua.l), VTY_NEWLINE); vty_out(vty, "%s GTPv%d Local Control(%s / TEIC: 0x%08x) ", pfx, pdp->lib->version, sgsn_gtp_ntoa(&pdp->lib->gsnlc), pdp->lib->teic_own); vty_out(vty, "Data(%s / TEID: 0x%08x)%s", sgsn_gtp_ntoa(&pdp->lib->gsnlu), pdp->lib->teid_own, VTY_NEWLINE); vty_out(vty, "%s GTPv%d Remote Control(%s / TEIC: 0x%08x) ", pfx, pdp->lib->version, sgsn_gtp_ntoa(&pdp->lib->gsnrc), pdp->lib->teic_gn); vty_out(vty, "Data(%s / TEID: 0x%08x)%s", sgsn_gtp_ntoa(&pdp->lib->gsnru), pdp->lib->teid_gn, VTY_NEWLINE); } vty_out_rate_ctr_group(vty, " ", pdp->ctrg); } static void vty_dump_mmctx(struct vty *vty, const char *pfx, struct sgsn_mm_ctx *mm, int pdp) { vty_out(vty, "%sMM Context for IMSI %s, IMEI %s, P-TMSI %08x%s", pfx, mm->imsi, mm->imei, mm->p_tmsi, VTY_NEWLINE); vty_out(vty, "%s MSISDN: %s, TLLI: %08x%s HLR: %s", pfx, mm->msisdn, mm->gb.tlli, mm->hlr, VTY_NEWLINE); vty_out(vty, "%s MM State: %s, Routeing Area: %s, Cell ID: %u%s", pfx, get_value_string(gprs_mm_st_strs, mm->gmm_state), osmo_rai_name(&mm->ra), mm->gb.cell_id, VTY_NEWLINE); vty_out_rate_ctr_group(vty, " ", mm->ctrg); if (pdp) { struct sgsn_pdp_ctx *pdp; llist_for_each_entry(pdp, &mm->pdp_list, list) vty_dump_pdp(vty, " ", pdp); } } DEFUN(show_sgsn, show_sgsn_cmd, "show sgsn", SHOW_STR "Display information about the SGSN") { if (sgsn->gsup_client) { struct ipa_client_conn *link = sgsn->gsup_client->link; vty_out(vty, " Remote authorization: %sconnected to %s:%d via GSUP%s", sgsn->gsup_client->is_connected ? "" : "not ", link->addr, link->port, VTY_NEWLINE); } if (sgsn->gsn) vty_out(vty, " GSN: signalling %s, user traffic %s%s", inet_ntoa(sgsn->gsn->gsnc), inet_ntoa(sgsn->gsn->gsnu), VTY_NEWLINE); /* FIXME: statistics */ return CMD_SUCCESS; } #define MMCTX_STR "MM Context\n" #define INCLUDE_PDP_STR "Include PDP Context Information\n" #if 0 DEFUN(show_mmctx_tlli, show_mmctx_tlli_cmd, "show mm-context tlli HEX [pdp]", SHOW_STR MMCTX_STR "Identify by TLLI\n" "TLLI\n" INCLUDE_PDP_STR) { uint32_t tlli; struct sgsn_mm_ctx *mm; tlli = strtoul(argv[0], NULL, 16); mm = sgsn_mm_ctx_by_tlli(tlli); if (!mm) { vty_out(vty, "No MM context for TLLI %08x%s", tlli, VTY_NEWLINE); return CMD_WARNING; } vty_dump_mmctx(vty, "", mm, argv[1] ? 1 : 0); return CMD_SUCCESS; } #endif DEFUN(swow_mmctx_imsi, show_mmctx_imsi_cmd, "show mm-context imsi IMSI [pdp]", SHOW_STR MMCTX_STR "Identify by IMSI\n" "IMSI of the MM Context\n" INCLUDE_PDP_STR) { struct sgsn_mm_ctx *mm; mm = sgsn_mm_ctx_by_imsi(argv[0]); if (!mm) { vty_out(vty, "No MM context for IMSI %s%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } vty_dump_mmctx(vty, "", mm, argv[1] ? 1 : 0); return CMD_SUCCESS; } DEFUN(swow_mmctx_all, show_mmctx_all_cmd, "show mm-context all [pdp]", SHOW_STR MMCTX_STR "All MM Contexts\n" INCLUDE_PDP_STR) { struct sgsn_mm_ctx *mm; llist_for_each_entry(mm, &sgsn_mm_ctxts, list) vty_dump_mmctx(vty, "", mm, argv[0] ? 1 : 0); return CMD_SUCCESS; } DEFUN(show_pdpctx_all, show_pdpctx_all_cmd, "show pdp-context all", SHOW_STR "Display information on PDP Context\n" "Show everything\n") { struct sgsn_pdp_ctx *pdp; llist_for_each_entry(pdp, &sgsn_pdp_ctxts, g_list) vty_dump_pdp(vty, "", pdp); return CMD_SUCCESS; } DEFUN(imsi_acl, cfg_imsi_acl_cmd, "imsi-acl (add|del) IMSI", "Access Control List of foreign IMSIs\n" "Add IMSI to ACL\n" "Remove IMSI from ACL\n" "IMSI of subscriber\n") { char imsi_sanitized[GSM23003_IMSI_MAX_DIGITS+1]; const char *op = argv[0]; const char *imsi = imsi_sanitized; int rc; /* Sanitize IMSI */ if (strlen(argv[1]) > GSM23003_IMSI_MAX_DIGITS) { vty_out(vty, "%% IMSI (%s) too long -- ignored!%s", argv[1], VTY_NEWLINE); return CMD_WARNING; } memset(imsi_sanitized, '0', sizeof(imsi_sanitized)); strcpy(imsi_sanitized+GSM23003_IMSI_MAX_DIGITS-strlen(argv[1]),argv[1]); if (!strcmp(op, "add")) rc = sgsn_acl_add(imsi, g_cfg); else rc = sgsn_acl_del(imsi, g_cfg); if (rc < 0) { vty_out(vty, "%% unable to %s ACL%s", op, VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } DEFUN(cfg_encrypt, cfg_encrypt_cmd, "encryption (GEA0|GEA1|GEA2|GEA3|GEA4)", "Set encryption algorithm for SGSN\n" "Use GEA0 (no encryption)\n" "Use GEA1\nUse GEA2\nUse GEA3\nUse GEA4\n") { enum gprs_ciph_algo c = get_string_value(gprs_cipher_names, argv[0]); if (c != GPRS_ALGO_GEA0) { if (!gprs_cipher_supported(c)) { vty_out(vty, "%% cipher %s is unsupported in current version%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } if (!g_cfg->require_authentication) { vty_out(vty, "%% unable to use encryption %s without authentication: please adjust auth-policy%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } } g_cfg->cipher = c; return CMD_SUCCESS; } DEFUN(cfg_auth_policy, cfg_auth_policy_cmd, "auth-policy (accept-all|closed|acl-only|remote)", "Configure the Authorization policy of the SGSN. This setting determines which subscribers are" " permitted to register to the network.\n" "Accept all IMSIs (DANGEROUS)\n" "Accept only home network subscribers or those in the ACL\n" "Accept only subscribers in the ACL\n" "Use remote subscription data only (HLR)\n") { int val = get_string_value(sgsn_auth_pol_strs, argv[0]); OSMO_ASSERT(val >= SGSN_AUTH_POLICY_OPEN && val <= SGSN_AUTH_POLICY_REMOTE); g_cfg->auth_policy = val; g_cfg->require_authentication = (val == SGSN_AUTH_POLICY_REMOTE); g_cfg->require_update_location = (val == SGSN_AUTH_POLICY_REMOTE); return CMD_SUCCESS; } /* Subscriber */ #include static void subscr_dump_full_vty(struct vty *vty, struct gprs_subscr *gsub, int pending) { #if 0 char expire_time[200]; #endif struct gsm_auth_tuple *at; int at_idx; struct sgsn_subscriber_pdp_data *pdp; vty_out(vty, " Authorized: %d%s", gsub->authorized, VTY_NEWLINE); vty_out(vty, " LAC: %d/0x%x%s", gsub->lac, gsub->lac, VTY_NEWLINE); vty_out(vty, " IMSI: %s%s", gsub->imsi, VTY_NEWLINE); if (gsub->tmsi != GSM_RESERVED_TMSI) vty_out(vty, " TMSI: %08X%s", gsub->tmsi, VTY_NEWLINE); if (gsub->sgsn_data->msisdn_len > 0) vty_out(vty, " MSISDN (BCD): %s%s", osmo_hexdump(gsub->sgsn_data->msisdn, gsub->sgsn_data->msisdn_len), VTY_NEWLINE); if (strlen(gsub->imei) > 0) vty_out(vty, " IMEI: %s%s", gsub->imei, VTY_NEWLINE); for (at_idx = 0; at_idx < ARRAY_SIZE(gsub->sgsn_data->auth_triplets); at_idx++) { at = &gsub->sgsn_data->auth_triplets[at_idx]; if (at->key_seq == GSM_KEY_SEQ_INVAL) continue; vty_out(vty, " A3A8 tuple (used %d times): ", at->use_count); vty_out(vty, " CKSN: %d, ", at->key_seq); if (at->vec.auth_types & OSMO_AUTH_TYPE_GSM) { vty_out(vty, "RAND: %s, ", osmo_hexdump(at->vec.rand, sizeof(at->vec.rand))); vty_out(vty, "SRES: %s, ", osmo_hexdump(at->vec.sres, sizeof(at->vec.sres))); vty_out(vty, "Kc: %s%s", osmo_hexdump(at->vec.kc, sizeof(at->vec.kc)), VTY_NEWLINE); } if (at->vec.auth_types & OSMO_AUTH_TYPE_UMTS) { vty_out(vty, " AUTN: %s, ", osmo_hexdump(at->vec.autn, sizeof(at->vec.autn))); vty_out(vty, "RES: %s, ", osmo_hexdump(at->vec.res, at->vec.res_len)); vty_out(vty, "IK: %s, ", osmo_hexdump(at->vec.ik, sizeof(at->vec.ik))); vty_out(vty, "CK: %s, ", osmo_hexdump(at->vec.ck, sizeof(at->vec.ck))); } } llist_for_each_entry(pdp, &gsub->sgsn_data->pdp_list, list) { vty_out(vty, " PDP info: Id: %d, Type: 0x%04x, APN: '%s' QoS: %s%s", pdp->context_id, pdp->pdp_type, pdp->apn_str, osmo_hexdump(pdp->qos_subscribed, pdp->qos_subscribed_len), VTY_NEWLINE); } #if 0 /* print the expiration time of a subscriber */ if (gsub->expire_lu) { strftime(expire_time, sizeof(expire_time), "%a, %d %b %Y %T %z", localtime(&gsub->expire_lu)); expire_time[sizeof(expire_time) - 1] = '\0'; vty_out(vty, " Expiration Time: %s%s", expire_time, VTY_NEWLINE); } #endif if (gsub->flags) vty_out(vty, " Flags: %s%s%s%s%s%s", gsub->flags & GPRS_SUBSCRIBER_FIRST_CONTACT ? "FIRST_CONTACT " : "", gsub->flags & GPRS_SUBSCRIBER_CANCELLED ? "CANCELLED " : "", gsub->flags & GPRS_SUBSCRIBER_UPDATE_LOCATION_PENDING ? "UPDATE_LOCATION_PENDING " : "", gsub->flags & GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING ? "AUTH_INFO_PENDING " : "", gsub->flags & GPRS_SUBSCRIBER_ENABLE_PURGE ? "ENABLE_PURGE " : "", VTY_NEWLINE); vty_out(vty, " Use count: %u%s", gsub->use_count, VTY_NEWLINE); } DEFUN(show_subscr_cache, show_subscr_cache_cmd, "show subscriber cache", SHOW_STR "Show information about subscribers\n" "Display contents of subscriber cache\n") { struct gprs_subscr *subscr; llist_for_each_entry(subscr, gprs_subscribers, entry) { vty_out(vty, " Subscriber:%s", VTY_NEWLINE); subscr_dump_full_vty(vty, subscr, 0); } return CMD_SUCCESS; } #define UPDATE_SUBSCR_STR "update-subscriber imsi IMSI " #define UPDATE_SUBSCR_HELP "Update subscriber list\n" \ "Use the IMSI to select the subscriber\n" \ "The IMSI\n" #define UPDATE_SUBSCR_INSERT_HELP "Insert data into the subscriber record\n" DEFUN(update_subscr_insert_auth_triplet, update_subscr_insert_auth_triplet_cmd, UPDATE_SUBSCR_STR "insert auth-triplet <1-5> sres SRES rand RAND kc KC", UPDATE_SUBSCR_HELP UPDATE_SUBSCR_INSERT_HELP "Update authentication triplet\n" "Triplet index\n" "Set SRES value\nSRES value (4 byte) in hex\n" "Set RAND value\nRAND value (16 byte) in hex\n" "Set Kc value\nKc value (8 byte) in hex\n") { const char *imsi = argv[0]; const int cksn = atoi(argv[1]) - 1; const char *sres_str = argv[2]; const char *rand_str = argv[3]; const char *kc_str = argv[4]; struct gsm_auth_tuple at = {0,}; struct gprs_subscr *subscr; subscr = gprs_subscr_get_by_imsi(imsi); if (!subscr) { vty_out(vty, "%% unable get subscriber record for %s%s", imsi, VTY_NEWLINE); return CMD_WARNING; } OSMO_ASSERT(subscr->sgsn_data); if (osmo_hexparse(sres_str, &at.vec.sres[0], sizeof(at.vec.sres)) < 0) { vty_out(vty, "%% invalid SRES value '%s'%s", sres_str, VTY_NEWLINE); goto failed; } if (osmo_hexparse(rand_str, &at.vec.rand[0], sizeof(at.vec.rand)) < 0) { vty_out(vty, "%% invalid RAND value '%s'%s", rand_str, VTY_NEWLINE); goto failed; } if (osmo_hexparse(kc_str, &at.vec.kc[0], sizeof(at.vec.kc)) < 0) { vty_out(vty, "%% invalid Kc value '%s'%s", kc_str, VTY_NEWLINE); goto failed; } at.key_seq = cksn; subscr->sgsn_data->auth_triplets[cksn] = at; subscr->sgsn_data->auth_triplets_updated = 1; gprs_subscr_put(subscr); return CMD_SUCCESS; failed: gprs_subscr_put(subscr); return CMD_SUCCESS; } DEFUN(update_subscr_cancel, update_subscr_cancel_cmd, UPDATE_SUBSCR_STR "cancel (update-procedure|subscription-withdraw)", UPDATE_SUBSCR_HELP "Cancel (remove) subscriber record\n" "The MS moved to another SGSN\n" "The subscription is no longer valid\n") { const char *imsi = argv[0]; const char *cancel_type = argv[1]; struct gprs_subscr *subscr; subscr = gprs_subscr_get_by_imsi(imsi); if (!subscr) { vty_out(vty, "%% no subscriber record for %s%s", imsi, VTY_NEWLINE); return CMD_WARNING; } if (strcmp(cancel_type, "update-procedure") == 0) subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE; else subscr->sgsn_data->error_cause = GMM_CAUSE_IMPL_DETACHED; gprs_subscr_cancel(subscr); gprs_subscr_put(subscr); return CMD_SUCCESS; } DEFUN(update_subscr_create, update_subscr_create_cmd, UPDATE_SUBSCR_STR "create", UPDATE_SUBSCR_HELP "Create a subscriber entry\n") { const char *imsi = argv[0]; struct gprs_subscr *subscr; subscr = gprs_subscr_get_by_imsi(imsi); if (subscr) { vty_out(vty, "%% subscriber record already exists for %s%s", imsi, VTY_NEWLINE); return CMD_WARNING; } subscr = gprs_subscr_get_or_create(imsi); subscr->keep_in_ram = 1; gprs_subscr_put(subscr); return CMD_SUCCESS; } DEFUN(update_subscr_destroy, update_subscr_destroy_cmd, UPDATE_SUBSCR_STR "destroy", UPDATE_SUBSCR_HELP "Destroy a subscriber entry\n") { const char *imsi = argv[0]; struct gprs_subscr *subscr; subscr = gprs_subscr_get_by_imsi(imsi); if (!subscr) { vty_out(vty, "%% subscriber record does not exist for %s%s", imsi, VTY_NEWLINE); return CMD_WARNING; } subscr->keep_in_ram = 0; subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE; gprs_subscr_cancel(subscr); if (subscr->use_count > 1) vty_out(vty, "%% subscriber is still in use%s", VTY_NEWLINE); gprs_subscr_put(subscr); return CMD_SUCCESS; } #define UL_ERR_STR "system-failure|data-missing|unexpected-data-value|" \ "unknown-subscriber|roaming-not-allowed" #define UL_ERR_HELP \ "Force error code SystemFailure\n" \ "Force error code DataMissing\n" \ "Force error code UnexpectedDataValue\n" \ "Force error code UnknownSubscriber\n" \ "Force error code RoamingNotAllowed\n" DEFUN(update_subscr_update_location_result, update_subscr_update_location_result_cmd, UPDATE_SUBSCR_STR "update-location-result (ok|" UL_ERR_STR ")", UPDATE_SUBSCR_HELP "Complete the update location procedure\n" "The update location request succeeded\n" UL_ERR_HELP) { const char *imsi = argv[0]; const char *ret_code_str = argv[1]; struct gprs_subscr *subscr; const struct value_string cause_mapping[] = { { GMM_CAUSE_NET_FAIL, "system-failure" }, { GMM_CAUSE_INV_MAND_INFO, "data-missing" }, { GMM_CAUSE_PROTO_ERR_UNSPEC, "unexpected-data-value" }, { GMM_CAUSE_IMSI_UNKNOWN, "unknown-subscriber" }, { GMM_CAUSE_GPRS_NOTALLOWED, "roaming-not-allowed" }, { 0, NULL } }; subscr = gprs_subscr_get_by_imsi(imsi); if (!subscr) { vty_out(vty, "%% unable to get subscriber record for %s%s", imsi, VTY_NEWLINE); return CMD_WARNING; } if (strcmp(ret_code_str, "ok") == 0) { subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE; subscr->authorized = 1; } else { subscr->sgsn_data->error_cause = get_string_value(cause_mapping, ret_code_str); subscr->authorized = 0; } gprs_subscr_update(subscr); gprs_subscr_put(subscr); return CMD_SUCCESS; } DEFUN(update_subscr_update_auth_info, update_subscr_update_auth_info_cmd, UPDATE_SUBSCR_STR "update-auth-info", UPDATE_SUBSCR_HELP "Complete the send authentication info procedure\n") { const char *imsi = argv[0]; struct gprs_subscr *subscr; subscr = gprs_subscr_get_by_imsi(imsi); if (!subscr) { vty_out(vty, "%% unable to get subscriber record for %s%s", imsi, VTY_NEWLINE); return CMD_WARNING; } gprs_subscr_update_auth_info(subscr); gprs_subscr_put(subscr); return CMD_SUCCESS; } DEFUN(cfg_gsup_remote_ip, cfg_gsup_remote_ip_cmd, "gsup remote-ip A.B.C.D", "GSUP Parameters\n" "Set the IP address of the remote GSUP server (e.g. OsmoHLR)." " This setting only applies if 'auth-policy remote' is used.\n" "IPv4 Address\n") { inet_aton(argv[0], &g_cfg->gsup_server_addr.sin_addr); return CMD_SUCCESS; } DEFUN(cfg_gsup_remote_port, cfg_gsup_remote_port_cmd, "gsup remote-port <0-65535>", "GSUP Parameters\n" "Set the TCP port of the remote GSUP server, see also 'gsup remote-ip'\n" "Remote TCP port\n") { g_cfg->gsup_server_port = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_gsup_oap_id, cfg_gsup_oap_id_cmd, "gsup oap-id <0-65535>", "GSUP Parameters\n" "Set the OAP client ID for authentication on the GSUP protocol." " This setting only applies if 'auth-policy remote' is used.\n" "OAP client ID (0 == disabled)\n") { /* VTY ensures range */ g_cfg->oap.client_id = (uint16_t)atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_gsup_oap_k, cfg_gsup_oap_k_cmd, "gsup oap-k K", "GSUP Parameters\n" "Set the OAP shared secret key K for authentication on the GSUP protocol." " This setting only applies if auth-policy remote is used.\n" "K value (16 byte) hex\n") { const char *k = argv[0]; g_cfg->oap.secret_k_present = 0; if ((!k) || (strlen(k) == 0)) goto disable; int k_len = osmo_hexparse(k, g_cfg->oap.secret_k, sizeof(g_cfg->oap.secret_k)); if (k_len != 16) { vty_out(vty, "%% need exactly 16 octets for oap-k, got %d.%s", k_len, VTY_NEWLINE); goto disable; } g_cfg->oap.secret_k_present = 1; return CMD_SUCCESS; disable: if (g_cfg->oap.client_id > 0) { vty_out(vty, "%% OAP client ID set, but invalid oap-k value disables OAP.%s", VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } DEFUN(cfg_gsup_oap_opc, cfg_gsup_oap_opc_cmd, "gsup oap-opc OPC", "GSUP Parameters\n" "Set the OAP shared secret OPC for authentication on the GSUP protocol." " This setting only applies if auth-policy remote is used.\n" "OPC value (16 byte) hex\n") { const char *opc = argv[0]; g_cfg->oap.secret_opc_present = 0; if ((!opc) || (strlen(opc) == 0)) goto disable; int opc_len = osmo_hexparse(opc, g_cfg->oap.secret_opc, sizeof(g_cfg->oap.secret_opc)); if (opc_len != 16) { vty_out(vty, "%% need exactly 16 octets for oap-opc, got %d.%s", opc_len, VTY_NEWLINE); goto disable; } g_cfg->oap.secret_opc_present = 1; return CMD_SUCCESS; disable: if (g_cfg->oap.client_id > 0) { vty_out(vty, "%% OAP client ID set, but invalid oap-opc value disables OAP.%s", VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } DEFUN(cfg_apn_name, cfg_apn_name_cmd, "access-point-name NAME", "Globally allow the given APN name for all subscribers.\n" "Add this NAME to the list\n") { return add_apn_ggsn_mapping(vty, argv[0], "", 0); } DEFUN(cfg_no_apn_name, cfg_no_apn_name_cmd, "no access-point-name NAME", NO_STR "Configure a global list of allowed APNs\n" "Remove entry with NAME\n") { struct apn_ctx *apn_ctx = sgsn_apn_ctx_by_name(argv[0], ""); if (!apn_ctx) return CMD_SUCCESS; sgsn_apn_ctx_free(apn_ctx); return CMD_SUCCESS; } DEFUN(cfg_cdr_filename, cfg_cdr_filename_cmd, "cdr filename NAME", "CDR\n" "Set the file name for the call-data-record file, logging the data usage of each subscriber.\n" "filename\n") { talloc_free(g_cfg->cdr.filename); g_cfg->cdr.filename = talloc_strdup(tall_vty_ctx, argv[0]); return CMD_SUCCESS; } DEFUN(cfg_no_cdr_filename, cfg_no_cdr_filename_cmd, "no cdr filename", NO_STR "CDR\nDisable saving CDR to file\n") { talloc_free(g_cfg->cdr.filename); g_cfg->cdr.filename = NULL; return CMD_SUCCESS; } DEFUN(cfg_cdr_trap, cfg_cdr_trap_cmd, "cdr trap", "CDR\nEnable sending CDR via TRAP CTRL messages\n") { g_cfg->cdr.trap = true; return CMD_SUCCESS; } DEFUN(cfg_no_cdr_trap, cfg_no_cdr_trap_cmd, "no cdr trap", NO_STR "CDR\nDisable sending CDR via TRAP CTRL messages\n") { g_cfg->cdr.trap = false; return CMD_SUCCESS; } DEFUN(cfg_cdr_interval, cfg_cdr_interval_cmd, "cdr interval <1-2147483647>", "CDR\n" "Set the interval for the call-data-record file\n" "interval in seconds\n") { g_cfg->cdr.interval = atoi(argv[0]); return CMD_SUCCESS; } #define COMPRESSION_STR "Configure compression\n" DEFUN(cfg_no_comp_rfc1144, cfg_no_comp_rfc1144_cmd, "no compression rfc1144", NO_STR COMPRESSION_STR "disable rfc1144 TCP/IP header compression\n") { g_cfg->pcomp_rfc1144.active = 0; g_cfg->pcomp_rfc1144.passive = 0; return CMD_SUCCESS; } DEFUN(cfg_comp_rfc1144, cfg_comp_rfc1144_cmd, "compression rfc1144 active slots <1-256>", COMPRESSION_STR "RFC1144 Header compresion scheme\n" "Compression is actively proposed\n" "Number of compression state slots\n" "Number of compression state slots\n") { g_cfg->pcomp_rfc1144.active = 1; g_cfg->pcomp_rfc1144.passive = 1; g_cfg->pcomp_rfc1144.s01 = atoi(argv[0]) - 1; return CMD_SUCCESS; } DEFUN(cfg_comp_rfc1144p, cfg_comp_rfc1144p_cmd, "compression rfc1144 passive", COMPRESSION_STR "RFC1144 Header compresion scheme\n" "Compression is available on request\n") { g_cfg->pcomp_rfc1144.active = 0; g_cfg->pcomp_rfc1144.passive = 1; return CMD_SUCCESS; } DEFUN(cfg_no_comp_v42bis, cfg_no_comp_v42bis_cmd, "no compression v42bis", NO_STR COMPRESSION_STR "disable V.42bis data compression\n") { g_cfg->dcomp_v42bis.active = 0; g_cfg->dcomp_v42bis.passive = 0; return CMD_SUCCESS; } DEFUN(cfg_comp_v42bis, cfg_comp_v42bis_cmd, "compression v42bis active direction (ms|sgsn|both) codewords <512-65535> strlen <6-250>", COMPRESSION_STR "V.42bis data compresion scheme\n" "Compression is actively proposed\n" "Direction in which the compression shall be active (p0)\n" "Compress ms->sgsn direction only\n" "Compress sgsn->ms direction only\n" "Both directions\n" "Number of codewords (p1)\n" "Number of codewords\n" "Maximum string length (p2)\n" "Maximum string length\n") { g_cfg->dcomp_v42bis.active = 1; g_cfg->dcomp_v42bis.passive = 1; switch (argv[0][0]) { case 'm': g_cfg->dcomp_v42bis.p0 = 1; break; case 's': g_cfg->dcomp_v42bis.p0 = 2; break; case 'b': g_cfg->dcomp_v42bis.p0 = 3; break; } g_cfg->dcomp_v42bis.p1 = atoi(argv[1]); g_cfg->dcomp_v42bis.p2 = atoi(argv[2]); return CMD_SUCCESS; } DEFUN(cfg_comp_v42bisp, cfg_comp_v42bisp_cmd, "compression v42bis passive", COMPRESSION_STR "V.42bis data compresion scheme\n" "Compression is available on request\n") { g_cfg->dcomp_v42bis.active = 0; g_cfg->dcomp_v42bis.passive = 1; return CMD_SUCCESS; } int sgsn_vty_init(struct sgsn_config *cfg) { g_cfg = cfg; install_element_ve(&show_sgsn_cmd); //install_element_ve(&show_mmctx_tlli_cmd); install_element_ve(&show_mmctx_imsi_cmd); install_element_ve(&show_mmctx_all_cmd); install_element_ve(&show_pdpctx_all_cmd); install_element_ve(&show_subscr_cache_cmd); install_element(ENABLE_NODE, &update_subscr_insert_auth_triplet_cmd); install_element(ENABLE_NODE, &update_subscr_create_cmd); install_element(ENABLE_NODE, &update_subscr_destroy_cmd); install_element(ENABLE_NODE, &update_subscr_cancel_cmd); install_element(ENABLE_NODE, &update_subscr_update_location_result_cmd); install_element(ENABLE_NODE, &update_subscr_update_auth_info_cmd); install_element(CONFIG_NODE, &cfg_sgsn_cmd); install_node(&sgsn_node, config_write_sgsn); install_element(SGSN_NODE, &cfg_sgsn_bind_addr_cmd); install_element(SGSN_NODE, &cfg_ggsn_remote_ip_cmd); //install_element(SGSN_NODE, &cfg_ggsn_remote_port_cmd); install_element(SGSN_NODE, &cfg_ggsn_gtp_version_cmd); install_element(SGSN_NODE, &cfg_imsi_acl_cmd); install_element(SGSN_NODE, &cfg_auth_policy_cmd); install_element(SGSN_NODE, &cfg_encrypt_cmd); install_element(SGSN_NODE, &cfg_gsup_remote_ip_cmd); install_element(SGSN_NODE, &cfg_gsup_remote_port_cmd); install_element(SGSN_NODE, &cfg_gsup_oap_id_cmd); install_element(SGSN_NODE, &cfg_gsup_oap_k_cmd); install_element(SGSN_NODE, &cfg_gsup_oap_opc_cmd); install_element(SGSN_NODE, &cfg_apn_ggsn_cmd); install_element(SGSN_NODE, &cfg_apn_imsi_ggsn_cmd); install_element(SGSN_NODE, &cfg_apn_name_cmd); install_element(SGSN_NODE, &cfg_no_apn_name_cmd); install_element(SGSN_NODE, &cfg_cdr_filename_cmd); install_element(SGSN_NODE, &cfg_no_cdr_filename_cmd); install_element(SGSN_NODE, &cfg_cdr_trap_cmd); install_element(SGSN_NODE, &cfg_no_cdr_trap_cmd); install_element(SGSN_NODE, &cfg_cdr_interval_cmd); install_element(SGSN_NODE, &cfg_ggsn_dynamic_lookup_cmd); install_element(SGSN_NODE, &cfg_grx_ggsn_cmd); install_element(SGSN_NODE, &cfg_sgsn_T3312_cmd); install_element(SGSN_NODE, &cfg_sgsn_T3322_cmd); install_element(SGSN_NODE, &cfg_sgsn_T3350_cmd); install_element(SGSN_NODE, &cfg_sgsn_T3360_cmd); install_element(SGSN_NODE, &cfg_sgsn_T3370_cmd); install_element(SGSN_NODE, &cfg_sgsn_T3313_cmd); install_element(SGSN_NODE, &cfg_sgsn_T3314_cmd); install_element(SGSN_NODE, &cfg_sgsn_T3316_cmd); install_element(SGSN_NODE, &cfg_sgsn_T3385_cmd); install_element(SGSN_NODE, &cfg_sgsn_T3386_cmd); install_element(SGSN_NODE, &cfg_sgsn_T3395_cmd); install_element(SGSN_NODE, &cfg_sgsn_T3397_cmd); install_element(SGSN_NODE, &cfg_no_comp_rfc1144_cmd); install_element(SGSN_NODE, &cfg_comp_rfc1144_cmd); install_element(SGSN_NODE, &cfg_comp_rfc1144p_cmd); install_element(SGSN_NODE, &cfg_no_comp_v42bis_cmd); install_element(SGSN_NODE, &cfg_comp_v42bis_cmd); install_element(SGSN_NODE, &cfg_comp_v42bisp_cmd); #ifdef BUILD_IU ranap_iu_vty_init(SGSN_NODE, &g_cfg->iu.rab_assign_addr_enc); #endif return 0; } int sgsn_parse_config(const char *config_file) { int rc; /* make sure sgsn_vty_init() was called before this */ OSMO_ASSERT(g_cfg); g_cfg->timers.T3312 = GSM0408_T3312_SECS; g_cfg->timers.T3322 = GSM0408_T3322_SECS; g_cfg->timers.T3350 = GSM0408_T3350_SECS; g_cfg->timers.T3360 = GSM0408_T3360_SECS; g_cfg->timers.T3370 = GSM0408_T3370_SECS; g_cfg->timers.T3313 = GSM0408_T3313_SECS; g_cfg->timers.T3314 = GSM0408_T3314_SECS; g_cfg->timers.T3316 = GSM0408_T3316_SECS; g_cfg->timers.T3385 = GSM0408_T3385_SECS; g_cfg->timers.T3386 = GSM0408_T3386_SECS; g_cfg->timers.T3395 = GSM0408_T3395_SECS; g_cfg->timers.T3397 = GSM0408_T3397_SECS; rc = vty_read_config_file(config_file, NULL); if (rc < 0) { fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file); return rc; } if (g_cfg->auth_policy == SGSN_AUTH_POLICY_REMOTE && !(g_cfg->gsup_server_addr.sin_addr.s_addr && g_cfg->gsup_server_port)) { fprintf(stderr, "Configuration error:" " 'auth-policy remote' requires both" " 'gsup remote-ip' and 'gsup remote-port'\n"); return -EINVAL; } return 0; } osmo-sgsn-1.3.0/src/gprs/slhc.c000066400000000000000000000562401327264017000163100ustar00rootroot00000000000000/* * Routines to compress and uncompress tcp packets (for transmission * over low speed serial lines). * * Copyright (c) 1989 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by the University of California, Berkeley. The name of the * University may not be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: * - Initial distribution. * * * modified for KA9Q Internet Software Package by * Katie Stevens (dkstevens@ucdavis.edu) * University of California, Davis * Computing Services * - 01-31-90 initial adaptation (from 1.19) * PPP.05 02-15-90 [ks] * PPP.08 05-02-90 [ks] use PPP protocol field to signal compression * PPP.15 09-90 [ks] improve mbuf handling * PPP.16 11-02 [karn] substantially rewritten to use NOS facilities * * - Feb 1991 Bill_Simpson@um.cc.umich.edu * variable number of conversation slots * allow zero or one slots * separate routines * status display * - Jul 1994 Dmitry Gorodchanin * Fixes for memory leaks. * - Oct 1994 Dmitry Gorodchanin * Modularization. * - Jan 1995 Bjorn Ekwall * Use ip_fast_csum from ip.h * - July 1995 Christos A. Polyzols * Spotted bug in tcp option checking * * * This module is a difficult issue. It's clearly inet code but it's also clearly * driver code belonging close to PPP and SLIP */ #include #include #include #include #include #include #include #include #include #define ERR_PTR(x) x static unsigned char *encode(unsigned char *cp, unsigned short n); static long decode(unsigned char **cpp); static unsigned char * put16(unsigned char *cp, unsigned short x); static unsigned short pull16(unsigned char **cpp); /* Replacement for kernel space function ip_fast_csum() */ static uint16_t ip_fast_csum(uint8_t *iph, int ihl) { int i; uint16_t temp; uint32_t accumulator = 0xFFFF; for(i=0;i0xFFFF) { accumulator++; accumulator&=0xFFFF; } } return (uint16_t)(htons(~accumulator)&0xFFFF); } /* Replacement for kernel space function put_unaligned() */ static void put_unaligned(uint16_t val, void *ptr) { memcpy(ptr,&val,sizeof(val)); } /* Allocate compression data structure * slots must be in range 0 to 255 (zero meaning no compression) * Returns pointer to structure or ERR_PTR() on error. */ struct slcompress * slhc_init(const void *ctx, int rslots, int tslots) { register short i; register struct cstate *ts; struct slcompress *comp; if (rslots < 0 || rslots > 255 || tslots < 0 || tslots > 255) return NULL; comp = (struct slcompress *)talloc_zero_size(ctx,sizeof(struct slcompress)); if (! comp) goto out_fail; if (rslots > 0) { size_t rsize = rslots * sizeof(struct cstate); comp->rstate = (struct cstate *) talloc_zero_size(ctx, rsize); if (! comp->rstate) goto out_free; comp->rslot_limit = rslots - 1; } if (tslots > 0) { size_t tsize = tslots * sizeof(struct cstate); comp->tstate = (struct cstate *) talloc_zero_size(ctx, tsize); if (! comp->tstate) goto out_free2; comp->tslot_limit = tslots - 1; } comp->xmit_oldest = 0; comp->xmit_current = 255; comp->recv_current = 255; /* * don't accept any packets with implicit index until we get * one with an explicit index. Otherwise the uncompress code * will try to use connection 255, which is almost certainly * out of range */ comp->flags |= SLF_TOSS; if ( tslots > 0 ) { ts = comp->tstate; for(i = comp->tslot_limit; i > 0; --i){ ts[i].cs_this = i; ts[i].next = &(ts[i - 1]); } ts[0].next = &(ts[comp->tslot_limit]); ts[0].cs_this = 0; } return comp; out_free2: talloc_free(comp->rstate); out_free: talloc_free(comp); out_fail: return NULL; } /* Free a compression data structure */ void slhc_free(struct slcompress *comp) { DEBUGP(DSLHC, "slhc_free(): Freeing compression states...\n"); if ( comp == NULLSLCOMPR ) return; if ( comp->tstate != NULLSLSTATE ) talloc_free(comp->tstate ); if ( comp->rstate != NULLSLSTATE ) talloc_free( comp->rstate ); talloc_free( comp ); } /* Put a short in host order into a char array in network order */ static inline unsigned char * put16(unsigned char *cp, unsigned short x) { *cp++ = x >> 8; *cp++ = x; return cp; } /* Encode a number */ static unsigned char * encode(unsigned char *cp, unsigned short n) { if(n >= 256 || n == 0){ *cp++ = 0; cp = put16(cp,n); } else { *cp++ = n; } DEBUGP(DSLHC, "encode(): n=%04x\n",n); return cp; } /* Pull a 16-bit integer in host order from buffer in network byte order */ static unsigned short pull16(unsigned char **cpp) { short rval; rval = *(*cpp)++; rval <<= 8; rval |= *(*cpp)++; return rval; } /* Decode a number */ static long decode(unsigned char **cpp) { register int x; x = *(*cpp)++; if(x == 0){ return pull16(cpp) & 0xffff; /* pull16 returns -1 on error */ } else { return x & 0xff; /* -1 if PULLCHAR returned error */ } } /* * icp and isize are the original packet. * ocp is a place to put a copy if necessary. * cpp is initially a pointer to icp. If the copy is used, * change it to ocp. */ int slhc_compress(struct slcompress *comp, unsigned char *icp, int isize, unsigned char *ocp, unsigned char **cpp, int compress_cid) { register struct cstate *ocs = &(comp->tstate[comp->xmit_oldest]); register struct cstate *lcs = ocs; register struct cstate *cs = lcs->next; register unsigned long deltaS, deltaA; register short changes = 0; int hlen; unsigned char new_seq[16]; register unsigned char *cp = new_seq; struct iphdr *ip; struct tcphdr *th, *oth; __sum16 csum; /* * Don't play with runt packets. */ if(isizeprotocol != IPPROTO_TCP || (ntohs(ip->frag_off) & 0x3fff)) { /* Send as regular IP */ if(ip->protocol != IPPROTO_TCP) comp->sls_o_nontcp++; else comp->sls_o_tcp++; DEBUGP(DSLHC, "slhc_compress(): Not a TCP packat, will not touch...\n"); return isize; } /* Extract TCP header */ th = (struct tcphdr *)(((unsigned char *)ip) + ip->ihl*4); hlen = ip->ihl*4 + th->doff*4; /* Bail if the TCP packet isn't `compressible' (i.e., ACK isn't set or * some other control bit is set). Also uncompressible if * it's a runt. */ if(hlen > isize || th->syn || th->fin || th->rst || ! (th->ack)){ /* TCP connection stuff; send as regular IP */ comp->sls_o_tcp++; DEBUGP(DSLHC, "slhc_compress(): Packet is part of a TCP connection, will not touch...\n"); return isize; } /* * Packet is compressible -- we're going to send either a * COMPRESSED_TCP or UNCOMPRESSED_TCP packet. Either way, * we need to locate (or create) the connection state. * * States are kept in a circularly linked list with * xmit_oldest pointing to the end of the list. The * list is kept in lru order by moving a state to the * head of the list whenever it is referenced. Since * the list is short and, empirically, the connection * we want is almost always near the front, we locate * states via linear search. If we don't find a state * for the datagram, the oldest state is (re-)used. */ DEBUGP(DSLHC, "slhc_compress(): Compressible packet detected!\n"); for ( ; ; ) { if( ip->saddr == cs->cs_ip.saddr && ip->daddr == cs->cs_ip.daddr && th->source == cs->cs_tcp.source && th->dest == cs->cs_tcp.dest) goto found; /* if current equal oldest, at end of list */ if ( cs == ocs ) break; lcs = cs; cs = cs->next; comp->sls_o_searches++; } /* * Didn't find it -- re-use oldest cstate. Send an * uncompressed packet that tells the other side what * connection number we're using for this conversation. * * Note that since the state list is circular, the oldest * state points to the newest and we only need to set * xmit_oldest to update the lru linkage. */ DEBUGP(DSLHC, "slhc_compress(): Header not yet seen, will memorize header for the next turn...\n"); comp->sls_o_misses++; comp->xmit_oldest = lcs->cs_this; goto uncompressed; found: DEBUGP(DSLHC, "slhc_compress(): Header already seen, trying to compress...\n"); /* * Found it -- move to the front on the connection list. */ if(lcs == ocs) { /* found at most recently used */ } else if (cs == ocs) { /* found at least recently used */ comp->xmit_oldest = lcs->cs_this; } else { /* more than 2 elements */ lcs->next = cs->next; cs->next = ocs->next; ocs->next = cs; } /* * Make sure that only what we expect to change changed. * Check the following: * IP protocol version, header length & type of service. * The "Don't fragment" bit. * The time-to-live field. * The TCP header length. * IP options, if any. * TCP options, if any. * If any of these things are different between the previous & * current datagram, we send the current datagram `uncompressed'. */ oth = &cs->cs_tcp; /* Display a little more debug information about which of the * header fields changed unexpectedly */ if(ip->version != cs->cs_ip.version) DEBUGP(DSLHC, "slhc_compress(): Unexpected change: ip->version != cs->cs_ip.version\n"); if(ip->ihl != cs->cs_ip.ihl) DEBUGP(DSLHC, "slhc_compress(): Unexpected change: ip->ihl != cs->cs_ip.ihl\n"); if(ip->tos != cs->cs_ip.tos) DEBUGP(DSLHC, "slhc_compress(): Unexpected change: ip->tos != cs->cs_ip.tos\n"); if((ip->frag_off & htons(0x4000)) != (cs->cs_ip.frag_off & htons(0x4000))) DEBUGP(DSLHC, "slhc_compress(): Unexpected change: (ip->frag_off & htons(0x4000)) != (cs->cs_ip.frag_off & htons(0x4000))\n"); if(ip->ttl != cs->cs_ip.ttl) DEBUGP(DSLHC, "slhc_compress(): Unexpected change: ip->ttl != cs->cs_ip.ttl\n"); if(th->doff != cs->cs_tcp.doff) DEBUGP(DSLHC, "slhc_compress(): Unexpected change: th->doff != cs->cs_tcp.doff\n"); if(ip->ihl > 5 && memcmp(ip+1,cs->cs_ipopt,((ip->ihl)-5)*4) != 0) { DEBUGP(DSLHC, "slhc_compress(): Unexpected change: (ip->ihl > 5 && memcmp(ip+1,cs->cs_ipopt,((ip->ihl)-5)*4) != 0)\n"); DEBUGP(DSLHC, "slhc_compress(): ip->ihl = %i\n", ip->ihl); DEBUGP(DSLHC, "slhc_compress(): ip+1 = %s\n", osmo_hexdump_nospc((uint8_t*)(ip+1),((ip->ihl)-5)*4)); DEBUGP(DSLHC, "slhc_compress(): Unexpected change: cs->cs_ipopt = %s\n", osmo_hexdump_nospc((uint8_t*)(cs->cs_ipopt),((ip->ihl)-5)*4)); } if(th->doff > 5 && memcmp(th+1,cs->cs_tcpopt,((th->doff)-5)*4) != 0) { DEBUGP(DSLHC, "slhc_compress(): Unexpected change: (th->doff > 5 && memcmp(th+1,cs->cs_tcpopt,((th->doff)-5)*4) != 0)\n"); DEBUGP(DSLHC, "slhc_compress(): th->doff = %i\n", th->doff); DEBUGP(DSLHC, "slhc_compress(): th+1 = %s\n", osmo_hexdump_nospc((uint8_t*)(th+1),((th->doff)-5)*4)); DEBUGP(DSLHC, "slhc_compress(): cs->cs_tcpopt = %s\n", osmo_hexdump_nospc((uint8_t*)cs->cs_tcpopt, ((th->doff)-5)*4)); } if(ip->version != cs->cs_ip.version || ip->ihl != cs->cs_ip.ihl || ip->tos != cs->cs_ip.tos || (ip->frag_off & htons(0x4000)) != (cs->cs_ip.frag_off & htons(0x4000)) || ip->ttl != cs->cs_ip.ttl || th->doff != cs->cs_tcp.doff || (ip->ihl > 5 && memcmp(ip+1,cs->cs_ipopt,((ip->ihl)-5)*4) != 0) || (th->doff > 5 && memcmp(th+1,cs->cs_tcpopt,((th->doff)-5)*4) != 0)){ DEBUGP(DSLHC, "slhc_compress(): The header contains unexpected changes, can't compress...\n"); goto uncompressed; } /* * Figure out which of the changing fields changed. The * receiver expects changes in the order: urgent, window, * ack, seq (the order minimizes the number of temporaries * needed in this section of code). */ if(th->urg){ deltaS = ntohs(th->urg_ptr); DEBUGP(DSLHC, "slhc_compress(): flag: Urgent Pointer (U) = 1\n"); cp = encode(cp,deltaS); changes |= NEW_U; } else if(th->urg_ptr != oth->urg_ptr){ /* argh! URG not set but urp changed -- a sensible * implementation should never do this but RFC793 * doesn't prohibit the change so we have to deal * with it. */ DEBUGP(DSLHC, "slhc_compress(): URG not set but urp changed, can't compress...\n"); goto uncompressed; } if((deltaS = ntohs(th->window) - ntohs(oth->window)) != 0){ DEBUGP(DSLHC, "slhc_compress(): flag: Delta Window (W) = 1\n"); cp = encode(cp,deltaS); changes |= NEW_W; } if((deltaA = ntohl(th->ack_seq) - ntohl(oth->ack_seq)) != 0L){ if(deltaA > 0x0000ffff) { DEBUGP(DSLHC, "slhc_compress(): (deltaA = ntohl(th->ack_seq) - ntohl(oth->ack_seq)) != 0L, can't compress...\n"); goto uncompressed; } DEBUGP(DSLHC, "slhc_compress(): flag: Delta Ack (A) = 1\n"); cp = encode(cp,deltaA); changes |= NEW_A; } if((deltaS = ntohl(th->seq) - ntohl(oth->seq)) != 0L){ if(deltaS > 0x0000ffff) { DEBUGP(DSLHC, "slhc_compress(): (deltaS = ntohl(th->seq) - ntohl(oth->seq)) != 0L, can't compress...\n"); goto uncompressed; } DEBUGP(DSLHC, "slhc_compress(): flag: Delta Sequence (S) = 1\n"); cp = encode(cp,deltaS); changes |= NEW_S; } switch(changes){ case 0: /* Nothing changed. If this packet contains data and the * last one didn't, this is probably a data packet following * an ack (normal on an interactive connection) and we send * it compressed. Otherwise it's probably a retransmit, * retransmitted ack or window probe. Send it uncompressed * in case the other side missed the compressed version. */ if(ip->tot_len != cs->cs_ip.tot_len && ntohs(cs->cs_ip.tot_len) == hlen) break; DEBUGP(DSLHC, "slhc_compress(): Retransmitted packet detected, can't compress...\n"); goto uncompressed; case SPECIAL_I: case SPECIAL_D: /* actual changes match one of our special case encodings -- * send packet uncompressed. */ DEBUGP(DSLHC, "slhc_compress(): Special case detected, can't compress...\n"); goto uncompressed; case NEW_S|NEW_A: if(deltaS == deltaA && deltaS == ntohs(cs->cs_ip.tot_len) - hlen){ /* special case for echoed terminal traffic */ DEBUGP(DSLHC, "slhc_compress(): Special case for echoed terminal traffic detected...\n"); DEBUGP(DSLHC, "slhc_compress(): flag: Delta Sequence (S) = 1, Delta Window (W) = 1, Urgent Pointer (U) = 1\n"); changes = SPECIAL_I; cp = new_seq; } break; case NEW_S: if(deltaS == ntohs(cs->cs_ip.tot_len) - hlen){ /* special case for data xfer */ DEBUGP(DSLHC, "slhc_compress(): Special case for data xfer detected...\n"); DEBUGP(DSLHC, "slhc_compress(): flag: Delta Sequence (S) = 1, Delta Ack (A) = 1, Delta Window (W) = 1, Urgent Pointer (U) = 1\n"); changes = SPECIAL_D; cp = new_seq; } break; } deltaS = ntohs(ip->id) - ntohs(cs->cs_ip.id); if(deltaS != 1){ DEBUGP(DSLHC, "slhc_compress(): flag: Delta IP ID (I) = 1\n"); cp = encode(cp,deltaS); changes |= NEW_I; } if(th->psh) { DEBUGP(DSLHC, "slhc_compress(): flag: Push (P) = 1\n"); changes |= TCP_PUSH_BIT; } /* Grab the cksum before we overwrite it below. Then update our * state with this packet's header. */ csum = th->check; memcpy(&cs->cs_ip,ip,20); memcpy(&cs->cs_tcp,th,20); /* We want to use the original packet as our compressed packet. * (cp - new_seq) is the number of bytes we need for compressed * sequence numbers. In addition we need one byte for the change * mask, one for the connection id and two for the tcp checksum. * So, (cp - new_seq) + 4 bytes of header are needed. */ deltaS = cp - new_seq; if(compress_cid == 0 || comp->xmit_current != cs->cs_this){ cp = ocp; *cpp = ocp; DEBUGP(DSLHC, "slhc_compress(): flag: Connection number (C) = 1\n"); *cp++ = changes | NEW_C; *cp++ = cs->cs_this; comp->xmit_current = cs->cs_this; } else { cp = ocp; *cpp = ocp; *cp++ = changes; } *(__sum16 *)cp = csum; cp += 2; /* deltaS is now the size of the change section of the compressed header */ DEBUGP(DSLHC, "slhc_compress(): Delta-list length (deltaS) = %li\n",deltaS); DEBUGP(DSLHC, "slhc_compress(): Original header len (hlen) = %i\n",hlen); memcpy(cp,new_seq,deltaS); /* Write list of deltas */ memcpy(cp+deltaS,icp+hlen,isize-hlen); comp->sls_o_compressed++; ocp[0] |= SL_TYPE_COMPRESSED_TCP; return isize - hlen + deltaS + (cp - ocp); /* Update connection state cs & send uncompressed packet (i.e., * a regular ip/tcp packet but with the 'conversation id' we hope * to use on future compressed packets in the protocol field). */ uncompressed: DEBUGP(DSLHC, "slhc_compress(): Packet will be sent uncompressed...\n"); memcpy(&cs->cs_ip,ip,20); memcpy(&cs->cs_tcp,th,20); if (ip->ihl > 5) memcpy(cs->cs_ipopt, ip+1, ((ip->ihl) - 5) * 4); if (th->doff > 5) memcpy(cs->cs_tcpopt, th+1, ((th->doff) - 5) * 4); comp->xmit_current = cs->cs_this; comp->sls_o_uncompressed++; memcpy(ocp, icp, isize); *cpp = ocp; ocp[9] = cs->cs_this; ocp[0] |= SL_TYPE_UNCOMPRESSED_TCP; return isize; } int slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize) { register int changes; long x; register struct tcphdr *thp; register struct iphdr *ip; register struct cstate *cs; int len, hdrlen; unsigned char *cp = icp; /* We've got a compressed packet; read the change byte */ comp->sls_i_compressed++; if(isize < 3){ comp->sls_i_error++; return 0; } changes = *cp++; if(changes & NEW_C){ /* Make sure the state index is in range, then grab the state. * If we have a good state index, clear the 'discard' flag. */ x = *cp++; /* Read conn index */ if(x < 0 || x > comp->rslot_limit) goto bad; comp->flags &=~ SLF_TOSS; comp->recv_current = x; } else { /* this packet has an implicit state index. If we've * had a line error since the last time we got an * explicit state index, we have to toss the packet. */ if(comp->flags & SLF_TOSS){ comp->sls_i_tossed++; return 0; } } cs = &comp->rstate[comp->recv_current]; thp = &cs->cs_tcp; ip = &cs->cs_ip; thp->check = *(__sum16 *)cp; cp += 2; thp->psh = (changes & TCP_PUSH_BIT) ? 1 : 0; /* * we can use the same number for the length of the saved header and * the current one, because the packet wouldn't have been sent * as compressed unless the options were the same as the previous one */ hdrlen = ip->ihl * 4 + thp->doff * 4; switch(changes & SPECIALS_MASK){ case SPECIAL_I: /* Echoed terminal traffic */ DEBUGP(DSLHC, "slhc_uncompress(): Echoed terminal traffic detected\n"); { register short i; i = ntohs(ip->tot_len) - hdrlen; thp->ack_seq = htonl( ntohl(thp->ack_seq) + i); thp->seq = htonl( ntohl(thp->seq) + i); } break; case SPECIAL_D: /* Unidirectional data */ DEBUGP(DSLHC, "slhc_uncompress(): Unidirectional data detected\n"); thp->seq = htonl( ntohl(thp->seq) + ntohs(ip->tot_len) - hdrlen); break; default: DEBUGP(DSLHC, "slhc_uncompress(): default packet type detected\n"); if(changes & NEW_U){ thp->urg = 1; if((x = decode(&cp)) == -1) { goto bad; } thp->urg_ptr = htons(x); } else thp->urg = 0; if(changes & NEW_W){ if((x = decode(&cp)) == -1) { goto bad; } thp->window = htons( ntohs(thp->window) + x); } if(changes & NEW_A){ if((x = decode(&cp)) == -1) { goto bad; } thp->ack_seq = htonl( ntohl(thp->ack_seq) + x); } if(changes & NEW_S){ if((x = decode(&cp)) == -1) { goto bad; } thp->seq = htonl( ntohl(thp->seq) + x); } break; } if(changes & NEW_I){ if((x = decode(&cp)) == -1) { goto bad; } ip->id = htons (ntohs (ip->id) + x); } else ip->id = htons (ntohs (ip->id) + 1); /* * At this point, cp points to the first byte of data in the * packet. Put the reconstructed TCP and IP headers back on the * packet. Recalculate IP checksum (but not TCP checksum). */ len = isize - (cp - icp); if (len < 0) goto bad; len += hdrlen; ip->tot_len = htons(len); ip->check = 0; DEBUGP(DSLHC, "slhc_uncompress(): making space for the reconstructed header...\n"); memmove(icp + hdrlen, cp, len - hdrlen); cp = icp; memcpy(cp, ip, 20); cp += 20; if (ip->ihl > 5) { memcpy(cp, cs->cs_ipopt, (ip->ihl - 5) * 4); cp += (ip->ihl - 5) * 4; } put_unaligned(ip_fast_csum(icp, ip->ihl), &((struct iphdr *)icp)->check); memcpy(cp, thp, 20); cp += 20; if (thp->doff > 5) { memcpy(cp, cs->cs_tcpopt, ((thp->doff) - 5) * 4); cp += ((thp->doff) - 5) * 4; } return len; bad: DEBUGP(DSLHC, "slhc_uncompress(): bad packet detected!\n"); comp->sls_i_error++; return slhc_toss( comp ); } int slhc_remember(struct slcompress *comp, unsigned char *icp, int isize) { register struct cstate *cs; unsigned ihl; unsigned char index; if(isize < 20) { /* The packet is shorter than a legal IP header */ comp->sls_i_runt++; DEBUGP(DSLHC, "slhc_remember(): The packet is shorter than a legal IP header ==> slhc_toss()\n"); return slhc_toss( comp ); } /* Peek at the IP header's IHL field to find its length */ ihl = icp[0] & 0xf; if(ihl < 20 / 4){ /* The IP header length field is too small */ comp->sls_i_runt++; DEBUGP(DSLHC, "slhc_remember(): The IP header length field is too small ==> slhc_toss()\n"); return slhc_toss( comp ); } index = icp[9]; icp[9] = IPPROTO_TCP; if (ip_fast_csum(icp, ihl)) { /* Bad IP header checksum; discard */ comp->sls_i_badcheck++; DEBUGP(DSLHC, "slhc_remember(): Bad IP header checksum; discard ==> slhc_toss()\n"); return slhc_toss( comp ); } if(index > comp->rslot_limit) { comp->sls_i_error++; DEBUGP(DSLHC, "slhc_remember(): index > comp->rslot_limit ==> slhc_toss()\n"); return slhc_toss(comp); } /* Update local state */ cs = &comp->rstate[comp->recv_current = index]; comp->flags &=~ SLF_TOSS; memcpy(&cs->cs_ip,icp,20); memcpy(&cs->cs_tcp,icp + ihl*4,20); if (ihl > 5) memcpy(cs->cs_ipopt, icp + sizeof(struct iphdr), (ihl - 5) * 4); if (cs->cs_tcp.doff > 5) memcpy(cs->cs_tcpopt, icp + ihl*4 + sizeof(struct tcphdr), (cs->cs_tcp.doff - 5) * 4); cs->cs_hsize = ihl*2 + cs->cs_tcp.doff*2; /* Put headers back on packet * Neither header checksum is recalculated */ comp->sls_i_uncompressed++; return isize; } int slhc_toss(struct slcompress *comp) { DEBUGP(DSLHC, "slhc_toss(): Reset compression state...\n"); if ( comp == NULLSLCOMPR ) return 0; comp->flags |= SLF_TOSS; return 0; } void slhc_i_status(struct slcompress *comp) { if (comp != NULLSLCOMPR) { DEBUGP(DSLHC, "slhc_i_status(): %d Cmp, %d Uncmp, %d Bad, %d Tossed\n", comp->sls_i_compressed, comp->sls_i_uncompressed, comp->sls_i_error, comp->sls_i_tossed); } } void slhc_o_status(struct slcompress *comp) { if (comp != NULLSLCOMPR) { DEBUGP(DSLHC, "slhc_o_status(): %d Cmp, %d Uncmp, %d AsIs, %d NotTCP %d Searches, %d Misses\n", comp->sls_o_compressed, comp->sls_o_uncompressed, comp->sls_o_tcp, comp->sls_o_nontcp, comp->sls_o_searches, comp->sls_o_misses); } } osmo-sgsn-1.3.0/src/gprs/v42bis.c000066400000000000000000000575061327264017000164760ustar00rootroot00000000000000/* * SpanDSP - a series of DSP components for telephony * * v42bis.c * * Written by Steve Underwood * * Copyright (C) 2005, 2011 Steve Underwood * * All rights reserved. * * This program 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 program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /* THIS IS A WORK IN PROGRESS. IT IS NOT FINISHED. Currently it performs the core compression and decompression functions OK. However, a number of the bells and whistles in V.42bis are incomplete. */ /*! \file */ #include #include #include #include #include #include #include #include #include #include #include #include #define span_log(x,y,msg, ...) DEBUGP(DV42BIS,msg, ##__VA_ARGS__) #define span_log_init(x,y,z) #define span_log_set_protocol(x,y) #define FALSE 0 #define TRUE 1 /* Fixed parameters from the spec. */ /* Character size (bits) */ #define V42BIS_N3 8 /* Number of characters in the alphabet */ #define V42BIS_N4 256 /* Index number of first dictionary entry used to store a string */ #define V42BIS_N5 (V42BIS_N4 + V42BIS_N6) /* Number of control codewords */ #define V42BIS_N6 3 /* V.42bis/9.2 */ #define V42BIS_ESC_STEP 51 /* Compreeibility monitoring parameters for assessing automated switches between transparent and compressed mode */ #define COMPRESSIBILITY_MONITOR (256*V42BIS_N3) #define COMPRESSIBILITY_MONITOR_HYSTERESIS 11 /* Control code words in compressed mode */ enum { V42BIS_ETM = 0, /* Enter transparent mode */ V42BIS_FLUSH = 1, /* Flush data */ V42BIS_STEPUP = 2 /* Step up codeword size */ }; /* Command codes in transparent mode */ enum { V42BIS_ECM = 0, /* Enter compression mode */ V42BIS_EID = 1, /* Escape character in data */ V42BIS_RESET = 2 /* Force reinitialisation */ }; static __inline__ void push_octet(v42bis_comp_state_t *s, int octet) { s->output_buf[s->output_octet_count++] = (uint8_t) octet; if (s->output_octet_count >= s->max_output_len) { s->handler(s->user_data, s->output_buf, s->output_octet_count); s->output_octet_count = 0; } } /*- End of function --------------------------------------------------------*/ static __inline__ void push_octets(v42bis_comp_state_t *s, const uint8_t buf[], int len) { int i; int chunk; i = 0; while ((s->output_octet_count + len - i) >= s->max_output_len) { chunk = s->max_output_len - s->output_octet_count; memcpy(&s->output_buf[s->output_octet_count], &buf[i], chunk); s->handler(s->user_data, s->output_buf, s->max_output_len); s->output_octet_count = 0; i += chunk; } chunk = len - i; if (chunk > 0) { memcpy(&s->output_buf[s->output_octet_count], &buf[i], chunk); s->output_octet_count += chunk; } } /*- End of function --------------------------------------------------------*/ static __inline__ void push_compressed_code(v42bis_comp_state_t *s, int code) { s->bit_buffer |= code << s->bit_count; s->bit_count += s->v42bis_parm_c2; while (s->bit_count >= 8) { push_octet(s, s->bit_buffer & 0xFF); s->bit_buffer >>= 8; s->bit_count -= 8; } } /*- End of function --------------------------------------------------------*/ static __inline__ void push_octet_alignment(v42bis_comp_state_t *s) { if ((s->bit_count & 7)) { s->bit_count += (8 - (s->bit_count & 7)); while (s->bit_count >= 8) { push_octet(s, s->bit_buffer & 0xFF); s->bit_buffer >>= 8; s->bit_count -= 8; } } } /*- End of function --------------------------------------------------------*/ static __inline__ void flush_octets(v42bis_comp_state_t *s) { if (s->output_octet_count > 0) { s->handler(s->user_data, s->output_buf, s->output_octet_count); s->output_octet_count = 0; } } /*- End of function --------------------------------------------------------*/ static void dictionary_init(v42bis_comp_state_t *s) { int i; memset(s->dict, 0, sizeof(s->dict)); for (i = 0; i < V42BIS_N4; i++) s->dict[i + V42BIS_N6].node_octet = i; s->v42bis_parm_c1 = V42BIS_N5; s->v42bis_parm_c2 = V42BIS_N3 + 1; s->v42bis_parm_c3 = V42BIS_N4 << 1; s->last_matched = 0; s->update_at = 0; s->last_added = 0; s->bit_buffer = 0; s->bit_count = 0; s->flushed_length = 0; s->string_length = 0; s->escape_code = 0; s->transparent = TRUE; s->escaped = FALSE; s->compression_performance = COMPRESSIBILITY_MONITOR; } /*- End of function --------------------------------------------------------*/ static uint16_t match_octet(v42bis_comp_state_t *s, uint16_t at, uint8_t octet) { uint16_t e; if (at == 0) return octet + V42BIS_N6; e = s->dict[at].child; while (e) { if (s->dict[e].node_octet == octet) return e; e = s->dict[e].next; } return 0; } /*- End of function --------------------------------------------------------*/ static uint16_t add_octet_to_dictionary(v42bis_comp_state_t *s, uint16_t at, uint8_t octet) { uint16_t newx; uint16_t next; uint16_t e; newx = s->v42bis_parm_c1; s->dict[newx].node_octet = octet; s->dict[newx].parent = at; s->dict[newx].child = 0; s->dict[newx].next = s->dict[at].child; s->dict[at].child = newx; next = newx; /* 6.5 Recovering a dictionary entry to use next */ do { /* 6.5(a) and (b) */ if (++next == s->v42bis_parm_n2) next = V42BIS_N5; } while (s->dict[next].child); /* 6.5(c) We need to reuse a leaf node */ if (s->dict[next].parent) { /* 6.5(d) Detach the leaf node from its parent, and re-use it */ e = s->dict[next].parent; if (s->dict[e].child == next) { s->dict[e].child = s->dict[next].next; } else { e = s->dict[e].child; while (s->dict[e].next != next) e = s->dict[e].next; s->dict[e].next = s->dict[next].next; } } s->v42bis_parm_c1 = next; return newx; } /*- End of function --------------------------------------------------------*/ static void send_string(v42bis_comp_state_t *s) { push_octets(s, s->string, s->string_length); s->string_length = 0; s->flushed_length = 0; } /*- End of function --------------------------------------------------------*/ static void expand_codeword_to_string(v42bis_comp_state_t *s, uint16_t code) { int i; uint16_t p; /* Work out the length */ for (i = 0, p = code; p; i++) p = s->dict[p].parent; s->string_length += i; /* Now expand the known length of string */ i = s->string_length - 1; for (p = code; p; ) { s->string[i--] = s->dict[p].node_octet; p = s->dict[p].parent; } } /*- End of function --------------------------------------------------------*/ static void send_encoded_data(v42bis_comp_state_t *s, uint16_t code) { int i; /* Update compressibility metric */ /* Integrate at the compressed bit rate, and leak at the pre-compression bit rate */ s->compression_performance += (s->v42bis_parm_c2 - s->compression_performance*s->string_length*V42BIS_N3/COMPRESSIBILITY_MONITOR); if (s->transparent) { for (i = 0; i < s->string_length; i++) { push_octet(s, s->string[i]); if (s->string[i] == s->escape_code) { push_octet(s, V42BIS_EID); s->escape_code += V42BIS_ESC_STEP; } } } else { /* Allow for any escape octets in the string */ for (i = 0; i < s->string_length; i++) { if (s->string[i] == s->escape_code) s->escape_code += V42BIS_ESC_STEP; } /* 7.4 Encoding - we now have the longest matchable string, and will need to output the code for it. */ while (code >= s->v42bis_parm_c3) { /* We need to increase the codeword size */ /* 7.4(a) */ push_compressed_code(s, V42BIS_STEPUP); /* 7.4(b) */ s->v42bis_parm_c2++; /* 7.4(c) */ s->v42bis_parm_c3 <<= 1; /* 7.4(d) this might need to be repeated, so we loop */ } /* 7.5 Transfer - output the last state of the string */ push_compressed_code(s, code); } s->string_length = 0; s->flushed_length = 0; } /*- End of function --------------------------------------------------------*/ static void go_compressed(v42bis_state_t *ss) { v42bis_comp_state_t *s; s = &ss->compress; if (!s->transparent) return; span_log(&ss->logging, SPAN_LOG_FLOW, "Changing to compressed mode\n"); /* Switch out of transparent now, between codes. We need to send the octet which did not match, just before switching. */ if (s->last_matched) { s->update_at = s->last_matched; send_encoded_data(s, s->last_matched); s->last_matched = 0; } push_octet(s, s->escape_code); push_octet(s, V42BIS_ECM); s->bit_buffer = 0; s->transparent = FALSE; } /*- End of function --------------------------------------------------------*/ static void go_transparent(v42bis_state_t *ss) { v42bis_comp_state_t *s; s = &ss->compress; if (s->transparent) return; span_log(&ss->logging, SPAN_LOG_FLOW, "Changing to transparent mode\n"); /* Switch into transparent now, between codes, and the unmatched octet should go out in transparent mode, just below */ if (s->last_matched) { s->update_at = s->last_matched; send_encoded_data(s, s->last_matched); s->last_matched = 0; } s->last_added = 0; push_compressed_code(s, V42BIS_ETM); push_octet_alignment(s); s->transparent = TRUE; } /*- End of function --------------------------------------------------------*/ static void monitor_for_mode_change(v42bis_state_t *ss) { v42bis_comp_state_t *s; s = &ss->compress; switch (s->compression_mode) { case V42BIS_COMPRESSION_MODE_DYNAMIC: /* 7.8 Data compressibility test */ if (s->transparent) { if (s->compression_performance < COMPRESSIBILITY_MONITOR - COMPRESSIBILITY_MONITOR_HYSTERESIS) { /* 7.8.1 Transition to compressed mode */ go_compressed(ss); } } else { if (s->compression_performance > COMPRESSIBILITY_MONITOR) { /* 7.8.2 Transition to transparent mode */ go_transparent(ss); } } /* 7.8.3 Reset function - TODO */ break; case V42BIS_COMPRESSION_MODE_ALWAYS: if (s->transparent) go_compressed(ss); break; case V42BIS_COMPRESSION_MODE_NEVER: if (!s->transparent) go_transparent(ss); break; } } /*- End of function --------------------------------------------------------*/ static int v42bis_comp_init(v42bis_comp_state_t *s, int p1, int p2, put_msg_func_t handler, void *user_data, int max_output_len) { memset(s, 0, sizeof(*s)); s->v42bis_parm_n2 = p1; s->v42bis_parm_n7 = p2; s->handler = handler; s->user_data = user_data; s->max_output_len = (max_output_len < V42BIS_MAX_OUTPUT_LENGTH) ? max_output_len : V42BIS_MAX_OUTPUT_LENGTH; s->output_octet_count = 0; dictionary_init(s); return 0; } /*- End of function --------------------------------------------------------*/ static int comp_exit(v42bis_comp_state_t *s) { s->v42bis_parm_n2 = 0; return 0; } /*- End of function --------------------------------------------------------*/ SPAN_DECLARE(int) v42bis_compress(v42bis_state_t *ss, const uint8_t buf[], int len) { v42bis_comp_state_t *s; int i; uint16_t code; s = &ss->compress; if (!s->v42bis_parm_p0) { /* Compression is off - just push the incoming data out */ push_octets(s, buf, len); return 0; } for (i = 0; i < len; ) { /* 6.4 Add the string to the dictionary */ if (s->update_at) { if (match_octet(s, s->update_at, buf[i]) == 0) s->last_added = add_octet_to_dictionary(s, s->update_at, buf[i]); s->update_at = 0; } /* Match string */ while (i < len) { code = match_octet(s, s->last_matched, buf[i]); if (code == 0) { s->update_at = s->last_matched; send_encoded_data(s, s->last_matched); s->last_matched = 0; break; } if (code == s->last_added) { s->last_added = 0; send_encoded_data(s, s->last_matched); s->last_matched = 0; break; } s->last_matched = code; /* 6.3(b) If the string matches a dictionary entry, and the entry is not that entry created by the last invocation of the string matching procedure, then the next character shall be read and appended to the string and this step repeated. */ s->string[s->string_length++] = buf[i++]; /* 6.4(a) The string must not exceed N7 in length */ if (s->string_length + s->flushed_length == s->v42bis_parm_n7) { send_encoded_data(s, s->last_matched); s->last_matched = 0; break; } } monitor_for_mode_change(ss); } return 0; } /*- End of function --------------------------------------------------------*/ SPAN_DECLARE(int) v42bis_compress_flush(v42bis_state_t *ss) { v42bis_comp_state_t *s; int len; s = &ss->compress; if (s->update_at) return 0; if (s->last_matched) { len = s->string_length; send_encoded_data(s, s->last_matched); s->flushed_length += len; } if (!s->transparent) { s->update_at = s->last_matched; s->last_matched = 0; s->flushed_length = 0; push_compressed_code(s, V42BIS_FLUSH); push_octet_alignment(s); } flush_octets(s); return 0; } /*- End of function --------------------------------------------------------*/ SPAN_DECLARE(int) v42bis_decompress(v42bis_state_t *ss, const uint8_t buf[], int len) { v42bis_comp_state_t *s; int i; int j; int yyy; uint16_t code; uint16_t p; uint8_t ch; uint8_t in; s = &ss->decompress; if (!s->v42bis_parm_p0) { /* Compression is off - just push the incoming data out */ push_octets(s, buf, len); return 0; } for (i = 0; i < len; ) { if (s->transparent) { in = buf[i]; if (s->escaped) { /* Command */ s->escaped = FALSE; switch (in) { case V42BIS_ECM: /* Enter compressed mode */ span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_ECM\n"); send_string(s); s->transparent = FALSE; s->update_at = s->last_matched; s->last_matched = 0; i++; continue; case V42BIS_EID: /* Escape symbol */ span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_EID\n"); in = s->escape_code; s->escape_code += V42BIS_ESC_STEP; break; case V42BIS_RESET: /* Reset dictionary */ span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_RESET\n"); /* TODO: */ send_string(s); dictionary_init(s); i++; continue; default: span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_???? - %" PRIu32 "\n", in); return -1; } } else if (in == s->escape_code) { s->escaped = TRUE; i++; continue; } yyy = TRUE; for (j = 0; j < 2 && yyy; j++) { if (s->update_at) { if (match_octet(s, s->update_at, in) == 0) s->last_added = add_octet_to_dictionary(s, s->update_at, in); s->update_at = 0; } code = match_octet(s, s->last_matched, in); if (code == 0) { s->update_at = s->last_matched; send_string(s); s->last_matched = 0; } else if (code == s->last_added) { s->last_added = 0; send_string(s); s->last_matched = 0; } else { s->last_matched = code; s->string[s->string_length++] = in; if (s->string_length + s->flushed_length == s->v42bis_parm_n7) { send_string(s); s->last_matched = 0; } i++; yyy = FALSE; } } } else { /* Get code from input */ while (s->bit_count < s->v42bis_parm_c2 && i < len) { s->bit_buffer |= buf[i++] << s->bit_count; s->bit_count += 8; } if (s->bit_count < s->v42bis_parm_c2) continue; code = s->bit_buffer & ((1 << s->v42bis_parm_c2) - 1); s->bit_buffer >>= s->v42bis_parm_c2; s->bit_count -= s->v42bis_parm_c2; if (code < V42BIS_N6) { /* We have a control code. */ switch (code) { case V42BIS_ETM: /* Enter transparent mode */ span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_ETM\n"); s->bit_count = 0; s->transparent = TRUE; s->last_matched = 0; s->last_added = 0; break; case V42BIS_FLUSH: /* Flush signal */ span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_FLUSH\n"); s->bit_count = 0; break; case V42BIS_STEPUP: /* Increase code word size */ span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_STEPUP\n"); s->v42bis_parm_c2++; s->v42bis_parm_c3 <<= 1; if (s->v42bis_parm_c2 > (s->v42bis_parm_n2 >> 3)) return -1; break; } continue; } /* Regular codeword */ if (code == s->v42bis_parm_c1) return -1; expand_codeword_to_string(s, code); if (s->update_at) { ch = s->string[0]; if ((p = match_octet(s, s->update_at, ch)) == 0) { s->last_added = add_octet_to_dictionary(s, s->update_at, ch); if (code == s->v42bis_parm_c1) return -1; } else if (p == s->last_added) { s->last_added = 0; } } s->update_at = ((s->string_length + s->flushed_length) == s->v42bis_parm_n7) ? 0 : code; /* Allow for any escapes which may be in this string */ for (j = 0; j < s->string_length; j++) { if (s->string[j] == s->escape_code) s->escape_code += V42BIS_ESC_STEP; } send_string(s); } } return 0; } /*- End of function --------------------------------------------------------*/ SPAN_DECLARE(int) v42bis_decompress_flush(v42bis_state_t *ss) { v42bis_comp_state_t *s; int len; s = &ss->decompress; len = s->string_length; send_string(s); s->flushed_length += len; flush_octets(s); return 0; } /*- End of function --------------------------------------------------------*/ SPAN_DECLARE(void) v42bis_compression_control(v42bis_state_t *s, int mode) { s->compress.compression_mode = mode; } /*- End of function --------------------------------------------------------*/ SPAN_DECLARE(v42bis_state_t *) v42bis_init(const void *ctx, v42bis_state_t *s, int negotiated_p0, int negotiated_p1, int negotiated_p2, put_msg_func_t encode_handler, void *encode_user_data, int max_encode_len, put_msg_func_t decode_handler, void *decode_user_data, int max_decode_len) { int ret; if (negotiated_p1 < V42BIS_MIN_DICTIONARY_SIZE || negotiated_p1 > 65535) return NULL; if (negotiated_p2 < V42BIS_MIN_STRING_SIZE || negotiated_p2 > V42BIS_MAX_STRING_SIZE) return NULL; if (s == NULL) { if ((s = (v42bis_state_t *) talloc_zero_size(ctx,sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); span_log_init(&s->logging, SPAN_LOG_NONE, NULL); span_log_set_protocol(&s->logging, "V.42bis"); if ((ret = v42bis_comp_init(&s->compress, negotiated_p1, negotiated_p2, encode_handler, encode_user_data, max_encode_len))) return NULL; if ((ret = v42bis_comp_init(&s->decompress, negotiated_p1, negotiated_p2, decode_handler, decode_user_data, max_decode_len))) { comp_exit(&s->compress); return NULL; } s->compress.v42bis_parm_p0 = negotiated_p0 & 2; s->decompress.v42bis_parm_p0 = negotiated_p0 & 1; return s; } /*- End of function --------------------------------------------------------*/ SPAN_DECLARE(int) v42bis_release(v42bis_state_t *s) { return 0; } /*- End of function --------------------------------------------------------*/ SPAN_DECLARE(int) v42bis_free(v42bis_state_t *s) { comp_exit(&s->compress); comp_exit(&s->decompress); talloc_free(s); return 0; } /*- End of function --------------------------------------------------------*/ /*- End of file ------------------------------------------------------------*/ osmo-sgsn-1.3.0/src/libcommon/000077500000000000000000000000001327264017000162105ustar00rootroot00000000000000osmo-sgsn-1.3.0/src/libcommon/Makefile.am000066400000000000000000000013351327264017000202460ustar00rootroot00000000000000AM_CPPFLAGS = \ $(all_includes) \ -I$(top_srcdir)/include \ -I$(top_builddir) \ $(NULL) AM_CFLAGS = \ -Wall \ $(LIBOSMOCORE_CFLAGS) \ $(LIBOSMOGSM_CFLAGS) \ $(LIBOSMOVTY_CFLAGS) \ $(LIBOSMOABIS_CFLAGS) \ $(COVERAGE_CFLAGS) \ $(NULL) noinst_LIBRARIES = \ libcommon.a \ $(NULL) libcommon_a_SOURCES = \ common_vty.c \ debug.c \ gsm_data.c \ gsm_data_shared.c \ gsup_client.c \ oap_client.c \ socket.c \ talloc_ctx.c \ gsm_subscriber_base.c \ $(NULL) noinst_PROGRAMS = \ gsup_test_client \ $(NULL) gsup_test_client_SOURCES = \ gsup_test_client.c \ $(NULL) gsup_test_client_LDADD = \ libcommon.a \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOGSM_LIBS) \ $(LIBOSMOVTY_LIBS) \ $(LIBOSMOABIS_LIBS) \ -lrt \ $(NULL) osmo-sgsn-1.3.0/src/libcommon/common_vty.c000066400000000000000000000067271327264017000205620ustar00rootroot00000000000000/* OpenBSC VTY common helpers */ /* (C) 2009-2010 by Harald Welte * (C) 2009-2010 by Holger Hans Peter Freyther * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 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 NAT_BSC_NODE: vty->node = NAT_NODE; { struct bsc_config *bsc_config = vty->index; vty->index = bsc_config->nat; } break; case PGROUP_NODE: vty->node = NAT_NODE; vty->index = NULL; break; case TRUNK_NODE: vty->node = MGCP_NODE; vty->index = NULL; break; case SMPP_ESME_NODE: vty->node = SMPP_NODE; vty->index = NULL; break; case SMPP_NODE: case MGCP_NODE: case GBPROXY_NODE: case SGSN_NODE: case NAT_NODE: case BSC_NODE: case MSC_NODE: case MNCC_INT_NODE: case NITB_NODE: vty->node = CONFIG_NODE; vty->index = NULL; break; case SUBSCR_NODE: vty->node = ENABLE_NODE; vty->index = NULL; break; default: osmo_ss7_vty_go_parent(vty); } return vty->node; } 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 SUBSCR_NODE: case CONFIG_NODE: return 0; default: return 1; } } osmo-sgsn-1.3.0/src/libcommon/debug.c000066400000000000000000000142041327264017000174430ustar00rootroot00000000000000/* OpenBSC Debugging/Logging support code */ /* (C) 2008-2010 by Harald Welte * (C) 2008 by Holger Hans Peter Freyther * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 /* default categories */ static const struct log_info_cat default_categories[] = { [DRLL] = { .name = "DRLL", .description = "A-bis Radio Link Layer (RLL)", .color = "\033[1;31m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DCC] = { .name = "DCC", .description = "Layer3 Call Control (CC)", .color = "\033[1;32m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DMM] = { .name = "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 Siganlling 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, }, [DMNCC] = { .name = "DMNCC", .description = "MNCC API for Call Control application", .color = "\033[1;39m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [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, }, [DSCCP] = { .name = "DSCCP", .description = "SCCP Protocol", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DMSC] = { .name = "DMSC", .description = "Mobile Switching Center", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DMGCP] = { .name = "DMGCP", .description = "Media Gateway Control Protocol", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DHO] = { .name = "DHO", .description = "Hand-Over", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DDB] = { .name = "DDB", .description = "Database Layer", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DREF] = { .name = "DREF", .description = "Reference Counting", .enabled = 0, .loglevel = LOGL_NOTICE, }, [DGPRS] = { .name = "DGPRS", .description = "GPRS Packet Service", .enabled = 1, .loglevel = LOGL_DEBUG, }, [DNS] = { .name = "DNS", .description = "GPRS Network Service (NS)", .enabled = 1, .loglevel = LOGL_INFO, }, [DBSSGP] = { .name = "DBSSGP", .description = "GPRS BSS Gateway Protocol (BSSGP)", .enabled = 1, .loglevel = LOGL_DEBUG, }, [DLLC] = { .name = "DLLC", .description = "GPRS Logical Link Control Protocol (LLC)", .enabled = 1, .loglevel = LOGL_DEBUG, }, [DSNDCP] = { .name = "DSNDCP", .description = "GPRS Sub-Network Dependent Control Protocol (SNDCP)", .enabled = 1, .loglevel = LOGL_DEBUG, }, [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, }, [DSMPP] = { .name = "DSMPP", .description = "SMPP interface for external SMS apps", .enabled = 1, .loglevel = LOGL_DEBUG, }, [DFILTER] = { .name = "DFILTER", .description = "BSC/NAT IMSI based filtering", .enabled = 1, .loglevel = LOGL_DEBUG, }, [DRANAP] = { .name = "DRANAP", .description = "Radio Access Network Application Part Protocol", .enabled = 1, .loglevel = LOGL_DEBUG, }, [DSUA] = { .name = "DSUA", .description = "SCCP User Adaptation Protocol", .enabled = 1, .loglevel = LOGL_DEBUG, }, [DPCU] = { .name = "DPCU", .description = "PCU Interface", .enabled = 1, .loglevel = LOGL_DEBUG, }, [DVLR] = { .name = "DVLR", .description = "Visitor Location Register", .enabled = 1, .loglevel = LOGL_DEBUG, }, [DIUCS] = { .name = "DIUCS", .description = "Iu-CS Protocol", .enabled = 1, .loglevel = LOGL_DEBUG, }, [DSIGTRAN] = { .name = "DSIGTRAN", .description = "SIGTRAN Signalling Transport", .color = "\033[1;29m", .enabled = 1, .loglevel = LOGL_DEBUG, }, }; static int filter_fn(const struct log_context *ctx, struct log_target *tar) { const struct vlr_subscr *vsub = ctx->ctx[LOG_CTX_VLR_SUBSCR]; const struct bsc_subscr *bsub = ctx->ctx[LOG_CTX_BSC_SUBSCR]; const struct gprs_nsvc *nsvc = ctx->ctx[LOG_CTX_GB_NSVC]; const struct gprs_nsvc *bvc = ctx->ctx[LOG_CTX_GB_BVC]; if ((tar->filter_map & (1 << LOG_FLT_VLR_SUBSCR)) != 0 && vsub && vsub == tar->filter_data[LOG_FLT_VLR_SUBSCR]) return 1; if ((tar->filter_map & (1 << LOG_FLT_BSC_SUBSCR)) != 0 && bsub && bsub == tar->filter_data[LOG_FLT_BSC_SUBSCR]) return 1; /* Filter on the NS Virtual Connection */ if ((tar->filter_map & (1 << LOG_FLT_GB_NSVC)) != 0 && nsvc && (nsvc == tar->filter_data[LOG_FLT_GB_NSVC])) return 1; /* Filter on the NS Virtual Connection */ if ((tar->filter_map & (1 << LOG_FLT_GB_BVC)) != 0 && bvc && (bvc == tar->filter_data[LOG_FLT_GB_BVC])) return 1; return 0; } const struct log_info log_info = { .filter_fn = filter_fn, .cat = default_categories, .num_cat = ARRAY_SIZE(default_categories), }; osmo-sgsn-1.3.0/src/libcommon/gsm_data.c000066400000000000000000000271051327264017000201400ustar00rootroot00000000000000/* (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 #include #include #include #include #include void *tall_bsc_ctx; 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 auth_policy_names[] = { { GSM_AUTH_POLICY_CLOSED, "closed" }, { GSM_AUTH_POLICY_ACCEPT_ALL, "accept-all" }, { GSM_AUTH_POLICY_TOKEN, "token" }, { GSM_AUTH_POLICY_REGEXP, "regexp" }, { 0, NULL } }; enum gsm_auth_policy gsm_auth_policy_parse(const char *arg) { return get_string_value(auth_policy_names, arg); } const char *gsm_auth_policy_name(enum gsm_auth_policy policy) { return get_value_string(auth_policy_names, policy); } static const struct value_string rrlp_mode_names[] = { { RRLP_MODE_NONE, "none" }, { RRLP_MODE_MS_BASED, "ms-based" }, { RRLP_MODE_MS_PREF, "ms-preferred" }, { RRLP_MODE_ASS_PREF, "ass-preferred" }, { 0, NULL } }; enum rrlp_mode rrlp_mode_parse(const char *arg) { return get_string_value(rrlp_mode_names, arg); } const char *rrlp_mode_name(enum rrlp_mode mode) { return get_value_string(rrlp_mode_names, mode); } 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 && !gsm_btsmodel_has_feature(bts->model, BTS_FEAT_GPRS)) { return 0; } if (mode == BTS_GPRS_EGPRS && !gsm_btsmodel_has_feature(bts->model, BTS_FEAT_EGPRS)) { return 0; } return 1; } int gsm_btsmodel_set_feature(struct gsm_bts_model *model, enum gsm_bts_features feat) { OSMO_ASSERT(_NUM_BTS_FEAT < MAX_BTS_FEATURES); return bitvec_set_bit_pos(&model->features, feat, 1); } bool gsm_btsmodel_has_feature(struct gsm_bts_model *model, enum gsm_bts_features feat) { OSMO_ASSERT(_NUM_BTS_FEAT < MAX_BTS_FEATURES); return bitvec_get_bit_pos(&model->features, feat); } 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->network = net; bts->type = type; bts->model = model; bts->bsic = bsic; 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->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 */ llist_add_tail(&bts->list, &net->bts_list); INIT_LLIST_HEAD(&bts->abis_queue); INIT_LLIST_HEAD(&bts->loc_list); return bts; } void gprs_ra_id_by_bts(struct gprs_ra_id *raid, struct gsm_bts *bts) { raid->mcc = bts->network->country_code; raid->mnc = bts->network->network_code; raid->lac = bts->location_area_code; raid->rac = bts->gprs.rac; } 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; } const struct value_string ran_type_names[] = { OSMO_VALUE_STRING(RAN_UNKNOWN), OSMO_VALUE_STRING(RAN_GERAN_A), OSMO_VALUE_STRING(RAN_UTRAN_IU), { 0, NULL } }; osmo-sgsn-1.3.0/src/libcommon/gsm_data_shared.c000066400000000000000000000533051327264017000214670ustar00rootroot00000000000000/* (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 #include 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_bts_features_descs[] = { { BTS_FEAT_HSCSD, "HSCSD" }, { BTS_FEAT_GPRS, "GPRS" }, { BTS_FEAT_EGPRS, "EGPRS" }, { BTS_FEAT_ECSD, "ECSD" }, { BTS_FEAT_HOPPING, "Frequency Hopping" }, { BTS_FEAT_MULTI_TSC, "Multi-TSC" }, { BTS_FEAT_OML_ALERTS, "OML Alerts" }, { BTS_FEAT_AGCH_PCH_PROP, "AGCH/PCH proportional allocation" }, { BTS_FEAT_CBCH, "CBCH" }, { 0, NULL } }; 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); #ifndef ROLE_BSC INIT_LLIST_HEAD(&lchan->sapi_cmds); #endif } } 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, }; struct gsm_bts *gsm_bts_alloc(void *ctx, uint8_t bts_num) { struct gsm_bts *bts = talloc_zero(ctx, struct gsm_bts); int i; if (!bts) return NULL; bts->nr = bts_num; bts->num_trx = 0; INIT_LLIST_HEAD(&bts->trx_list); 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)); /* create our primary TRX */ bts->c0 = gsm_bts_trx_alloc(bts); if (!bts->c0) { 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; bts->features.data = &bts->_features_data[0]; bts->features.data_len = sizeof(bts->_features_data); /* si handling */ bts->bcch_change_mark = 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 */ 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: #ifdef ROLE_BSC OSMO_ASSERT(lchan_nr == 0); #else /* * FIXME: On octphy and litecell, we hit above assertion (see * Max's comment at https://gerrit.osmocom.org/589 ); disabled * for BTS until this is clarified; remove the #ifdef when it * is fixed. */ #warning "fix caller that passes lchan_nr != 0" #endif 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)); } osmo-sgsn-1.3.0/src/libcommon/gsm_subscriber_base.c000066400000000000000000000037101327264017000223600ustar00rootroot00000000000000/* The concept of a subscriber as seen by the BSC */ /* (C) 2008 by Harald Welte * (C) 2009-2010 by Holger Hans Peter Freyther * (C) 2010 by On-Waves * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include #include #include LLIST_HEAD(active_subscribers); void *tall_subscr_ctx; /* return static buffer with printable name of VLR subscriber */ const char *vlr_subscr_name(struct vlr_subscr *vsub) { static char buf[32]; if (!vsub) return "unknown"; if (vsub->msisdn[0]) snprintf(buf, sizeof(buf), "MSISDN:%s", vsub->msisdn); else if (vsub->imsi[0]) snprintf(buf, sizeof(buf), "IMSI:%s", vsub->imsi); else if (vsub->tmsi != GSM_RESERVED_TMSI) snprintf(buf, sizeof(buf), "TMSI:0x%08x", vsub->tmsi); else if (vsub->tmsi_new != GSM_RESERVED_TMSI) snprintf(buf, sizeof(buf), "TMSI(new):0x%08x", vsub->tmsi_new); else return "unknown"; buf[sizeof(buf)-1] = '\0'; return buf; } const char *vlr_subscr_msisdn_or_name(struct vlr_subscr *vsub) { if (!vsub || !vsub->msisdn[0]) return vlr_subscr_name(vsub); return vsub->msisdn; } osmo-sgsn-1.3.0/src/libcommon/gsup_client.c000066400000000000000000000210431327264017000206700ustar00rootroot00000000000000/* Generic Subscriber Update Protocol client */ /* (C) 2014-2016 by Sysmocom s.f.m.c. GmbH * All Rights Reserved * * Author: Jacob Erlbeck * 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 #include #include extern void *tall_bsc_ctx; static void start_test_procedure(struct gsup_client *gsupc); static void gsup_client_send_ping(struct gsup_client *gsupc) { struct msgb *msg = gsup_client_msgb_alloc(); msg->l2h = msgb_put(msg, 1); msg->l2h[0] = IPAC_MSGT_PING; ipa_msg_push_header(msg, IPAC_PROTO_IPACCESS); ipa_client_conn_send(gsupc->link, msg); } static int gsup_client_connect(struct gsup_client *gsupc) { int rc; if (gsupc->is_connected) return 0; if (osmo_timer_pending(&gsupc->connect_timer)) { LOGP(DLGSUP, LOGL_DEBUG, "GSUP connect: connect timer already running\n"); osmo_timer_del(&gsupc->connect_timer); } if (osmo_timer_pending(&gsupc->ping_timer)) { LOGP(DLGSUP, LOGL_DEBUG, "GSUP connect: ping timer already running\n"); osmo_timer_del(&gsupc->ping_timer); } if (ipa_client_conn_clear_queue(gsupc->link) > 0) LOGP(DLGSUP, LOGL_DEBUG, "GSUP connect: discarded stored messages\n"); rc = ipa_client_conn_open(gsupc->link); if (rc >= 0) { LOGP(DLGSUP, LOGL_NOTICE, "GSUP connecting to %s:%d\n", gsupc->link->addr, gsupc->link->port); return 0; } LOGP(DLGSUP, LOGL_ERROR, "GSUP failed to connect to %s:%d: %s\n", gsupc->link->addr, gsupc->link->port, strerror(-rc)); if (rc == -EBADF || rc == -ENOTSOCK || rc == -EAFNOSUPPORT || rc == -EINVAL) return rc; osmo_timer_schedule(&gsupc->connect_timer, GSUP_CLIENT_RECONNECT_INTERVAL, 0); LOGP(DLGSUP, LOGL_INFO, "Scheduled timer to retry GSUP connect to %s:%d\n", gsupc->link->addr, gsupc->link->port); return 0; } static void connect_timer_cb(void *gsupc_) { struct gsup_client *gsupc = gsupc_; if (gsupc->is_connected) return; gsup_client_connect(gsupc); } static void client_send(struct gsup_client *gsupc, int proto_ext, struct msgb *msg_tx) { ipa_prepend_header_ext(msg_tx, proto_ext); ipa_msg_push_header(msg_tx, IPAC_PROTO_OSMO); ipa_client_conn_send(gsupc->link, msg_tx); /* msg_tx is now queued and will be freed. */ } static void gsup_client_oap_register(struct gsup_client *gsupc) { struct msgb *msg_tx; int rc; rc = oap_client_register(&gsupc->oap_state, &msg_tx); if ((rc < 0) || (!msg_tx)) { LOGP(DLGSUP, LOGL_ERROR, "GSUP OAP set up, but cannot register.\n"); return; } client_send(gsupc, IPAC_PROTO_EXT_OAP, msg_tx); } static void gsup_client_updown_cb(struct ipa_client_conn *link, int up) { struct gsup_client *gsupc = link->data; LOGP(DLGSUP, LOGL_INFO, "GSUP link to %s:%d %s\n", link->addr, link->port, up ? "UP" : "DOWN"); gsupc->is_connected = up; if (up) { start_test_procedure(gsupc); if (gsupc->oap_state.state == OAP_INITIALIZED) gsup_client_oap_register(gsupc); osmo_timer_del(&gsupc->connect_timer); } else { osmo_timer_del(&gsupc->ping_timer); osmo_timer_schedule(&gsupc->connect_timer, GSUP_CLIENT_RECONNECT_INTERVAL, 0); } } static int gsup_client_oap_handle(struct gsup_client *gsupc, struct msgb *msg_rx) { int rc; struct msgb *msg_tx; /* If the oap_state is disabled, this will reject the messages. */ rc = oap_client_handle(&gsupc->oap_state, msg_rx, &msg_tx); msgb_free(msg_rx); if (rc < 0) return rc; if (msg_tx) client_send(gsupc, IPAC_PROTO_EXT_OAP, msg_tx); return 0; } static int gsup_client_read_cb(struct ipa_client_conn *link, struct msgb *msg) { struct ipaccess_head *hh = (struct ipaccess_head *) msg->data; struct ipaccess_head_ext *he = (struct ipaccess_head_ext *) msgb_l2(msg); struct gsup_client *gsupc = (struct gsup_client *)link->data; int rc; struct ipaccess_unit ipa_dev = { /* see gsup_client_create() on const vs non-const */ .unit_name = (char*)gsupc->unit_name, }; OSMO_ASSERT(ipa_dev.unit_name); msg->l2h = &hh->data[0]; rc = ipaccess_bts_handle_ccm(link, &ipa_dev, msg); if (rc < 0) { LOGP(DLGSUP, LOGL_NOTICE, "GSUP received an invalid IPA/CCM message from %s:%d\n", link->addr, link->port); /* Link has been closed */ gsupc->is_connected = 0; msgb_free(msg); return -1; } if (rc == 1) { uint8_t msg_type = *(msg->l2h); /* CCM message */ if (msg_type == IPAC_MSGT_PONG) { LOGP(DLGSUP, LOGL_DEBUG, "GSUP receiving PONG\n"); gsupc->got_ipa_pong = 1; } msgb_free(msg); return 0; } if (hh->proto != IPAC_PROTO_OSMO) goto invalid; if (!he || msgb_l2len(msg) < sizeof(*he)) goto invalid; msg->l2h = &he->data[0]; if (he->proto == IPAC_PROTO_EXT_GSUP) { OSMO_ASSERT(gsupc->read_cb != NULL); gsupc->read_cb(gsupc, msg); /* expecting read_cb() to free msg */ } else if (he->proto == IPAC_PROTO_EXT_OAP) { return gsup_client_oap_handle(gsupc, msg); /* gsup_client_oap_handle frees msg */ } else goto invalid; return 0; invalid: LOGP(DLGSUP, LOGL_NOTICE, "GSUP received an invalid IPA message from %s:%d, size = %d\n", link->addr, link->port, msgb_length(msg)); msgb_free(msg); return -1; } static void ping_timer_cb(void *gsupc_) { struct gsup_client *gsupc = gsupc_; LOGP(DLGSUP, LOGL_INFO, "GSUP ping callback (%s, %s PONG)\n", gsupc->is_connected ? "connected" : "not connected", gsupc->got_ipa_pong ? "got" : "didn't get"); if (gsupc->got_ipa_pong) { start_test_procedure(gsupc); return; } LOGP(DLGSUP, LOGL_NOTICE, "GSUP ping timed out, reconnecting\n"); ipa_client_conn_close(gsupc->link); gsupc->is_connected = 0; gsup_client_connect(gsupc); } static void start_test_procedure(struct gsup_client *gsupc) { osmo_timer_setup(&gsupc->ping_timer, ping_timer_cb, gsupc); gsupc->got_ipa_pong = 0; osmo_timer_schedule(&gsupc->ping_timer, GSUP_CLIENT_PING_INTERVAL, 0); LOGP(DLGSUP, LOGL_DEBUG, "GSUP sending PING\n"); gsup_client_send_ping(gsupc); } struct gsup_client *gsup_client_create(const char *unit_name, const char *ip_addr, unsigned int tcp_port, gsup_client_read_cb_t read_cb, struct oap_client_config *oapc_config) { struct gsup_client *gsupc; int rc; gsupc = talloc_zero(tall_bsc_ctx, struct gsup_client); OSMO_ASSERT(gsupc); /* struct ipaccess_unit has a non-const unit_name, so let's copy to be * able to have a non-const unit_name here as well. To not taint the * public gsup_client API, let's store it in a const char* anyway. */ gsupc->unit_name = talloc_strdup(gsupc, unit_name); OSMO_ASSERT(gsupc->unit_name); /* a NULL oapc_config will mark oap_state disabled. */ rc = oap_client_init(oapc_config, &gsupc->oap_state); if (rc != 0) goto failed; gsupc->link = ipa_client_conn_create(gsupc, /* no e1inp */ NULL, 0, ip_addr, tcp_port, gsup_client_updown_cb, gsup_client_read_cb, /* default write_cb */ NULL, gsupc); if (!gsupc->link) goto failed; osmo_timer_setup(&gsupc->connect_timer, connect_timer_cb, gsupc); rc = gsup_client_connect(gsupc); if (rc < 0) goto failed; gsupc->read_cb = read_cb; return gsupc; failed: gsup_client_destroy(gsupc); return NULL; } void gsup_client_destroy(struct gsup_client *gsupc) { osmo_timer_del(&gsupc->connect_timer); osmo_timer_del(&gsupc->ping_timer); if (gsupc->link) { ipa_client_conn_close(gsupc->link); ipa_client_conn_destroy(gsupc->link); gsupc->link = NULL; } talloc_free(gsupc); } int gsup_client_send(struct gsup_client *gsupc, struct msgb *msg) { if (!gsupc || !gsupc->is_connected) { LOGP(DGPRS, LOGL_ERROR, "GSUP not connected, unable to send %s\n", msgb_hexdump(msg)); msgb_free(msg); return -ENOTCONN; } client_send(gsupc, IPAC_PROTO_EXT_GSUP, msg); return 0; } struct msgb *gsup_client_msgb_alloc(void) { return msgb_alloc_headroom(4000, 64, __func__); } osmo-sgsn-1.3.0/src/libcommon/gsup_test_client.c000066400000000000000000000152461327264017000217370ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include static struct gsup_client *g_gc; /*********************************************************************** * IMSI Operation ***********************************************************************/ static LLIST_HEAD(g_imsi_ops); struct imsi_op_stats { uint32_t num_alloc; uint32_t num_released; uint32_t num_rx_success; uint32_t num_rx_error; uint32_t num_timeout; }; enum imsi_op_type { IMSI_OP_SAI, IMSI_OP_LU, IMSI_OP_ISD, _NUM_IMSI_OP }; static const struct value_string imsi_op_names[] = { { IMSI_OP_SAI, "SAI" }, { IMSI_OP_LU, "LU" }, { IMSI_OP_ISD, "ISD" }, { 0, NULL } }; static struct imsi_op_stats imsi_op_stats[_NUM_IMSI_OP]; struct imsi_op { struct llist_head list; char imsi[17]; enum imsi_op_type type; struct osmo_timer_list timer; }; static struct imsi_op *imsi_op_find(const char *imsi, enum imsi_op_type type) { struct imsi_op *io; llist_for_each_entry(io, &g_imsi_ops, list) { if (!strcmp(io->imsi, imsi) && io->type == type) return io; } return NULL; } static void imsi_op_timer_cb(void *data); static struct imsi_op *imsi_op_alloc(void *ctx, const char *imsi, enum imsi_op_type type) { struct imsi_op *io; if (imsi_op_find(imsi, type)) return NULL; io = talloc_zero(ctx, struct imsi_op); osmo_strlcpy(io->imsi, imsi, sizeof(io->imsi)); io->type = type; osmo_timer_setup(&io->timer, imsi_op_timer_cb, io); llist_add(&io->list, &g_imsi_ops); imsi_op_stats[type].num_alloc++; return io; } static void imsi_op_release(struct imsi_op *io) { osmo_timer_del(&io->timer); llist_del(&io->list); imsi_op_stats[io->type].num_released++; talloc_free(io); } static void imsi_op_timer_cb(void *data) { struct imsi_op *io = data; printf("%s: Timer expiration\n", io->imsi); imsi_op_stats[io->type].num_timeout++; imsi_op_release(io); } /* allocate + generate + send Send-Auth-Info */ int req_auth_info(const char *imsi) { struct imsi_op *io = imsi_op_alloc(g_gc, imsi, IMSI_OP_SAI); struct osmo_gsup_message gsup = {0}; struct msgb *msg = msgb_alloc_headroom(1200, 200, __func__); osmo_strlcpy(gsup.imsi, io->imsi, sizeof(gsup.imsi)); gsup.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST; osmo_gsup_encode(msg, &gsup); return gsup_client_send(g_gc, msg); } /* allocate + generate + send Send-Auth-Info */ int req_loc_upd(const char *imsi) { struct imsi_op *io = imsi_op_alloc(g_gc, imsi, IMSI_OP_LU); struct osmo_gsup_message gsup = {0}; struct msgb *msg = msgb_alloc_headroom(1200, 200, __func__); osmo_strlcpy(gsup.imsi, io->imsi, sizeof(gsup.imsi)); gsup.message_type = OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST; osmo_gsup_encode(msg, &gsup); return gsup_client_send(g_gc, msg); } int resp_isd(struct imsi_op *io) { struct osmo_gsup_message gsup = {0}; struct msgb *msg = msgb_alloc_headroom(1200, 200, __func__); osmo_strlcpy(gsup.imsi, io->imsi, sizeof(gsup.imsi)); gsup.message_type = OSMO_GSUP_MSGT_INSERT_DATA_RESULT; osmo_gsup_encode(msg, &gsup); imsi_op_release(io); return gsup_client_send(g_gc, msg); } /* receive an incoming GSUP message */ static void imsi_op_rx_gsup(struct imsi_op *io, const struct osmo_gsup_message *gsup) { int is_error = 0; if (OSMO_GSUP_IS_MSGT_ERROR(gsup->message_type)) { imsi_op_stats[io->type].num_rx_error++; is_error = 1; } else imsi_op_stats[io->type].num_rx_success++; switch (io->type) { case IMSI_OP_SAI: printf("%s; SAI Response%s\n", io->imsi, is_error ? ": ERROR" : ""); /* now that we have auth tuples, request LU */ req_loc_upd(io->imsi); imsi_op_release(io); break; case IMSI_OP_LU: printf("%s; LU Response%s\n", io->imsi, is_error ? ": ERROR" : ""); imsi_op_release(io); break; case IMSI_OP_ISD: printf("%s; ISD Request%s\n", io->imsi, is_error ? ": ERROR" : ""); resp_isd(io); break; default: printf("%s: Unknown\n", io->imsi); imsi_op_release(io); break; } } static int op_type_by_gsup_msgt(enum osmo_gsup_message_type msg_type) { switch (msg_type) { case OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT: case OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR: return IMSI_OP_SAI; case OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: case OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR: return IMSI_OP_LU; case OSMO_GSUP_MSGT_INSERT_DATA_REQUEST: return IMSI_OP_ISD; default: printf("Unknown GSUP msg_type %u\n", msg_type); return -1; } } static int gsupc_read_cb(struct gsup_client *gsupc, struct msgb *msg) { struct osmo_gsup_message gsup_msg = {0}; struct imsi_op *io; int rc; DEBUGP(DGPRS, "Rx GSUP %s\n", osmo_hexdump(msgb_l2(msg), msgb_l2len(msg))); rc = osmo_gsup_decode(msgb_l2(msg), msgb_l2len(msg), &gsup_msg); if (rc < 0) return rc; if (!gsup_msg.imsi[0]) return -1; rc = op_type_by_gsup_msgt(gsup_msg.message_type); if (rc < 0) return rc; switch (rc) { case IMSI_OP_SAI: case IMSI_OP_LU: io = imsi_op_find(gsup_msg.imsi, rc); if (!io) return -1; break; case IMSI_OP_ISD: /* ISD is an inbound transaction */ io = imsi_op_alloc(g_gc, gsup_msg.imsi, IMSI_OP_ISD); break; } imsi_op_rx_gsup(io, &gsup_msg); msgb_free(msg); return 0; } static void print_report(void) { unsigned int i; for (i = 0; i < ARRAY_SIZE(imsi_op_stats); i++) { struct imsi_op_stats *st = &imsi_op_stats[i]; const char *name = get_value_string(imsi_op_names, i); printf("%s: %u alloc, %u released, %u success, %u error , %u tout\n", name, st->num_alloc, st->num_released, st->num_rx_success, st->num_rx_error, st->num_timeout); } } static void sig_cb(int sig) { switch (sig) { case SIGINT: print_report(); exit(0); break; } } void *tall_bsc_ctx = NULL; /* default categories */ static struct log_info_cat default_categories[] = { }; static const struct log_info gsup_test_client_log_info = { .cat = default_categories, .num_cat = ARRAY_SIZE(default_categories), }; int main(int argc, char **argv) { unsigned long long i; char *server_host = "127.0.0.1"; uint16_t server_port = OSMO_GSUP_PORT; void *ctx = talloc_named_const(NULL, 0, "gsup_test_client"); osmo_init_logging2(ctx, &gsup_test_client_log_info); g_gc = gsup_client_create(ctx, "GSUPTEST", server_host, server_port, gsupc_read_cb, NULL); signal(SIGINT, sig_cb); for (i = 0; i < 10000; i++) { unsigned long long imsi = 901790000000000 + i; char imsi_buf[17]; snprintf(imsi_buf, sizeof(imsi_buf), "%015llu", imsi); req_auth_info(imsi_buf); osmo_select_main(0); } while (1) { osmo_select_main(0); } print_report(); exit(0); } osmo-sgsn-1.3.0/src/libcommon/oap_client.c000066400000000000000000000171111327264017000204720ustar00rootroot00000000000000/* Osmocom Authentication Protocol API */ /* (C) 2015 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 #include int oap_client_init(struct oap_client_config *config, struct oap_client_state *state) { OSMO_ASSERT(state->state == OAP_UNINITIALIZED); if (!config) goto disable; if (config->client_id == 0) goto disable; if (config->secret_k_present == 0) { LOGP(DLOAP, LOGL_NOTICE, "OAP: client ID set, but secret K missing.\n"); goto disable; } if (config->secret_opc_present == 0) { LOGP(DLOAP, LOGL_NOTICE, "OAP: client ID set, but secret OPC missing.\n"); goto disable; } state->client_id = config->client_id; memcpy(state->secret_k, config->secret_k, sizeof(state->secret_k)); memcpy(state->secret_opc, config->secret_opc, sizeof(state->secret_opc)); state->state = OAP_INITIALIZED; return 0; disable: state->state = OAP_DISABLED; return 0; } /* From the given state and received RAND and AUTN octets, validate the * server's authenticity and formulate the matching milenage reply octets in * *tx_xres. The state is not modified. * On success, and if tx_res is not NULL, exactly 8 octets will be written to * *tx_res. If not NULL, tx_res must point at allocated memory of at least 8 * octets. The caller will want to send XRES back to the server in a challenge * response message and update the state. * Return 0 on success; -1 if OAP is disabled; -2 if rx_random and rx_autn fail * the authentication check; -3 for any other errors. */ static int oap_evaluate_challenge(const struct oap_client_state *state, const uint8_t *rx_random, const uint8_t *rx_autn, uint8_t *tx_xres) { struct osmo_auth_vector vec; struct osmo_sub_auth_data auth = { .type = OSMO_AUTH_TYPE_UMTS, .algo = OSMO_AUTH_ALG_MILENAGE, }; osmo_static_assert(sizeof(((struct osmo_sub_auth_data*)0)->u.umts.k) == sizeof(state->secret_k), _secret_k_size_match); osmo_static_assert(sizeof(((struct osmo_sub_auth_data*)0)->u.umts.opc) == sizeof(state->secret_opc), _secret_opc_size_match); switch (state->state) { case OAP_UNINITIALIZED: case OAP_DISABLED: return -1; default: break; } memcpy(auth.u.umts.k, state->secret_k, sizeof(auth.u.umts.k)); memcpy(auth.u.umts.opc, state->secret_opc, sizeof(auth.u.umts.opc)); memset(auth.u.umts.amf, '\0', sizeof(auth.u.umts.amf)); auth.u.umts.sqn = 41; /* TODO use incrementing sequence nr */ memset(&vec, 0, sizeof(vec)); osmo_auth_gen_vec(&vec, &auth, rx_random); if (vec.res_len != 8) { LOGP(DLOAP, LOGL_ERROR, "OAP: Expected XRES to be 8 octets, got %d\n", vec.res_len); return -3; } if (osmo_constant_time_cmp(vec.autn, rx_autn, sizeof(vec.autn)) != 0) { LOGP(DLOAP, LOGL_ERROR, "OAP: AUTN mismatch!\n"); LOGP(DLOAP, LOGL_INFO, "OAP: AUTN from server: %s\n", osmo_hexdump_nospc(rx_autn, sizeof(vec.autn))); LOGP(DLOAP, LOGL_INFO, "OAP: AUTN expected: %s\n", osmo_hexdump_nospc(vec.autn, sizeof(vec.autn))); return -2; } if (tx_xres != NULL) memcpy(tx_xres, vec.res, 8); return 0; } struct msgb *oap_client_encoded(const struct osmo_oap_message *oap_msg) { struct msgb *msg = msgb_alloc_headroom(1000, 64, __func__); OSMO_ASSERT(msg); osmo_oap_encode(msg, oap_msg); return msg; } /* Create a new msgb containing an OAP registration message. * On error, return NULL. */ static struct msgb* oap_msg_register(uint16_t client_id) { struct osmo_oap_message oap_msg = {0}; if (client_id < 1) { LOGP(DLOAP, LOGL_ERROR, "OAP: Invalid client ID: %d\n", client_id); return NULL; } oap_msg.message_type = OAP_MSGT_REGISTER_REQUEST; oap_msg.client_id = client_id; return oap_client_encoded(&oap_msg); } int oap_client_register(struct oap_client_state *state, struct msgb **msg_tx) { *msg_tx = oap_msg_register(state->client_id); if (!(*msg_tx)) return -1; state->state = OAP_REQUESTED_CHALLENGE; return 0; } /* Create a new msgb containing an OAP challenge response message. * xres must point at 8 octets to return as challenge response. * On error, return NULL. */ static struct msgb* oap_msg_challenge_response(uint8_t *xres) { struct osmo_oap_message oap_reply = {0}; oap_reply.message_type = OAP_MSGT_CHALLENGE_RESULT; memcpy(oap_reply.xres, xres, sizeof(oap_reply.xres)); oap_reply.xres_present = 1; return oap_client_encoded(&oap_reply); } static int handle_challenge(struct oap_client_state *state, struct osmo_oap_message *oap_rx, struct msgb **msg_tx) { int rc; uint8_t xres[8]; if (!(oap_rx->rand_present && oap_rx->autn_present)) { LOGP(DLOAP, LOGL_ERROR, "OAP challenge incomplete (rand_present: %d, autn_present: %d)\n", oap_rx->rand_present, oap_rx->autn_present); rc = -2; goto failure; } rc = oap_evaluate_challenge(state, oap_rx->rand, oap_rx->autn, xres); if (rc < 0) goto failure; *msg_tx = oap_msg_challenge_response(xres); if ((*msg_tx) == NULL) { rc = -1; goto failure; } state->state = OAP_SENT_CHALLENGE_RESULT; return 0; failure: OSMO_ASSERT(rc < 0); state->state = OAP_INITIALIZED; return rc; } int oap_client_handle(struct oap_client_state *state, const struct msgb *msg_rx, struct msgb **msg_tx) { uint8_t *data = msgb_l2(msg_rx); size_t data_len = msgb_l2len(msg_rx); struct osmo_oap_message oap_msg = {0}; int rc = 0; *msg_tx = NULL; OSMO_ASSERT(data); rc = osmo_oap_decode(&oap_msg, data, data_len); if (rc < 0) { LOGP(DLOAP, LOGL_ERROR, "Decoding OAP message failed with error '%s' (%d)\n", get_value_string(gsm48_gmm_cause_names, -rc), -rc); return -10; } switch (state->state) { case OAP_UNINITIALIZED: LOGP(DLOAP, LOGL_ERROR, "Received OAP message %d, but the OAP client is" " not initialized\n", oap_msg.message_type); return -ENOTCONN; case OAP_DISABLED: LOGP(DLOAP, LOGL_ERROR, "Received OAP message %d, but the OAP client is" " disabled\n", oap_msg.message_type); return -ENOTCONN; default: break; } switch (oap_msg.message_type) { case OAP_MSGT_CHALLENGE_REQUEST: return handle_challenge(state, &oap_msg, msg_tx); case OAP_MSGT_REGISTER_RESULT: /* successfully registered */ state->state = OAP_REGISTERED; break; case OAP_MSGT_REGISTER_ERROR: LOGP(DLOAP, LOGL_ERROR, "OAP registration failed\n"); state->state = OAP_INITIALIZED; if (state->registration_failures < 3) { state->registration_failures ++; return oap_client_register(state, msg_tx); } return -11; case OAP_MSGT_REGISTER_REQUEST: case OAP_MSGT_CHALLENGE_RESULT: LOGP(DLOAP, LOGL_ERROR, "Received invalid OAP message type for OAP client side: %d\n", (int)oap_msg.message_type); return -12; default: LOGP(DLOAP, LOGL_ERROR, "Unknown OAP message type: %d\n", (int)oap_msg.message_type); return -13; } return 0; } osmo-sgsn-1.3.0/src/libcommon/socket.c000066400000000000000000000052041327264017000176450ustar00rootroot00000000000000/* OpenBSC sokcet code, taken from Abis input driver for ip.access */ /* (C) 2009 by Harald Welte * (C) 2010 by Holger Hans Peter Freyther * (C) 2010 by On-Waves * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int make_sock(struct osmo_fd *bfd, int proto, uint32_t ip, uint16_t port, int priv_nr, int (*cb)(struct osmo_fd *fd, unsigned int what), void *data) { struct sockaddr_in addr; int ret, on = 1; int type = SOCK_STREAM; switch (proto) { case IPPROTO_TCP: type = SOCK_STREAM; break; case IPPROTO_UDP: type = SOCK_DGRAM; break; #ifdef IPPROTO_GRE case IPPROTO_GRE: type = SOCK_RAW; break; #endif default: return -EINVAL; } bfd->fd = socket(AF_INET, type, proto); bfd->cb = cb; bfd->when = BSC_FD_READ; bfd->data = data; bfd->priv_nr = priv_nr; if (bfd->fd < 0) { LOGP(DLINP, LOGL_ERROR, "could not create socket.\n"); return -EIO; } memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); if (ip != INADDR_ANY) addr.sin_addr.s_addr = htonl(ip); else addr.sin_addr.s_addr = INADDR_ANY; setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr)); if (ret < 0) { LOGP(DLINP, LOGL_ERROR, "could not bind socket %s\n", strerror(errno)); close(bfd->fd); return -EIO; } if (proto == IPPROTO_TCP) { ret = listen(bfd->fd, 1); if (ret < 0) { perror("listen"); close(bfd->fd); return ret; } } ret = osmo_fd_register(bfd); if (ret < 0) { perror("register_listen_fd"); close(bfd->fd); return ret; } return 0; } osmo-sgsn-1.3.0/src/libcommon/talloc_ctx.c000066400000000000000000000043231327264017000205120ustar00rootroot00000000000000/* OpenBSC allocation contexts initialization code */ /* (C) 2011-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 extern void *tall_bsc_ctx; extern void *tall_fle_ctx; extern void *tall_locop_ctx; extern void *tall_authciphop_ctx; extern void *tall_gsms_ctx; extern void *tall_subscr_ctx; extern void *tall_sub_req_ctx; extern void *tall_call_ctx; extern void *tall_paging_ctx; extern void *tall_sigh_ctx; extern void *tall_tqe_ctx; extern void *tall_trans_ctx; extern void *tall_map_ctx; extern void *tall_upq_ctx; extern void *tall_ctr_ctx; void talloc_ctx_init(void *ctx_root) { msgb_talloc_ctx_init(ctx_root, 0); tall_fle_ctx = talloc_named_const(ctx_root, 0, "bs11_file_list_entry"); tall_locop_ctx = talloc_named_const(ctx_root, 0, "loc_updating_oper"); tall_authciphop_ctx = talloc_named_const(ctx_root, 0, "auth_ciph_oper"); tall_gsms_ctx = talloc_named_const(ctx_root, 0, "sms"); tall_subscr_ctx = talloc_named_const(ctx_root, 0, "subscriber"); tall_call_ctx = talloc_named_const(ctx_root, 0, "gsm_call"); tall_paging_ctx = talloc_named_const(ctx_root, 0, "paging_request"); tall_sigh_ctx = talloc_named_const(ctx_root, 0, "signal_handler"); tall_tqe_ctx = talloc_named_const(ctx_root, 0, "subch_txq_entry"); tall_trans_ctx = talloc_named_const(ctx_root, 0, "transaction"); tall_map_ctx = talloc_named_const(ctx_root, 0, "trau_map_entry"); tall_upq_ctx = talloc_named_const(ctx_root, 0, "trau_upq_entry"); tall_ctr_ctx = talloc_named_const(ctx_root, 0, "counter"); } osmo-sgsn-1.3.0/tests/000077500000000000000000000000001327264017000146045ustar00rootroot00000000000000osmo-sgsn-1.3.0/tests/Makefile.am000066400000000000000000000042411327264017000166410ustar00rootroot00000000000000SUBDIRS = \ gprs \ gbproxy \ gtphub \ sgsn \ oap \ xid \ sndcp_xid \ slhc \ v42bis \ $(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 \ $(NULL) TESTSUITE = $(srcdir)/testsuite DISTCLEANFILES = \ atconfig \ $(NULL) if ENABLE_EXT_TESTS python-tests: $(BUILT_SOURCES) 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 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-sgsn-1.3.0/tests/atlocal.in000066400000000000000000000002161327264017000165520ustar00rootroot00000000000000enable_sgsn_test='@found_libgtp_and_libcares@' enable_oap_test='@found_libgtp_and_libcares@' enable_gtphub_test='@found_libgtp_and_libcares@' osmo-sgsn-1.3.0/tests/ctrl_test_runner.py000077500000000000000000000140401327264017000205540ustar00rootroot00000000000000#!/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 TestCtrlSGSN(TestCtrlBase): def ctrl_command(self): return ["./src/gprs/osmo-sgsn", "-c", "doc/examples/osmo-sgsn/osmo-sgsn.cfg"] def ctrl_app(self): return (4251, "./src/gprs/osmo-sgsn", "OsmoSGSN", "sgsn") def testListSubscribers(self): # TODO. Add command to mark a subscriber as active r = self.do_get('subscriber-list-active-v1') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'subscriber-list-active-v1') self.assertEquals(r['value'], None) def add_sgsn_test(suite, workdir): if not os.path.isfile(os.path.join(workdir, "src/gprs/osmo-sgsn")): print("Skipping the SGSN test") return test = unittest.TestLoader().loadTestsFromTestCase(TestCtrlSGSN) 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_sgsn_test(suite, workdir) res = unittest.TextTestRunner(verbosity=verbose_level).run(suite) sys.exit(len(res.errors) + len(res.failures)) osmo-sgsn-1.3.0/tests/gbproxy/000077500000000000000000000000001327264017000162765ustar00rootroot00000000000000osmo-sgsn-1.3.0/tests/gbproxy/Makefile.am000066400000000000000000000016701327264017000203360ustar00rootroot00000000000000AM_CPPFLAGS = \ $(all_includes) \ -I$(top_srcdir)/include \ $(NULL) AM_CFLAGS = \ -Wall \ -ggdb3 \ $(LIBOSMOCORE_CFLAGS) \ $(LIBOSMOGSM_CFLAGS) \ $(LIBOSMOABIS_CFLAGS) \ $(NULL) AM_LDFLAGS = \ $(COVERAGE_LDFLAGS) \ $(NULL) EXTRA_DIST = \ gbproxy_test.ok \ $(NULL) noinst_PROGRAMS = \ gbproxy_test \ $(NULL) gbproxy_test_SOURCES = \ gbproxy_test.c \ $(NULL) gbproxy_test_LDFLAGS = \ -Wl,--wrap=osmo_get_rand_id \ $(NULL) gbproxy_test_LDADD = \ $(top_builddir)/src/gprs/gb_proxy.o \ $(top_builddir)/src/gprs/gb_proxy_patch.o \ $(top_builddir)/src/gprs/gb_proxy_peer.o \ $(top_builddir)/src/gprs/gb_proxy_tlli.o \ $(top_builddir)/src/gprs/gprs_gb_parse.o \ $(top_builddir)/src/gprs/gprs_llc_parse.o \ $(top_builddir)/src/gprs/crc24.o \ $(top_builddir)/src/gprs/gprs_utils.o \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOGB_LIBS) \ $(LIBOSMOGSM_LIBS) \ $(LIBOSMOVTY_LIBS) \ $(LIBOSMOABIS_LIBS) \ $(LIBRARY_DL) \ -lrt \ $(NULL) osmo-sgsn-1.3.0/tests/gbproxy/gbproxy_test.c000066400000000000000000004744641327264017000212160ustar00rootroot00000000000000/* test routines for gbproxy * send NS messages to the gbproxy and dumps what happens * (C) 2013 by sysmocom s.f.m.c. GmbH * Author: Jacob Erlbeck */ #undef _GNU_SOURCE #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define REMOTE_BSS_ADDR 0x01020304 #define REMOTE_SGSN_ADDR 0x05060708 #define SGSN_NSEI 0x0100 #define REMOTE_SGSN2_ADDR 0x15161718 #define SGSN2_NSEI 0x0102 #define MATCH_ANY (-1) void *tall_bsc_ctx = NULL; struct gbproxy_config gbcfg = {0}; struct llist_head *received_messages = NULL; /* override, requires '-Wl,--wrap=osmo_get_rand_id' */ int __real_osmo_get_rand_id(uint8_t *data, size_t len); int mock_osmo_get_rand_id(uint8_t *data, size_t len); int (*osmo_get_rand_id_cb)(uint8_t *, size_t) = &mock_osmo_get_rand_id; int __wrap_osmo_get_rand_id(uint8_t *buf, size_t num) { return (*osmo_get_rand_id_cb)(buf, num); } static int rand_seq_num = 0; int mock_osmo_get_rand_id(uint8_t *buf, size_t num) { uint32_t val; OSMO_ASSERT(num == sizeof(val)); val = 0x00dead00 + rand_seq_num; rand_seq_num++; memcpy(buf, &val, num); return 1; } static void cleanup_test() { rand_seq_num = 0; } static int dump_global(FILE *stream, int indent) { unsigned int i; const struct rate_ctr_group_desc *desc; int rc; rc = fprintf(stream, "%*sGbproxy global:\n", indent, ""); if (rc < 0) return rc; desc = gbcfg.ctrg->desc; for (i = 0; i < desc->num_ctr; i++) { struct rate_ctr *ctr = &gbcfg.ctrg->ctr[i]; if (ctr->current) { rc = fprintf(stream, "%*s %s: %llu\n", indent, "", desc->ctr_desc[i].description, (long long)ctr->current); if (rc < 0) return rc; } } return 0; } static int dump_peers(FILE *stream, int indent, time_t now, struct gbproxy_config *cfg) { struct gbproxy_peer *peer; struct gprs_ra_id raid; unsigned int i; const struct rate_ctr_group_desc *desc; int rc; rc = fprintf(stream, "%*sPeers:\n", indent, ""); if (rc < 0) return rc; llist_for_each_entry(peer, &cfg->bts_peers, list) { struct gbproxy_link_info *link_info; struct gbproxy_patch_state *state = &peer->patch_state; gsm48_parse_ra(&raid, peer->ra); rc = fprintf(stream, "%*s NSEI %u, BVCI %u, %sblocked, RAI %s\n", indent, "", peer->nsei, peer->bvci, peer->blocked ? "" : "not ", osmo_rai_name(&raid)); if (rc < 0) return rc; desc = peer->ctrg->desc; for (i = 0; i < desc->num_ctr; i++) { struct rate_ctr *ctr = &peer->ctrg->ctr[i]; if (ctr->current) { rc = fprintf(stream, "%*s %s: %llu\n", indent, "", desc->ctr_desc[i].description, (long long)ctr->current); if (rc < 0) return rc; } } fprintf(stream, "%*s TLLI-Cache: %d\n", indent, "", state->logical_link_count); llist_for_each_entry(link_info, &state->logical_links, list) { char mi_buf[200]; time_t age = now ? now - link_info->timestamp : 0; int stored_msgs = 0; struct llist_head *iter; enum gbproxy_match_id match_id; llist_for_each(iter, &link_info->stored_msgs) stored_msgs++; if (link_info->imsi_len > 0) { snprintf(mi_buf, sizeof(mi_buf), "(invalid)"); gsm48_mi_to_string(mi_buf, sizeof(mi_buf), link_info->imsi, link_info->imsi_len); } else { snprintf(mi_buf, sizeof(mi_buf), "(none)"); } fprintf(stream, "%*s TLLI %08x", indent, "", link_info->tlli.current); if (link_info->tlli.assigned) fprintf(stream, "/%08x", link_info->tlli.assigned); if (link_info->sgsn_tlli.current) { fprintf(stream, " -> %08x", link_info->sgsn_tlli.current); if (link_info->sgsn_tlli.assigned) fprintf(stream, "/%08x", link_info->sgsn_tlli.assigned); } fprintf(stream, ", IMSI %s, AGE %d", mi_buf, (int)age); if (stored_msgs) fprintf(stream, ", STORED %d", stored_msgs); for (match_id = 0; match_id < ARRAY_SIZE(cfg->matches); ++match_id) { if (cfg->matches[match_id].enable && link_info->is_matching[match_id]) { fprintf(stream, ", IMSI matches"); break; } } if (link_info->imsi_acq_pending) fprintf(stream, ", IMSI acquisition in progress"); if (cfg->route_to_sgsn2) fprintf(stream, ", SGSN NSEI %d", link_info->sgsn_nsei); if (link_info->is_deregistered) fprintf(stream, ", DE-REGISTERED"); rc = fprintf(stream, "\n"); if (rc < 0) return rc; } } return 0; } const uint8_t *convert_ra(struct gprs_ra_id *raid) { static struct gsm48_ra_id r; gsm48_encode_ra(&r, raid); return (const uint8_t *)&r; } /* DTAP - Attach Request */ static const unsigned char dtap_attach_req[] = { 0x08, 0x01, 0x02, 0xf5, 0xe0, 0x21, 0x08, 0x02, 0x05, 0xf4, 0xfb, 0xc5, 0x46, 0x79, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, 0x19, 0x18, 0xb3, 0x43, 0x2b, 0x25, 0x96, 0x62, 0x00, 0x60, 0x80, 0x9a, 0xc2, 0xc6, 0x62, 0x00, 0x60, 0x80, 0xba, 0xc8, 0xc6, 0x62, 0x00, 0x60, 0x80, 0x00, }; /* DTAP - Attach Request (invalid RAI) */ static const unsigned char dtap_attach_req2[] = { 0x08, 0x01, 0x02, 0xf5, 0xe0, 0x21, 0x08, 0x02, 0x05, 0xf4, 0xfb, 0x00, 0xbe, 0xef, 0x99, 0x99, 0x99, 0x40, 0x50, 0x60, 0x19, 0x18, 0xb3, 0x43, 0x2b, 0x25, 0x96, 0x62, 0x00, 0x60, 0x80, 0x9a, 0xc2, 0xc6, 0x62, 0x00, 0x60, 0x80, 0xba, 0xc8, 0xc6, 0x62, 0x00, 0x60, 0x80, 0x00, }; /* DTAP - Attach Request (P-TMSI 0x3f32b700) */ static const unsigned char dtap_attach_req3[] = { 0x08, 0x01, 0x02, 0xf5, 0xe0, 0x21, 0x08, 0x02, 0x05, 0xf4, 0xef, 0xe2, 0xb7, 0x00, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, 0x19, 0x18, 0xb3, 0x43, 0x2b, 0x25, 0x96, 0x62, 0x00, 0x60, 0x80, 0x9a, 0xc2, 0xc6, 0x62, 0x00, 0x60, 0x80, 0xba, 0xc8, 0xc6, 0x62, 0x00, 0x60, 0x80, 0x00, }; /* DTAP - Attach Request (IMSI 12131415161718) */ static const unsigned char dtap_attach_req4[] = { 0x08, 0x01, 0x02, 0xf5, 0xe0, 0x21, 0x08, 0x02, 0x08, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, 0x19, 0x18, 0xb3, 0x43, 0x2b, 0x25, 0x96, 0x62, 0x00, 0x60, 0x80, 0x9a, 0xc2, 0xc6, 0x62, 0x00, 0x60, 0x80, 0xba, 0xc8, 0xc6, 0x62, 0x00, 0x60, 0x80, 0x00, }; /* DTAP - Identity Request */ static const unsigned char dtap_identity_req[] = { 0x08, 0x15, 0x01 }; /* DTAP - Identity Response */ static const unsigned char dtap_identity_resp[] = { 0x08, 0x16, 0x08, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18 }; /* DTAP - Identity Response, IMSI 2 */ static const unsigned char dtap_identity2_resp[] = { 0x08, 0x16, 0x08, 0x11, 0x12, 0x99, 0x99, 0x99, 0x16, 0x17, 0x18 }; /* DTAP - Identity Response, IMSI 3 */ static const unsigned char dtap_identity3_resp[] = { 0x08, 0x16, 0x08, 0x11, 0x12, 0x99, 0x99, 0x99, 0x26, 0x27, 0x28 }; /* DTAP - Attach Accept */ static const unsigned char dtap_attach_acc[] = { 0x08, 0x02, 0x01, 0x49, 0x04, 0x21, 0x63, 0x54, 0x40, 0x50, 0x60, 0x19, 0xcd, 0xd7, 0x08, 0x17, 0x16, 0x18, 0x05, 0xf4, 0xef, 0xe2, 0xb7, 0x00 }; /* DTAP - Attach Accept, P-TMSI 2 */ static const unsigned char dtap_attach_acc2[] = { 0x08, 0x02, 0x01, 0x49, 0x04, 0x21, 0x63, 0x54, 0x40, 0x50, 0x60, 0x19, 0xcd, 0xd7, 0x08, 0x17, 0x16, 0x18, 0x05, 0xf4, 0xe0, 0x98, 0x76, 0x54 }; /* DTAP - Attach Complete */ static const unsigned char dtap_attach_complete[] = { 0x08, 0x03 }; /* DTAP - Attach Reject (GPRS services not allowed) */ static const unsigned char dtap_attach_rej7[] = { 0x08, 0x04, 0x07 }; /* DTAP - GMM Information */ static const unsigned char dtap_gmm_information[] = { 0x08, 0x21 }; /* DTAP - Routing Area Update Request */ static const unsigned char dtap_ra_upd_req[] = { 0x08, 0x08, 0x10, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, 0x1d, 0x19, 0x13, 0x42, 0x33, 0x57, 0x2b, 0xf7, 0xc8, 0x48, 0x02, 0x13, 0x48, 0x50, 0xc8, 0x48, 0x02, 0x14, 0x48, 0x50, 0xc8, 0x48, 0x02, 0x17, 0x49, 0x10, 0xc8, 0x48, 0x02, 0x00, 0x19, 0x8b, 0xb2, 0x92, 0x17, 0x16, 0x27, 0x07, 0x04, 0x31, 0x02, 0xe5, 0xe0, 0x32, 0x02, 0x20, 0x00 }; /* DTAP - Routing Area Update Accept */ static const unsigned char dtap_ra_upd_acc[] = { 0x08, 0x09, 0x00, 0x49, 0x21, 0x63, 0x54, 0x40, 0x50, 0x60, 0x19, 0x54, 0xab, 0xb3, 0x18, 0x05, 0xf4, 0xef, 0xe2, 0xb7, 0x00, 0x17, 0x16, }; /* DTAP - Routing Area Update Accept, P-TMSI 2 */ static const unsigned char dtap_ra_upd_acc2[] = { 0x08, 0x09, 0x00, 0x49, 0x21, 0x63, 0x54, 0x40, 0x50, 0x60, 0x19, 0x54, 0xab, 0xb3, 0x18, 0x05, 0xf4, 0xe0, 0x98, 0x76, 0x54, 0x17, 0x16, }; /* DTAP - Routing Area Update Accept, P-TMSI 3 */ static const unsigned char dtap_ra_upd_acc3[] = { 0x08, 0x09, 0x00, 0x49, 0x21, 0x63, 0x54, 0x40, 0x50, 0x60, 0x19, 0x54, 0xab, 0xb3, 0x18, 0x05, 0xf4, 0xe0, 0x54, 0x32, 0x10, 0x17, 0x16, }; /* DTAP - Routing Area Update Complete */ static const unsigned char dtap_ra_upd_complete[] = { 0x08, 0x0a }; /* DTAP - Routing Area Update Reject */ /* cause = 10 ("Implicitly detached"), force_standby = 0 */ static const unsigned char dtap_ra_upd_rej[] = { 0x08, 0x0b, 0x0a, 0x00, }; /* DTAP - Activate PDP Context Request */ static const unsigned char dtap_act_pdp_ctx_req[] = { 0x0a, 0x41, 0x05, 0x03, 0x0c, 0x00, 0x00, 0x1f, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x21, 0x28, 0x03, 0x02, 0x61, 0x62, 0x27, 0x14, 0x80, 0x80, 0x21, 0x10, 0x01, 0x00, 0x00, 0x10, 0x81, 0x06, 0x00, 0x00, 0x00, 0x00, 0x83, 0x06, 0x00, 0x00, 0x00, 0x00 }; /* DTAP - Detach Request (MO) */ /* normal detach, power_off = 1 */ static const unsigned char dtap_detach_po_req[] = { 0x08, 0x05, 0x09, 0x18, 0x05, 0xf4, 0xef, 0xe2, 0xb7, 0x00, 0x19, 0x03, 0xb9, 0x97, 0xcb }; /* DTAP - Detach Request (MO) */ /* normal detach, power_off = 0 */ static const unsigned char dtap_detach_req[] = { 0x08, 0x05, 0x01, 0x18, 0x05, 0xf4, 0xef, 0xe2, 0xb7, 0x00, 0x19, 0x03, 0xb9, 0x97, 0xcb }; /* DTAP - Detach Accept (MO) */ static const unsigned char dtap_detach_acc[] = { 0x08, 0x06, 0x00 }; /* DTAP - Detach Request (MT) */ /* normal detach, reattach required, implicitly detached */ static const unsigned char dtap_mt_detach_rea_req[] = { 0x08, 0x05, 0x01, 0x25, 0x0a }; /* DTAP - Detach Request (MT) */ /* normal detach, reattach not required, implicitly detached */ static const unsigned char dtap_mt_detach_req[] = { 0x08, 0x05, 0x02, 0x25, 0x0a }; /* DTAP - Detach Accept (MT) */ static const unsigned char dtap_mt_detach_acc[] = { 0x08, 0x06 }; /* GPRS-LLC - SAPI: LLGMM, U, XID */ static const unsigned char llc_u_xid_ul[] = { 0x41, 0xfb, 0x01, 0x00, 0x0e, 0x00, 0x64, 0x11, 0x05, 0x16, 0x01, 0x90, 0x66, 0xb3, 0x28 }; /* GPRS-LLC - SAPI: LLGMM, U, XID */ static const unsigned char llc_u_xid_dl[] = { 0x41, 0xfb, 0x30, 0x84, 0x10, 0x61, 0xb6, 0x64, 0xe4, 0xa9, 0x1a, 0x9e }; /* GPRS-LLC - SAPI: LL11, UI, NSAPI 5, DNS query */ static const unsigned char llc_ui_ll11_dns_query_ul[] = { 0x0b, 0xc0, 0x01, 0x65, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x38, 0x95, 0x72, 0x00, 0x00, 0x45, 0x11, 0x20, 0x85, 0x0a, 0xc0, 0x07, 0xe4, 0xac, 0x10, 0x01, 0x0a, 0xad, 0xab, 0x00, 0x35, 0x00, 0x24, 0x0e, 0x1c, 0x3b, 0xe0, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x6d, 0x05, 0x68, 0x65, 0x69, 0x73, 0x65, 0x02, 0x64, 0x65, 0x00, 0x00, 0x01, 0x00, 0x01, 0x47, 0x8f, 0x07 }; /* GPRS-LLC - SAPI: LL11, UI, NSAPI 5, DNS query */ static const unsigned char llc_ui_ll11_dns_resp_dl[] = { 0x4b, 0xc0, 0x01, 0x65, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0xc6, 0x00, 0x00, 0x40, 0x00, 0x3e, 0x11, 0x7c, 0x69, 0xac, 0x10, 0x01, 0x0a, 0x0a, 0xc0, 0x07, 0xe4, 0x00, 0x35, 0xad, 0xab, 0x00, 0xb2, 0x74, 0x4e, 0x3b, 0xe0, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x05, 0x00, 0x00, 0x01, 0x6d, 0x05, 0x68, 0x65, 0x69, 0x73, 0x65, 0x02, 0x64, 0x65, 0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x10, 0x00, 0x04, 0xc1, 0x63, 0x90, 0x58, 0xc0, 0x0e, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x10, 0x00, 0x16, 0x03, 0x6e, 0x73, 0x32, 0x0c, 0x70, 0x6f, 0x70, 0x2d, 0x68, 0x61, 0x6e, 0x6e, 0x6f, 0x76, 0x65, 0x72, 0x03, 0x6e, 0x65, 0x74, 0x00, 0xc0, 0x0e, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x10, 0x00, 0x10, 0x02, 0x6e, 0x73, 0x01, 0x73, 0x08, 0x70, 0x6c, 0x75, 0x73, 0x6c, 0x69, 0x6e, 0x65, 0xc0, 0x14, 0xc0, 0x0e, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x10, 0x00, 0x05, 0x02, 0x6e, 0x73, 0xc0, 0x0e, 0xc0, 0x0e, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x10, 0x00, 0x05, 0x02, 0x6e, 0x73, 0xc0, 0x5f, 0xc0, 0x0e, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x10, 0x00, 0x12, 0x02, 0x6e, 0x73, 0x0c, 0x70, 0x6f, 0x70, 0x2d, 0x68, 0x61, 0x6e, 0x6e, 0x6f, 0x76, 0x65, 0x72, 0xc0, 0x14, 0xaa, 0xdf, 0x31 }; static int gprs_process_message(struct gprs_ns_inst *nsi, const char *text, struct sockaddr_in *peer, const unsigned char* data, size_t data_len); static void send_ns_reset(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr, enum ns_cause cause, uint16_t nsvci, uint16_t nsei) { /* GPRS Network Service, PDU type: NS_RESET, */ unsigned char msg[12] = { 0x02, 0x00, 0x81, 0x01, 0x01, 0x82, 0x11, 0x22, 0x04, 0x82, 0x11, 0x22 }; msg[3] = cause; msg[6] = nsvci / 256; msg[7] = nsvci % 256; msg[10] = nsei / 256; msg[11] = nsei % 256; gprs_process_message(nsi, "RESET", src_addr, msg, sizeof(msg)); } static void send_ns_reset_ack(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr, uint16_t nsvci, uint16_t nsei) { /* GPRS Network Service, PDU type: NS_RESET_ACK, */ unsigned char msg[9] = { 0x03, 0x01, 0x82, 0x11, 0x22, 0x04, 0x82, 0x11, 0x22 }; msg[3] = nsvci / 256; msg[4] = nsvci % 256; msg[7] = nsei / 256; msg[8] = nsei % 256; gprs_process_message(nsi, "RESET_ACK", src_addr, msg, sizeof(msg)); } static void send_ns_alive(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr) { /* GPRS Network Service, PDU type: NS_ALIVE */ unsigned char msg[1] = { 0x0a }; gprs_process_message(nsi, "ALIVE", src_addr, msg, sizeof(msg)); } static void send_ns_alive_ack(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr) { /* GPRS Network Service, PDU type: NS_ALIVE_ACK */ unsigned char msg[1] = { 0x0b }; gprs_process_message(nsi, "ALIVE_ACK", src_addr, msg, sizeof(msg)); } static void send_ns_unblock(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr) { /* GPRS Network Service, PDU type: NS_UNBLOCK */ unsigned char msg[1] = { 0x06 }; gprs_process_message(nsi, "UNBLOCK", src_addr, msg, sizeof(msg)); } static void send_ns_unblock_ack(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr) { /* GPRS Network Service, PDU type: NS_UNBLOCK_ACK */ unsigned char msg[1] = { 0x07 }; gprs_process_message(nsi, "UNBLOCK_ACK", src_addr, msg, sizeof(msg)); } static void send_ns_unitdata(struct gprs_ns_inst *nsi, const char *text, struct sockaddr_in *src_addr, uint16_t nsbvci, const unsigned char *bssgp_msg, size_t bssgp_msg_size) { /* GPRS Network Service, PDU type: NS_UNITDATA */ unsigned char msg[4096] = { 0x00, 0x00, 0x00, 0x00 }; OSMO_ASSERT(bssgp_msg_size <= sizeof(msg) - 4); msg[2] = nsbvci / 256; msg[3] = nsbvci % 256; memcpy(msg + 4, bssgp_msg, bssgp_msg_size); gprs_process_message(nsi, text ? text : "UNITDATA", src_addr, msg, bssgp_msg_size + 4); } static void send_bssgp_ul_unitdata( struct gprs_ns_inst *nsi, const char *text, struct sockaddr_in *src_addr, uint16_t nsbvci, uint32_t tlli, struct gprs_ra_id *raid, uint16_t cell_id, const uint8_t *llc_msg, size_t llc_msg_size) { /* GPRS Network Service, PDU type: NS_UNITDATA */ /* Base Station Subsystem GPRS Protocol: UL_UNITDATA */ unsigned char msg[4096] = { 0x01, /* TLLI */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 0x88, /* RAI */ 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, /* CELL ID */ 0x00, 0x00, 0x00, 0x80, 0x0e, /* LLC LEN */ 0x00, 0x00, }; size_t bssgp_msg_size = 23 + llc_msg_size; OSMO_ASSERT(bssgp_msg_size <= sizeof(msg)); gsm48_encode_ra((struct gsm48_ra_id *)(msg + 10), raid); msg[1] = (uint8_t)(tlli >> 24); msg[2] = (uint8_t)(tlli >> 16); msg[3] = (uint8_t)(tlli >> 8); msg[4] = (uint8_t)(tlli >> 0); msg[16] = cell_id / 256; msg[17] = cell_id % 256; msg[21] = llc_msg_size / 256; msg[22] = llc_msg_size % 256; memcpy(msg + 23, llc_msg, llc_msg_size); send_ns_unitdata(nsi, text ? text : "BSSGP UL UNITDATA", src_addr, nsbvci, msg, bssgp_msg_size); } static void send_bssgp_dl_unitdata( struct gprs_ns_inst *nsi, const char *text, struct sockaddr_in *src_addr, uint16_t nsbvci, uint32_t tlli, int with_racap_drx, const uint8_t *imsi, size_t imsi_size, const uint8_t *llc_msg, size_t llc_msg_size) { /* Base Station Subsystem GPRS Protocol: DL_UNITDATA */ unsigned char msg[4096] = { 0x00, /* TLLI */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x20, 0x16, 0x82, 0x02, 0x58, }; unsigned char racap_drx[] = { 0x13, 0x99, 0x18, 0xb3, 0x43, 0x2b, 0x25, 0x96, 0x62, 0x00, 0x60, 0x80, 0x9a, 0xc2, 0xc6, 0x62, 0x00, 0x60, 0x80, 0xba, 0xc8, 0xc6, 0x62, 0x00, 0x60, 0x80, 0x00, 0x0a, 0x82, 0x08, 0x02 }; size_t bssgp_msg_size = 0; OSMO_ASSERT(51 + imsi_size + llc_msg_size <= sizeof(msg)); msg[1] = (uint8_t)(tlli >> 24); msg[2] = (uint8_t)(tlli >> 16); msg[3] = (uint8_t)(tlli >> 8); msg[4] = (uint8_t)(tlli >> 0); bssgp_msg_size = 12; if (with_racap_drx) { memcpy(msg + bssgp_msg_size, racap_drx, sizeof(racap_drx)); bssgp_msg_size += sizeof(racap_drx); } if (imsi) { OSMO_ASSERT(imsi_size <= 127); msg[bssgp_msg_size] = BSSGP_IE_IMSI; msg[bssgp_msg_size + 1] = 0x80 | imsi_size; memcpy(msg + bssgp_msg_size + 2, imsi, imsi_size); bssgp_msg_size += 2 + imsi_size; } if ((bssgp_msg_size % 4) != 0) { size_t abytes = (4 - (bssgp_msg_size + 2) % 4) % 4; msg[bssgp_msg_size] = BSSGP_IE_ALIGNMENT; msg[bssgp_msg_size + 1] = 0x80 | abytes; memset(msg + bssgp_msg_size + 2, 0, abytes); bssgp_msg_size += 2 + abytes; } msg[bssgp_msg_size] = BSSGP_IE_LLC_PDU; if (llc_msg_size < 128) { msg[bssgp_msg_size + 1] = 0x80 | llc_msg_size; bssgp_msg_size += 2; } else { msg[bssgp_msg_size + 1] = llc_msg_size / 256; msg[bssgp_msg_size + 2] = llc_msg_size % 256; bssgp_msg_size += 3; } memcpy(msg + bssgp_msg_size, llc_msg, llc_msg_size); bssgp_msg_size += llc_msg_size; send_ns_unitdata(nsi, text ? text : "BSSGP DL UNITDATA", src_addr, nsbvci, msg, bssgp_msg_size); } static void send_bssgp_reset(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr, uint16_t bvci) { /* GPRS Network Service, PDU type: NS_UNITDATA, BVCI 0 * BSSGP RESET */ unsigned char msg[18] = { 0x22, 0x04, 0x82, 0x4a, 0x2e, 0x07, 0x81, 0x08, 0x08, 0x88, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, 0x10, 0x00 }; msg[3] = bvci / 256; msg[4] = bvci % 256; send_ns_unitdata(nsi, "BVC_RESET", src_addr, 0, msg, sizeof(msg)); } static void send_bssgp_reset_ack(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr, uint16_t bvci) { /* GPRS Network Service, PDU type: NS_UNITDATA, BVCI 0 * BSSGP RESET_ACK */ static unsigned char msg[5] = { 0x23, 0x04, 0x82, 0x00, 0x00 }; msg[3] = bvci / 256; msg[4] = bvci % 256; send_ns_unitdata(nsi, "BVC_RESET_ACK", src_addr, 0, msg, sizeof(msg)); } static void send_bssgp_suspend(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr, uint32_t tlli, struct gprs_ra_id *raid) { /* Base Station Subsystem GPRS Protocol, BSSGP SUSPEND */ unsigned char msg[15] = { 0x0b, 0x1f, 0x84, /* TLLI */ 0xff, 0xff, 0xff, 0xff, 0x1b, 0x86, /* RAI */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; msg[3] = (uint8_t)(tlli >> 24); msg[4] = (uint8_t)(tlli >> 16); msg[5] = (uint8_t)(tlli >> 8); msg[6] = (uint8_t)(tlli >> 0); gsm48_encode_ra((struct gsm48_ra_id *)(msg + 9), raid); send_ns_unitdata(nsi, "BVC_SUSPEND", src_addr, 0, msg, sizeof(msg)); } static void send_bssgp_suspend_ack(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr, uint32_t tlli, struct gprs_ra_id *raid) { /* Base Station Subsystem GPRS Protocol, BSSGP SUSPEND ACK */ unsigned char msg[18] = { 0x0c, 0x1f, 0x84, /* TLLI */ 0xff, 0xff, 0xff, 0xff, 0x1b, 0x86, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1d, 0x81, 0x01 }; msg[3] = (uint8_t)(tlli >> 24); msg[4] = (uint8_t)(tlli >> 16); msg[5] = (uint8_t)(tlli >> 8); msg[6] = (uint8_t)(tlli >> 0); gsm48_encode_ra((struct gsm48_ra_id *)(msg + 9), raid); send_ns_unitdata(nsi, "BVC_SUSPEND_ACK", src_addr, 0, msg, sizeof(msg)); } static void send_bssgp_llc_discarded(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr, uint16_t bvci, uint32_t tlli, unsigned n_frames, unsigned n_octets) { /* Base Station Subsystem GPRS Protocol: LLC-DISCARDED (0x2c) */ unsigned char msg[] = { 0x2c, 0x1f, 0x84, /* TLLI */ 0xff, 0xff, 0xff, 0xff, 0x0f, 0x81, /* n frames */ 0xff, 0x04, 0x82, /* BVCI */ 0xff, 0xff, 0x25, 0x83, /* n octets */ 0xff, 0xff, 0xff }; msg[3] = (uint8_t)(tlli >> 24); msg[4] = (uint8_t)(tlli >> 16); msg[5] = (uint8_t)(tlli >> 8); msg[6] = (uint8_t)(tlli >> 0); msg[9] = (uint8_t)(n_frames); msg[12] = (uint8_t)(bvci >> 8); msg[13] = (uint8_t)(bvci >> 0); msg[16] = (uint8_t)(n_octets >> 16); msg[17] = (uint8_t)(n_octets >> 8); msg[18] = (uint8_t)(n_octets >> 0); send_ns_unitdata(nsi, "LLC_DISCARDED", src_addr, 0, msg, sizeof(msg)); } static void send_bssgp_paging(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr, const uint8_t *imsi, size_t imsi_size, struct gprs_ra_id *raid, uint32_t ptmsi) { /* Base Station Subsystem GPRS Protocol, BSSGP SUSPEND */ unsigned char msg[100] = { 0x06, }; const unsigned char drx_ie[] = {0x0a, 0x82, 0x07, 0x04}; const unsigned char qos_ie[] = {0x18, 0x83, 0x00, 0x00, 0x00}; size_t bssgp_msg_size = 1; if (imsi) { OSMO_ASSERT(imsi_size <= 127); msg[bssgp_msg_size] = BSSGP_IE_IMSI; msg[bssgp_msg_size + 1] = 0x80 | imsi_size; memcpy(msg + bssgp_msg_size + 2, imsi, imsi_size); bssgp_msg_size += 2 + imsi_size; } memcpy(msg + bssgp_msg_size, drx_ie, sizeof(drx_ie)); bssgp_msg_size += sizeof(drx_ie); if (raid) { msg[bssgp_msg_size] = BSSGP_IE_ROUTEING_AREA; msg[bssgp_msg_size+1] = 0x86; gsm48_encode_ra((struct gsm48_ra_id *)(msg + bssgp_msg_size + 2), raid); bssgp_msg_size += 8; } memcpy(msg + bssgp_msg_size, qos_ie, sizeof(qos_ie)); bssgp_msg_size += sizeof(qos_ie); if (ptmsi != GSM_RESERVED_TMSI) { const uint32_t ptmsi_be = htonl(ptmsi); msg[bssgp_msg_size] = BSSGP_IE_TMSI; msg[bssgp_msg_size+1] = 0x84; memcpy(msg + bssgp_msg_size + 2, &ptmsi_be, 4); bssgp_msg_size += 6; } send_ns_unitdata(nsi, "PAGING_PS", src_addr, 0, msg, bssgp_msg_size); } static void send_bssgp_flow_control_bvc(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr, uint16_t bvci, uint8_t tag) { /* GPRS Network Service, PDU type: NS_UNITDATA, * BSSGP FLOW_CONTROL_BVC */ unsigned char msg[] = { 0x26, 0x1e, 0x81, /* Tag */ 0xff, 0x05, 0x82, 0x01, 0xdc, 0x03, 0x82, 0x02, 0x76, 0x01, 0x82, 0x00, 0x50, 0x1c, 0x82, 0x02, 0x58, 0x06, 0x82, 0x00, 0x03 }; msg[3] = tag; send_ns_unitdata(nsi, "FLOW_CONTROL_BVC", src_addr, bvci, msg, sizeof(msg)); } static void send_bssgp_flow_control_bvc_ack(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr, uint16_t bvci, uint8_t tag) { /* GPRS Network Service, PDU type: NS_UNITDATA, * BSSGP FLOW_CONTROL_BVC_ACK */ unsigned char msg[] = { 0x27, 0x1e, 0x81, /* Tag */ 0xce }; msg[3] = tag; send_ns_unitdata(nsi, "FLOW_CONTROL_BVC_ACK", src_addr, bvci, msg, sizeof(msg)); } static void send_llc_ul_ui( struct gprs_ns_inst *nsi, const char *text, struct sockaddr_in *src_addr, uint16_t nsbvci, uint32_t tlli, struct gprs_ra_id *raid, uint16_t cell_id, unsigned sapi, unsigned nu, const uint8_t *msg, size_t msg_size) { unsigned char llc_msg[4096] = { 0x00, 0xc0, 0x01 }; size_t llc_msg_size = 3 + msg_size + 3; uint8_t e_bit = 0; uint8_t pm_bit = 1; unsigned fcs; nu &= 0x01ff; OSMO_ASSERT(llc_msg_size <= sizeof(llc_msg)); llc_msg[0] = (sapi & 0x0f); llc_msg[1] = 0xc0 | (nu >> 6); /* UI frame */ llc_msg[2] = (nu << 2) | ((e_bit & 1) << 1) | (pm_bit & 1); memcpy(llc_msg + 3, msg, msg_size); fcs = gprs_llc_fcs(llc_msg, msg_size + 3); llc_msg[3 + msg_size + 0] = (uint8_t)(fcs >> 0); llc_msg[3 + msg_size + 1] = (uint8_t)(fcs >> 8); llc_msg[3 + msg_size + 2] = (uint8_t)(fcs >> 16); send_bssgp_ul_unitdata(nsi, text ? text : "LLC UI", src_addr, nsbvci, tlli, raid, cell_id, llc_msg, llc_msg_size); } static void send_llc_dl_ui( struct gprs_ns_inst *nsi, const char *text, struct sockaddr_in *src_addr, uint16_t nsbvci, uint32_t tlli, int with_racap_drx, const uint8_t *imsi, size_t imsi_size, unsigned sapi, unsigned nu, const uint8_t *msg, size_t msg_size) { /* GPRS Network Service, PDU type: NS_UNITDATA */ /* Base Station Subsystem GPRS Protocol: UL_UNITDATA */ unsigned char llc_msg[4096] = { 0x00, 0x00, 0x01 }; size_t llc_msg_size = 3 + msg_size + 3; uint8_t e_bit = 0; uint8_t pm_bit = 1; unsigned fcs; nu &= 0x01ff; OSMO_ASSERT(llc_msg_size <= sizeof(llc_msg)); llc_msg[0] = 0x40 | (sapi & 0x0f); llc_msg[1] = 0xc0 | (nu >> 6); /* UI frame */ llc_msg[2] = (nu << 2) | ((e_bit & 1) << 1) | (pm_bit & 1); memcpy(llc_msg + 3, msg, msg_size); fcs = gprs_llc_fcs(llc_msg, msg_size + 3); llc_msg[3 + msg_size + 0] = (uint8_t)(fcs >> 0); llc_msg[3 + msg_size + 1] = (uint8_t)(fcs >> 8); llc_msg[3 + msg_size + 2] = (uint8_t)(fcs >> 16); send_bssgp_dl_unitdata(nsi, text ? text : "LLC UI", src_addr, nsbvci, tlli, with_racap_drx, imsi, imsi_size, llc_msg, llc_msg_size); } static void setup_ns(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr, uint16_t nsvci, uint16_t nsei) { printf("Setup NS-VC: remote 0x%08x:%d, " "NSVCI 0x%04x(%d), NSEI 0x%04x(%d)\n\n", ntohl(src_addr->sin_addr.s_addr), ntohs(src_addr->sin_port), nsvci, nsvci, nsei, nsei); send_ns_reset(nsi, src_addr, NS_CAUSE_OM_INTERVENTION, nsvci, nsei); send_ns_alive(nsi, src_addr); send_ns_unblock(nsi, src_addr); send_ns_alive_ack(nsi, src_addr); } static void setup_bssgp(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr, uint16_t bvci) { printf("Setup BSSGP: remote 0x%08x:%d, " "BVCI 0x%04x(%d)\n\n", ntohl(src_addr->sin_addr.s_addr), ntohs(src_addr->sin_port), bvci, bvci); send_bssgp_reset(nsi, src_addr, bvci); } static void connect_sgsn(struct gprs_ns_inst *nsi, struct sockaddr_in *sgsn_peer, uint32_t sgsn_nsei) { gprs_ns_nsip_connect(nsi, sgsn_peer, sgsn_nsei, sgsn_nsei+1); send_ns_reset_ack(nsi, sgsn_peer, sgsn_nsei+1, sgsn_nsei); send_ns_alive_ack(nsi, sgsn_peer); send_ns_unblock_ack(nsi, sgsn_peer); send_ns_alive(nsi, sgsn_peer); } static void configure_sgsn_peer(struct sockaddr_in *sgsn_peer) { sgsn_peer->sin_family = AF_INET; sgsn_peer->sin_port = htons(32000); sgsn_peer->sin_addr.s_addr = htonl(REMOTE_SGSN_ADDR); } static void configure_sgsn2_peer(struct sockaddr_in *sgsn_peer) { sgsn_peer->sin_family = AF_INET; sgsn_peer->sin_port = htons(32001); sgsn_peer->sin_addr.s_addr = htonl(REMOTE_SGSN2_ADDR); } static void configure_bss_peers(struct sockaddr_in *bss_peers, size_t size) { size_t i; for (i = 0; i < size; ++i) { bss_peers[i].sin_family = AF_INET; bss_peers[i].sin_port = htons((i + 1) * 1111); bss_peers[i].sin_addr.s_addr = htonl(REMOTE_BSS_ADDR); } } int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, struct sockaddr_in *saddr, enum gprs_ns_ll ll); /* override */ int gprs_ns_callback(enum gprs_ns_evt event, struct gprs_nsvc *nsvc, struct msgb *msg, uint16_t bvci) { printf("CALLBACK, event %d, msg length %zu, bvci 0x%04x\n%s\n\n", event, msgb_bssgp_len(msg), bvci, osmo_hexdump(msgb_l2(msg), msgb_l2len(msg))); switch (event) { case GPRS_NS_EVT_UNIT_DATA: return gbprox_rcvmsg(&gbcfg, msg, nsvc->nsei, bvci, nsvc->nsvci); default: break; } return 0; } /* override */ ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) { typedef ssize_t (*sendto_t)(int, const void *, size_t, int, const struct sockaddr *, socklen_t); static sendto_t real_sendto = NULL; uint32_t dest_host = htonl(((struct sockaddr_in *)dest_addr)->sin_addr.s_addr); int dest_port = htons(((struct sockaddr_in *)dest_addr)->sin_port); if (!real_sendto) real_sendto = dlsym(RTLD_NEXT, "sendto"); if (dest_host == REMOTE_BSS_ADDR) printf("MESSAGE to BSS at 0x%08x:%d, msg length %zu\n%s\n\n", dest_host, dest_port, len, osmo_hexdump(buf, len)); else if (dest_host == REMOTE_SGSN_ADDR) printf("MESSAGE to SGSN at 0x%08x:%d, msg length %zu\n%s\n\n", dest_host, dest_port, len, osmo_hexdump(buf, len)); else if (dest_host == REMOTE_SGSN2_ADDR) printf("MESSAGE to SGSN 2 at 0x%08x:%d, msg length %zu\n%s\n\n", dest_host, dest_port, len, osmo_hexdump(buf, len)); else return real_sendto(sockfd, buf, len, flags, dest_addr, addrlen); return len; } /* override */ int gprs_ns_sendmsg(struct gprs_ns_inst *nsi, struct msgb *msg) { typedef int (*gprs_ns_sendmsg_t)(struct gprs_ns_inst *nsi, struct msgb *msg); static gprs_ns_sendmsg_t real_gprs_ns_sendmsg = NULL; uint16_t bvci = msgb_bvci(msg); uint16_t nsei = msgb_nsei(msg); size_t len = msgb_length(msg); if (!real_gprs_ns_sendmsg) real_gprs_ns_sendmsg = dlsym(RTLD_NEXT, "gprs_ns_sendmsg"); if (nsei == SGSN_NSEI) printf("NS UNITDATA MESSAGE to SGSN, BVCI 0x%04x, " "msg length %zu (%s)\n", bvci, len, __func__); else if (nsei == SGSN2_NSEI) printf("NS UNITDATA MESSAGE to SGSN 2, BVCI 0x%04x, " "msg length %zu (%s)\n", bvci, len, __func__); else printf("NS UNITDATA MESSAGE to BSS, BVCI 0x%04x, " "msg length %zu (%s)\n", bvci, len, __func__); if (received_messages) { struct msgb *msg_copy; msg_copy = gprs_msgb_copy(msg, "received_messages"); llist_add_tail(&msg_copy->list, received_messages); } return real_gprs_ns_sendmsg(nsi, msg); } /* Get the next message from the receive FIFO * * \returns a pointer to the message which will be invalidated at the next call * to expect_msg. Returns NULL, if there is no message left. */ static struct msgb *expect_msg(void) { static struct msgb *msg = NULL; msgb_free(msg); msg = NULL; if (!received_messages) return NULL; if (llist_empty(received_messages)) return NULL; msg = llist_entry(received_messages->next, struct msgb, list); llist_del(&msg->list); return msg; } struct expect_result { struct msgb *msg; struct gprs_gb_parse_context parse_ctx; }; static struct expect_result *expect_bssgp_msg( int match_nsei, int match_bvci, int match_pdu_type) { static struct expect_result result; static const struct expect_result empty_result = {0,}; static struct msgb *msg; uint16_t nsei; int rc; memcpy(&result, &empty_result, sizeof(result)); msg = expect_msg(); if (!msg) return NULL; nsei = msgb_nsei(msg); if (match_nsei != MATCH_ANY && match_nsei != nsei) { fprintf(stderr, "%s: NSEI mismatch (expected %u, got %u)\n", __func__, match_nsei, nsei); return NULL; } if (match_bvci != MATCH_ANY && match_bvci != msgb_bvci(msg)) { fprintf(stderr, "%s: BVCI mismatch (expected %u, got %u)\n", __func__, match_bvci, msgb_bvci(msg)); return NULL; } result.msg = msg; result.parse_ctx.to_bss = nsei != SGSN_NSEI && nsei != SGSN2_NSEI; result.parse_ctx.peer_nsei = nsei; if (!msgb_bssgph(msg)) { fprintf(stderr, "%s: Expected BSSGP\n", __func__); return NULL; } rc = gprs_gb_parse_bssgp(msgb_bssgph(msg), msgb_bssgp_len(msg), &result.parse_ctx); if (!rc) { fprintf(stderr, "%s: Failed to parse message\n", __func__); return NULL; } if (match_pdu_type != MATCH_ANY && match_pdu_type != result.parse_ctx.pdu_type) { fprintf(stderr, "%s: PDU type mismatch (expected %u, got %u)\n", __func__, match_pdu_type, result.parse_ctx.pdu_type); return NULL; } return &result; } static struct expect_result *expect_llc_msg( int match_nsei, int match_bvci, int match_sapi, int match_type) { static struct expect_result *result; result = expect_bssgp_msg(match_nsei, match_bvci, MATCH_ANY); if (!result) return NULL; if (!result->parse_ctx.llc) { fprintf(stderr, "%s: Expected LLC message\n", __func__); return NULL; } if (match_sapi != MATCH_ANY && match_sapi != result->parse_ctx.llc_hdr_parsed.sapi) { fprintf(stderr, "%s: LLC SAPI mismatch (expected %u, got %u)\n", __func__, match_sapi, result->parse_ctx.llc_hdr_parsed.sapi); return NULL; } if (match_type != MATCH_ANY && match_type != result->parse_ctx.llc_hdr_parsed.cmd) { fprintf(stderr, "%s: LLC command/type mismatch (expected %u, got %u)\n", __func__, match_type, result->parse_ctx.llc_hdr_parsed.cmd); return NULL; } return result; } static struct expect_result *expect_gmm_msg(int match_nsei, int match_bvci, int match_type) { static struct expect_result *result; result = expect_llc_msg(match_nsei, match_bvci, GPRS_SAPI_GMM, GPRS_LLC_UI); if (!result) return NULL; if (!result->parse_ctx.g48_hdr) { fprintf(stderr, "%s: Expected GSM 04.08 message\n", __func__); return NULL; } if (match_type != MATCH_ANY && match_type != result->parse_ctx.g48_hdr->msg_type) { fprintf(stderr, "%s: GSM 04.08 message type mismatch (expected %u, got %u)\n", __func__, match_type, result->parse_ctx.g48_hdr->msg_type); return NULL; } return result; } static void dump_rate_ctr_group(FILE *stream, const char *prefix, struct rate_ctr_group *ctrg) { unsigned int i; for (i = 0; i < ctrg->desc->num_ctr; i++) { struct rate_ctr *ctr = &ctrg->ctr[i]; if (ctr->current && !strchr(ctrg->desc->ctr_desc[i].name, '.')) fprintf(stream, " %s%s: %llu%s", prefix, ctrg->desc->ctr_desc[i].description, (long long)ctr->current, "\n"); }; } /* Signal handler for signals from NS layer */ static int test_signal(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct ns_signal_data *nssd = signal_data; int rc; if (subsys != SS_L_NS) return 0; switch (signal) { case S_NS_RESET: printf("==> got signal NS_RESET, NS-VC 0x%04x/%s\n", nssd->nsvc->nsvci, gprs_ns_ll_str(nssd->nsvc)); break; case S_NS_ALIVE_EXP: printf("==> got signal NS_ALIVE_EXP, NS-VC 0x%04x/%s\n", nssd->nsvc->nsvci, gprs_ns_ll_str(nssd->nsvc)); break; case S_NS_BLOCK: printf("==> got signal NS_BLOCK, NS-VC 0x%04x/%s\n", nssd->nsvc->nsvci, gprs_ns_ll_str(nssd->nsvc)); break; case S_NS_UNBLOCK: printf("==> got signal NS_UNBLOCK, NS-VC 0x%04x/%s\n", nssd->nsvc->nsvci, gprs_ns_ll_str(nssd->nsvc)); break; case S_NS_REPLACED: printf("==> got signal NS_REPLACED: 0x%04x/%s", nssd->nsvc->nsvci, gprs_ns_ll_str(nssd->nsvc)); printf(" -> 0x%04x/%s\n", nssd->old_nsvc->nsvci, gprs_ns_ll_str(nssd->old_nsvc)); break; default: printf("==> got signal %d, NS-VC 0x%04x/%s\n", signal, nssd->nsvc->nsvci, gprs_ns_ll_str(nssd->nsvc)); break; } printf("\n"); rc = gbprox_signal(subsys, signal, handler_data, signal_data); return rc; } static int gprs_process_message(struct gprs_ns_inst *nsi, const char *text, struct sockaddr_in *peer, const unsigned char* data, size_t data_len) { struct msgb *msg; int ret; if (data_len > NS_ALLOC_SIZE - NS_ALLOC_HEADROOM) { fprintf(stderr, "message too long: %zu\n", data_len); return -1; } msg = gprs_ns_msgb_alloc(); OSMO_ASSERT(msg); memmove(msg->data, data, data_len); msg->l2h = msg->data; msgb_put(msg, data_len); printf("PROCESSING %s from 0x%08x:%d\n%s\n\n", text, ntohl(peer->sin_addr.s_addr), ntohs(peer->sin_port), osmo_hexdump(data, data_len)); ret = gprs_ns_rcvmsg(nsi, msg, peer, GPRS_NS_LL_UDP); printf("result (%s) = %d\n\n", text, ret); msgb_free(msg); return ret; } static void gprs_dump_nsi(struct gprs_ns_inst *nsi) { struct gprs_nsvc *nsvc; printf("Current NS-VCIs:\n"); llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) { struct sockaddr_in *peer = &(nsvc->ip.bts_addr); printf(" VCI 0x%04x, NSEI 0x%04x, peer 0x%08x:%d%s%s\n", nsvc->nsvci, nsvc->nsei, ntohl(peer->sin_addr.s_addr), ntohs(peer->sin_port), nsvc->state & NSE_S_BLOCKED ? ", blocked" : "", nsvc->state & NSE_S_ALIVE ? "" : ", dead" ); dump_rate_ctr_group(stdout, " ", nsvc->ctrg); } printf("\n"); } static void test_gbproxy() { struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, tall_bsc_ctx); struct sockaddr_in bss_peer[4] = {{0},}; struct sockaddr_in sgsn_peer= {0}; bssgp_nsi = nsi; gbcfg.nsi = bssgp_nsi; gbcfg.nsip_sgsn_nsei = SGSN_NSEI; configure_sgsn_peer(&sgsn_peer); configure_bss_peers(bss_peer, ARRAY_SIZE(bss_peer)); printf("=== %s ===\n", __func__); printf("--- Initialise SGSN ---\n\n"); connect_sgsn(nsi, &sgsn_peer, SGSN_NSEI); gprs_dump_nsi(nsi); printf("--- Initialise BSS 1 ---\n\n"); setup_ns(nsi, &bss_peer[0], 0x1001, 0x1000); setup_bssgp(nsi, &bss_peer[0], 0x1002); gprs_dump_nsi(nsi); dump_peers(stdout, 0, 0, &gbcfg); send_bssgp_reset_ack(nsi, &sgsn_peer, 0x1002); printf("--- Initialise BSS 2 ---\n\n"); setup_ns(nsi, &bss_peer[1], 0x2001, 0x2000); setup_bssgp(nsi, &bss_peer[1], 0x2002); gprs_dump_nsi(nsi); dump_peers(stdout, 0, 0, &gbcfg); send_bssgp_reset_ack(nsi, &sgsn_peer, 0x2002); printf("--- Move BSS 1 to new port ---\n\n"); setup_ns(nsi, &bss_peer[2], 0x1001, 0x1000); gprs_dump_nsi(nsi); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Move BSS 2 to former BSS 1 port ---\n\n"); setup_ns(nsi, &bss_peer[0], 0x2001, 0x2000); gprs_dump_nsi(nsi); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Move BSS 1 to current BSS 2 port ---\n\n"); setup_ns(nsi, &bss_peer[0], 0x2001, 0x2000); gprs_dump_nsi(nsi); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Move BSS 2 to new port ---\n\n"); setup_ns(nsi, &bss_peer[3], 0x2001, 0x2000); gprs_dump_nsi(nsi); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Move BSS 2 to former BSS 1 port ---\n\n"); setup_ns(nsi, &bss_peer[2], 0x2001, 0x2000); gprs_dump_nsi(nsi); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Move BSS 1 to original BSS 1 port ---\n\n"); setup_ns(nsi, &bss_peer[0], 0x1001, 0x1000); gprs_dump_nsi(nsi); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Reset BSS 1 with a new BVCI ---\n\n"); setup_bssgp(nsi, &bss_peer[0], 0x1012); gprs_dump_nsi(nsi); dump_peers(stdout, 0, 0, &gbcfg); send_bssgp_reset_ack(nsi, &sgsn_peer, 0x1012); printf("--- Reset BSS 1 with the old BVCI ---\n\n"); setup_bssgp(nsi, &bss_peer[0], 0x1002); gprs_dump_nsi(nsi); dump_peers(stdout, 0, 0, &gbcfg); send_bssgp_reset_ack(nsi, &sgsn_peer, 0x1002); printf("--- Reset BSS 1 with the old BVCI again ---\n\n"); setup_bssgp(nsi, &bss_peer[0], 0x1002); gprs_dump_nsi(nsi); dump_peers(stdout, 0, 0, &gbcfg); send_bssgp_reset_ack(nsi, &sgsn_peer, 0x1002); printf("--- Send message from BSS 1 to SGSN, BVCI 0x1012 ---\n\n"); send_ns_unitdata(nsi, NULL, &bss_peer[0], 0x1012, (uint8_t *)"", 0); printf("--- Send message from SGSN to BSS 1, BVCI 0x1012 ---\n\n"); send_ns_unitdata(nsi, NULL, &sgsn_peer, 0x1012, (uint8_t *)"", 0); printf("--- Send message from BSS 1 to SGSN, BVCI 0x1002 ---\n\n"); send_ns_unitdata(nsi, NULL, &bss_peer[0], 0x1012, (uint8_t *)"", 0); printf("--- Send message from SGSN to BSS 1, BVCI 0x1002 ---\n\n"); send_ns_unitdata(nsi, NULL, &sgsn_peer, 0x1012, (uint8_t *)"", 0); printf("--- Send message from BSS 2 to SGSN, BVCI 0x2002 ---\n\n"); send_ns_unitdata(nsi, NULL, &bss_peer[0], 0x2002, (uint8_t *)"", 0); printf("--- Send message from SGSN to BSS 2, BVCI 0x2002 ---\n\n"); send_ns_unitdata(nsi, NULL, &sgsn_peer, 0x2002, (uint8_t *)"", 0); printf("--- Reset BSS 1 with the old BVCI on BSS2's link ---\n\n"); setup_bssgp(nsi, &bss_peer[2], 0x1002); gprs_dump_nsi(nsi); dump_peers(stdout, 0, 0, &gbcfg); dump_global(stdout, 0); send_bssgp_reset_ack(nsi, &sgsn_peer, 0x1002); printf("--- Send message from BSS 1 to SGSN, BVCI 0x1002 ---\n\n"); send_ns_unitdata(nsi, NULL, &bss_peer[0], 0x1012, (uint8_t *)"", 0); printf("--- Send message from SGSN to BSS 1, BVCI 0x1002 ---\n\n"); send_ns_unitdata(nsi, NULL, &sgsn_peer, 0x1012, (uint8_t *)"", 0); printf("--- Send message from SGSN to BSS 1, BVCI 0x10ff (invalid) ---\n\n"); send_ns_unitdata(nsi, NULL, &sgsn_peer, 0x10ff, (uint8_t *)"", 0); /* Find peer */ OSMO_ASSERT(gbproxy_peer_by_bvci(&gbcfg, 0xeeee) == NULL); OSMO_ASSERT(gbproxy_peer_by_bvci(&gbcfg, 0x1000) == NULL); OSMO_ASSERT(gbproxy_peer_by_bvci(&gbcfg, 0x1012) != NULL); OSMO_ASSERT(gbproxy_peer_by_nsei(&gbcfg, 0xeeee) == NULL); OSMO_ASSERT(gbproxy_peer_by_nsei(&gbcfg, 0x1012) == NULL); OSMO_ASSERT(gbproxy_peer_by_nsei(&gbcfg, 0x1000) != NULL); /* Cleanup */ OSMO_ASSERT(gbproxy_cleanup_peers(&gbcfg, 0, 0) == 0); OSMO_ASSERT(gbproxy_cleanup_peers(&gbcfg, 0x1000, 0xeeee) == 0); OSMO_ASSERT(gbproxy_cleanup_peers(&gbcfg, 0, 0x1002) == 0); OSMO_ASSERT(gbproxy_cleanup_peers(&gbcfg, 0x1000, 0x1012) == 1); OSMO_ASSERT(gbproxy_cleanup_peers(&gbcfg, 0x1000, 0x1012) == 0); dump_peers(stdout, 0, 0, &gbcfg); dump_global(stdout, 0); gbprox_reset(&gbcfg); gprs_ns_destroy(nsi); nsi = NULL; } static void test_gbproxy_ident_changes() { struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, tall_bsc_ctx); struct sockaddr_in bss_peer[1] = {{0},}; struct sockaddr_in sgsn_peer= {0}; uint16_t nsei[2] = {0x1000, 0x2000}; uint16_t nsvci[2] = {0x1001, 0x2001}; uint16_t bvci[4] = {0x1002, 0x2002, 0x3002, 0x4002}; bssgp_nsi = nsi; gbcfg.nsi = bssgp_nsi; gbcfg.nsip_sgsn_nsei = SGSN_NSEI; configure_sgsn_peer(&sgsn_peer); configure_bss_peers(bss_peer, ARRAY_SIZE(bss_peer)); printf("=== %s ===\n", __func__); printf("--- Initialise SGSN ---\n\n"); connect_sgsn(nsi, &sgsn_peer, SGSN_NSEI); gprs_dump_nsi(nsi); printf("--- Initialise BSS 1 ---\n\n"); setup_ns(nsi, &bss_peer[0], nsvci[0], nsei[0]); gprs_dump_nsi(nsi); printf("--- Setup BVCI 1 ---\n\n"); setup_bssgp(nsi, &bss_peer[0], bvci[0]); send_bssgp_reset_ack(nsi, &sgsn_peer, bvci[0]); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Setup BVCI 2 ---\n\n"); setup_bssgp(nsi, &bss_peer[0], bvci[1]); send_bssgp_reset_ack(nsi, &sgsn_peer, bvci[1]); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Send message from BSS 1 to SGSN and back, BVCI 1 ---\n\n"); send_ns_unitdata(nsi, NULL, &bss_peer[0], bvci[0], (uint8_t *)"", 0); send_ns_unitdata(nsi, NULL, &sgsn_peer, bvci[0], (uint8_t *)"", 0); printf("--- Send message from BSS 1 to SGSN and back, BVCI 2 ---\n\n"); send_ns_unitdata(nsi, NULL, &bss_peer[0], bvci[1], (uint8_t *)"", 0); send_ns_unitdata(nsi, NULL, &sgsn_peer, bvci[1], (uint8_t *)"", 0); printf("--- Change NSEI ---\n\n"); setup_ns(nsi, &bss_peer[0], nsvci[0], nsei[1]); gprs_dump_nsi(nsi); printf("--- Setup BVCI 1 ---\n\n"); setup_bssgp(nsi, &bss_peer[0], bvci[0]); send_bssgp_reset_ack(nsi, &sgsn_peer, bvci[0]); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Setup BVCI 3 ---\n\n"); setup_bssgp(nsi, &bss_peer[0], bvci[2]); send_bssgp_reset_ack(nsi, &sgsn_peer, bvci[2]); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Send message from BSS 1 to SGSN and back, BVCI 1 ---\n\n"); send_ns_unitdata(nsi, NULL, &bss_peer[0], bvci[0], (uint8_t *)"", 0); send_ns_unitdata(nsi, NULL, &sgsn_peer, bvci[0], (uint8_t *)"", 0); printf("--- Send message from BSS 1 to SGSN and back, BVCI 2 " " (should fail) ---\n\n"); send_ns_unitdata(nsi, NULL, &bss_peer[0], bvci[1], (uint8_t *)"", 0); dump_peers(stdout, 0, 0, &gbcfg); send_ns_unitdata(nsi, NULL, &sgsn_peer, bvci[1], (uint8_t *)"", 0); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Send message from BSS 1 to SGSN and back, BVCI 3 ---\n\n"); send_ns_unitdata(nsi, NULL, &bss_peer[0], bvci[2], (uint8_t *)"", 0); send_ns_unitdata(nsi, NULL, &sgsn_peer, bvci[2], (uint8_t *)"", 0); printf("--- Change NSVCI ---\n\n"); setup_ns(nsi, &bss_peer[0], nsvci[1], nsei[1]); gprs_dump_nsi(nsi); printf("--- Setup BVCI 1 ---\n\n"); setup_bssgp(nsi, &bss_peer[0], bvci[0]); send_bssgp_reset_ack(nsi, &sgsn_peer, bvci[0]); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Setup BVCI 4 ---\n\n"); setup_bssgp(nsi, &bss_peer[0], bvci[3]); send_bssgp_reset_ack(nsi, &sgsn_peer, bvci[3]); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Send message from BSS 1 to SGSN and back, BVCI 1 ---\n\n"); send_ns_unitdata(nsi, NULL, &bss_peer[0], bvci[0], (uint8_t *)"", 0); send_ns_unitdata(nsi, NULL, &sgsn_peer, bvci[0], (uint8_t *)"", 0); printf("--- Send message from BSS 1 to SGSN and back, BVCI 2 " " (should fail) ---\n\n"); send_ns_unitdata(nsi, NULL, &bss_peer[0], bvci[1], (uint8_t *)"", 0); dump_peers(stdout, 0, 0, &gbcfg); send_ns_unitdata(nsi, NULL, &sgsn_peer, bvci[1], (uint8_t *)"", 0); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Send message from BSS 1 to SGSN and back, BVCI 3 ---\n\n"); send_ns_unitdata(nsi, NULL, &bss_peer[0], bvci[2], (uint8_t *)"", 0); send_ns_unitdata(nsi, NULL, &sgsn_peer, bvci[2], (uint8_t *)"", 0); printf("--- Send message from BSS 1 to SGSN and back, BVCI 4 ---\n\n"); send_ns_unitdata(nsi, NULL, &bss_peer[0], bvci[3], (uint8_t *)"", 0); send_ns_unitdata(nsi, NULL, &sgsn_peer, bvci[3], (uint8_t *)"", 0); dump_global(stdout, 0); dump_peers(stdout, 0, 0, &gbcfg); gbprox_reset(&gbcfg); gprs_ns_destroy(nsi); nsi = NULL; } static void test_gbproxy_ra_patching() { struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, tall_bsc_ctx); struct sockaddr_in bss_peer[1] = {{0},}; struct sockaddr_in sgsn_peer= {0}; struct gprs_ra_id rai_bss = {.mcc = 112, .mnc = 332, .lac = 16464, .rac = 96}; struct gprs_ra_id rai_sgsn = {.mcc = 123, .mnc = 456, .lac = 16464, .rac = 96}; struct gprs_ra_id rai_unknown = {.mcc = 1, .mnc = 99, .lac = 99, .rac = 96}; uint16_t cell_id = 0x7530; const char *err_msg = NULL; const uint32_t ptmsi = 0xefe2b700; const uint32_t local_tlli = 0xefe2b700; const uint32_t foreign_tlli = 0xbbc54679; const uint32_t foreign_tlli2 = 0xbb00beef; const uint8_t imsi[] = {0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}; const char *patch_re = "^9898|^121314"; struct gbproxy_link_info *link_info; struct gbproxy_peer *peer; LLIST_HEAD(rcv_list); struct expect_result *expect_res; OSMO_ASSERT(local_tlli == gprs_tmsi2tlli(ptmsi, TLLI_LOCAL)); bssgp_nsi = nsi; gbcfg.nsi = bssgp_nsi; gbcfg.nsip_sgsn_nsei = SGSN_NSEI; gbcfg.core_plmn = (struct osmo_plmn_id){ .mcc = 123, .mnc = 456 }; gbcfg.core_apn = talloc_zero_size(tall_bsc_ctx, 100); gbcfg.core_apn_size = gprs_str_to_apn(gbcfg.core_apn, 100, "foo.bar"); gbcfg.patch_ptmsi = 0; configure_sgsn_peer(&sgsn_peer); configure_bss_peers(bss_peer, ARRAY_SIZE(bss_peer)); if (gbproxy_set_patch_filter(&gbcfg.matches[GBPROX_MATCH_PATCHING], patch_re, &err_msg) != 0) { fprintf(stderr, "Failed to compile RE '%s': %s\n", patch_re, err_msg); exit(1); } printf("=== %s ===\n", __func__); printf("--- Initialise SGSN ---\n\n"); connect_sgsn(nsi, &sgsn_peer, SGSN_NSEI); gprs_dump_nsi(nsi); printf("--- Initialise BSS 1 ---\n\n"); setup_ns(nsi, &bss_peer[0], 0x1001, 0x1000); received_messages = &rcv_list; setup_bssgp(nsi, &bss_peer[0], 0x1002); gprs_dump_nsi(nsi); dump_peers(stdout, 0, 0, &gbcfg); peer = gbproxy_peer_by_nsei(&gbcfg, 0x1000); OSMO_ASSERT(peer != NULL); OSMO_ASSERT(expect_bssgp_msg(SGSN_NSEI, 0, BSSGP_PDUT_BVC_RESET)); send_bssgp_reset_ack(nsi, &sgsn_peer, 0x1002); OSMO_ASSERT(expect_bssgp_msg(0x1000, 0, BSSGP_PDUT_BVC_RESET_ACK)); send_bssgp_suspend(nsi, &bss_peer[0], 0xccd1758b, &rai_bss); OSMO_ASSERT(expect_bssgp_msg(SGSN_NSEI, 0, BSSGP_PDUT_SUSPEND)); send_bssgp_suspend_ack(nsi, &sgsn_peer, 0xccd1758b, &rai_sgsn); OSMO_ASSERT(expect_bssgp_msg(0x1000, 0, BSSGP_PDUT_SUSPEND_ACK)); dump_global(stdout, 0); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(2 == peer->ctrg->ctr[GBPROX_PEER_CTR_RAID_PATCHED_BSS].current); OSMO_ASSERT(1 == peer->ctrg->ctr[GBPROX_PEER_CTR_RAID_PATCHED_SGSN].current); printf("--- Send message from BSS 1 to SGSN, BVCI 0x1002 ---\n\n"); send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, 0, dtap_attach_req, sizeof(dtap_attach_req)); OSMO_ASSERT(4 == peer->ctrg->ctr[GBPROX_PEER_CTR_RAID_PATCHED_BSS].current); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_REQ)); send_llc_dl_ui(nsi, "IDENT REQUEST", &sgsn_peer, 0x1002, foreign_tlli, 0, NULL, 0, GPRS_SAPI_GMM, 0, dtap_identity_req, sizeof(dtap_identity_req)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ID_REQ)); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, 3, dtap_identity_resp, sizeof(dtap_identity_resp)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ID_RESP)); OSMO_ASSERT(5 == peer->ctrg->ctr[GBPROX_PEER_CTR_RAID_PATCHED_BSS].current); OSMO_ASSERT(1 == peer->ctrg->ctr[GBPROX_PEER_CTR_RAID_PATCHED_SGSN].current); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer, 0x1002, foreign_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, 1, dtap_attach_acc, sizeof(dtap_attach_acc)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ATTACH_ACK)); OSMO_ASSERT(2 == peer->ctrg->ctr[GBPROX_PEER_CTR_RAID_PATCHED_SGSN].current); OSMO_ASSERT(gbproxy_peer_by_rai(&gbcfg, convert_ra(&rai_bss)) != NULL); OSMO_ASSERT(gbproxy_peer_by_rai(&gbcfg, convert_ra(&rai_sgsn)) == NULL); OSMO_ASSERT(gbproxy_peer_by_rai(&gbcfg, convert_ra(&rai_unknown)) == NULL); OSMO_ASSERT(gbproxy_peer_by_lai(&gbcfg, convert_ra(&rai_bss)) != NULL); OSMO_ASSERT(gbproxy_peer_by_lai(&gbcfg, convert_ra(&rai_sgsn)) == NULL); OSMO_ASSERT(gbproxy_peer_by_lai(&gbcfg, convert_ra(&rai_unknown)) == NULL); OSMO_ASSERT(gbproxy_peer_by_lac(&gbcfg, convert_ra(&rai_bss)) != NULL); OSMO_ASSERT(gbproxy_peer_by_lac(&gbcfg, convert_ra(&rai_sgsn)) != NULL); OSMO_ASSERT(gbproxy_peer_by_lac(&gbcfg, convert_ra(&rai_unknown)) == NULL); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_tlli); OSMO_ASSERT(link_info->tlli.current != local_tlli); OSMO_ASSERT(!link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.assigned == local_tlli); OSMO_ASSERT(link_info->sgsn_tlli.current != local_tlli); OSMO_ASSERT(!link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, 4, dtap_attach_complete, sizeof(dtap_attach_complete)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_COMPL)); OSMO_ASSERT(6 == peer->ctrg->ctr[GBPROX_PEER_CTR_RAID_PATCHED_BSS].current); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_tlli); OSMO_ASSERT(link_info->tlli.current != local_tlli); OSMO_ASSERT(link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.assigned == local_tlli); OSMO_ASSERT(link_info->sgsn_tlli.current != local_tlli); OSMO_ASSERT(link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); /* Replace APN (1) */ send_llc_ul_ui(nsi, "ACT PDP CTX REQ (REPLACE APN)", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, 3, dtap_act_pdp_ctx_req, sizeof(dtap_act_pdp_ctx_req)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GSM_ACT_PDP_REQ)); OSMO_ASSERT(7 == peer->ctrg->ctr[GBPROX_PEER_CTR_RAID_PATCHED_BSS].current); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_tlli); OSMO_ASSERT(link_info->tlli.current != local_tlli); OSMO_ASSERT(link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.assigned == local_tlli); OSMO_ASSERT(link_info->sgsn_tlli.current != local_tlli); OSMO_ASSERT(link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); send_llc_dl_ui(nsi, "GMM INFO", &sgsn_peer, 0x1002, local_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, 2, dtap_gmm_information, sizeof(dtap_gmm_information)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_INFO)); OSMO_ASSERT(2 == peer->ctrg->ctr[GBPROX_PEER_CTR_RAID_PATCHED_SGSN].current); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == 0); OSMO_ASSERT(link_info->tlli.current == local_tlli); OSMO_ASSERT(link_info->sgsn_tlli.assigned == 0); OSMO_ASSERT(link_info->sgsn_tlli.current == local_tlli); /* Replace APN (2) */ send_llc_ul_ui(nsi, "ACT PDP CTX REQ (REPLACE APN)", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, 3, dtap_act_pdp_ctx_req, sizeof(dtap_act_pdp_ctx_req)); expect_res = expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GSM_ACT_PDP_REQ); OSMO_ASSERT(expect_res != NULL); OSMO_ASSERT(expect_res->parse_ctx.apn_ie_len == gbcfg.core_apn_size + 2); OSMO_ASSERT(8 == peer->ctrg->ctr[GBPROX_PEER_CTR_RAID_PATCHED_BSS].current); gbcfg.core_apn[0] = 0; gbcfg.core_apn_size = 0; /* Remove APN */ send_llc_ul_ui(nsi, "ACT PDP CTX REQ (REMOVE APN)", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, 3, dtap_act_pdp_ctx_req, sizeof(dtap_act_pdp_ctx_req)); expect_res = expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GSM_ACT_PDP_REQ); OSMO_ASSERT(expect_res != NULL); OSMO_ASSERT(expect_res->parse_ctx.apn_ie_len == 0); OSMO_ASSERT(9 == peer->ctrg->ctr[GBPROX_PEER_CTR_RAID_PATCHED_BSS].current); dump_peers(stdout, 0, 0, &gbcfg); /* Detach */ send_llc_ul_ui(nsi, "DETACH REQ", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, 6, dtap_detach_req, sizeof(dtap_detach_req)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_DETACH_REQ)); OSMO_ASSERT(10 == peer->ctrg->ctr[GBPROX_PEER_CTR_RAID_PATCHED_BSS].current); OSMO_ASSERT(2 == peer->ctrg->ctr[GBPROX_PEER_CTR_RAID_PATCHED_SGSN].current); send_llc_dl_ui(nsi, "DETACH ACC", &sgsn_peer, 0x1002, local_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, 5, dtap_detach_acc, sizeof(dtap_detach_acc)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_DETACH_ACK)); dump_peers(stdout, 0, 0, &gbcfg); printf("--- RA update ---\n\n"); send_llc_ul_ui(nsi, "RA UPD REQ", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, 0x7080, GPRS_SAPI_GMM, 5, dtap_ra_upd_req, sizeof(dtap_ra_upd_req)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_RA_UPD_REQ)); OSMO_ASSERT(12 == peer->ctrg->ctr[GBPROX_PEER_CTR_RAID_PATCHED_BSS].current); send_llc_dl_ui(nsi, "RA UPD ACC", &sgsn_peer, 0x1002, foreign_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, 6, dtap_ra_upd_acc, sizeof(dtap_ra_upd_acc)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_RA_UPD_ACK)); OSMO_ASSERT(3 == peer->ctrg->ctr[GBPROX_PEER_CTR_RAID_PATCHED_SGSN].current); /* Remove APN */ send_llc_ul_ui(nsi, "ACT PDP CTX REQ (REMOVE APN)", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, 3, dtap_act_pdp_ctx_req, sizeof(dtap_act_pdp_ctx_req)); expect_res = expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GSM_ACT_PDP_REQ); OSMO_ASSERT(expect_res != NULL); OSMO_ASSERT(expect_res->parse_ctx.apn_ie_len == 0); OSMO_ASSERT(13 == peer->ctrg->ctr[GBPROX_PEER_CTR_RAID_PATCHED_BSS].current); dump_peers(stdout, 0, 0, &gbcfg); /* Detach (power off -> no Detach Accept) */ send_llc_ul_ui(nsi, "DETACH REQ (PWR OFF)", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, 6, dtap_detach_po_req, sizeof(dtap_detach_po_req)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_DETACH_REQ)); OSMO_ASSERT(14 == peer->ctrg->ctr[GBPROX_PEER_CTR_RAID_PATCHED_BSS].current); dump_global(stdout, 0); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Bad cases ---\n\n"); /* The RAI in the Attach Request message differs from the RAI in the * BSSGP message, only patch the latter */ send_llc_ul_ui(nsi, "ATTACH REQUEST (foreign RAI)", &bss_peer[0], 0x1002, foreign_tlli2, &rai_bss, cell_id, GPRS_SAPI_GMM, 0, dtap_attach_req2, sizeof(dtap_attach_req2)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_REQ)); OSMO_ASSERT(15 == peer->ctrg->ctr[GBPROX_PEER_CTR_RAID_PATCHED_BSS].current); printf("TLLI is already detached, shouldn't patch\n"); send_llc_ul_ui(nsi, "ACT PDP CTX REQ", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, 3, dtap_act_pdp_ctx_req, sizeof(dtap_act_pdp_ctx_req)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GSM_ACT_PDP_REQ)); printf("Invalid RAI, shouldn't patch\n"); send_bssgp_suspend_ack(nsi, &sgsn_peer, 0xccd1758b, &rai_unknown); /* TODO: The following breaks with the current libosmocore, enable it * again (and remove the plain expect_msg), when the msgb_bssgph patch * is integrated */ /* OSMO_ASSERT(expect_bssgp_msg(SGSN_NSEI, 0, BSSGP_PDUT_STATUS)); */ OSMO_ASSERT(expect_msg()); dump_global(stdout, 0); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!expect_msg()); received_messages = NULL; talloc_free(gbcfg.core_apn); gbcfg.core_apn = NULL; gbproxy_clear_patch_filter(&gbcfg.matches[GBPROX_MATCH_PATCHING]); gbprox_reset(&gbcfg); gprs_ns_destroy(nsi); nsi = NULL; } static void test_gbproxy_ptmsi_assignment() { struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, tall_bsc_ctx); struct sockaddr_in bss_peer[1] = {{0},}; struct sockaddr_in sgsn_peer= {0}; struct gprs_ra_id rai_bss = {.mcc = 112, .mnc = 332, .lac = 16464, .rac = 96}; struct gprs_ra_id rai_unknown = {.mcc = 1, .mnc = 99, .lac = 99, .rac = 96}; uint16_t cell_id = 0x1234; const uint32_t ptmsi = 0xefe2b700; const uint32_t local_tlli = 0xefe2b700; const uint32_t foreign_tlli1 = 0x8000dead; const uint32_t foreign_tlli2 = 0x8000beef; const uint8_t imsi1[] = {0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}; const uint8_t imsi2[] = {0x11, 0x12, 0x99, 0x99, 0x99, 0x16, 0x17, 0x18}; struct gbproxy_link_info *link_info, *link_info2; struct gbproxy_peer *peer; unsigned bss_nu = 0; unsigned sgsn_nu = 0; OSMO_ASSERT(local_tlli == gprs_tmsi2tlli(ptmsi, TLLI_LOCAL)); bssgp_nsi = nsi; gbcfg.nsi = bssgp_nsi; gbcfg.nsip_sgsn_nsei = SGSN_NSEI; gbcfg.core_plmn = (struct osmo_plmn_id){}; gbcfg.core_apn = talloc_zero_size(tall_bsc_ctx, 100); gbcfg.core_apn_size = gprs_str_to_apn(gbcfg.core_apn, 100, "foo.bar"); gbcfg.patch_ptmsi = 0; configure_sgsn_peer(&sgsn_peer); configure_bss_peers(bss_peer, ARRAY_SIZE(bss_peer)); printf("=== %s ===\n", __func__); printf("--- Initialise SGSN ---\n\n"); connect_sgsn(nsi, &sgsn_peer, SGSN_NSEI); printf("--- Initialise BSS 1 ---\n\n"); setup_ns(nsi, &bss_peer[0], 0x1001, 0x1000); setup_bssgp(nsi, &bss_peer[0], 0x1002); peer = gbproxy_peer_by_nsei(&gbcfg, 0x1000); OSMO_ASSERT(peer != NULL); send_bssgp_reset_ack(nsi, &sgsn_peer, 0x1002); gprs_dump_nsi(nsi); dump_global(stdout, 0); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Establish first LLC connection ---\n\n"); send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_tlli1, &rai_unknown, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req, sizeof(dtap_attach_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "IDENT REQUEST", &sgsn_peer, 0x1002, foreign_tlli1, 0, NULL, 0, GPRS_SAPI_GMM, sgsn_nu++, dtap_identity_req, sizeof(dtap_identity_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_tlli1, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_identity_resp, sizeof(dtap_identity_resp)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer, 0x1002, foreign_tlli1, 1, imsi1, sizeof(imsi1), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli1); link_info2 = gbproxy_link_info_by_tlli(peer, local_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info == link_info2); OSMO_ASSERT(link_info->tlli.assigned == local_tlli); OSMO_ASSERT(link_info->tlli.current == foreign_tlli1); OSMO_ASSERT(!link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->tlli.ptmsi == ptmsi); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, local_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_tlli); OSMO_ASSERT(link_info->tlli.current == foreign_tlli1); OSMO_ASSERT(link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->tlli.ptmsi == ptmsi); send_llc_dl_ui(nsi, "GMM INFO", &sgsn_peer, 0x1002, local_tlli, 1, imsi1, sizeof(imsi1), GPRS_SAPI_GMM, sgsn_nu++, dtap_gmm_information, sizeof(dtap_gmm_information)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_imsi(peer, imsi1, ARRAY_SIZE(imsi1)); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.ptmsi == ptmsi); OSMO_ASSERT(!gbproxy_link_info_by_imsi(peer, imsi2, ARRAY_SIZE(imsi2))); link_info2 = gbproxy_link_info_by_tlli(peer, local_tlli); OSMO_ASSERT(link_info == link_info2); OSMO_ASSERT(link_info->tlli.assigned == 0); OSMO_ASSERT(link_info->tlli.current == local_tlli); OSMO_ASSERT(link_info->tlli.ptmsi == ptmsi); printf("--- Establish second LLC connection with the same P-TMSI ---\n\n"); send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_tlli2, &rai_unknown, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req, sizeof(dtap_attach_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "IDENT REQUEST", &sgsn_peer, 0x1002, foreign_tlli2, 0, NULL, 0, GPRS_SAPI_GMM, sgsn_nu++, dtap_identity_req, sizeof(dtap_identity_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_tlli2, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_identity2_resp, sizeof(dtap_identity2_resp)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer, 0x1002, foreign_tlli2, 1, imsi2, sizeof(imsi2), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli2); link_info2 = gbproxy_link_info_by_tlli(peer, local_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info == link_info2); OSMO_ASSERT(link_info->tlli.assigned == local_tlli); OSMO_ASSERT(link_info->tlli.current == foreign_tlli2); OSMO_ASSERT(!link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->tlli.ptmsi == ptmsi); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, local_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_tlli); OSMO_ASSERT(link_info->tlli.current == foreign_tlli2); OSMO_ASSERT(link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->tlli.ptmsi == ptmsi); send_llc_dl_ui(nsi, "GMM INFO", &sgsn_peer, 0x1002, local_tlli, 1, imsi2, sizeof(imsi2), GPRS_SAPI_GMM, sgsn_nu++, dtap_gmm_information, sizeof(dtap_gmm_information)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_imsi(peer, imsi2, ARRAY_SIZE(imsi2)); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.ptmsi == ptmsi); OSMO_ASSERT(!gbproxy_link_info_by_imsi(peer, imsi1, ARRAY_SIZE(imsi1))); link_info2 = gbproxy_link_info_by_tlli(peer, local_tlli); OSMO_ASSERT(link_info == link_info2); OSMO_ASSERT(link_info->tlli.assigned == 0); OSMO_ASSERT(link_info->tlli.current == local_tlli); OSMO_ASSERT(link_info->tlli.ptmsi == ptmsi); dump_global(stdout, 0); talloc_free(gbcfg.core_apn); gbcfg.core_apn = NULL; gbprox_reset(&gbcfg); gprs_ns_destroy(nsi); nsi = NULL; cleanup_test(); } static void test_gbproxy_ptmsi_patching() { struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, tall_bsc_ctx); struct sockaddr_in bss_peer[1] = {{0},}; struct sockaddr_in sgsn_peer= {0}; struct gprs_ra_id rai_bss = {.mcc = 112, .mnc = 332, .lac = 16464, .rac = 96}; struct gprs_ra_id rai_sgsn = {.mcc = 123, .mnc = 456, .lac = 16464, .rac = 96}; struct gprs_ra_id rai_wrong_mcc_sgsn = {.mcc = 999, .mnc = 456, .lac = 16464, .rac = 96}; struct gprs_ra_id rai_unknown = {.mcc = 1, .mnc = 99, .lac = 99, .rac = 96}; uint16_t cell_id = 0x1234; const uint32_t sgsn_ptmsi = 0xefe2b700; const uint32_t sgsn_ptmsi2 = 0xe0987654; const uint32_t sgsn_ptmsi3 = 0xe0543210; const uint32_t local_sgsn_tlli = 0xefe2b700; const uint32_t local_sgsn_tlli2 = 0xe0987654; const uint32_t local_sgsn_tlli3 = 0xe0543210; const uint32_t random_sgsn_tlli = 0x78dead00; const uint32_t unknown_sgsn_tlli = 0xeebadbad; const uint32_t bss_ptmsi = 0xc0dead01; const uint32_t bss_ptmsi2 = 0xc0dead02; const uint32_t bss_ptmsi3 = 0xc0dead03; const uint32_t local_bss_tlli = 0xc0dead01; const uint32_t local_bss_tlli2 = 0xc0dead02; const uint32_t local_bss_tlli3 = 0xc0dead03; const uint32_t foreign_bss_tlli = 0x8000dead; const uint8_t imsi[] = {0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}; struct gbproxy_link_info *link_info; struct gbproxy_peer *peer; unsigned bss_nu = 0; unsigned sgsn_nu = 0; int old_ctr; OSMO_ASSERT(local_sgsn_tlli == gprs_tmsi2tlli(sgsn_ptmsi, TLLI_LOCAL)); OSMO_ASSERT(local_sgsn_tlli2 == gprs_tmsi2tlli(sgsn_ptmsi2, TLLI_LOCAL)); OSMO_ASSERT(local_sgsn_tlli3 == gprs_tmsi2tlli(sgsn_ptmsi3, TLLI_LOCAL)); OSMO_ASSERT(local_bss_tlli == gprs_tmsi2tlli(bss_ptmsi, TLLI_LOCAL)); OSMO_ASSERT(local_bss_tlli2 == gprs_tmsi2tlli(bss_ptmsi2, TLLI_LOCAL)); OSMO_ASSERT(local_bss_tlli3 == gprs_tmsi2tlli(bss_ptmsi3, TLLI_LOCAL)); bssgp_nsi = nsi; gbcfg.nsi = bssgp_nsi; gbcfg.nsip_sgsn_nsei = SGSN_NSEI; gbcfg.core_plmn = (struct osmo_plmn_id){ .mcc = 123, .mnc = 456 }; gbcfg.core_apn = talloc_zero_size(tall_bsc_ctx, 100); gbcfg.core_apn_size = gprs_str_to_apn(gbcfg.core_apn, 100, "foo.bar"); gbcfg.patch_ptmsi = 1; configure_sgsn_peer(&sgsn_peer); configure_bss_peers(bss_peer, ARRAY_SIZE(bss_peer)); printf("=== %s ===\n", __func__); printf("--- Initialise SGSN ---\n\n"); connect_sgsn(nsi, &sgsn_peer, SGSN_NSEI); printf("--- Initialise BSS 1 ---\n\n"); setup_ns(nsi, &bss_peer[0], 0x1001, 0x1000); setup_bssgp(nsi, &bss_peer[0], 0x1002); peer = gbproxy_peer_by_nsei(&gbcfg, 0x1000); OSMO_ASSERT(peer != NULL); send_bssgp_reset_ack(nsi, &sgsn_peer, 0x1002); gprs_dump_nsi(nsi); dump_global(stdout, 0); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Send message from BSS 1 to SGSN, BVCI 0x1002 ---\n\n"); send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_bss_tlli, &rai_unknown, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req, sizeof(dtap_attach_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "IDENT REQUEST", &sgsn_peer, 0x1002, random_sgsn_tlli, 0, NULL, 0, GPRS_SAPI_GMM, sgsn_nu++, dtap_identity_req, sizeof(dtap_identity_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_identity_resp, sizeof(dtap_identity_resp)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer, 0x1002, random_sgsn_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_sgsn_tlli(peer, random_sgsn_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_bss_tlli); OSMO_ASSERT(link_info->tlli.current == foreign_bss_tlli); OSMO_ASSERT(!link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->tlli.ptmsi == bss_ptmsi); OSMO_ASSERT(link_info->sgsn_tlli.assigned == local_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.current == random_sgsn_tlli); OSMO_ASSERT(!link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.ptmsi == sgsn_ptmsi); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_bss_tlli); OSMO_ASSERT(link_info->tlli.current == foreign_bss_tlli); OSMO_ASSERT(link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.assigned == local_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.current == random_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); send_llc_dl_ui(nsi, "GMM INFO", &sgsn_peer, 0x1002, local_sgsn_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_gmm_information, sizeof(dtap_gmm_information)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.current == local_bss_tlli); OSMO_ASSERT(link_info->tlli.assigned == 0); OSMO_ASSERT(link_info->sgsn_tlli.current == local_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.assigned == 0); send_llc_ul_ui(nsi, "ACT PDP CTX REQ (REPLACE APN)", &bss_peer[0], 0x1002, local_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_act_pdp_ctx_req, sizeof(dtap_act_pdp_ctx_req)); dump_peers(stdout, 0, 0, &gbcfg); /* Non-DTAP */ send_bssgp_ul_unitdata(nsi, "XID (UL)", &bss_peer[0], 0x1002, local_bss_tlli, &rai_bss, cell_id, llc_u_xid_ul, sizeof(llc_u_xid_ul)); send_bssgp_dl_unitdata(nsi, "XID (DL)", &sgsn_peer, 0x1002, local_sgsn_tlli, 1, imsi, sizeof(imsi), llc_u_xid_dl, sizeof(llc_u_xid_dl)); send_bssgp_ul_unitdata(nsi, "LL11 DNS QUERY (UL)", &bss_peer[0], 0x1002, local_bss_tlli, &rai_bss, cell_id, llc_ui_ll11_dns_query_ul, sizeof(llc_ui_ll11_dns_query_ul)); send_bssgp_dl_unitdata(nsi, "LL11 DNS RESP (DL)", &sgsn_peer, 0x1002, local_sgsn_tlli, 1, imsi, sizeof(imsi), llc_ui_ll11_dns_resp_dl, sizeof(llc_ui_ll11_dns_resp_dl)); dump_peers(stdout, 0, 0, &gbcfg); /* Repeated RA Update Requests */ send_llc_ul_ui(nsi, "RA UPD REQ (P-TMSI 2)", &bss_peer[0], 0x1002, local_bss_tlli, &rai_bss, 0x7080, GPRS_SAPI_GMM, bss_nu++, dtap_ra_upd_req, sizeof(dtap_ra_upd_req)); send_llc_dl_ui(nsi, "RA UDP ACC (P-TMSI 2)", &sgsn_peer, 0x1002, local_sgsn_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_ra_upd_acc2, sizeof(dtap_ra_upd_acc2)); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli2, SGSN_NSEI) != NULL); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_bss_tlli2); OSMO_ASSERT(link_info->tlli.current == local_bss_tlli); OSMO_ASSERT(!link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->tlli.ptmsi == bss_ptmsi2); OSMO_ASSERT(link_info->sgsn_tlli.assigned == local_sgsn_tlli2); OSMO_ASSERT(link_info->sgsn_tlli.current == local_sgsn_tlli); OSMO_ASSERT(!link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.ptmsi == sgsn_ptmsi2); send_llc_ul_ui(nsi, "RA UPD REQ (P-TMSI 3)", &bss_peer[0], 0x1002, local_bss_tlli2, &rai_bss, 0x7080, GPRS_SAPI_GMM, bss_nu++, dtap_ra_upd_req, sizeof(dtap_ra_upd_req)); send_llc_dl_ui(nsi, "RA UDP ACC (P-TMSI 3)", &sgsn_peer, 0x1002, local_sgsn_tlli2, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_ra_upd_acc3, sizeof(dtap_ra_upd_acc3)); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli2, SGSN_NSEI) == NULL); OSMO_ASSERT(gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli3, SGSN_NSEI) != NULL); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_bss_tlli3); OSMO_ASSERT(link_info->tlli.current == local_bss_tlli); OSMO_ASSERT(!link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->tlli.ptmsi == bss_ptmsi3); OSMO_ASSERT(link_info->sgsn_tlli.assigned == local_sgsn_tlli3); OSMO_ASSERT(link_info->sgsn_tlli.current == local_sgsn_tlli); OSMO_ASSERT(!link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.ptmsi == sgsn_ptmsi3); send_llc_ul_ui(nsi, "RA UPD COMPLETE", &bss_peer[0], 0x1002, local_bss_tlli3, &rai_bss, 0x7080, GPRS_SAPI_GMM, bss_nu++, dtap_ra_upd_complete, sizeof(dtap_ra_upd_complete)); link_info = gbproxy_link_info_by_tlli(peer, local_bss_tlli3); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); send_llc_dl_ui(nsi, "GMM INFO", &sgsn_peer, 0x1002, local_sgsn_tlli3, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_gmm_information, sizeof(dtap_gmm_information)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli3, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.current == local_bss_tlli3); OSMO_ASSERT(link_info->tlli.assigned == 0); OSMO_ASSERT(link_info->sgsn_tlli.current == local_sgsn_tlli3); OSMO_ASSERT(link_info->sgsn_tlli.assigned == 0); /* Other messages */ send_bssgp_llc_discarded(nsi, &bss_peer[0], 0x1002, local_bss_tlli3, 1, 12); dump_peers(stdout, 0, 0, &gbcfg); send_bssgp_suspend(nsi, &bss_peer[0], local_bss_tlli3, &rai_bss); dump_peers(stdout, 0, 0, &gbcfg); send_bssgp_suspend_ack(nsi, &sgsn_peer, local_sgsn_tlli3, &rai_sgsn); dump_peers(stdout, 0, 0, &gbcfg); old_ctr = peer->ctrg->ctr[GBPROX_PEER_CTR_PTMSI_PATCHED_SGSN].current; send_bssgp_paging(nsi, &sgsn_peer, imsi, sizeof(imsi), &rai_bss, sgsn_ptmsi3); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(old_ctr + 1 == peer->ctrg->ctr[GBPROX_PEER_CTR_PTMSI_PATCHED_SGSN].current); /* Bad case: Invalid BVCI */ send_bssgp_llc_discarded(nsi, &bss_peer[0], 0xeee1, local_bss_tlli3, 1, 12); dump_global(stdout, 0); /* Bad case: Invalid RAI */ send_bssgp_suspend_ack(nsi, &sgsn_peer, local_sgsn_tlli3, &rai_unknown); dump_global(stdout, 0); /* Bad case: Invalid MCC (LAC ok) */ send_bssgp_suspend_ack(nsi, &sgsn_peer, local_sgsn_tlli3, &rai_wrong_mcc_sgsn); dump_global(stdout, 0); /* Bad case: Invalid TLLI from SGSN (IMSI unknown) */ send_llc_dl_ui(nsi, "GMM INFO", &sgsn_peer, 0x1002, unknown_sgsn_tlli, 1, NULL, 0, GPRS_SAPI_GMM, 2, dtap_gmm_information, sizeof(dtap_gmm_information)); /* Bad case: Invalid TLLI from SGSN (IMSI known) */ send_llc_dl_ui(nsi, "GMM INFO", &sgsn_peer, 0x1002, unknown_sgsn_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, 3, dtap_gmm_information, sizeof(dtap_gmm_information)); /* Detach */ send_llc_ul_ui(nsi, "DETACH REQ", &bss_peer[0], 0x1002, local_bss_tlli3, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_detach_req, sizeof(dtap_detach_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "DETACH ACC", &sgsn_peer, 0x1002, local_sgsn_tlli3, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_detach_acc, sizeof(dtap_detach_acc)); dump_peers(stdout, 0, 0, &gbcfg); dump_global(stdout, 0); talloc_free(gbcfg.core_apn); gbcfg.core_apn = NULL; gbprox_reset(&gbcfg); gprs_ns_destroy(nsi); nsi = NULL; cleanup_test(); } static void test_gbproxy_ptmsi_patching_bad_cases() { struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, tall_bsc_ctx); struct sockaddr_in bss_peer[1] = {{0},}; struct sockaddr_in sgsn_peer= {0}; struct gprs_ra_id rai_bss = {.mcc = 112, .mnc = 332, .lac = 16464, .rac = 96}; struct gprs_ra_id rai_unknown = {.mcc = 1, .mnc = 99, .lac = 99, .rac = 96}; uint16_t cell_id = 0x1234; const uint32_t sgsn_ptmsi = 0xefe2b700; const uint32_t local_sgsn_tlli = 0xefe2b700; const uint32_t random_sgsn_tlli = 0x78dead00; const uint32_t bss_ptmsi = 0xc0dead01; const uint32_t local_bss_tlli = 0xc0dead01; const uint32_t foreign_bss_tlli = 0x8000dead; const uint8_t imsi[] = {0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}; struct gbproxy_link_info *link_info; struct gbproxy_peer *peer; unsigned bss_nu = 0; unsigned sgsn_nu = 0; OSMO_ASSERT(local_sgsn_tlli == gprs_tmsi2tlli(sgsn_ptmsi, TLLI_LOCAL)); OSMO_ASSERT(local_bss_tlli == gprs_tmsi2tlli(bss_ptmsi, TLLI_LOCAL)); bssgp_nsi = nsi; gbcfg.nsi = bssgp_nsi; gbcfg.nsip_sgsn_nsei = SGSN_NSEI; gbcfg.core_plmn = (struct osmo_plmn_id){ .mcc = 123, .mnc = 456 }; gbcfg.core_apn = talloc_zero_size(tall_bsc_ctx, 100); gbcfg.core_apn_size = gprs_str_to_apn(gbcfg.core_apn, 100, "foo.bar"); gbcfg.patch_ptmsi = 1; configure_sgsn_peer(&sgsn_peer); configure_bss_peers(bss_peer, ARRAY_SIZE(bss_peer)); printf("=== %s ===\n", __func__); printf("--- Initialise SGSN ---\n\n"); connect_sgsn(nsi, &sgsn_peer, SGSN_NSEI); printf("--- Initialise BSS 1 ---\n\n"); setup_ns(nsi, &bss_peer[0], 0x1001, 0x1000); setup_bssgp(nsi, &bss_peer[0], 0x1002); peer = gbproxy_peer_by_nsei(&gbcfg, 0x1000); OSMO_ASSERT(peer != NULL); send_bssgp_reset_ack(nsi, &sgsn_peer, 0x1002); gprs_dump_nsi(nsi); dump_global(stdout, 0); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Send message from BSS 1 to SGSN, BVCI 0x1002 ---\n\n"); send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_bss_tlli, &rai_unknown, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req, sizeof(dtap_attach_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "IDENT REQUEST", &sgsn_peer, 0x1002, random_sgsn_tlli, 0, NULL, 0, GPRS_SAPI_GMM, sgsn_nu++, dtap_identity_req, sizeof(dtap_identity_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_identity_resp, sizeof(dtap_identity_resp)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer, 0x1002, random_sgsn_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_sgsn_tlli(peer, random_sgsn_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_bss_tlli); OSMO_ASSERT(link_info->tlli.current == foreign_bss_tlli); OSMO_ASSERT(!link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->tlli.ptmsi == bss_ptmsi); OSMO_ASSERT(link_info->sgsn_tlli.assigned == local_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.current == random_sgsn_tlli); OSMO_ASSERT(!link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.ptmsi == sgsn_ptmsi); send_llc_dl_ui(nsi, "ATTACH ACCEPT (duplicated)", &sgsn_peer, 0x1002, random_sgsn_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_sgsn_tlli(peer, random_sgsn_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_bss_tlli); OSMO_ASSERT(link_info->tlli.current == foreign_bss_tlli); OSMO_ASSERT(!link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->tlli.ptmsi == bss_ptmsi); OSMO_ASSERT(link_info->sgsn_tlli.assigned == local_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.current == random_sgsn_tlli); OSMO_ASSERT(!link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.ptmsi == sgsn_ptmsi); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_bss_tlli); OSMO_ASSERT(link_info->tlli.current == foreign_bss_tlli); OSMO_ASSERT(link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.assigned == local_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.current == random_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); send_llc_dl_ui(nsi, "GMM INFO", &sgsn_peer, 0x1002, local_sgsn_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_gmm_information, sizeof(dtap_gmm_information)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.current == local_bss_tlli); OSMO_ASSERT(link_info->tlli.assigned == 0); OSMO_ASSERT(link_info->sgsn_tlli.current == local_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.assigned == 0); /* Detach */ send_llc_ul_ui(nsi, "DETACH REQ", &bss_peer[0], 0x1002, local_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_detach_req, sizeof(dtap_detach_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "DETACH ACC", &sgsn_peer, 0x1002, local_sgsn_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_detach_acc, sizeof(dtap_detach_acc)); dump_peers(stdout, 0, 0, &gbcfg); dump_global(stdout, 0); talloc_free(gbcfg.core_apn); gbcfg.core_apn = NULL; gbprox_reset(&gbcfg); gprs_ns_destroy(nsi); nsi = NULL; cleanup_test(); } static void test_gbproxy_imsi_acquisition() { struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, tall_bsc_ctx); struct sockaddr_in bss_peer[1] = {{0},}; struct sockaddr_in sgsn_peer= {0}; struct gprs_ra_id rai_bss = {.mcc = 112, .mnc = 332, .lac = 16464, .rac = 96}; struct gprs_ra_id rai_sgsn = {.mcc = 123, .mnc = 456, .lac = 16464, .rac = 96}; struct gprs_ra_id rai_wrong_mcc_sgsn = {.mcc = 999, .mnc = 456, .lac = 16464, .rac = 96}; struct gprs_ra_id rai_unknown = {.mcc = 1, .mnc = 99, .lac = 99, .rac = 96}; uint16_t cell_id = 0x1234; const uint32_t sgsn_ptmsi = 0xefe2b700; const uint32_t local_sgsn_tlli = 0xefe2b700; const uint32_t random_sgsn_tlli = 0x78dead00; const uint32_t random_sgsn_tlli2 = 0x78dead02; const uint32_t bss_ptmsi = 0xc0dead01; const uint32_t local_bss_tlli = 0xc0dead01; const uint32_t foreign_bss_tlli = 0x8000dead; const uint32_t other_bss_tlli = 0x8000beef; const uint8_t imsi[] = {0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}; struct gbproxy_link_info *link_info; struct gbproxy_peer *peer; unsigned bss_nu = 0; unsigned sgsn_nu = 0; OSMO_ASSERT(local_sgsn_tlli == gprs_tmsi2tlli(sgsn_ptmsi, TLLI_LOCAL)); bssgp_nsi = nsi; gbcfg.nsi = bssgp_nsi; gbcfg.nsip_sgsn_nsei = SGSN_NSEI; gbcfg.core_plmn = (struct osmo_plmn_id){ .mcc = 123, .mnc = 456 }; gbcfg.core_apn = talloc_zero_size(tall_bsc_ctx, 100); gbcfg.core_apn_size = gprs_str_to_apn(gbcfg.core_apn, 100, "foo.bar"); gbcfg.patch_ptmsi = 1; gbcfg.acquire_imsi = 1; configure_sgsn_peer(&sgsn_peer); configure_bss_peers(bss_peer, ARRAY_SIZE(bss_peer)); printf("=== %s ===\n", __func__); printf("--- Initialise SGSN ---\n\n"); connect_sgsn(nsi, &sgsn_peer, SGSN_NSEI); printf("--- Initialise BSS 1 ---\n\n"); setup_ns(nsi, &bss_peer[0], 0x1001, 0x1000); setup_bssgp(nsi, &bss_peer[0], 0x1002); peer = gbproxy_peer_by_nsei(&gbcfg, 0x1000); OSMO_ASSERT(peer != NULL); send_bssgp_reset_ack(nsi, &sgsn_peer, 0x1002); gprs_dump_nsi(nsi); dump_global(stdout, 0); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Send message from BSS 1 to SGSN, BVCI 0x1002 ---\n\n"); send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req, sizeof(dtap_attach_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_identity_resp, sizeof(dtap_identity_resp)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "IDENT REQUEST", &sgsn_peer, 0x1002, random_sgsn_tlli, 0, NULL, 0, GPRS_SAPI_GMM, sgsn_nu++, dtap_identity_req, sizeof(dtap_identity_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_identity_resp, sizeof(dtap_identity_resp)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer, 0x1002, random_sgsn_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_sgsn_tlli(peer, random_sgsn_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_bss_tlli); OSMO_ASSERT(link_info->tlli.current == foreign_bss_tlli); OSMO_ASSERT(!link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->tlli.ptmsi == bss_ptmsi); OSMO_ASSERT(link_info->sgsn_tlli.assigned == local_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.current == random_sgsn_tlli); OSMO_ASSERT(!link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.ptmsi == sgsn_ptmsi); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_bss_tlli); OSMO_ASSERT(link_info->tlli.current == foreign_bss_tlli); OSMO_ASSERT(link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.assigned == local_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.current == random_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); send_llc_dl_ui(nsi, "GMM INFO", &sgsn_peer, 0x1002, local_sgsn_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_gmm_information, sizeof(dtap_gmm_information)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.current == local_bss_tlli); OSMO_ASSERT(link_info->tlli.assigned == 0); OSMO_ASSERT(link_info->sgsn_tlli.current == local_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.assigned == 0); /* Non-DTAP */ send_bssgp_ul_unitdata(nsi, "XID (UL)", &bss_peer[0], 0x1002, local_bss_tlli, &rai_bss, cell_id, llc_u_xid_ul, sizeof(llc_u_xid_ul)); send_bssgp_dl_unitdata(nsi, "XID (DL)", &sgsn_peer, 0x1002, local_sgsn_tlli, 1, imsi, sizeof(imsi), llc_u_xid_dl, sizeof(llc_u_xid_dl)); send_bssgp_ul_unitdata(nsi, "LL11 DNS QUERY (UL)", &bss_peer[0], 0x1002, local_bss_tlli, &rai_bss, cell_id, llc_ui_ll11_dns_query_ul, sizeof(llc_ui_ll11_dns_query_ul)); send_bssgp_dl_unitdata(nsi, "LL11 DNS RESP (DL)", &sgsn_peer, 0x1002, local_sgsn_tlli, 1, imsi, sizeof(imsi), llc_ui_ll11_dns_resp_dl, sizeof(llc_ui_ll11_dns_resp_dl)); dump_peers(stdout, 0, 0, &gbcfg); /* Other messages */ send_bssgp_llc_discarded(nsi, &bss_peer[0], 0x1002, local_bss_tlli, 1, 12); dump_peers(stdout, 0, 0, &gbcfg); send_bssgp_llc_discarded(nsi, &sgsn_peer, 0x1002, local_sgsn_tlli, 1, 12); dump_peers(stdout, 0, 0, &gbcfg); send_bssgp_suspend(nsi, &bss_peer[0], local_bss_tlli, &rai_bss); dump_peers(stdout, 0, 0, &gbcfg); send_bssgp_suspend_ack(nsi, &sgsn_peer, local_sgsn_tlli, &rai_sgsn); dump_peers(stdout, 0, 0, &gbcfg); /* Bad case: Invalid BVCI */ send_bssgp_llc_discarded(nsi, &bss_peer[0], 0xeee1, local_bss_tlli, 1, 12); dump_global(stdout, 0); /* Bad case: Invalid RAI */ send_bssgp_suspend_ack(nsi, &sgsn_peer, local_sgsn_tlli, &rai_unknown); dump_global(stdout, 0); /* Bad case: Invalid MCC (LAC ok) */ send_bssgp_suspend_ack(nsi, &sgsn_peer, local_sgsn_tlli, &rai_wrong_mcc_sgsn); dump_global(stdout, 0); /* Detach */ send_llc_ul_ui(nsi, "DETACH REQ", &bss_peer[0], 0x1002, local_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_detach_req, sizeof(dtap_detach_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "DETACH ACC", &sgsn_peer, 0x1002, local_sgsn_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_detach_acc, sizeof(dtap_detach_acc)); dump_peers(stdout, 0, 0, &gbcfg); /* RA Update request */ send_llc_ul_ui(nsi, "RA UPD REQ", &bss_peer[0], 0x1002, foreign_bss_tlli, &rai_unknown, 0x7080, GPRS_SAPI_GMM, bss_nu++, dtap_ra_upd_req, sizeof(dtap_ra_upd_req)); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_identity_resp, sizeof(dtap_identity_resp)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "RA UDP ACC", &sgsn_peer, 0x1002, random_sgsn_tlli2, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_ra_upd_acc, sizeof(dtap_ra_upd_acc)); dump_peers(stdout, 0, 0, &gbcfg); /* Detach */ send_llc_ul_ui(nsi, "DETACH REQ", &bss_peer[0], 0x1002, local_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_detach_req, sizeof(dtap_detach_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "DETACH ACC", &sgsn_peer, 0x1002, local_sgsn_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_detach_acc, sizeof(dtap_detach_acc)); dump_peers(stdout, 0, 0, &gbcfg); /* Special case: Repeated Attach Requests */ send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_bss_tlli, &rai_unknown, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req, sizeof(dtap_attach_req)); send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_bss_tlli, &rai_unknown, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req, sizeof(dtap_attach_req)); send_llc_ul_ui(nsi, "DETACH REQ", &bss_peer[0], 0x1002, foreign_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_detach_req, sizeof(dtap_detach_req)); dump_peers(stdout, 0, 0, &gbcfg); /* Special case: Detach from an unknown TLLI */ send_llc_ul_ui(nsi, "DETACH REQ (unknown TLLI)", &bss_peer[0], 0x1002, other_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_detach_req, sizeof(dtap_detach_req)); dump_peers(stdout, 0, 0, &gbcfg); /* Special case: Repeated RA Update Requests */ send_llc_ul_ui(nsi, "RA UPD REQ", &bss_peer[0], 0x1002, foreign_bss_tlli, &rai_unknown, 0x7080, GPRS_SAPI_GMM, bss_nu++, dtap_ra_upd_req, sizeof(dtap_ra_upd_req)); send_llc_ul_ui(nsi, "RA UPD REQ", &bss_peer[0], 0x1002, foreign_bss_tlli, &rai_unknown, 0x7080, GPRS_SAPI_GMM, bss_nu++, dtap_ra_upd_req, sizeof(dtap_ra_upd_req)); send_llc_ul_ui(nsi, "DETACH REQ", &bss_peer[0], 0x1002, foreign_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_detach_req, sizeof(dtap_detach_req)); dump_peers(stdout, 0, 0, &gbcfg); dump_global(stdout, 0); talloc_free(gbcfg.core_apn); gbcfg.core_apn = NULL; gbprox_reset(&gbcfg); gprs_ns_destroy(nsi); nsi = NULL; cleanup_test(); } static void test_gbproxy_secondary_sgsn() { struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, tall_bsc_ctx); struct sockaddr_in bss_peer[1] = {{0},}; struct sockaddr_in sgsn_peer[2]= {{0},}; struct gprs_ra_id rai_bss = {.mcc = 112, .mnc = 332, .lac = 16464, .rac = 96}; struct gprs_ra_id rai_sgsn = {.mcc = 123, .mnc = 456, .lac = 16464, .rac = 96}; struct gprs_ra_id rai_unknown = {.mcc = 1, .mnc = 99, .lac = 99, .rac = 96}; uint16_t cell_id = 0x1234; const uint32_t sgsn_ptmsi = 0xefe2b700; const uint32_t local_sgsn_tlli = 0xefe2b700; const uint32_t random_sgsn_tlli = 0x78dead00; const uint32_t bss_ptmsi = 0xc0dead01; const uint32_t local_bss_tlli = 0xc0dead01; const uint32_t foreign_bss_tlli = 0x8000dead; const uint32_t sgsn_ptmsi2 = 0xe0987654; const uint32_t local_sgsn_tlli2 = 0xe0987654; const uint32_t random_sgsn_tlli2 = 0x78dead02; const uint32_t bss_ptmsi2 = 0xc0dead03; const uint32_t local_bss_tlli2 = 0xc0dead03; const uint32_t foreign_bss_tlli2 = 0x8000beef; const uint32_t random_sgsn_tlli3 = 0x78dead04; const uint32_t bss_ptmsi3 = 0xc0dead05; const uint32_t local_bss_tlli3 = 0xc0dead05; const uint32_t foreign_bss_tlli3 = 0x8000feed; const uint8_t imsi1[] = {0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}; const uint8_t imsi2[] = {0x11, 0x12, 0x99, 0x99, 0x99, 0x16, 0x17, 0x18}; const uint8_t imsi3[] = {0x11, 0x12, 0x99, 0x99, 0x99, 0x26, 0x27, 0x28}; struct gbproxy_link_info *link_info; struct gbproxy_link_info *other_info; struct gbproxy_peer *peer; unsigned bss_nu = 0; unsigned sgsn_nu = 0; const char *err_msg = NULL; const char *filter_re = "999999"; OSMO_ASSERT(local_sgsn_tlli == gprs_tmsi2tlli(sgsn_ptmsi, TLLI_LOCAL)); OSMO_ASSERT(local_sgsn_tlli2 == gprs_tmsi2tlli(sgsn_ptmsi2, TLLI_LOCAL)); bssgp_nsi = nsi; gbcfg.nsi = bssgp_nsi; gbcfg.nsip_sgsn_nsei = SGSN_NSEI; gbcfg.core_plmn = (struct osmo_plmn_id){ .mcc = 123, .mnc = 456 }; gbcfg.core_apn = talloc_zero_size(tall_bsc_ctx, 100); gbcfg.core_apn_size = gprs_str_to_apn(gbcfg.core_apn, 100, "foo.bar"); gbcfg.patch_ptmsi = 1; gbcfg.acquire_imsi = 1; gbcfg.route_to_sgsn2 = 1; gbcfg.nsip_sgsn2_nsei = SGSN2_NSEI; if (gbproxy_set_patch_filter(&gbcfg.matches[GBPROX_MATCH_ROUTING], filter_re, &err_msg) != 0) { fprintf(stderr, "gbprox_set_patch_filter: got error: %s\n", err_msg); OSMO_ASSERT(err_msg == NULL); } configure_sgsn_peer(&sgsn_peer[0]); configure_sgsn2_peer(&sgsn_peer[1]); configure_bss_peers(bss_peer, ARRAY_SIZE(bss_peer)); printf("=== %s ===\n", __func__); printf("--- Initialise SGSN 1 ---\n\n"); connect_sgsn(nsi, &sgsn_peer[0], SGSN_NSEI); printf("--- Initialise SGSN 2 ---\n\n"); connect_sgsn(nsi, &sgsn_peer[1], SGSN2_NSEI); printf("--- Initialise BSS 1 ---\n\n"); setup_ns(nsi, &bss_peer[0], 0x1001, 0x1000); setup_bssgp(nsi, &bss_peer[0], 0x0); send_bssgp_reset_ack(nsi, &sgsn_peer[0], 0x0); setup_bssgp(nsi, &bss_peer[0], 0x1002); send_bssgp_reset_ack(nsi, &sgsn_peer[0], 0x1002); send_bssgp_reset_ack(nsi, &sgsn_peer[1], 0x1002); peer = gbproxy_peer_by_nsei(&gbcfg, 0x1000); OSMO_ASSERT(peer != NULL); gprs_dump_nsi(nsi); dump_global(stdout, 0); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Flow control ---\n\n"); send_bssgp_flow_control_bvc(nsi, &bss_peer[0], 0x1002, 1); send_bssgp_flow_control_bvc_ack(nsi, &sgsn_peer[0], 0x1002, 1); send_bssgp_flow_control_bvc_ack(nsi, &sgsn_peer[1], 0x1002, 1); printf("--- Establish GPRS connection (SGSN 1) ---\n\n"); send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_bss_tlli, &rai_unknown, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req, sizeof(dtap_attach_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_identity_resp, sizeof(dtap_identity_resp)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "IDENT REQUEST", &sgsn_peer[0], 0x1002, random_sgsn_tlli, 0, NULL, 0, GPRS_SAPI_GMM, sgsn_nu++, dtap_identity_req, sizeof(dtap_identity_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_identity_resp, sizeof(dtap_identity_resp)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer[0], 0x1002, random_sgsn_tlli, 1, imsi1, sizeof(imsi1), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli, SGSN2_NSEI)); link_info = gbproxy_link_info_by_sgsn_tlli(peer, random_sgsn_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_bss_tlli); OSMO_ASSERT(link_info->tlli.current == foreign_bss_tlli); OSMO_ASSERT(!link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->tlli.ptmsi == bss_ptmsi); OSMO_ASSERT(link_info->sgsn_tlli.assigned == local_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.current == random_sgsn_tlli); OSMO_ASSERT(!link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.ptmsi == sgsn_ptmsi); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli, SGSN2_NSEI)); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_bss_tlli); OSMO_ASSERT(link_info->tlli.current == foreign_bss_tlli); OSMO_ASSERT(link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.assigned == local_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.current == random_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); send_llc_dl_ui(nsi, "GMM INFO", &sgsn_peer[0], 0x1002, local_sgsn_tlli, 1, imsi1, sizeof(imsi1), GPRS_SAPI_GMM, sgsn_nu++, dtap_gmm_information, sizeof(dtap_gmm_information)); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli, SGSN2_NSEI)); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.current == local_bss_tlli); OSMO_ASSERT(link_info->tlli.assigned == 0); OSMO_ASSERT(link_info->sgsn_tlli.current == local_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.assigned == 0); /* Non-DTAP */ send_bssgp_ul_unitdata(nsi, "XID (UL)", &bss_peer[0], 0x1002, local_bss_tlli, &rai_bss, cell_id, llc_u_xid_ul, sizeof(llc_u_xid_ul)); send_bssgp_dl_unitdata(nsi, "XID (DL)", &sgsn_peer[0], 0x1002, local_sgsn_tlli, 1, imsi1, sizeof(imsi1), llc_u_xid_dl, sizeof(llc_u_xid_dl)); send_bssgp_ul_unitdata(nsi, "LL11 DNS QUERY (UL)", &bss_peer[0], 0x1002, local_bss_tlli, &rai_bss, cell_id, llc_ui_ll11_dns_query_ul, sizeof(llc_ui_ll11_dns_query_ul)); send_bssgp_dl_unitdata(nsi, "LL11 DNS RESP (DL)", &sgsn_peer[0], 0x1002, local_sgsn_tlli, 1, imsi1, sizeof(imsi1), llc_ui_ll11_dns_resp_dl, sizeof(llc_ui_ll11_dns_resp_dl)); dump_peers(stdout, 0, 0, &gbcfg); /* Other messages */ send_bssgp_llc_discarded(nsi, &bss_peer[0], 0x1002, local_bss_tlli, 1, 12); dump_peers(stdout, 0, 0, &gbcfg); send_bssgp_llc_discarded(nsi, &sgsn_peer[0], 0x1002, local_sgsn_tlli, 1, 12); dump_peers(stdout, 0, 0, &gbcfg); send_bssgp_suspend(nsi, &bss_peer[0], local_bss_tlli, &rai_bss); dump_peers(stdout, 0, 0, &gbcfg); send_bssgp_suspend_ack(nsi, &sgsn_peer[0], local_sgsn_tlli, &rai_sgsn); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Establish GPRS connection (SGSN 2) ---\n\n"); send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_bss_tlli2, &rai_unknown, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req, sizeof(dtap_attach_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_bss_tlli2, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_identity2_resp, sizeof(dtap_identity2_resp)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "IDENT REQUEST", &sgsn_peer[1], 0x1002, random_sgsn_tlli2, 0, NULL, 0, GPRS_SAPI_GMM, sgsn_nu++, dtap_identity_req, sizeof(dtap_identity_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_bss_tlli2, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_identity2_resp, sizeof(dtap_identity2_resp)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer[1], 0x1002, random_sgsn_tlli2, 1, imsi2, sizeof(imsi2), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc2, sizeof(dtap_attach_acc2)); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!gbproxy_link_info_by_sgsn_tlli(peer, random_sgsn_tlli2, SGSN_NSEI)); link_info = gbproxy_link_info_by_sgsn_tlli(peer, random_sgsn_tlli2, SGSN2_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_bss_tlli2); OSMO_ASSERT(link_info->tlli.current == foreign_bss_tlli2); OSMO_ASSERT(!link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->tlli.ptmsi == bss_ptmsi2); OSMO_ASSERT(link_info->sgsn_tlli.assigned == local_sgsn_tlli2); OSMO_ASSERT(link_info->sgsn_tlli.current == random_sgsn_tlli2); OSMO_ASSERT(!link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.ptmsi == sgsn_ptmsi2); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_bss_tlli2, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli2, SGSN_NSEI)); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli2, SGSN2_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_bss_tlli2); OSMO_ASSERT(link_info->tlli.current == foreign_bss_tlli2); OSMO_ASSERT(link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.assigned == local_sgsn_tlli2); OSMO_ASSERT(link_info->sgsn_tlli.current == random_sgsn_tlli2); OSMO_ASSERT(link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); send_llc_dl_ui(nsi, "GMM INFO", &sgsn_peer[1], 0x1002, local_sgsn_tlli2, 1, imsi2, sizeof(imsi2), GPRS_SAPI_GMM, sgsn_nu++, dtap_gmm_information, sizeof(dtap_gmm_information)); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli2, SGSN_NSEI)); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli2, SGSN2_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.current == local_bss_tlli2); OSMO_ASSERT(link_info->tlli.assigned == 0); OSMO_ASSERT(link_info->sgsn_tlli.current == local_sgsn_tlli2); OSMO_ASSERT(link_info->sgsn_tlli.assigned == 0); /* Non-DTAP */ send_bssgp_ul_unitdata(nsi, "XID (UL)", &bss_peer[0], 0x1002, local_bss_tlli2, &rai_bss, cell_id, llc_u_xid_ul, sizeof(llc_u_xid_ul)); send_bssgp_dl_unitdata(nsi, "XID (DL)", &sgsn_peer[1], 0x1002, local_sgsn_tlli2, 1, imsi2, sizeof(imsi2), llc_u_xid_dl, sizeof(llc_u_xid_dl)); send_bssgp_ul_unitdata(nsi, "LL11 DNS QUERY (UL)", &bss_peer[0], 0x1002, local_bss_tlli2, &rai_bss, cell_id, llc_ui_ll11_dns_query_ul, sizeof(llc_ui_ll11_dns_query_ul)); send_bssgp_dl_unitdata(nsi, "LL11 DNS RESP (DL)", &sgsn_peer[1], 0x1002, local_sgsn_tlli2, 1, imsi2, sizeof(imsi2), llc_ui_ll11_dns_resp_dl, sizeof(llc_ui_ll11_dns_resp_dl)); dump_peers(stdout, 0, 0, &gbcfg); /* Other messages */ send_bssgp_llc_discarded(nsi, &bss_peer[0], 0x1002, local_bss_tlli2, 1, 12); dump_peers(stdout, 0, 0, &gbcfg); send_bssgp_llc_discarded(nsi, &sgsn_peer[1], 0x1002, local_sgsn_tlli2, 1, 12); dump_peers(stdout, 0, 0, &gbcfg); send_bssgp_suspend(nsi, &bss_peer[0], local_bss_tlli2, &rai_bss); dump_peers(stdout, 0, 0, &gbcfg); send_bssgp_suspend_ack(nsi, &sgsn_peer[1], local_sgsn_tlli2, &rai_sgsn); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Establish GPRS connection (SGSN 2, P-TMSI collision) ---\n\n"); send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_bss_tlli3, &rai_unknown, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req, sizeof(dtap_attach_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_bss_tlli3, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_identity3_resp, sizeof(dtap_identity3_resp)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "IDENT REQUEST", &sgsn_peer[1], 0x1002, random_sgsn_tlli3, 0, NULL, 0, GPRS_SAPI_GMM, sgsn_nu++, dtap_identity_req, sizeof(dtap_identity_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_bss_tlli3, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_identity3_resp, sizeof(dtap_identity3_resp)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "ATTACH ACCEPT (P-TMSI 1)", &sgsn_peer[1], 0x1002, random_sgsn_tlli3, 1, imsi3, sizeof(imsi3), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!gbproxy_link_info_by_sgsn_tlli(peer, random_sgsn_tlli3, SGSN_NSEI)); link_info = gbproxy_link_info_by_sgsn_tlli(peer, random_sgsn_tlli3, SGSN2_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_bss_tlli3); OSMO_ASSERT(link_info->tlli.current == foreign_bss_tlli3); OSMO_ASSERT(!link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->tlli.ptmsi == bss_ptmsi3); OSMO_ASSERT(link_info->sgsn_tlli.assigned == local_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.current == random_sgsn_tlli3); OSMO_ASSERT(!link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.ptmsi == sgsn_ptmsi); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_bss_tlli3, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); dump_peers(stdout, 0, 0, &gbcfg); other_info = gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli, SGSN_NSEI); OSMO_ASSERT(other_info); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli, SGSN2_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info != other_info); OSMO_ASSERT(link_info->tlli.assigned == local_bss_tlli3); OSMO_ASSERT(link_info->tlli.current == foreign_bss_tlli3); OSMO_ASSERT(link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.assigned == local_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.current == random_sgsn_tlli3); OSMO_ASSERT(link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); send_llc_dl_ui(nsi, "GMM INFO", &sgsn_peer[1], 0x1002, local_sgsn_tlli, 1, imsi3, sizeof(imsi3), GPRS_SAPI_GMM, sgsn_nu++, dtap_gmm_information, sizeof(dtap_gmm_information)); dump_peers(stdout, 0, 0, &gbcfg); other_info = gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli, SGSN_NSEI); OSMO_ASSERT(other_info); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli, SGSN2_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info != other_info); OSMO_ASSERT(link_info->tlli.current == local_bss_tlli3); OSMO_ASSERT(link_info->tlli.assigned == 0); OSMO_ASSERT(link_info->sgsn_tlli.current == local_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.assigned == 0); printf("--- Shutdown GPRS connection (SGSN 1) ---\n\n"); /* Detach */ send_llc_ul_ui(nsi, "DETACH REQ", &bss_peer[0], 0x1002, local_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_detach_req, sizeof(dtap_detach_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "DETACH ACC", &sgsn_peer[0], 0x1002, local_sgsn_tlli, 1, imsi1, sizeof(imsi1), GPRS_SAPI_GMM, sgsn_nu++, dtap_detach_acc, sizeof(dtap_detach_acc)); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Shutdown GPRS connection (SGSN 2) ---\n\n"); send_llc_ul_ui(nsi, "DETACH REQ", &bss_peer[0], 0x1002, local_bss_tlli2, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_detach_req, sizeof(dtap_detach_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "DETACH ACC", &sgsn_peer[1], 0x1002, local_sgsn_tlli2, 1, imsi2, sizeof(imsi2), GPRS_SAPI_GMM, sgsn_nu++, dtap_detach_acc, sizeof(dtap_detach_acc)); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Shutdown GPRS connection (SGSN 2, P-TMSI 1) ---\n\n"); send_llc_ul_ui(nsi, "DETACH REQ", &bss_peer[0], 0x1002, local_bss_tlli3, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_detach_req, sizeof(dtap_detach_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "DETACH ACC", &sgsn_peer[1], 0x1002, local_sgsn_tlli, 1, imsi3, sizeof(imsi3), GPRS_SAPI_GMM, sgsn_nu++, dtap_detach_acc, sizeof(dtap_detach_acc)); dump_peers(stdout, 0, 0, &gbcfg); dump_global(stdout, 0); talloc_free(gbcfg.core_apn); gbcfg.core_apn = NULL; gbproxy_clear_patch_filter(&gbcfg.matches[GBPROX_MATCH_ROUTING]); gbprox_reset(&gbcfg); gprs_ns_destroy(nsi); nsi = NULL; cleanup_test(); } static void test_gbproxy_keep_info() { struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, tall_bsc_ctx); struct sockaddr_in bss_peer[1] = {{0},}; struct sockaddr_in sgsn_peer= {0}; struct gprs_ra_id rai_bss = {.mcc = 112, .mnc = 332, .lac = 16464, .rac = 96}; uint16_t cell_id = 0x1234; const uint32_t ptmsi = 0xefe2b700; const uint32_t local_tlli = 0xefe2b700; const uint32_t foreign_tlli = 0xafe2b700; const uint8_t imsi[] = {0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}; struct gbproxy_link_info *link_info, *link_info2; struct gbproxy_peer *peer; unsigned bss_nu = 0; unsigned sgsn_nu = 0; LLIST_HEAD(rcv_list); OSMO_ASSERT(local_tlli == gprs_tmsi2tlli(ptmsi, TLLI_LOCAL)); bssgp_nsi = nsi; gbcfg.nsi = bssgp_nsi; gbcfg.nsip_sgsn_nsei = SGSN_NSEI; gbcfg.patch_ptmsi = 0; gbcfg.acquire_imsi = 1; gbcfg.core_plmn = (struct osmo_plmn_id){}; gbcfg.core_apn = NULL; gbcfg.core_apn_size = 0; gbcfg.route_to_sgsn2 = 0; gbcfg.nsip_sgsn2_nsei = 0xffff; gbcfg.keep_link_infos = GBPROX_KEEP_ALWAYS; configure_sgsn_peer(&sgsn_peer); configure_bss_peers(bss_peer, ARRAY_SIZE(bss_peer)); printf("=== %s ===\n", __func__); printf("--- Initialise SGSN ---\n\n"); connect_sgsn(nsi, &sgsn_peer, SGSN_NSEI); printf("--- Initialise BSS 1 ---\n\n"); setup_ns(nsi, &bss_peer[0], 0x1001, 0x1000); setup_bssgp(nsi, &bss_peer[0], 0x1002); peer = gbproxy_peer_by_nsei(&gbcfg, 0x1000); OSMO_ASSERT(peer != NULL); send_bssgp_reset_ack(nsi, &sgsn_peer, 0x1002); gprs_dump_nsi(nsi); dump_global(stdout, 0); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Send message from BSS 1 to SGSN, BVCI 0x1002 ---\n\n"); received_messages = &rcv_list; send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req, sizeof(dtap_attach_req)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ID_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->imsi_len == 0); OSMO_ASSERT(!link_info->is_deregistered); OSMO_ASSERT(link_info->imsi_acq_pending); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_identity_resp, sizeof(dtap_identity_resp)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->imsi_len > 0); OSMO_ASSERT(!link_info->imsi_acq_pending); OSMO_ASSERT(gprs_tlli_type(link_info->sgsn_tlli.current) == TLLI_FOREIGN); send_llc_dl_ui(nsi, "IDENT REQUEST", &sgsn_peer, 0x1002, foreign_tlli, 0, NULL, 0, GPRS_SAPI_GMM, sgsn_nu++, dtap_identity_req, sizeof(dtap_identity_req)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ID_REQ)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_identity_resp, sizeof(dtap_identity_resp)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ID_RESP)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->imsi_len > 0); OSMO_ASSERT(gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi))); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer, 0x1002, foreign_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ATTACH_ACK)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_COMPL)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "GMM INFO", &sgsn_peer, 0x1002, local_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_gmm_information, sizeof(dtap_gmm_information)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_INFO)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, local_tlli); OSMO_ASSERT(link_info); /* Detach (MO) */ send_llc_ul_ui(nsi, "DETACH REQ", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_detach_req, sizeof(dtap_detach_req)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_DETACH_REQ)); link_info = gbproxy_link_info_by_tlli(peer, local_tlli); OSMO_ASSERT(link_info); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "DETACH ACC", &sgsn_peer, 0x1002, local_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_detach_acc, sizeof(dtap_detach_acc)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_DETACH_ACK)); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!gbproxy_link_info_by_tlli(peer, local_tlli)); link_info = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->is_deregistered); OSMO_ASSERT(!expect_msg()); /* Re-Attach */ send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req3, sizeof(dtap_attach_req3)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info2 = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info == link_info2); OSMO_ASSERT(link_info->imsi_len != 0); OSMO_ASSERT(!link_info->is_deregistered); OSMO_ASSERT(!link_info->imsi_acq_pending); OSMO_ASSERT(gprs_tlli_type(link_info->sgsn_tlli.current) == TLLI_FOREIGN); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer, 0x1002, foreign_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ATTACH_ACK)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_COMPL)); dump_peers(stdout, 0, 0, &gbcfg); /* Detach (MT) */ send_llc_dl_ui(nsi, "DETACH REQ (re-attach)", &sgsn_peer, 0x1002, local_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_mt_detach_rea_req, sizeof(dtap_mt_detach_rea_req)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_DETACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, local_tlli); OSMO_ASSERT(link_info); send_llc_ul_ui(nsi, "DETACH ACC", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_mt_detach_acc, sizeof(dtap_mt_detach_acc)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_DETACH_ACK)); OSMO_ASSERT(!expect_msg()); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!gbproxy_link_info_by_tlli(peer, local_tlli)); link_info = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->is_deregistered); /* Re-Attach */ send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req3, sizeof(dtap_attach_req3)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info2 = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info == link_info2); OSMO_ASSERT(link_info->imsi_len != 0); OSMO_ASSERT(!link_info->is_deregistered); OSMO_ASSERT(!link_info->imsi_acq_pending); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer, 0x1002, foreign_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ATTACH_ACK)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_COMPL)); dump_peers(stdout, 0, 0, &gbcfg); /* Detach (MT) */ send_llc_dl_ui(nsi, "DETACH REQ", &sgsn_peer, 0x1002, local_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_mt_detach_req, sizeof(dtap_mt_detach_req)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_DETACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, local_tlli); OSMO_ASSERT(link_info); send_llc_ul_ui(nsi, "DETACH ACC", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_mt_detach_acc, sizeof(dtap_mt_detach_acc)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_DETACH_ACK)); OSMO_ASSERT(!expect_msg()); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!gbproxy_link_info_by_tlli(peer, local_tlli)); link_info = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->is_deregistered); /* Re-Attach with IMSI */ send_llc_ul_ui(nsi, "ATTACH REQUEST (IMSI)", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req4, sizeof(dtap_attach_req4)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info2 = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info == link_info2); OSMO_ASSERT(link_info->imsi_len != 0); OSMO_ASSERT(!link_info->is_deregistered); OSMO_ASSERT(!link_info->imsi_acq_pending); OSMO_ASSERT(link_info->sgsn_tlli.current == foreign_tlli); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer, 0x1002, foreign_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ATTACH_ACK)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_COMPL)); dump_peers(stdout, 0, 0, &gbcfg); /* Detach (MT) */ send_llc_dl_ui(nsi, "DETACH REQ", &sgsn_peer, 0x1002, local_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_mt_detach_req, sizeof(dtap_mt_detach_req)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_DETACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, local_tlli); OSMO_ASSERT(link_info); send_llc_ul_ui(nsi, "DETACH ACC", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_mt_detach_acc, sizeof(dtap_mt_detach_acc)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_DETACH_ACK)); OSMO_ASSERT(!expect_msg()); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!gbproxy_link_info_by_tlli(peer, local_tlli)); link_info = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->is_deregistered); /* Re-Attach */ send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req3, sizeof(dtap_attach_req3)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info2 = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info == link_info2); OSMO_ASSERT(link_info->imsi_len != 0); OSMO_ASSERT(!link_info->is_deregistered); OSMO_ASSERT(!link_info->imsi_acq_pending); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer, 0x1002, foreign_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ATTACH_ACK)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_COMPL)); dump_peers(stdout, 0, 0, &gbcfg); /* RA update procedure (reject -> Detach) */ send_llc_ul_ui(nsi, "RA UPD REQ", &bss_peer[0], 0x1002, local_tlli, &rai_bss, 0x7080, GPRS_SAPI_GMM, bss_nu++, dtap_ra_upd_req, sizeof(dtap_ra_upd_req)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_RA_UPD_REQ)); send_llc_dl_ui(nsi, "RA UDP REJ", &sgsn_peer, 0x1002, local_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_ra_upd_rej, sizeof(dtap_ra_upd_rej)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_RA_UPD_REJ)); OSMO_ASSERT(!expect_msg()); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!gbproxy_link_info_by_tlli(peer, local_tlli)); link_info = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->is_deregistered); /* Bad case: Re-Attach with wrong (initial) P-TMSI */ send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req, sizeof(dtap_attach_req)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ID_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info2 = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info != link_info2); OSMO_ASSERT(link_info->imsi_len == 0); OSMO_ASSERT(!link_info->is_deregistered); OSMO_ASSERT(link_info->imsi_acq_pending); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_identity_resp, sizeof(dtap_identity_resp)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info2 = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info == link_info2); OSMO_ASSERT(link_info->imsi_len != 0); OSMO_ASSERT(!link_info->is_deregistered); OSMO_ASSERT(!link_info->imsi_acq_pending); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer, 0x1002, foreign_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ATTACH_ACK)); dump_peers(stdout, 0, 0, &gbcfg); link_info2 = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info == link_info2); OSMO_ASSERT(link_info->imsi_len > 0); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_COMPL)); dump_peers(stdout, 0, 0, &gbcfg); /* Detach (MT) */ send_llc_dl_ui(nsi, "DETACH REQ", &sgsn_peer, 0x1002, local_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_mt_detach_req, sizeof(dtap_mt_detach_req)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_DETACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, local_tlli); OSMO_ASSERT(link_info); send_llc_ul_ui(nsi, "DETACH ACC", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_mt_detach_acc, sizeof(dtap_mt_detach_acc)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_DETACH_ACK)); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!gbproxy_link_info_by_tlli(peer, local_tlli)); link_info = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->is_deregistered); OSMO_ASSERT(!expect_msg()); /* Bad case: Re-Attach with local TLLI */ send_llc_ul_ui(nsi, "ATTACH REQUEST (local TLLI)", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req3, sizeof(dtap_attach_req3)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info2 = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); link_info = gbproxy_link_info_by_tlli(peer, local_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info == link_info2); OSMO_ASSERT(link_info->imsi_len != 0); OSMO_ASSERT(!link_info->is_deregistered); OSMO_ASSERT(!link_info->imsi_acq_pending); OSMO_ASSERT(link_info->sgsn_tlli.current == local_tlli); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer, 0x1002, local_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ATTACH_ACK)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_COMPL)); dump_peers(stdout, 0, 0, &gbcfg); /* Detach (MT) */ send_llc_dl_ui(nsi, "DETACH REQ (re-attach)", &sgsn_peer, 0x1002, local_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_mt_detach_rea_req, sizeof(dtap_mt_detach_rea_req)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_DETACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, local_tlli); OSMO_ASSERT(link_info); send_llc_ul_ui(nsi, "DETACH ACC", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_mt_detach_acc, sizeof(dtap_mt_detach_acc)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_DETACH_ACK)); OSMO_ASSERT(!expect_msg()); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!gbproxy_link_info_by_tlli(peer, local_tlli)); link_info = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->is_deregistered); /* Bad case: Unexpected Re-Attach with IMSI after completed attachment * procedure */ send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req3, sizeof(dtap_attach_req3)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info2 = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info == link_info2); OSMO_ASSERT(link_info->imsi_len != 0); OSMO_ASSERT(!link_info->is_deregistered); OSMO_ASSERT(!link_info->imsi_acq_pending); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer, 0x1002, foreign_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ATTACH_ACK)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_COMPL)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "GMM INFO", &sgsn_peer, 0x1002, local_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_gmm_information, sizeof(dtap_gmm_information)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_INFO)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "ATTACH REQUEST (unexpected, IMSI)", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req4, sizeof(dtap_attach_req4)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info2 = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info == link_info2); OSMO_ASSERT(link_info->imsi_len != 0); OSMO_ASSERT(!link_info->is_deregistered); OSMO_ASSERT(!link_info->imsi_acq_pending); OSMO_ASSERT(link_info->sgsn_tlli.current == foreign_tlli); OSMO_ASSERT(link_info->sgsn_tlli.assigned == 0); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer, 0x1002, foreign_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ATTACH_ACK)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_COMPL)); dump_peers(stdout, 0, 0, &gbcfg); /* Detach (MT) */ send_llc_dl_ui(nsi, "DETACH REQ", &sgsn_peer, 0x1002, local_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_mt_detach_req, sizeof(dtap_mt_detach_req)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_DETACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, local_tlli); OSMO_ASSERT(link_info); send_llc_ul_ui(nsi, "DETACH ACC", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_mt_detach_acc, sizeof(dtap_mt_detach_acc)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_DETACH_ACK)); OSMO_ASSERT(!expect_msg()); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!gbproxy_link_info_by_tlli(peer, local_tlli)); link_info = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->is_deregistered); /* Bad case: Unexpected Re-Attach with P-TMSI after completed attachment * procedure */ send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req3, sizeof(dtap_attach_req3)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info2 = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info == link_info2); OSMO_ASSERT(link_info->imsi_len != 0); OSMO_ASSERT(!link_info->is_deregistered); OSMO_ASSERT(!link_info->imsi_acq_pending); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer, 0x1002, foreign_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ATTACH_ACK)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_COMPL)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "GMM INFO", &sgsn_peer, 0x1002, local_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_gmm_information, sizeof(dtap_gmm_information)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_INFO)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "ATTACH REQUEST (unexpected)", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req3, sizeof(dtap_attach_req3)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info2 = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info == link_info2); OSMO_ASSERT(link_info->imsi_len != 0); OSMO_ASSERT(!link_info->is_deregistered); OSMO_ASSERT(!link_info->imsi_acq_pending); OSMO_ASSERT(link_info->sgsn_tlli.current == foreign_tlli); OSMO_ASSERT(link_info->sgsn_tlli.assigned == 0); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer, 0x1002, foreign_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ATTACH_ACK)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_COMPL)); dump_peers(stdout, 0, 0, &gbcfg); /* Detach (MT) */ send_llc_dl_ui(nsi, "DETACH REQ", &sgsn_peer, 0x1002, local_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_mt_detach_req, sizeof(dtap_mt_detach_req)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_DETACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, local_tlli); OSMO_ASSERT(link_info); send_llc_ul_ui(nsi, "DETACH ACC", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_mt_detach_acc, sizeof(dtap_mt_detach_acc)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_DETACH_ACK)); OSMO_ASSERT(!expect_msg()); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!gbproxy_link_info_by_tlli(peer, local_tlli)); link_info = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->is_deregistered); /* Attach rejected */ gbproxy_delete_link_infos(peer); send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req, sizeof(dtap_attach_req)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ID_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->imsi_len == 0); OSMO_ASSERT(!link_info->is_deregistered); OSMO_ASSERT(link_info->imsi_acq_pending); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_identity_resp, sizeof(dtap_identity_resp)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info2 = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info == link_info2); OSMO_ASSERT(link_info->imsi_len != 0); OSMO_ASSERT(!link_info->is_deregistered); OSMO_ASSERT(!link_info->imsi_acq_pending); send_llc_dl_ui(nsi, "ATTACH REJECT", &sgsn_peer, 0x1002, foreign_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_rej7, sizeof(dtap_attach_rej7)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ATTACH_REJ)); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!gbproxy_link_info_by_tlli(peer, foreign_tlli)); OSMO_ASSERT(!expect_msg()); /* Attach (incomplete) and Detach (MO) */ gbproxy_delete_link_infos(peer); send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req, sizeof(dtap_attach_req)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ID_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->imsi_len == 0); OSMO_ASSERT(!link_info->is_deregistered); OSMO_ASSERT(link_info->imsi_acq_pending); send_llc_ul_ui(nsi, "DETACH REQ (MO)", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_detach_req, sizeof(dtap_detach_req)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_DETACH_ACK)); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!expect_msg()); /* Attach (incomplete) and Detach (MT) */ gbproxy_delete_link_infos(peer); send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req, sizeof(dtap_attach_req)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ID_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->imsi_len == 0); OSMO_ASSERT(!link_info->is_deregistered); OSMO_ASSERT(link_info->imsi_acq_pending); send_llc_dl_ui(nsi, "DETACH REQ (MT)", &sgsn_peer, 0x1002, foreign_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_mt_detach_req, sizeof(dtap_mt_detach_req)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_DETACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); send_llc_ul_ui(nsi, "DETACH ACC", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_mt_detach_acc, sizeof(dtap_mt_detach_acc)); /* TODO: The stored messaged should be cleaned when receiving a Detach * Ack. Remove the first OSMO_ASSERT when this is fixed. */ OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_REQ)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_DETACH_ACK)); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!gbproxy_link_info_by_tlli(peer, foreign_tlli)); link_info = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->is_deregistered); OSMO_ASSERT(!expect_msg()); received_messages = NULL; dump_global(stdout, 0); talloc_free(gbcfg.core_apn); gbcfg.core_apn = NULL; gbprox_reset(&gbcfg); gprs_ns_destroy(nsi); nsi = NULL; cleanup_test(); } struct gbproxy_link_info *register_tlli( struct gbproxy_peer *peer, uint32_t tlli, const uint8_t *imsi, size_t imsi_len, time_t now) { struct gbproxy_link_info *link_info; int imsi_matches = -1; int tlli_already_known = 0; struct gbproxy_config *cfg = peer->cfg; /* Check, whether the IMSI matches */ if (gprs_is_mi_imsi(imsi, imsi_len)) { imsi_matches = gbproxy_check_imsi( &cfg->matches[GBPROX_MATCH_PATCHING], imsi, imsi_len); if (imsi_matches < 0) return NULL; } link_info = gbproxy_link_info_by_tlli(peer, tlli); if (!link_info) { link_info = gbproxy_link_info_by_imsi(peer, imsi, imsi_len); if (link_info) { /* TLLI has changed somehow, adjust it */ LOGP(DGPRS, LOGL_INFO, "The TLLI has changed from %08x to %08x\n", link_info->tlli.current, tlli); link_info->tlli.current = tlli; } } if (!link_info) { link_info = gbproxy_link_info_alloc(peer); link_info->tlli.current = tlli; } else { gbproxy_detach_link_info(peer, link_info); tlli_already_known = 1; } OSMO_ASSERT(link_info != NULL); if (!tlli_already_known) LOGP(DGPRS, LOGL_INFO, "Adding TLLI %08x to list\n", tlli); gbproxy_attach_link_info(peer, now, link_info); gbproxy_update_link_info(link_info, imsi, imsi_len); if (imsi_matches >= 0) link_info->is_matching[GBPROX_MATCH_PATCHING] = imsi_matches; return link_info; } static void test_gbproxy_tlli_expire(void) { struct gbproxy_config cfg = {0}; struct gbproxy_peer *peer; const char *err_msg = NULL; const uint8_t imsi1[] = { GSM_MI_TYPE_IMSI, 0x23, 0x24, 0x25, 0x26 }; const uint8_t imsi2[] = { GSM_MI_TYPE_IMSI, 0x26, 0x27, 0x28, 0x29 }; const uint8_t imsi3[] = { GSM_MI_TYPE_IMSI | 0x10, 0x32, 0x54, 0x76, 0xf8 }; const uint32_t tlli1 = 1234 | 0xc0000000; const uint32_t tlli2 = 5678 | 0xc0000000; const uint32_t tlli3 = 3456 | 0xc0000000; const char *filter_re = ".*"; time_t now = 1407479214; printf("Test TLLI info expiry\n\n"); gbproxy_init_config(&cfg); if (gbproxy_set_patch_filter(&cfg.matches[GBPROX_MATCH_PATCHING], filter_re, &err_msg) != 0) { fprintf(stderr, "gbprox_set_patch_filter: got error: %s\n", err_msg); OSMO_ASSERT(err_msg == NULL); } { struct gbproxy_link_info *link_info; printf("Test TLLI replacement:\n"); cfg.tlli_max_len = 0; cfg.tlli_max_age = 0; peer = gbproxy_peer_alloc(&cfg, 20); OSMO_ASSERT(peer->patch_state.logical_link_count == 0); printf(" Add TLLI 1, IMSI 1\n"); link_info = register_tlli(peer, tlli1, imsi1, ARRAY_SIZE(imsi1), now); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.current == tlli1); OSMO_ASSERT(peer->patch_state.logical_link_count == 1); /* replace the old entry */ printf(" Add TLLI 2, IMSI 1 (should replace TLLI 1)\n"); link_info = register_tlli(peer, tlli2, imsi1, ARRAY_SIZE(imsi1), now); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.current == tlli2); OSMO_ASSERT(peer->patch_state.logical_link_count == 1); dump_peers(stdout, 2, now, &cfg); /* verify that 5678 has survived */ link_info = gbproxy_link_info_by_imsi(peer, imsi1, ARRAY_SIZE(imsi1)); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.current == tlli2); link_info = gbproxy_link_info_by_imsi(peer, imsi2, ARRAY_SIZE(imsi2)); OSMO_ASSERT(!link_info); printf("\n"); gbproxy_peer_free(peer); } { struct gbproxy_link_info *link_info; printf("Test IMSI replacement:\n"); cfg.tlli_max_len = 0; cfg.tlli_max_age = 0; peer = gbproxy_peer_alloc(&cfg, 20); OSMO_ASSERT(peer->patch_state.logical_link_count == 0); printf(" Add TLLI 1, IMSI 1\n"); link_info = register_tlli(peer, tlli1, imsi1, ARRAY_SIZE(imsi1), now); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.current == tlli1); OSMO_ASSERT(peer->patch_state.logical_link_count == 1); /* try to replace the old entry */ printf(" Add TLLI 1, IMSI 2 (should replace IMSI 1)\n"); link_info = register_tlli(peer, tlli1, imsi2, ARRAY_SIZE(imsi2), now); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.current == tlli1); OSMO_ASSERT(peer->patch_state.logical_link_count == 1); dump_peers(stdout, 2, now, &cfg); /* verify that 5678 has survived */ link_info = gbproxy_link_info_by_imsi(peer, imsi1, ARRAY_SIZE(imsi1)); OSMO_ASSERT(!link_info); link_info = gbproxy_link_info_by_imsi(peer, imsi2, ARRAY_SIZE(imsi2)); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.current == tlli1); printf("\n"); gbproxy_peer_free(peer); } { struct gbproxy_link_info *link_info; int num_removed; printf("Test TLLI expiry, max_len == 1:\n"); cfg.tlli_max_len = 1; cfg.tlli_max_age = 0; peer = gbproxy_peer_alloc(&cfg, 20); OSMO_ASSERT(peer->patch_state.logical_link_count == 0); printf(" Add TLLI 1, IMSI 1\n"); register_tlli(peer, tlli1, imsi1, ARRAY_SIZE(imsi1), now); OSMO_ASSERT(peer->patch_state.logical_link_count == 1); /* replace the old entry */ printf(" Add TLLI 2, IMSI 2 (should replace IMSI 1)\n"); register_tlli(peer, tlli2, imsi2, ARRAY_SIZE(imsi2), now); OSMO_ASSERT(peer->patch_state.logical_link_count == 2); num_removed = gbproxy_remove_stale_link_infos(peer, now + 2); OSMO_ASSERT(num_removed == 1); OSMO_ASSERT(peer->patch_state.logical_link_count == 1); dump_peers(stdout, 2, now, &cfg); /* verify that 5678 has survived */ link_info = gbproxy_link_info_by_imsi(peer, imsi1, ARRAY_SIZE(imsi1)); OSMO_ASSERT(!link_info); link_info = gbproxy_link_info_by_imsi(peer, imsi2, ARRAY_SIZE(imsi2)); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.current == tlli2); printf("\n"); gbproxy_peer_free(peer); } { struct gbproxy_link_info *link_info; int num_removed; printf("Test TLLI expiry, max_age == 1:\n"); cfg.tlli_max_len = 0; cfg.tlli_max_age = 1; peer = gbproxy_peer_alloc(&cfg, 20); OSMO_ASSERT(peer->patch_state.logical_link_count == 0); printf(" Add TLLI 1, IMSI 1 (should expire after timeout)\n"); register_tlli(peer, tlli1, imsi1, ARRAY_SIZE(imsi1), now); OSMO_ASSERT(peer->patch_state.logical_link_count == 1); printf(" Add TLLI 2, IMSI 2 (should not expire after timeout)\n"); register_tlli(peer, tlli2, imsi2, ARRAY_SIZE(imsi2), now + 1); OSMO_ASSERT(peer->patch_state.logical_link_count == 2); num_removed = gbproxy_remove_stale_link_infos(peer, now + 2); OSMO_ASSERT(num_removed == 1); OSMO_ASSERT(peer->patch_state.logical_link_count == 1); dump_peers(stdout, 2, now + 2, &cfg); /* verify that 5678 has survived */ link_info = gbproxy_link_info_by_imsi(peer, imsi1, ARRAY_SIZE(imsi1)); OSMO_ASSERT(!link_info); link_info = gbproxy_link_info_by_imsi(peer, imsi2, ARRAY_SIZE(imsi2)); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.current == tlli2); printf("\n"); gbproxy_peer_free(peer); } { struct gbproxy_link_info *link_info; int num_removed; printf("Test TLLI expiry, max_len == 2, max_age == 1:\n"); cfg.tlli_max_len = 0; cfg.tlli_max_age = 1; peer = gbproxy_peer_alloc(&cfg, 20); OSMO_ASSERT(peer->patch_state.logical_link_count == 0); printf(" Add TLLI 1, IMSI 1 (should expire)\n"); register_tlli(peer, tlli1, imsi1, ARRAY_SIZE(imsi1), now); OSMO_ASSERT(peer->patch_state.logical_link_count == 1); printf(" Add TLLI 2, IMSI 2 (should expire after timeout)\n"); register_tlli(peer, tlli2, imsi2, ARRAY_SIZE(imsi2), now + 1); OSMO_ASSERT(peer->patch_state.logical_link_count == 2); printf(" Add TLLI 3, IMSI 3 (should not expire after timeout)\n"); register_tlli(peer, tlli3, imsi3, ARRAY_SIZE(imsi3), now + 2); OSMO_ASSERT(peer->patch_state.logical_link_count == 3); dump_peers(stdout, 2, now + 2, &cfg); printf(" Remove stale TLLIs\n"); num_removed = gbproxy_remove_stale_link_infos(peer, now + 3); OSMO_ASSERT(num_removed == 2); OSMO_ASSERT(peer->patch_state.logical_link_count == 1); dump_peers(stdout, 2, now + 2, &cfg); /* verify that tlli3 has survived */ link_info = gbproxy_link_info_by_imsi(peer, imsi1, ARRAY_SIZE(imsi1)); OSMO_ASSERT(!link_info); link_info = gbproxy_link_info_by_imsi(peer, imsi2, ARRAY_SIZE(imsi2)); OSMO_ASSERT(!link_info); link_info = gbproxy_link_info_by_imsi(peer, imsi3, ARRAY_SIZE(imsi3)); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.current == tlli3); printf("\n"); gbproxy_peer_free(peer); } gbproxy_clear_patch_filter(&cfg.matches[GBPROX_MATCH_PATCHING]); gbprox_reset(&cfg); /* gbprox_reset() frees the rate_ctr, but re-allocates it again. */ rate_ctr_group_free(cfg.ctrg); cleanup_test(); } static void test_gbproxy_imsi_matching(void) { const char *err_msg = NULL; const uint8_t imsi1[] = { GSM_MI_TYPE_IMSI | 0x10, 0x32, 0x54, 0xf6 }; const uint8_t imsi2[] = { GSM_MI_TYPE_IMSI | GSM_MI_ODD | 0x10, 0x32, 0x54, 0x76 }; const uint8_t imsi3_bad[] = { GSM_MI_TYPE_IMSI | 0x10, 0xee, 0x54, 0xff }; const uint8_t tmsi1[] = { GSM_MI_TYPE_TMSI | 0xf0, 0x11, 0x22, 0x33, 0x44 }; const uint8_t tmsi2_bad[] = { GSM_MI_TYPE_TMSI | 0xf0, 0x11, 0x22 }; const uint8_t imei1[] = { GSM_MI_TYPE_IMEI | 0x10, 0x32, 0x54, 0xf6 }; const uint8_t imei2[] = { GSM_MI_TYPE_IMEI | GSM_MI_ODD | 0x10, 0x32, 0x54, 0x76 }; const char *filter_re1 = ".*"; const char *filter_re2 = "^1234"; const char *filter_re3 = "^4321"; const char *filter_re4_bad = "^12["; struct gbproxy_match match = {0,}; printf("=== Test IMSI/TMSI matching ===\n\n"); OSMO_ASSERT(match.enable == 0); OSMO_ASSERT(gbproxy_set_patch_filter(&match, filter_re1, &err_msg) == 0); OSMO_ASSERT(match.enable == 1); OSMO_ASSERT(gbproxy_set_patch_filter(&match, filter_re2, &err_msg) == 0); OSMO_ASSERT(match.enable == 1); err_msg = NULL; OSMO_ASSERT(gbproxy_set_patch_filter(&match, filter_re4_bad, &err_msg) == -1); OSMO_ASSERT(err_msg != NULL); OSMO_ASSERT(match.enable == 0); OSMO_ASSERT(gbproxy_set_patch_filter(&match, filter_re2, &err_msg) == 0); OSMO_ASSERT(match.enable == 1); OSMO_ASSERT(gbproxy_set_patch_filter(&match, NULL, &err_msg) == 0); OSMO_ASSERT(match.enable == 0); OSMO_ASSERT(gbproxy_set_patch_filter(&match, filter_re2, &err_msg) == 0); OSMO_ASSERT(match.enable == 1); gbproxy_clear_patch_filter(&match); OSMO_ASSERT(match.enable == 0); OSMO_ASSERT(gbproxy_set_patch_filter(&match, filter_re2, &err_msg) == 0); OSMO_ASSERT(match.enable == 1); OSMO_ASSERT(gbproxy_check_imsi(&match, imsi1, ARRAY_SIZE(imsi1)) == 1); OSMO_ASSERT(gbproxy_check_imsi(&match, imsi2, ARRAY_SIZE(imsi2)) == 1); /* imsi3_bad contains 0xE and 0xF digits, but the conversion function * doesn't complain, so gbproxy_check_imsi() doesn't return -1 in this * case. */ OSMO_ASSERT(gbproxy_check_imsi(&match, imsi3_bad, ARRAY_SIZE(imsi3_bad)) == 0); OSMO_ASSERT(gbproxy_check_imsi(&match, tmsi1, ARRAY_SIZE(tmsi1)) == -1); OSMO_ASSERT(gbproxy_check_imsi(&match, tmsi2_bad, ARRAY_SIZE(tmsi2_bad)) == -1); OSMO_ASSERT(gbproxy_check_imsi(&match, imei1, ARRAY_SIZE(imei1)) == -1); OSMO_ASSERT(gbproxy_check_imsi(&match, imei2, ARRAY_SIZE(imei2)) == -1); OSMO_ASSERT(gbproxy_set_patch_filter(&match, filter_re3, &err_msg) == 0); OSMO_ASSERT(match.enable == 1); OSMO_ASSERT(gbproxy_check_imsi(&match, imsi1, ARRAY_SIZE(imsi1)) == 0); OSMO_ASSERT(gbproxy_check_imsi(&match, imsi2, ARRAY_SIZE(imsi2)) == 0); OSMO_ASSERT(gbproxy_check_imsi(&match, imsi3_bad, ARRAY_SIZE(imsi3_bad)) == 0); OSMO_ASSERT(gbproxy_check_imsi(&match, tmsi1, ARRAY_SIZE(tmsi1)) == -1); OSMO_ASSERT(gbproxy_check_imsi(&match, tmsi2_bad, ARRAY_SIZE(tmsi2_bad)) == -1); OSMO_ASSERT(gbproxy_check_imsi(&match, imei1, ARRAY_SIZE(imei1)) == -1); OSMO_ASSERT(gbproxy_check_imsi(&match, imei2, ARRAY_SIZE(imei2)) == -1); /* TODO: Check correct length but wrong type with is_mi_tmsi */ gbproxy_clear_patch_filter(&match); OSMO_ASSERT(match.enable == 0); cleanup_test(); } static void test_gbproxy_stored_messages() { struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, tall_bsc_ctx); struct sockaddr_in bss_peer[1] = {{0},}; struct sockaddr_in sgsn_peer= {0}; struct gprs_ra_id rai_bss = {.mcc = 112, .mnc = 332, .lac = 16464, .rac = 96}; struct gprs_ra_id rai_unknown = {.mcc = 1, .mnc = 99, .lac = 99, .rac = 96}; uint16_t cell_id = 0x1234; const uint32_t ptmsi = 0xefe2b700; const uint32_t local_tlli = 0xefe2b700; const uint32_t foreign_tlli1 = 0x8000dead; struct gbproxy_peer *peer; unsigned bss_nu = 0; unsigned sgsn_nu = 0; OSMO_ASSERT(local_tlli == gprs_tmsi2tlli(ptmsi, TLLI_LOCAL)); bssgp_nsi = nsi; gbcfg.nsi = bssgp_nsi; gbcfg.nsip_sgsn_nsei = SGSN_NSEI; gbcfg.core_plmn = (struct osmo_plmn_id){}; gbcfg.core_apn = talloc_zero_size(tall_bsc_ctx, 100); gbcfg.core_apn_size = gprs_str_to_apn(gbcfg.core_apn, 100, "foo.bar"); gbcfg.patch_ptmsi = 0; gbcfg.acquire_imsi = 1; gbcfg.keep_link_infos = 0; configure_sgsn_peer(&sgsn_peer); configure_bss_peers(bss_peer, ARRAY_SIZE(bss_peer)); printf("=== %s ===\n", __func__); printf("--- Initialise SGSN ---\n\n"); connect_sgsn(nsi, &sgsn_peer, SGSN_NSEI); printf("--- Initialise BSS 1 ---\n\n"); setup_ns(nsi, &bss_peer[0], 0x1001, 0x1000); setup_bssgp(nsi, &bss_peer[0], 0x1002); peer = gbproxy_peer_by_nsei(&gbcfg, 0x1000); OSMO_ASSERT(peer != NULL); send_bssgp_reset_ack(nsi, &sgsn_peer, 0x1002); gprs_dump_nsi(nsi); dump_global(stdout, 0); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Establish first LLC connection ---\n\n"); send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_tlli1, &rai_unknown, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req, sizeof(dtap_attach_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "IDENT REQUEST", &sgsn_peer, 0x1002, foreign_tlli1, 0, NULL, 0, GPRS_SAPI_GMM, sgsn_nu++, dtap_identity_req, sizeof(dtap_identity_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "DETACH ACCEPT", &bss_peer[0], 0x1002, foreign_tlli1, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_detach_acc, sizeof(dtap_detach_acc)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_tlli1, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_identity_resp, sizeof(dtap_identity_resp)); dump_peers(stdout, 0, 0, &gbcfg); dump_global(stdout, 0); talloc_free(gbcfg.core_apn); gbcfg.core_apn = NULL; gbprox_reset(&gbcfg); gprs_ns_destroy(nsi); nsi = NULL; cleanup_test(); } static struct log_info_cat gprs_categories[] = { [DGPRS] = { .name = "DGPRS", .description = "GPRS Packet Service", .enabled = 1, .loglevel = LOGL_DEBUG, }, [DNS] = { .name = "DNS", .description = "GPRS Network Service (NS)", .enabled = 1, .loglevel = LOGL_INFO, }, [DBSSGP] = { .name = "DBSSGP", .description = "GPRS BSS Gateway Protocol (BSSGP)", .enabled = 1, .loglevel = LOGL_DEBUG, }, }; static struct log_info info = { .cat = gprs_categories, .num_cat = ARRAY_SIZE(gprs_categories), }; int main(int argc, char **argv) { talloc_enable_leak_report(); tall_bsc_ctx = talloc_named_const(NULL, 0, "gbproxy_test"); void *log_ctx = talloc_named_const(tall_bsc_ctx, 0, "log"); msgb_talloc_ctx_init(tall_bsc_ctx, 0); osmo_init_logging2(log_ctx, &info); log_set_use_color(osmo_stderr_target, 0); log_set_print_filename(osmo_stderr_target, 0); osmo_signal_register_handler(SS_L_NS, &test_signal, &gbcfg); log_set_print_filename(osmo_stderr_target, 0); log_set_log_level(osmo_stderr_target, LOGL_DEBUG); log_set_all_filter(osmo_stderr_target, 1); rate_ctr_init(tall_bsc_ctx); setlinebuf(stdout); printf("===== GbProxy test START\n"); gbproxy_init_config(&gbcfg); test_gbproxy(); test_gbproxy_ident_changes(); test_gbproxy_imsi_matching(); test_gbproxy_ptmsi_assignment(); test_gbproxy_ra_patching(); test_gbproxy_ptmsi_patching(); test_gbproxy_ptmsi_patching_bad_cases(); test_gbproxy_imsi_acquisition(); test_gbproxy_secondary_sgsn(); test_gbproxy_keep_info(); test_gbproxy_tlli_expire(); test_gbproxy_stored_messages(); gbprox_reset(&gbcfg); /* gbprox_reset() frees the rate_ctr, but re-allocates it again. */ rate_ctr_group_free(gbcfg.ctrg); printf("===== GbProxy test END\n\n"); talloc_free(log_ctx); /* expecting root and msgb ctx, empty */ OSMO_ASSERT(talloc_total_blocks(tall_bsc_ctx) == 2); talloc_free(tall_bsc_ctx); return 0; } osmo-sgsn-1.3.0/tests/gbproxy/gbproxy_test.ok000066400000000000000000012023421327264017000213660ustar00rootroot00000000000000===== GbProxy test START === test_gbproxy === --- Initialise SGSN --- MESSAGE to SGSN at 0x05060708:32000, msg length 12 02 00 81 01 01 82 01 01 04 82 01 00 PROCESSING RESET_ACK from 0x05060708:32000 03 01 82 01 01 04 82 01 00 MESSAGE to SGSN at 0x05060708:32000, msg length 1 0a result (RESET_ACK) = 1 PROCESSING ALIVE_ACK from 0x05060708:32000 0b MESSAGE to SGSN at 0x05060708:32000, msg length 1 06 result (ALIVE_ACK) = 1 PROCESSING UNBLOCK_ACK from 0x05060708:32000 07 ==> got signal NS_UNBLOCK, NS-VC 0x0101/5.6.7.8:32000 result (UNBLOCK_ACK) = 0 PROCESSING ALIVE from 0x05060708:32000 0a MESSAGE to SGSN at 0x05060708:32000, msg length 1 0b result (ALIVE) = 1 Current NS-VCIs: VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 Packets at NS Level ( In): 4 Packets at NS Level (Out): 4 Bytes at NS Level ( In): 12 Bytes at NS Level (Out): 15 NS-VC Block count : 1 --- Initialise BSS 1 --- Setup NS-VC: remote 0x01020304:1111, NSVCI 0x1001(4097), NSEI 0x1000(4096) PROCESSING RESET from 0x01020304:1111 02 00 81 01 01 82 10 01 04 82 10 00 ==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 9 03 01 82 10 01 04 82 10 00 MESSAGE to BSS at 0x01020304:1111, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:1111 0a MESSAGE to BSS at 0x01020304:1111, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:1111 06 ==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:1111 0b result (ALIVE_ACK) = 0 Setup BSSGP: remote 0x01020304:1111, BVCI 0x1002(4098) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 result (BVC_RESET) = 22 Current NS-VCIs: VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111 Packets at NS Level ( In): 5 Packets at NS Level (Out): 4 Bytes at NS Level ( In): 37 Bytes at NS Level (Out): 12 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 Packets at NS Level ( In): 4 Packets at NS Level (Out): 5 Bytes at NS Level ( In): 12 Bytes at NS Level (Out): 37 NS-VC Block count : 1 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 10 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 10 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 10 02 result (BVC_RESET_ACK) = 9 --- Initialise BSS 2 --- Setup NS-VC: remote 0x01020304:2222, NSVCI 0x2001(8193), NSEI 0x2000(8192) PROCESSING RESET from 0x01020304:2222 02 00 81 01 01 82 20 01 04 82 20 00 ==> got signal NS_RESET, NS-VC 0x2001/1.2.3.4:2222 MESSAGE to BSS at 0x01020304:2222, msg length 9 03 01 82 20 01 04 82 20 00 MESSAGE to BSS at 0x01020304:2222, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:2222 0a MESSAGE to BSS at 0x01020304:2222, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:2222 06 ==> got signal NS_UNBLOCK, NS-VC 0x2001/1.2.3.4:2222 MESSAGE to BSS at 0x01020304:2222, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:2222 0b result (ALIVE_ACK) = 0 Setup BSSGP: remote 0x01020304:2222, BVCI 0x2002(8194) PROCESSING BVC_RESET from 0x01020304:2222 00 00 00 00 22 04 82 20 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 20 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 20 02 07 81 08 08 88 11 22 33 40 50 60 10 00 result (BVC_RESET) = 22 Current NS-VCIs: VCI 0x2001, NSEI 0x2000, peer 0x01020304:2222 Packets at NS Level ( In): 5 Packets at NS Level (Out): 4 Bytes at NS Level ( In): 37 Bytes at NS Level (Out): 12 VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111 Packets at NS Level ( In): 5 Packets at NS Level (Out): 5 Bytes at NS Level ( In): 37 Bytes at NS Level (Out): 21 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 Packets at NS Level ( In): 5 Packets at NS Level (Out): 6 Bytes at NS Level ( In): 21 Bytes at NS Level (Out): 59 NS-VC Block count : 1 Peers: NSEI 8192, BVCI 8194, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 20 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 20 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:2222, msg length 9 00 00 00 00 23 04 82 20 02 result (BVC_RESET_ACK) = 9 --- Move BSS 1 to new port --- Setup NS-VC: remote 0x01020304:3333, NSVCI 0x1001(4097), NSEI 0x1000(4096) PROCESSING RESET from 0x01020304:3333 02 00 81 01 01 82 10 01 04 82 10 00 ==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:3333 MESSAGE to BSS at 0x01020304:3333, msg length 9 03 01 82 10 01 04 82 10 00 MESSAGE to BSS at 0x01020304:3333, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:3333 0a MESSAGE to BSS at 0x01020304:3333, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:3333 06 ==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:3333 MESSAGE to BSS at 0x01020304:3333, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:3333 0b result (ALIVE_ACK) = 0 Current NS-VCIs: VCI 0x2001, NSEI 0x2000, peer 0x01020304:2222 Packets at NS Level ( In): 5 Packets at NS Level (Out): 5 Bytes at NS Level ( In): 37 Bytes at NS Level (Out): 21 VCI 0x1001, NSEI 0x1000, peer 0x01020304:3333 Packets at NS Level ( In): 9 Packets at NS Level (Out): 9 Bytes at NS Level ( In): 52 Bytes at NS Level (Out): 33 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 Packets at NS Level ( In): 6 Packets at NS Level (Out): 6 Bytes at NS Level ( In): 30 Bytes at NS Level (Out): 59 NS-VC Block count : 1 Peers: NSEI 8192, BVCI 8194, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 --- Move BSS 2 to former BSS 1 port --- Setup NS-VC: remote 0x01020304:1111, NSVCI 0x2001(8193), NSEI 0x2000(8192) PROCESSING RESET from 0x01020304:1111 02 00 81 01 01 82 20 01 04 82 20 00 ==> got signal NS_RESET, NS-VC 0x2001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 9 03 01 82 20 01 04 82 20 00 MESSAGE to BSS at 0x01020304:1111, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:1111 0a MESSAGE to BSS at 0x01020304:1111, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:1111 06 ==> got signal NS_UNBLOCK, NS-VC 0x2001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:1111 0b result (ALIVE_ACK) = 0 Current NS-VCIs: VCI 0x2001, NSEI 0x2000, peer 0x01020304:1111 Packets at NS Level ( In): 9 Packets at NS Level (Out): 9 Bytes at NS Level ( In): 52 Bytes at NS Level (Out): 33 VCI 0x1001, NSEI 0x1000, peer 0x01020304:3333 Packets at NS Level ( In): 9 Packets at NS Level (Out): 9 Bytes at NS Level ( In): 52 Bytes at NS Level (Out): 33 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 Packets at NS Level ( In): 6 Packets at NS Level (Out): 6 Bytes at NS Level ( In): 30 Bytes at NS Level (Out): 59 NS-VC Block count : 1 Peers: NSEI 8192, BVCI 8194, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 --- Move BSS 1 to current BSS 2 port --- Setup NS-VC: remote 0x01020304:1111, NSVCI 0x2001(8193), NSEI 0x2000(8192) PROCESSING RESET from 0x01020304:1111 02 00 81 01 01 82 20 01 04 82 20 00 ==> got signal NS_RESET, NS-VC 0x2001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 9 03 01 82 20 01 04 82 20 00 MESSAGE to BSS at 0x01020304:1111, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:1111 0a MESSAGE to BSS at 0x01020304:1111, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:1111 06 ==> got signal NS_UNBLOCK, NS-VC 0x2001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:1111 0b result (ALIVE_ACK) = 0 Current NS-VCIs: VCI 0x2001, NSEI 0x2000, peer 0x01020304:1111 Packets at NS Level ( In): 13 Packets at NS Level (Out): 13 Bytes at NS Level ( In): 67 Bytes at NS Level (Out): 45 VCI 0x1001, NSEI 0x1000, peer 0x01020304:3333 Packets at NS Level ( In): 9 Packets at NS Level (Out): 9 Bytes at NS Level ( In): 52 Bytes at NS Level (Out): 33 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 Packets at NS Level ( In): 6 Packets at NS Level (Out): 6 Bytes at NS Level ( In): 30 Bytes at NS Level (Out): 59 NS-VC Block count : 1 Peers: NSEI 8192, BVCI 8194, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 --- Move BSS 2 to new port --- Setup NS-VC: remote 0x01020304:4444, NSVCI 0x2001(8193), NSEI 0x2000(8192) PROCESSING RESET from 0x01020304:4444 02 00 81 01 01 82 20 01 04 82 20 00 ==> got signal NS_RESET, NS-VC 0x2001/1.2.3.4:4444 MESSAGE to BSS at 0x01020304:4444, msg length 9 03 01 82 20 01 04 82 20 00 MESSAGE to BSS at 0x01020304:4444, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:4444 0a MESSAGE to BSS at 0x01020304:4444, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:4444 06 ==> got signal NS_UNBLOCK, NS-VC 0x2001/1.2.3.4:4444 MESSAGE to BSS at 0x01020304:4444, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:4444 0b result (ALIVE_ACK) = 0 Current NS-VCIs: VCI 0x2001, NSEI 0x2000, peer 0x01020304:4444 Packets at NS Level ( In): 17 Packets at NS Level (Out): 17 Bytes at NS Level ( In): 82 Bytes at NS Level (Out): 57 VCI 0x1001, NSEI 0x1000, peer 0x01020304:3333 Packets at NS Level ( In): 9 Packets at NS Level (Out): 9 Bytes at NS Level ( In): 52 Bytes at NS Level (Out): 33 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 Packets at NS Level ( In): 6 Packets at NS Level (Out): 6 Bytes at NS Level ( In): 30 Bytes at NS Level (Out): 59 NS-VC Block count : 1 Peers: NSEI 8192, BVCI 8194, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 --- Move BSS 2 to former BSS 1 port --- Setup NS-VC: remote 0x01020304:3333, NSVCI 0x2001(8193), NSEI 0x2000(8192) PROCESSING RESET from 0x01020304:3333 02 00 81 01 01 82 20 01 04 82 20 00 ==> got signal NS_REPLACED: 0x2001/1.2.3.4:4444 -> 0x1001/1.2.3.4:3333 ==> got signal NS_RESET, NS-VC 0x2001/1.2.3.4:3333 MESSAGE to BSS at 0x01020304:3333, msg length 9 03 01 82 20 01 04 82 20 00 MESSAGE to BSS at 0x01020304:3333, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:3333 0a MESSAGE to BSS at 0x01020304:3333, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:3333 06 ==> got signal NS_UNBLOCK, NS-VC 0x2001/1.2.3.4:3333 MESSAGE to BSS at 0x01020304:3333, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:3333 0b result (ALIVE_ACK) = 0 Current NS-VCIs: VCI 0x2001, NSEI 0x2000, peer 0x01020304:3333 Packets at NS Level ( In): 20 Packets at NS Level (Out): 21 Bytes at NS Level ( In): 85 Bytes at NS Level (Out): 69 NS-VC replaced other count: 1 VCI 0x1001, NSEI 0x1000, peer 0x00000000:0 Packets at NS Level ( In): 10 Packets at NS Level (Out): 9 Bytes at NS Level ( In): 64 Bytes at NS Level (Out): 33 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 Packets at NS Level ( In): 6 Packets at NS Level (Out): 6 Bytes at NS Level ( In): 30 Bytes at NS Level (Out): 59 NS-VC Block count : 1 Peers: NSEI 8192, BVCI 8194, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 --- Move BSS 1 to original BSS 1 port --- Setup NS-VC: remote 0x01020304:1111, NSVCI 0x1001(4097), NSEI 0x1000(4096) PROCESSING RESET from 0x01020304:1111 02 00 81 01 01 82 10 01 04 82 10 00 ==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 9 03 01 82 10 01 04 82 10 00 MESSAGE to BSS at 0x01020304:1111, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:1111 0a MESSAGE to BSS at 0x01020304:1111, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:1111 06 ==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:1111 0b result (ALIVE_ACK) = 0 Current NS-VCIs: VCI 0x2001, NSEI 0x2000, peer 0x01020304:3333 Packets at NS Level ( In): 20 Packets at NS Level (Out): 21 Bytes at NS Level ( In): 85 Bytes at NS Level (Out): 69 NS-VC replaced other count: 1 VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111 Packets at NS Level ( In): 14 Packets at NS Level (Out): 13 Bytes at NS Level ( In): 79 Bytes at NS Level (Out): 45 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 Packets at NS Level ( In): 6 Packets at NS Level (Out): 6 Bytes at NS Level ( In): 30 Bytes at NS Level (Out): 59 NS-VC Block count : 1 Peers: NSEI 8192, BVCI 8194, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 --- Reset BSS 1 with a new BVCI --- Setup BSSGP: remote 0x01020304:1111, BVCI 0x1012(4114) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 10 12 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 10 12 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 10 12 07 81 08 08 88 11 22 33 40 50 60 10 00 result (BVC_RESET) = 22 Current NS-VCIs: VCI 0x2001, NSEI 0x2000, peer 0x01020304:3333 Packets at NS Level ( In): 20 Packets at NS Level (Out): 21 Bytes at NS Level ( In): 85 Bytes at NS Level (Out): 69 NS-VC replaced other count: 1 VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111 Packets at NS Level ( In): 15 Packets at NS Level (Out): 13 Bytes at NS Level ( In): 101 Bytes at NS Level (Out): 45 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 Packets at NS Level ( In): 6 Packets at NS Level (Out): 7 Bytes at NS Level ( In): 30 Bytes at NS Level (Out): 81 NS-VC Block count : 1 Peers: NSEI 4096, BVCI 4114, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 8192, BVCI 8194, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 10 12 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 10 12 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 10 12 result (BVC_RESET_ACK) = 9 --- Reset BSS 1 with the old BVCI --- Setup BSSGP: remote 0x01020304:1111, BVCI 0x1002(4098) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 result (BVC_RESET) = 22 Current NS-VCIs: VCI 0x2001, NSEI 0x2000, peer 0x01020304:3333 Packets at NS Level ( In): 20 Packets at NS Level (Out): 21 Bytes at NS Level ( In): 85 Bytes at NS Level (Out): 69 NS-VC replaced other count: 1 VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111 Packets at NS Level ( In): 16 Packets at NS Level (Out): 14 Bytes at NS Level ( In): 123 Bytes at NS Level (Out): 54 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 Packets at NS Level ( In): 7 Packets at NS Level (Out): 8 Bytes at NS Level ( In): 39 Bytes at NS Level (Out): 103 NS-VC Block count : 1 Peers: NSEI 4096, BVCI 4114, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 8192, BVCI 8194, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 10 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 10 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 10 02 result (BVC_RESET_ACK) = 9 --- Reset BSS 1 with the old BVCI again --- Setup BSSGP: remote 0x01020304:1111, BVCI 0x1002(4098) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 result (BVC_RESET) = 22 Current NS-VCIs: VCI 0x2001, NSEI 0x2000, peer 0x01020304:3333 Packets at NS Level ( In): 20 Packets at NS Level (Out): 21 Bytes at NS Level ( In): 85 Bytes at NS Level (Out): 69 NS-VC replaced other count: 1 VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111 Packets at NS Level ( In): 17 Packets at NS Level (Out): 15 Bytes at NS Level ( In): 145 Bytes at NS Level (Out): 63 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 Packets at NS Level ( In): 8 Packets at NS Level (Out): 9 Bytes at NS Level ( In): 48 Bytes at NS Level (Out): 125 NS-VC Block count : 1 Peers: NSEI 4096, BVCI 4114, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 8192, BVCI 8194, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 10 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 10 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 10 02 result (BVC_RESET_ACK) = 9 --- Send message from BSS 1 to SGSN, BVCI 0x1012 --- PROCESSING UNITDATA from 0x01020304:1111 00 00 10 12 CALLBACK, event 0, msg length 0, bvci 0x1012 00 00 10 12 NS UNITDATA MESSAGE to SGSN, BVCI 0x1012, msg length 0 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 4 00 00 10 12 result (UNITDATA) = 4 --- Send message from SGSN to BSS 1, BVCI 0x1012 --- PROCESSING UNITDATA from 0x05060708:32000 00 00 10 12 CALLBACK, event 0, msg length 0, bvci 0x1012 00 00 10 12 NS UNITDATA MESSAGE to BSS, BVCI 0x1012, msg length 0 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 4 00 00 10 12 result (UNITDATA) = 4 --- Send message from BSS 1 to SGSN, BVCI 0x1002 --- PROCESSING UNITDATA from 0x01020304:1111 00 00 10 12 CALLBACK, event 0, msg length 0, bvci 0x1012 00 00 10 12 NS UNITDATA MESSAGE to SGSN, BVCI 0x1012, msg length 0 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 4 00 00 10 12 result (UNITDATA) = 4 --- Send message from SGSN to BSS 1, BVCI 0x1002 --- PROCESSING UNITDATA from 0x05060708:32000 00 00 10 12 CALLBACK, event 0, msg length 0, bvci 0x1012 00 00 10 12 NS UNITDATA MESSAGE to BSS, BVCI 0x1012, msg length 0 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 4 00 00 10 12 result (UNITDATA) = 4 --- Send message from BSS 2 to SGSN, BVCI 0x2002 --- PROCESSING UNITDATA from 0x01020304:1111 00 00 20 02 CALLBACK, event 0, msg length 0, bvci 0x2002 00 00 20 02 NS UNITDATA MESSAGE to SGSN, BVCI 0x2002, msg length 0 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 4 00 00 20 02 result (UNITDATA) = 4 --- Send message from SGSN to BSS 2, BVCI 0x2002 --- PROCESSING UNITDATA from 0x05060708:32000 00 00 20 02 CALLBACK, event 0, msg length 0, bvci 0x2002 00 00 20 02 NS UNITDATA MESSAGE to BSS, BVCI 0x2002, msg length 0 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:3333, msg length 4 00 00 20 02 result (UNITDATA) = 4 --- Reset BSS 1 with the old BVCI on BSS2's link --- Setup BSSGP: remote 0x01020304:3333, BVCI 0x1002(4098) PROCESSING BVC_RESET from 0x01020304:3333 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 result (BVC_RESET) = 22 Current NS-VCIs: VCI 0x2001, NSEI 0x2000, peer 0x01020304:3333 Packets at NS Level ( In): 21 Packets at NS Level (Out): 22 Bytes at NS Level ( In): 107 Bytes at NS Level (Out): 73 NS-VC replaced other count: 1 VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111 Packets at NS Level ( In): 20 Packets at NS Level (Out): 18 Bytes at NS Level ( In): 157 Bytes at NS Level (Out): 80 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 Packets at NS Level ( In): 12 Packets at NS Level (Out): 13 Bytes at NS Level ( In): 69 Bytes at NS Level (Out): 159 NS-VC Block count : 1 Peers: NSEI 4096, BVCI 4114, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 8192, BVCI 8194, not blocked, RAI 112-332-16464-96 NSEI mismatch : 1 TLLI-Cache: 0 NSEI 8192, BVCI 4098, not blocked, RAI 112-332-16464-96 NSEI mismatch : 1 TLLI-Cache: 0 Gbproxy global: PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 10 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 10 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:3333, msg length 9 00 00 00 00 23 04 82 10 02 result (BVC_RESET_ACK) = 9 --- Send message from BSS 1 to SGSN, BVCI 0x1002 --- PROCESSING UNITDATA from 0x01020304:1111 00 00 10 12 CALLBACK, event 0, msg length 0, bvci 0x1012 00 00 10 12 NS UNITDATA MESSAGE to SGSN, BVCI 0x1012, msg length 0 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 4 00 00 10 12 result (UNITDATA) = 4 --- Send message from SGSN to BSS 1, BVCI 0x1002 --- PROCESSING UNITDATA from 0x05060708:32000 00 00 10 12 CALLBACK, event 0, msg length 0, bvci 0x1012 00 00 10 12 NS UNITDATA MESSAGE to BSS, BVCI 0x1012, msg length 0 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 4 00 00 10 12 result (UNITDATA) = 4 --- Send message from SGSN to BSS 1, BVCI 0x10ff (invalid) --- PROCESSING UNITDATA from 0x05060708:32000 00 00 10 ff CALLBACK, event 0, msg length 0, bvci 0x10ff 00 00 10 ff NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 10 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 14 00 00 00 00 41 07 81 05 04 82 10 ff 15 80 result (UNITDATA) = 14 Peers: NSEI 8192, BVCI 8194, not blocked, RAI 112-332-16464-96 NSEI mismatch : 1 TLLI-Cache: 0 NSEI 8192, BVCI 4098, not blocked, RAI 112-332-16464-96 NSEI mismatch : 1 TLLI-Cache: 0 Gbproxy global: Invalid BVC Identifier : 1 === test_gbproxy_ident_changes === --- Initialise SGSN --- MESSAGE to SGSN at 0x05060708:32000, msg length 12 02 00 81 01 01 82 01 01 04 82 01 00 PROCESSING RESET_ACK from 0x05060708:32000 03 01 82 01 01 04 82 01 00 MESSAGE to SGSN at 0x05060708:32000, msg length 1 0a result (RESET_ACK) = 1 PROCESSING ALIVE_ACK from 0x05060708:32000 0b MESSAGE to SGSN at 0x05060708:32000, msg length 1 06 result (ALIVE_ACK) = 1 PROCESSING UNBLOCK_ACK from 0x05060708:32000 07 ==> got signal NS_UNBLOCK, NS-VC 0x0101/5.6.7.8:32000 result (UNBLOCK_ACK) = 0 PROCESSING ALIVE from 0x05060708:32000 0a MESSAGE to SGSN at 0x05060708:32000, msg length 1 0b result (ALIVE) = 1 Current NS-VCIs: VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 Packets at NS Level ( In): 4 Packets at NS Level (Out): 4 Bytes at NS Level ( In): 12 Bytes at NS Level (Out): 15 NS-VC Block count : 1 --- Initialise BSS 1 --- Setup NS-VC: remote 0x01020304:1111, NSVCI 0x1001(4097), NSEI 0x1000(4096) PROCESSING RESET from 0x01020304:1111 02 00 81 01 01 82 10 01 04 82 10 00 ==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 9 03 01 82 10 01 04 82 10 00 MESSAGE to BSS at 0x01020304:1111, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:1111 0a MESSAGE to BSS at 0x01020304:1111, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:1111 06 ==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:1111 0b result (ALIVE_ACK) = 0 Current NS-VCIs: VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111 Packets at NS Level ( In): 4 Packets at NS Level (Out): 4 Bytes at NS Level ( In): 15 Bytes at NS Level (Out): 12 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 Packets at NS Level ( In): 4 Packets at NS Level (Out): 4 Bytes at NS Level ( In): 12 Bytes at NS Level (Out): 15 NS-VC Block count : 1 --- Setup BVCI 1 --- Setup BSSGP: remote 0x01020304:1111, BVCI 0x1002(4098) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 result (BVC_RESET) = 22 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 10 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 10 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 10 02 result (BVC_RESET_ACK) = 9 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 --- Setup BVCI 2 --- Setup BSSGP: remote 0x01020304:1111, BVCI 0x2002(8194) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 20 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 20 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 20 02 07 81 08 08 88 11 22 33 40 50 60 10 00 result (BVC_RESET) = 22 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 20 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 20 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 20 02 result (BVC_RESET_ACK) = 9 Peers: NSEI 4096, BVCI 8194, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 --- Send message from BSS 1 to SGSN and back, BVCI 1 --- PROCESSING UNITDATA from 0x01020304:1111 00 00 10 02 CALLBACK, event 0, msg length 0, bvci 0x1002 00 00 10 02 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 0 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 4 00 00 10 02 result (UNITDATA) = 4 PROCESSING UNITDATA from 0x05060708:32000 00 00 10 02 CALLBACK, event 0, msg length 0, bvci 0x1002 00 00 10 02 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 0 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 4 00 00 10 02 result (UNITDATA) = 4 --- Send message from BSS 1 to SGSN and back, BVCI 2 --- PROCESSING UNITDATA from 0x01020304:1111 00 00 20 02 CALLBACK, event 0, msg length 0, bvci 0x2002 00 00 20 02 NS UNITDATA MESSAGE to SGSN, BVCI 0x2002, msg length 0 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 4 00 00 20 02 result (UNITDATA) = 4 PROCESSING UNITDATA from 0x05060708:32000 00 00 20 02 CALLBACK, event 0, msg length 0, bvci 0x2002 00 00 20 02 NS UNITDATA MESSAGE to BSS, BVCI 0x2002, msg length 0 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 4 00 00 20 02 result (UNITDATA) = 4 --- Change NSEI --- Setup NS-VC: remote 0x01020304:1111, NSVCI 0x1001(4097), NSEI 0x2000(8192) PROCESSING RESET from 0x01020304:1111 02 00 81 01 01 82 10 01 04 82 20 00 ==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 9 03 01 82 10 01 04 82 20 00 MESSAGE to BSS at 0x01020304:1111, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:1111 0a MESSAGE to BSS at 0x01020304:1111, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:1111 06 ==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:1111 0b result (ALIVE_ACK) = 0 Current NS-VCIs: VCI 0x1001, NSEI 0x2000, peer 0x01020304:1111 Packets at NS Level ( In): 12 Packets at NS Level (Out): 12 Bytes at NS Level ( In): 82 Bytes at NS Level (Out): 50 NS-VC changed NSEI count : 1 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 Packets at NS Level ( In): 8 Packets at NS Level (Out): 8 Bytes at NS Level ( In): 38 Bytes at NS Level (Out): 67 NS-VC Block count : 1 --- Setup BVCI 1 --- Setup BSSGP: remote 0x01020304:1111, BVCI 0x1002(4098) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 result (BVC_RESET) = 22 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 10 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 10 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 10 02 result (BVC_RESET_ACK) = 9 Peers: NSEI 4096, BVCI 8194, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 8192, BVCI 4098, not blocked, RAI 112-332-16464-96 NSEI mismatch : 1 TLLI-Cache: 0 --- Setup BVCI 3 --- Setup BSSGP: remote 0x01020304:1111, BVCI 0x3002(12290) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 30 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 30 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 30 02 07 81 08 08 88 11 22 33 40 50 60 10 00 result (BVC_RESET) = 22 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 30 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 30 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 30 02 result (BVC_RESET_ACK) = 9 Peers: NSEI 8192, BVCI 12290, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 8194, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 8192, BVCI 4098, not blocked, RAI 112-332-16464-96 NSEI mismatch : 1 TLLI-Cache: 0 --- Send message from BSS 1 to SGSN and back, BVCI 1 --- PROCESSING UNITDATA from 0x01020304:1111 00 00 10 02 CALLBACK, event 0, msg length 0, bvci 0x1002 00 00 10 02 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 0 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 4 00 00 10 02 result (UNITDATA) = 4 PROCESSING UNITDATA from 0x05060708:32000 00 00 10 02 CALLBACK, event 0, msg length 0, bvci 0x1002 00 00 10 02 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 0 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 4 00 00 10 02 result (UNITDATA) = 4 --- Send message from BSS 1 to SGSN and back, BVCI 2 (should fail) --- PROCESSING UNITDATA from 0x01020304:1111 00 00 20 02 CALLBACK, event 0, msg length 0, bvci 0x2002 00 00 20 02 NS UNITDATA MESSAGE to SGSN, BVCI 0x2002, msg length 0 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 4 00 00 20 02 result (UNITDATA) = 4 Peers: NSEI 8192, BVCI 12290, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 8194, not blocked, RAI 112-332-16464-96 NSEI mismatch : 1 TLLI-Cache: 0 NSEI 8192, BVCI 4098, not blocked, RAI 112-332-16464-96 NSEI mismatch : 1 TLLI-Cache: 0 PROCESSING UNITDATA from 0x05060708:32000 00 00 20 02 CALLBACK, event 0, msg length 0, bvci 0x2002 00 00 20 02 NS UNITDATA MESSAGE to BSS, BVCI 0x2002, msg length 0 (gprs_ns_sendmsg) result (UNITDATA) = -22 Peers: NSEI 8192, BVCI 12290, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 8194, not blocked, RAI 112-332-16464-96 NSEI mismatch : 1 NS Transmission error : 1 TLLI-Cache: 0 NSEI 8192, BVCI 4098, not blocked, RAI 112-332-16464-96 NSEI mismatch : 1 TLLI-Cache: 0 --- Send message from BSS 1 to SGSN and back, BVCI 3 --- PROCESSING UNITDATA from 0x01020304:1111 00 00 30 02 CALLBACK, event 0, msg length 0, bvci 0x3002 00 00 30 02 NS UNITDATA MESSAGE to SGSN, BVCI 0x3002, msg length 0 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 4 00 00 30 02 result (UNITDATA) = 4 PROCESSING UNITDATA from 0x05060708:32000 00 00 30 02 CALLBACK, event 0, msg length 0, bvci 0x3002 00 00 30 02 NS UNITDATA MESSAGE to BSS, BVCI 0x3002, msg length 0 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 4 00 00 30 02 result (UNITDATA) = 4 --- Change NSVCI --- Setup NS-VC: remote 0x01020304:1111, NSVCI 0x2001(8193), NSEI 0x2000(8192) PROCESSING RESET from 0x01020304:1111 02 00 81 01 01 82 20 01 04 82 20 00 ==> got signal NS_REPLACED: 0x2001/0.0.0.0:0 -> 0x1001/1.2.3.4:1111 ==> got signal NS_RESET, NS-VC 0x2001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 9 03 01 82 20 01 04 82 20 00 MESSAGE to BSS at 0x01020304:1111, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:1111 0a MESSAGE to BSS at 0x01020304:1111, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:1111 06 ==> got signal NS_UNBLOCK, NS-VC 0x2001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:1111 0b result (ALIVE_ACK) = 0 Current NS-VCIs: VCI 0x2001, NSEI 0x2000, peer 0x01020304:1111 Packets at NS Level ( In): 3 Packets at NS Level (Out): 4 Bytes at NS Level ( In): 3 Bytes at NS Level (Out): 12 NS-VC replaced other count: 1 VCI 0x1001, NSEI 0x2000, peer 0x00000000:0 Packets at NS Level ( In): 18 Packets at NS Level (Out): 16 Bytes at NS Level ( In): 150 Bytes at NS Level (Out): 76 NS-VC changed NSEI count : 1 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 Packets at NS Level ( In): 13 Packets at NS Level (Out): 13 Bytes at NS Level ( In): 68 Bytes at NS Level (Out): 123 NS-VC Block count : 1 --- Setup BVCI 1 --- Setup BSSGP: remote 0x01020304:1111, BVCI 0x1002(4098) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 result (BVC_RESET) = 22 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 10 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 10 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 10 02 result (BVC_RESET_ACK) = 9 Peers: NSEI 8192, BVCI 12290, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 8194, not blocked, RAI 112-332-16464-96 NSEI mismatch : 1 NS Transmission error : 1 TLLI-Cache: 0 NSEI 8192, BVCI 4098, not blocked, RAI 112-332-16464-96 NSEI mismatch : 1 TLLI-Cache: 0 --- Setup BVCI 4 --- Setup BSSGP: remote 0x01020304:1111, BVCI 0x4002(16386) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 40 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 40 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 40 02 07 81 08 08 88 11 22 33 40 50 60 10 00 result (BVC_RESET) = 22 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 40 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 40 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 40 02 result (BVC_RESET_ACK) = 9 Peers: NSEI 8192, BVCI 16386, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 8192, BVCI 12290, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 8194, not blocked, RAI 112-332-16464-96 NSEI mismatch : 1 NS Transmission error : 1 TLLI-Cache: 0 NSEI 8192, BVCI 4098, not blocked, RAI 112-332-16464-96 NSEI mismatch : 1 TLLI-Cache: 0 --- Send message from BSS 1 to SGSN and back, BVCI 1 --- PROCESSING UNITDATA from 0x01020304:1111 00 00 10 02 CALLBACK, event 0, msg length 0, bvci 0x1002 00 00 10 02 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 0 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 4 00 00 10 02 result (UNITDATA) = 4 PROCESSING UNITDATA from 0x05060708:32000 00 00 10 02 CALLBACK, event 0, msg length 0, bvci 0x1002 00 00 10 02 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 0 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 4 00 00 10 02 result (UNITDATA) = 4 --- Send message from BSS 1 to SGSN and back, BVCI 2 (should fail) --- PROCESSING UNITDATA from 0x01020304:1111 00 00 20 02 CALLBACK, event 0, msg length 0, bvci 0x2002 00 00 20 02 NS UNITDATA MESSAGE to SGSN, BVCI 0x2002, msg length 0 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 4 00 00 20 02 result (UNITDATA) = 4 Peers: NSEI 8192, BVCI 16386, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 8192, BVCI 12290, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 8194, not blocked, RAI 112-332-16464-96 NSEI mismatch : 2 NS Transmission error : 1 TLLI-Cache: 0 NSEI 8192, BVCI 4098, not blocked, RAI 112-332-16464-96 NSEI mismatch : 1 TLLI-Cache: 0 PROCESSING UNITDATA from 0x05060708:32000 00 00 20 02 CALLBACK, event 0, msg length 0, bvci 0x2002 00 00 20 02 NS UNITDATA MESSAGE to BSS, BVCI 0x2002, msg length 0 (gprs_ns_sendmsg) result (UNITDATA) = -22 Peers: NSEI 8192, BVCI 16386, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 8192, BVCI 12290, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 8194, not blocked, RAI 112-332-16464-96 NSEI mismatch : 2 NS Transmission error : 2 TLLI-Cache: 0 NSEI 8192, BVCI 4098, not blocked, RAI 112-332-16464-96 NSEI mismatch : 1 TLLI-Cache: 0 --- Send message from BSS 1 to SGSN and back, BVCI 3 --- PROCESSING UNITDATA from 0x01020304:1111 00 00 30 02 CALLBACK, event 0, msg length 0, bvci 0x3002 00 00 30 02 NS UNITDATA MESSAGE to SGSN, BVCI 0x3002, msg length 0 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 4 00 00 30 02 result (UNITDATA) = 4 PROCESSING UNITDATA from 0x05060708:32000 00 00 30 02 CALLBACK, event 0, msg length 0, bvci 0x3002 00 00 30 02 NS UNITDATA MESSAGE to BSS, BVCI 0x3002, msg length 0 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 4 00 00 30 02 result (UNITDATA) = 4 --- Send message from BSS 1 to SGSN and back, BVCI 4 --- PROCESSING UNITDATA from 0x01020304:1111 00 00 40 02 CALLBACK, event 0, msg length 0, bvci 0x4002 00 00 40 02 NS UNITDATA MESSAGE to SGSN, BVCI 0x4002, msg length 0 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 4 00 00 40 02 result (UNITDATA) = 4 PROCESSING UNITDATA from 0x05060708:32000 00 00 40 02 CALLBACK, event 0, msg length 0, bvci 0x4002 00 00 40 02 NS UNITDATA MESSAGE to BSS, BVCI 0x4002, msg length 0 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 4 00 00 40 02 result (UNITDATA) = 4 Gbproxy global: Peers: NSEI 8192, BVCI 16386, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 8192, BVCI 12290, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 8194, not blocked, RAI 112-332-16464-96 NSEI mismatch : 2 NS Transmission error : 2 TLLI-Cache: 0 NSEI 8192, BVCI 4098, not blocked, RAI 112-332-16464-96 NSEI mismatch : 1 TLLI-Cache: 0 === Test IMSI/TMSI matching === === test_gbproxy_ptmsi_assignment === --- Initialise SGSN --- MESSAGE to SGSN at 0x05060708:32000, msg length 12 02 00 81 01 01 82 01 01 04 82 01 00 PROCESSING RESET_ACK from 0x05060708:32000 03 01 82 01 01 04 82 01 00 MESSAGE to SGSN at 0x05060708:32000, msg length 1 0a result (RESET_ACK) = 1 PROCESSING ALIVE_ACK from 0x05060708:32000 0b MESSAGE to SGSN at 0x05060708:32000, msg length 1 06 result (ALIVE_ACK) = 1 PROCESSING UNBLOCK_ACK from 0x05060708:32000 07 ==> got signal NS_UNBLOCK, NS-VC 0x0101/5.6.7.8:32000 result (UNBLOCK_ACK) = 0 PROCESSING ALIVE from 0x05060708:32000 0a MESSAGE to SGSN at 0x05060708:32000, msg length 1 0b result (ALIVE) = 1 --- Initialise BSS 1 --- Setup NS-VC: remote 0x01020304:1111, NSVCI 0x1001(4097), NSEI 0x1000(4096) PROCESSING RESET from 0x01020304:1111 02 00 81 01 01 82 10 01 04 82 10 00 ==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 9 03 01 82 10 01 04 82 10 00 MESSAGE to BSS at 0x01020304:1111, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:1111 0a MESSAGE to BSS at 0x01020304:1111, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:1111 06 ==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:1111 0b result (ALIVE_ACK) = 0 Setup BSSGP: remote 0x01020304:1111, BVCI 0x1002(4098) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 result (BVC_RESET) = 22 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 10 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 10 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 10 02 result (BVC_RESET_ACK) = 9 Current NS-VCIs: VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111 Packets at NS Level ( In): 5 Packets at NS Level (Out): 5 Bytes at NS Level ( In): 37 Bytes at NS Level (Out): 21 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 Packets at NS Level ( In): 5 Packets at NS Level (Out): 5 Bytes at NS Level ( In): 21 Bytes at NS Level (Out): 37 NS-VC Block count : 1 Gbproxy global: Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 --- Establish first LLC connection --- PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 result (ATTACH REQUEST) = 79 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 8000dead, IMSI (none), AGE 0 PROCESSING IDENT REQUEST from 0x05060708:32000 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba CALLBACK, event 0, msg length 23, bvci 0x1002 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 23 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 27 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba result (IDENT REQUEST) = 27 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 8000dead, IMSI (none), AGE 0 PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 05 08 16 08 11 12 13 14 15 16 17 18 ad 05 28 CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 05 08 16 08 11 12 13 14 15 16 17 18 ad 05 28 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 40 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 44 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 05 08 16 08 11 12 13 14 15 16 17 18 ad 05 28 result (IDENT RESPONSE) = 44 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 8000dead, IMSI 12131415161718, AGE 0 PROCESSING ATTACH ACCEPT from 0x05060708:32000 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 53 62 f1 CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 53 62 f1 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 53 62 f1 result (ATTACH ACCEPT) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 1 Attach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead/efe2b700 -> 8000dead/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 09 08 03 39 d7 bc CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 09 08 03 39 d7 bc NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 09 08 03 39 d7 bc result (ATTACH COMPLETE) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead/efe2b700 -> 8000dead/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING GMM INFO from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 09 08 21 04 ba 3d CALLBACK, event 0, msg length 66, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 09 08 21 04 ba 3d NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 66 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 70 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 09 08 21 04 ba 3d result (GMM INFO) = 70 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0 --- Establish second LLC connection with the same P-TMSI --- PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 80 00 be ef 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 0d 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 46 42 6e CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 80 00 be ef 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 0d 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 46 42 6e NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 80 00 be ef 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 0d 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 46 42 6e result (ATTACH REQUEST) = 79 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 2 Attach Accept count : 1 Attach Completed count : 1 TLLI cache size : 2 TLLI-Cache: 2 TLLI 8000beef -> 8000beef, IMSI (none), AGE 0 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING IDENT REQUEST from 0x05060708:32000 00 00 10 02 00 80 00 be ef 00 50 20 16 82 02 58 0e 89 41 c0 0d 08 15 01 0c a6 18 CALLBACK, event 0, msg length 23, bvci 0x1002 00 00 10 02 00 80 00 be ef 00 50 20 16 82 02 58 0e 89 41 c0 0d 08 15 01 0c a6 18 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 23 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 27 00 00 10 02 00 80 00 be ef 00 50 20 16 82 02 58 0e 89 41 c0 0d 08 15 01 0c a6 18 result (IDENT REQUEST) = 27 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 2 Attach Accept count : 1 Attach Completed count : 1 TLLI cache size : 2 TLLI-Cache: 2 TLLI 8000beef -> 8000beef, IMSI (none), AGE 0 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 80 00 be ef 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 11 08 16 08 11 12 99 99 99 16 17 18 bf d2 01 CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 80 00 be ef 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 11 08 16 08 11 12 99 99 99 16 17 18 bf d2 01 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 40 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 44 00 00 10 02 01 80 00 be ef 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 11 08 16 08 11 12 99 99 99 16 17 18 bf d2 01 result (IDENT RESPONSE) = 44 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 2 Attach Accept count : 1 Attach Completed count : 1 TLLI cache size : 2 TLLI-Cache: 2 TLLI 8000beef -> 8000beef, IMSI 12199999961718, AGE 0 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH ACCEPT from 0x05060708:32000 00 00 10 02 00 80 00 be ef 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 9e 41 c0 11 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 3a 6d d4 CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 80 00 be ef 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 9e 41 c0 11 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 3a 6d d4 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 80 00 be ef 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 9e 41 c0 11 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 3a 6d d4 result (ATTACH ACCEPT) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 2 Attach Accept count : 2 Attach Completed count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000beef/efe2b700 -> 8000beef/efe2b700, IMSI 12199999961718, AGE 0 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 15 08 03 86 ac 47 CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 15 08 03 86 ac 47 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 15 08 03 86 ac 47 result (ATTACH COMPLETE) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 2 Attach Accept count : 2 Attach Completed count : 2 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000beef/efe2b700 -> 8000beef/efe2b700, IMSI 12199999961718, AGE 0 PROCESSING GMM INFO from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 88 41 c0 15 08 21 bb c1 c6 CALLBACK, event 0, msg length 66, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 88 41 c0 15 08 21 bb c1 c6 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 66 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 70 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 88 41 c0 15 08 21 bb c1 c6 result (GMM INFO) = 70 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 2 Attach Accept count : 2 Attach Completed count : 2 TLLI cache size : 1 TLLI-Cache: 1 TLLI efe2b700 -> efe2b700, IMSI 12199999961718, AGE 0 Gbproxy global: === test_gbproxy_ra_patching === --- Initialise SGSN --- MESSAGE to SGSN at 0x05060708:32000, msg length 12 02 00 81 01 01 82 01 01 04 82 01 00 PROCESSING RESET_ACK from 0x05060708:32000 03 01 82 01 01 04 82 01 00 MESSAGE to SGSN at 0x05060708:32000, msg length 1 0a result (RESET_ACK) = 1 PROCESSING ALIVE_ACK from 0x05060708:32000 0b MESSAGE to SGSN at 0x05060708:32000, msg length 1 06 result (ALIVE_ACK) = 1 PROCESSING UNBLOCK_ACK from 0x05060708:32000 07 ==> got signal NS_UNBLOCK, NS-VC 0x0101/5.6.7.8:32000 result (UNBLOCK_ACK) = 0 PROCESSING ALIVE from 0x05060708:32000 0a MESSAGE to SGSN at 0x05060708:32000, msg length 1 0b result (ALIVE) = 1 Current NS-VCIs: VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 Packets at NS Level ( In): 4 Packets at NS Level (Out): 4 Bytes at NS Level ( In): 12 Bytes at NS Level (Out): 15 NS-VC Block count : 1 --- Initialise BSS 1 --- Setup NS-VC: remote 0x01020304:1111, NSVCI 0x1001(4097), NSEI 0x1000(4096) PROCESSING RESET from 0x01020304:1111 02 00 81 01 01 82 10 01 04 82 10 00 ==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 9 03 01 82 10 01 04 82 10 00 MESSAGE to BSS at 0x01020304:1111, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:1111 0a MESSAGE to BSS at 0x01020304:1111, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:1111 06 ==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:1111 0b result (ALIVE_ACK) = 0 Setup BSSGP: remote 0x01020304:1111, BVCI 0x1002(4098) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 10 02 07 81 08 08 88 21 63 54 40 50 60 10 00 result (BVC_RESET) = 22 Current NS-VCIs: VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111 Packets at NS Level ( In): 5 Packets at NS Level (Out): 4 Bytes at NS Level ( In): 37 Bytes at NS Level (Out): 12 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 Packets at NS Level ( In): 4 Packets at NS Level (Out): 5 Bytes at NS Level ( In): 12 Bytes at NS Level (Out): 37 NS-VC Block count : 1 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 1 TLLI-Cache: 0 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 10 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 10 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 10 02 result (BVC_RESET_ACK) = 9 PROCESSING BVC_SUSPEND from 0x01020304:1111 00 00 00 00 0b 1f 84 cc d1 75 8b 1b 86 11 22 33 40 50 60 CALLBACK, event 0, msg length 15, bvci 0x0000 00 00 00 00 0b 1f 84 cc d1 75 8b 1b 86 11 22 33 40 50 60 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 15 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 19 00 00 00 00 0b 1f 84 cc d1 75 8b 1b 86 21 63 54 40 50 60 result (BVC_SUSPEND) = 19 PROCESSING BVC_SUSPEND_ACK from 0x05060708:32000 00 00 00 00 0c 1f 84 cc d1 75 8b 1b 86 21 63 54 40 50 60 1d 81 01 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 0c 1f 84 cc d1 75 8b 1b 86 21 63 54 40 50 60 1d 81 01 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 22 00 00 00 00 0c 1f 84 cc d1 75 8b 1b 86 11 22 33 40 50 60 1d 81 01 result (BVC_SUSPEND_ACK) = 22 Gbproxy global: Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 2 RAID patched (SGSN): 1 TLLI from SGSN unknown : 1 TLLI-Cache: 0 --- Send message from BSS 1 to SGSN, BVCI 0x1002 --- PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 bb c5 46 79 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 bb c5 46 79 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 bb c5 46 79 00 00 04 08 88 21 63 54 40 50 60 75 30 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 21 63 54 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 8e cd 32 result (ATTACH REQUEST) = 79 PROCESSING IDENT REQUEST from 0x05060708:32000 00 00 10 02 00 bb c5 46 79 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba CALLBACK, event 0, msg length 23, bvci 0x1002 00 00 10 02 00 bb c5 46 79 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 23 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 27 00 00 10 02 00 bb c5 46 79 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba result (IDENT REQUEST) = 27 PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 bb c5 46 79 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 11 01 c0 0d 08 16 08 11 12 13 14 15 16 17 18 b7 1b 9a CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 bb c5 46 79 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 11 01 c0 0d 08 16 08 11 12 13 14 15 16 17 18 b7 1b 9a NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 40 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 44 00 00 10 02 01 bb c5 46 79 00 00 04 08 88 21 63 54 40 50 60 75 30 00 80 0e 00 11 01 c0 0d 08 16 08 11 12 13 14 15 16 17 18 b7 1b 9a result (IDENT RESPONSE) = 44 PROCESSING ATTACH ACCEPT from 0x05060708:32000 00 00 10 02 00 bb c5 46 79 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 53 62 f1 CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 bb c5 46 79 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 53 62 f1 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 bb c5 46 79 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 11 22 33 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 be 38 c0 result (ATTACH ACCEPT) = 92 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 08 01 c0 11 08 03 ea 67 11 CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 08 01 c0 11 08 03 ea 67 11 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 75 30 00 80 0e 00 08 01 c0 11 08 03 ea 67 11 result (ATTACH COMPLETE) = 35 PROCESSING ACT PDP CTX REQ (REPLACE APN) from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 35 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 28 03 02 61 62 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 5a ff 02 CALLBACK, event 0, msg length 76, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 35 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 28 03 02 61 62 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 5a ff 02 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 81 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 85 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 75 30 00 80 0e 00 3a 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 28 08 03 66 6f 6f 03 62 61 72 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 24 9d 75 result (ACT PDP CTX REQ (REPLACE APN)) = 85 PROCESSING GMM INFO from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 09 08 21 04 ba 3d CALLBACK, event 0, msg length 66, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 09 08 21 04 ba 3d NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 66 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 70 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 09 08 21 04 ba 3d result (GMM INFO) = 70 PROCESSING ACT PDP CTX REQ (REPLACE APN) from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 35 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 28 03 02 61 62 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 5a ff 02 CALLBACK, event 0, msg length 76, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 35 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 28 03 02 61 62 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 5a ff 02 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 81 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 85 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 75 30 00 80 0e 00 3a 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 28 08 03 66 6f 6f 03 62 61 72 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 24 9d 75 result (ACT PDP CTX REQ (REPLACE APN)) = 85 PROCESSING ACT PDP CTX REQ (REMOVE APN) from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 35 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 28 03 02 61 62 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 5a ff 02 CALLBACK, event 0, msg length 76, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 35 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 28 03 02 61 62 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 5a ff 02 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 71 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 75 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 75 30 00 80 0e 00 30 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 85 fa 60 result (ACT PDP CTX REQ (REMOVE APN)) = 75 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 9 RAID patched (SGSN): 2 APN patched : 3 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 PDP Activation Request count : 3 TLLI from SGSN unknown : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0, IMSI matches PROCESSING DETACH REQ from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 15 01 c0 19 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 7e e1 41 CALLBACK, event 0, msg length 44, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 15 01 c0 19 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 7e e1 41 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 44 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 48 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 75 30 00 80 0e 00 15 01 c0 19 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 7e e1 41 result (DETACH REQ) = 48 PROCESSING DETACH ACC from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 15 08 06 00 f7 35 f0 CALLBACK, event 0, msg length 67, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 15 08 06 00 f7 35 f0 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 67 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 71 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 15 08 06 00 f7 35 f0 result (DETACH ACC) = 71 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 10 RAID patched (SGSN): 2 APN patched : 3 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 Detach Request count : 1 Detach Accept count : 1 PDP Activation Request count : 3 TLLI from SGSN unknown : 1 TLLI-Cache: 0 --- RA update --- PROCESSING RA UPD REQ from 0x01020304:1111 00 00 10 02 01 bb c5 46 79 00 00 04 08 88 11 22 33 40 50 60 70 80 00 80 0e 00 3e 01 c0 15 08 08 10 11 22 33 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 96 3e 97 CALLBACK, event 0, msg length 85, bvci 0x1002 00 00 10 02 01 bb c5 46 79 00 00 04 08 88 11 22 33 40 50 60 70 80 00 80 0e 00 3e 01 c0 15 08 08 10 11 22 33 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 96 3e 97 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 85 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 89 00 00 10 02 01 bb c5 46 79 00 00 04 08 88 21 63 54 40 50 60 70 80 00 80 0e 00 3e 01 c0 15 08 08 10 21 63 54 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 1d f0 41 result (RA UPD REQ) = 89 PROCESSING RA UPD ACC from 0x05060708:32000 00 00 10 02 00 bb c5 46 79 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9d 41 c0 19 08 09 00 49 21 63 54 40 50 60 19 54 ab b3 18 05 f4 ef e2 b7 00 17 16 d7 59 65 CALLBACK, event 0, msg length 87, bvci 0x1002 00 00 10 02 00 bb c5 46 79 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9d 41 c0 19 08 09 00 49 21 63 54 40 50 60 19 54 ab b3 18 05 f4 ef e2 b7 00 17 16 d7 59 65 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 87 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 91 00 00 10 02 00 bb c5 46 79 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9d 41 c0 19 08 09 00 49 11 22 33 40 50 60 19 54 ab b3 18 05 f4 ef e2 b7 00 17 16 3a 03 54 result (RA UPD ACC) = 91 PROCESSING ACT PDP CTX REQ (REMOVE APN) from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 35 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 28 03 02 61 62 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 5a ff 02 CALLBACK, event 0, msg length 76, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 35 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 28 03 02 61 62 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 5a ff 02 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 71 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 75 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 75 30 00 80 0e 00 30 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 85 fa 60 result (ACT PDP CTX REQ (REMOVE APN)) = 75 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 13 RAID patched (SGSN): 3 APN patched : 4 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 RoutingArea Update Request count: 1 RoutingArea Update Accept count : 1 Detach Request count : 1 Detach Accept count : 1 PDP Activation Request count : 4 TLLI from SGSN unknown : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI bbc54679/efe2b700 -> bbc54679/efe2b700, IMSI 12131415161718, AGE 0, IMSI matches PROCESSING DETACH REQ (PWR OFF) from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 15 01 c0 19 08 05 09 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 84 0c eb CALLBACK, event 0, msg length 44, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 15 01 c0 19 08 05 09 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 84 0c eb NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 44 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 48 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 75 30 00 80 0e 00 15 01 c0 19 08 05 09 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 84 0c eb result (DETACH REQ (PWR OFF)) = 48 Gbproxy global: Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 14 RAID patched (SGSN): 3 APN patched : 4 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 RoutingArea Update Request count: 1 RoutingArea Update Accept count : 1 Detach Request count : 2 Detach Accept count : 1 PDP Activation Request count : 4 TLLI from SGSN unknown : 1 TLLI-Cache: 0 --- Bad cases --- PROCESSING ATTACH REQUEST (foreign RAI) from 0x01020304:1111 00 00 10 02 01 bb 00 be ef 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb 00 be ef 99 99 99 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 2d c7 df CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 bb 00 be ef 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb 00 be ef 99 99 99 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 2d c7 df NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 bb 00 be ef 00 00 04 08 88 21 63 54 40 50 60 75 30 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb 00 be ef 99 99 99 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 2d c7 df result (ATTACH REQUEST (foreign RAI)) = 79 TLLI is already detached, shouldn't patch PROCESSING ACT PDP CTX REQ from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 35 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 28 03 02 61 62 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 5a ff 02 CALLBACK, event 0, msg length 76, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 35 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 28 03 02 61 62 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 5a ff 02 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 76 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 80 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 75 30 00 80 0e 00 35 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 28 03 02 61 62 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 5a ff 02 result (ACT PDP CTX REQ) = 80 Invalid RAI, shouldn't patch PROCESSING BVC_SUSPEND_ACK from 0x05060708:32000 00 00 00 00 0c 1f 84 cc d1 75 8b 1b 86 00 f1 99 00 63 60 1d 81 01 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 0c 1f 84 cc d1 75 8b 1b 86 00 f1 99 00 63 60 1d 81 01 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 24 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 28 00 00 00 00 41 07 81 21 15 92 0c 1f 84 cc d1 75 8b 1b 86 00 f1 99 00 63 60 1d 81 01 result (BVC_SUSPEND_ACK) = 28 Gbproxy global: Invalid Routing Area Identifier : 1 Patch error: no peer : 1 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 16 RAID patched (SGSN): 3 APN patched : 4 Attach Request count : 2 Attach Accept count : 1 Attach Completed count : 1 RoutingArea Update Request count: 1 RoutingArea Update Accept count : 1 Detach Request count : 2 Detach Accept count : 1 PDP Activation Request count : 5 TLLI from SGSN unknown : 1 TLLI cache size : 2 TLLI-Cache: 2 TLLI efe2b700 -> efe2b700, IMSI (none), AGE 0 TLLI bb00beef -> bb00beef, IMSI (none), AGE 0 === test_gbproxy_ptmsi_patching === --- Initialise SGSN --- MESSAGE to SGSN at 0x05060708:32000, msg length 12 02 00 81 01 01 82 01 01 04 82 01 00 PROCESSING RESET_ACK from 0x05060708:32000 03 01 82 01 01 04 82 01 00 MESSAGE to SGSN at 0x05060708:32000, msg length 1 0a result (RESET_ACK) = 1 PROCESSING ALIVE_ACK from 0x05060708:32000 0b MESSAGE to SGSN at 0x05060708:32000, msg length 1 06 result (ALIVE_ACK) = 1 PROCESSING UNBLOCK_ACK from 0x05060708:32000 07 ==> got signal NS_UNBLOCK, NS-VC 0x0101/5.6.7.8:32000 result (UNBLOCK_ACK) = 0 PROCESSING ALIVE from 0x05060708:32000 0a MESSAGE to SGSN at 0x05060708:32000, msg length 1 0b result (ALIVE) = 1 --- Initialise BSS 1 --- Setup NS-VC: remote 0x01020304:1111, NSVCI 0x1001(4097), NSEI 0x1000(4096) PROCESSING RESET from 0x01020304:1111 02 00 81 01 01 82 10 01 04 82 10 00 ==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 9 03 01 82 10 01 04 82 10 00 MESSAGE to BSS at 0x01020304:1111, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:1111 0a MESSAGE to BSS at 0x01020304:1111, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:1111 06 ==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:1111 0b result (ALIVE_ACK) = 0 Setup BSSGP: remote 0x01020304:1111, BVCI 0x1002(4098) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 10 02 07 81 08 08 88 21 63 54 40 50 60 10 00 result (BVC_RESET) = 22 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 10 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 10 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 10 02 result (BVC_RESET_ACK) = 9 Current NS-VCIs: VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111 Packets at NS Level ( In): 5 Packets at NS Level (Out): 5 Bytes at NS Level ( In): 37 Bytes at NS Level (Out): 21 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 Packets at NS Level ( In): 5 Packets at NS Level (Out): 5 Bytes at NS Level ( In): 21 Bytes at NS Level (Out): 37 NS-VC Block count : 1 Gbproxy global: Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 1 TLLI-Cache: 0 --- Send message from BSS 1 to SGSN, BVCI 0x1002 --- PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 78 de ad 00 00 00 04 08 88 21 63 54 00 63 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 result (ATTACH REQUEST) = 79 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 2 TLLI patched (BSS ): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 78dead00, IMSI (none), AGE 0 PROCESSING IDENT REQUEST from 0x05060708:32000 00 00 10 02 00 78 de ad 00 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba CALLBACK, event 0, msg length 23, bvci 0x1002 00 00 10 02 00 78 de ad 00 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 23 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 27 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba result (IDENT REQUEST) = 27 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 2 TLLI patched (BSS ): 1 TLLI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 78dead00, IMSI (none), AGE 0 PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 05 08 16 08 11 12 13 14 15 16 17 18 ad 05 28 CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 05 08 16 08 11 12 13 14 15 16 17 18 ad 05 28 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 40 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 44 00 00 10 02 01 78 de ad 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 11 01 c0 05 08 16 08 11 12 13 14 15 16 17 18 ad 05 28 result (IDENT RESPONSE) = 44 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 3 TLLI patched (BSS ): 2 TLLI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 78dead00, IMSI 12131415161718, AGE 0 PROCESSING ATTACH ACCEPT from 0x05060708:32000 00 00 10 02 00 78 de ad 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 53 62 f1 CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 78 de ad 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 53 62 f1 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 11 22 33 40 50 60 19 cd d7 08 17 16 18 05 f4 c0 de ad 01 0c 0a 29 result (ATTACH ACCEPT) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 3 RAID patched (SGSN): 1 TLLI patched (BSS ): 2 TLLI patched (SGSN): 2 P-TMSI patched (SGSN): 1 Attach Request count : 1 Attach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead/c0dead01 -> 78dead00/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 c0 de ad 01 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 09 08 03 39 d7 bc CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 c0 de ad 01 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 09 08 03 39 d7 bc NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 08 01 c0 09 08 03 39 d7 bc result (ATTACH COMPLETE) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 4 RAID patched (SGSN): 1 TLLI patched (BSS ): 3 TLLI patched (SGSN): 2 P-TMSI patched (SGSN): 1 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead/c0dead01 -> 78dead00/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING GMM INFO from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 09 08 21 04 ba 3d CALLBACK, event 0, msg length 66, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 09 08 21 04 ba 3d NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 66 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 70 00 00 10 02 00 c0 de ad 01 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 09 08 21 04 ba 3d result (GMM INFO) = 70 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 4 RAID patched (SGSN): 1 TLLI patched (BSS ): 3 TLLI patched (SGSN): 3 P-TMSI patched (SGSN): 1 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ACT PDP CTX REQ (REPLACE APN) from 0x01020304:1111 00 00 10 02 01 c0 de ad 01 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 35 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 28 03 02 61 62 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 5a ff 02 CALLBACK, event 0, msg length 76, bvci 0x1002 00 00 10 02 01 c0 de ad 01 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 35 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 28 03 02 61 62 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 5a ff 02 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 81 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 85 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 3a 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 28 08 03 66 6f 6f 03 62 61 72 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 24 9d 75 result (ACT PDP CTX REQ (REPLACE APN)) = 85 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 5 RAID patched (SGSN): 1 APN patched : 1 TLLI patched (BSS ): 4 TLLI patched (SGSN): 3 P-TMSI patched (SGSN): 1 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 PDP Activation Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING XID (UL) from 0x01020304:1111 00 00 10 02 01 c0 de ad 01 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 0f 41 fb 01 00 0e 00 64 11 05 16 01 90 66 b3 28 CALLBACK, event 0, msg length 38, bvci 0x1002 00 00 10 02 01 c0 de ad 01 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 0f 41 fb 01 00 0e 00 64 11 05 16 01 90 66 b3 28 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 38 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 42 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 0f 41 fb 01 00 0e 00 64 11 05 16 01 90 66 b3 28 result (XID (UL)) = 42 PROCESSING XID (DL) from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8c 41 fb 30 84 10 61 b6 64 e4 a9 1a 9e CALLBACK, event 0, msg length 70, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8c 41 fb 30 84 10 61 b6 64 e4 a9 1a 9e NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 70 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 74 00 00 10 02 00 c0 de ad 01 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8c 41 fb 30 84 10 61 b6 64 e4 a9 1a 9e result (XID (DL)) = 74 PROCESSING LL11 DNS QUERY (UL) from 0x01020304:1111 00 00 10 02 01 c0 de ad 01 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 42 0b c0 01 65 00 00 00 45 00 00 38 95 72 00 00 45 11 20 85 0a c0 07 e4 ac 10 01 0a ad ab 00 35 00 24 0e 1c 3b e0 01 00 00 01 00 00 00 00 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 47 8f 07 CALLBACK, event 0, msg length 89, bvci 0x1002 00 00 10 02 01 c0 de ad 01 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 42 0b c0 01 65 00 00 00 45 00 00 38 95 72 00 00 45 11 20 85 0a c0 07 e4 ac 10 01 0a ad ab 00 35 00 24 0e 1c 3b e0 01 00 00 01 00 00 00 00 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 47 8f 07 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 89 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 93 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 42 0b c0 01 65 00 00 00 45 00 00 38 95 72 00 00 45 11 20 85 0a c0 07 e4 ac 10 01 0a ad ab 00 35 00 24 0e 1c 3b e0 01 00 00 01 00 00 00 00 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 47 8f 07 result (LL11 DNS QUERY (UL)) = 93 PROCESSING LL11 DNS RESP (DL) from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 00 d0 4b c0 01 65 00 00 00 45 00 00 c6 00 00 40 00 3e 11 7c 69 ac 10 01 0a 0a c0 07 e4 00 35 ad ab 00 b2 74 4e 3b e0 81 80 00 01 00 01 00 05 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 c0 0c 00 01 00 01 00 00 0e 10 00 04 c1 63 90 58 c0 0e 00 02 00 01 00 00 0e 10 00 16 03 6e 73 32 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 03 6e 65 74 00 c0 0e 00 02 00 01 00 00 0e 10 00 10 02 6e 73 01 73 08 70 6c 75 73 6c 69 6e 65 c0 14 c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 0e c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 5f c0 0e 00 02 00 01 00 00 0e 10 00 12 02 6e 73 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 c0 14 aa df 31 CALLBACK, event 0, msg length 267, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 00 d0 4b c0 01 65 00 00 00 45 00 00 c6 00 00 40 00 3e 11 7c 69 ac 10 01 0a 0a c0 07 e4 00 35 ad ab 00 b2 74 4e 3b e0 81 80 00 01 00 01 00 05 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 c0 0c 00 01 00 01 00 00 0e 10 00 04 c1 63 90 58 c0 0e 00 02 00 01 00 00 0e 10 00 16 03 6e 73 32 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 03 6e 65 74 00 c0 0e 00 02 00 01 00 00 0e 10 00 10 02 6e 73 01 73 08 70 6c 75 73 6c 69 6e 65 c0 14 c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 0e c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 5f c0 0e 00 02 00 01 00 00 0e 10 00 12 02 6e 73 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 c0 14 aa df 31 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 267 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 271 00 00 10 02 00 c0 de ad 01 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 00 d0 4b c0 01 65 00 00 00 45 00 00 c6 00 00 40 00 3e 11 7c 69 ac 10 01 0a 0a c0 07 e4 00 35 ad ab 00 b2 74 4e 3b e0 81 80 00 01 00 01 00 05 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 c0 0c 00 01 00 01 00 00 0e 10 00 04 c1 63 90 58 c0 0e 00 02 00 01 00 00 0e 10 00 16 03 6e 73 32 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 03 6e 65 74 00 c0 0e 00 02 00 01 00 00 0e 10 00 10 02 6e 73 01 73 08 70 6c 75 73 6c 69 6e 65 c0 14 c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 0e c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 5f c0 0e 00 02 00 01 00 00 0e 10 00 12 02 6e 73 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 c0 14 aa df 31 result (LL11 DNS RESP (DL)) = 271 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 7 RAID patched (SGSN): 1 APN patched : 1 TLLI patched (BSS ): 6 TLLI patched (SGSN): 5 P-TMSI patched (SGSN): 1 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 PDP Activation Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING RA UPD REQ (P-TMSI 2) from 0x01020304:1111 00 00 10 02 01 c0 de ad 01 00 00 04 08 88 11 22 33 40 50 60 70 80 00 80 0e 00 3e 01 c0 11 08 08 10 11 22 33 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 e2 6d 78 CALLBACK, event 0, msg length 85, bvci 0x1002 00 00 10 02 01 c0 de ad 01 00 00 04 08 88 11 22 33 40 50 60 70 80 00 80 0e 00 3e 01 c0 11 08 08 10 11 22 33 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 e2 6d 78 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 85 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 89 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 70 80 00 80 0e 00 3e 01 c0 11 08 08 10 21 63 54 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 69 a3 ae result (RA UPD REQ (P-TMSI 2)) = 89 PROCESSING RA UDP ACC (P-TMSI 2) from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9d 41 c0 0d 08 09 00 49 21 63 54 40 50 60 19 54 ab b3 18 05 f4 e0 98 76 54 17 16 9f e8 ea CALLBACK, event 0, msg length 87, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9d 41 c0 0d 08 09 00 49 21 63 54 40 50 60 19 54 ab b3 18 05 f4 e0 98 76 54 17 16 9f e8 ea NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 87 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 91 00 00 10 02 00 c0 de ad 01 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9d 41 c0 0d 08 09 00 49 11 22 33 40 50 60 19 54 ab b3 18 05 f4 c0 de ad 02 17 16 bb 4d a0 result (RA UDP ACC (P-TMSI 2)) = 91 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 9 RAID patched (SGSN): 2 APN patched : 1 TLLI patched (BSS ): 7 TLLI patched (SGSN): 6 P-TMSI patched (SGSN): 2 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 RoutingArea Update Request count: 1 RoutingArea Update Accept count : 1 PDP Activation Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c0dead01/c0dead02 -> efe2b700/e0987654, IMSI 12131415161718, AGE 0 PROCESSING RA UPD REQ (P-TMSI 3) from 0x01020304:1111 00 00 10 02 01 c0 de ad 02 00 00 04 08 88 11 22 33 40 50 60 70 80 00 80 0e 00 3e 01 c0 15 08 08 10 11 22 33 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 96 3e 97 CALLBACK, event 0, msg length 85, bvci 0x1002 00 00 10 02 01 c0 de ad 02 00 00 04 08 88 11 22 33 40 50 60 70 80 00 80 0e 00 3e 01 c0 15 08 08 10 11 22 33 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 96 3e 97 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 85 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 89 00 00 10 02 01 e0 98 76 54 00 00 04 08 88 21 63 54 40 50 60 70 80 00 80 0e 00 3e 01 c0 15 08 08 10 21 63 54 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 1d f0 41 result (RA UPD REQ (P-TMSI 3)) = 89 PROCESSING RA UDP ACC (P-TMSI 3) from 0x05060708:32000 00 00 10 02 00 e0 98 76 54 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9d 41 c0 11 08 09 00 49 21 63 54 40 50 60 19 54 ab b3 18 05 f4 e0 54 32 10 17 16 1b a3 a8 CALLBACK, event 0, msg length 87, bvci 0x1002 00 00 10 02 00 e0 98 76 54 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9d 41 c0 11 08 09 00 49 21 63 54 40 50 60 19 54 ab b3 18 05 f4 e0 54 32 10 17 16 1b a3 a8 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 87 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 91 00 00 10 02 00 c0 de ad 02 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9d 41 c0 11 08 09 00 49 11 22 33 40 50 60 19 54 ab b3 18 05 f4 c0 de ad 03 17 16 6e 58 26 result (RA UDP ACC (P-TMSI 3)) = 91 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 11 RAID patched (SGSN): 3 APN patched : 1 TLLI patched (BSS ): 8 TLLI patched (SGSN): 7 P-TMSI patched (SGSN): 3 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 RoutingArea Update Request count: 2 RoutingArea Update Accept count : 2 PDP Activation Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c0dead01/c0dead03 -> efe2b700/e0543210, IMSI 12131415161718, AGE 0 PROCESSING RA UPD COMPLETE from 0x01020304:1111 00 00 10 02 01 c0 de ad 03 00 00 04 08 88 11 22 33 40 50 60 70 80 00 80 0e 00 08 01 c0 19 08 0a d5 5f 5e CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 c0 de ad 03 00 00 04 08 88 11 22 33 40 50 60 70 80 00 80 0e 00 08 01 c0 19 08 0a d5 5f 5e NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 e0 54 32 10 00 00 04 08 88 21 63 54 40 50 60 70 80 00 80 0e 00 08 01 c0 19 08 0a d5 5f 5e result (RA UPD COMPLETE) = 35 PROCESSING GMM INFO from 0x05060708:32000 00 00 10 02 00 e0 54 32 10 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 15 08 21 bb c1 c6 CALLBACK, event 0, msg length 66, bvci 0x1002 00 00 10 02 00 e0 54 32 10 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 15 08 21 bb c1 c6 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 66 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 70 00 00 10 02 00 c0 de ad 03 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 15 08 21 bb c1 c6 result (GMM INFO) = 70 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 12 RAID patched (SGSN): 3 APN patched : 1 TLLI patched (BSS ): 9 TLLI patched (SGSN): 8 P-TMSI patched (SGSN): 3 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 RoutingArea Update Request count: 2 RoutingArea Update Accept count : 2 RoutingArea Update Compltd count: 1 PDP Activation Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c0dead03 -> e0543210, IMSI 12131415161718, AGE 0 PROCESSING LLC_DISCARDED from 0x01020304:1111 00 00 00 00 2c 1f 84 c0 de ad 03 0f 81 01 04 82 10 02 25 83 00 00 0c CALLBACK, event 0, msg length 19, bvci 0x0000 00 00 00 00 2c 1f 84 c0 de ad 03 0f 81 01 04 82 10 02 25 83 00 00 0c NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 19 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 23 00 00 00 00 2c 1f 84 e0 54 32 10 0f 81 01 04 82 10 02 25 83 00 00 0c result (LLC_DISCARDED) = 23 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 12 RAID patched (SGSN): 3 APN patched : 1 TLLI patched (BSS ): 10 TLLI patched (SGSN): 8 P-TMSI patched (SGSN): 3 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 RoutingArea Update Request count: 2 RoutingArea Update Accept count : 2 RoutingArea Update Compltd count: 1 PDP Activation Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c0dead03 -> e0543210, IMSI 12131415161718, AGE 0 PROCESSING BVC_SUSPEND from 0x01020304:1111 00 00 00 00 0b 1f 84 c0 de ad 03 1b 86 11 22 33 40 50 60 CALLBACK, event 0, msg length 15, bvci 0x0000 00 00 00 00 0b 1f 84 c0 de ad 03 1b 86 11 22 33 40 50 60 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 15 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 19 00 00 00 00 0b 1f 84 e0 54 32 10 1b 86 21 63 54 40 50 60 result (BVC_SUSPEND) = 19 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 13 RAID patched (SGSN): 3 APN patched : 1 TLLI patched (BSS ): 11 TLLI patched (SGSN): 8 P-TMSI patched (SGSN): 3 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 RoutingArea Update Request count: 2 RoutingArea Update Accept count : 2 RoutingArea Update Compltd count: 1 PDP Activation Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c0dead03 -> e0543210, IMSI 12131415161718, AGE 0 PROCESSING BVC_SUSPEND_ACK from 0x05060708:32000 00 00 00 00 0c 1f 84 e0 54 32 10 1b 86 21 63 54 40 50 60 1d 81 01 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 0c 1f 84 e0 54 32 10 1b 86 21 63 54 40 50 60 1d 81 01 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 22 00 00 00 00 0c 1f 84 c0 de ad 03 1b 86 11 22 33 40 50 60 1d 81 01 result (BVC_SUSPEND_ACK) = 22 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 13 RAID patched (SGSN): 4 APN patched : 1 TLLI patched (BSS ): 11 TLLI patched (SGSN): 9 P-TMSI patched (SGSN): 3 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 RoutingArea Update Request count: 2 RoutingArea Update Accept count : 2 RoutingArea Update Compltd count: 1 PDP Activation Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c0dead03 -> e0543210, IMSI 12131415161718, AGE 0 PROCESSING PAGING_PS from 0x05060708:32000 00 00 00 00 06 0d 88 11 12 13 14 15 16 17 18 0a 82 07 04 1b 86 11 22 33 40 50 60 18 83 00 00 00 20 84 e0 54 32 10 CALLBACK, event 0, msg length 34, bvci 0x0000 00 00 00 00 06 0d 88 11 12 13 14 15 16 17 18 0a 82 07 04 1b 86 11 22 33 40 50 60 18 83 00 00 00 20 84 e0 54 32 10 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 34 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 38 00 00 00 00 06 0d 88 11 12 13 14 15 16 17 18 0a 82 07 04 1b 86 11 22 33 40 50 60 18 83 00 00 00 20 84 c0 de ad 03 result (PAGING_PS) = 38 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 13 RAID patched (SGSN): 5 APN patched : 1 TLLI patched (BSS ): 11 TLLI patched (SGSN): 9 P-TMSI patched (SGSN): 4 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 RoutingArea Update Request count: 2 RoutingArea Update Accept count : 2 RoutingArea Update Compltd count: 1 PDP Activation Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c0dead03 -> e0543210, IMSI 12131415161718, AGE 0 PROCESSING LLC_DISCARDED from 0x01020304:1111 00 00 00 00 2c 1f 84 c0 de ad 03 0f 81 01 04 82 ee e1 25 83 00 00 0c CALLBACK, event 0, msg length 19, bvci 0x0000 00 00 00 00 2c 1f 84 c0 de ad 03 0f 81 01 04 82 ee e1 25 83 00 00 0c NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 19 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 23 00 00 00 00 2c 1f 84 e0 54 32 10 0f 81 01 04 82 ee e1 25 83 00 00 0c result (LLC_DISCARDED) = 23 Gbproxy global: PROCESSING BVC_SUSPEND_ACK from 0x05060708:32000 00 00 00 00 0c 1f 84 e0 54 32 10 1b 86 00 f1 99 00 63 60 1d 81 01 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 0c 1f 84 e0 54 32 10 1b 86 00 f1 99 00 63 60 1d 81 01 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 24 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 28 00 00 00 00 41 07 81 21 15 92 0c 1f 84 e0 54 32 10 1b 86 00 f1 99 00 63 60 1d 81 01 result (BVC_SUSPEND_ACK) = 28 Gbproxy global: Invalid Routing Area Identifier : 1 Patch error: no peer : 1 PROCESSING BVC_SUSPEND_ACK from 0x05060708:32000 00 00 00 00 0c 1f 84 e0 54 32 10 1b 86 99 69 54 40 50 60 1d 81 01 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 0c 1f 84 e0 54 32 10 1b 86 99 69 54 40 50 60 1d 81 01 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 22 00 00 00 00 0c 1f 84 c0 de ad 03 1b 86 11 22 33 40 50 60 1d 81 01 result (BVC_SUSPEND_ACK) = 22 Gbproxy global: Invalid Routing Area Identifier : 1 Patch error: no peer : 1 PROCESSING GMM INFO from 0x05060708:32000 00 00 10 02 00 ee ba db ad 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 00 83 00 00 00 0e 88 41 c0 09 08 21 04 ba 3d CALLBACK, event 0, msg length 58, bvci 0x1002 00 00 10 02 00 ee ba db ad 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 00 83 00 00 00 0e 88 41 c0 09 08 21 04 ba 3d NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 58 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 62 00 00 10 02 00 ee ba db ad 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 00 83 00 00 00 0e 88 41 c0 09 08 21 04 ba 3d result (GMM INFO) = 62 PROCESSING GMM INFO from 0x05060708:32000 00 00 10 02 00 ee ba db ad 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 0d 08 21 68 71 6b CALLBACK, event 0, msg length 66, bvci 0x1002 00 00 10 02 00 ee ba db ad 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 0d 08 21 68 71 6b NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 66 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 70 00 00 10 02 00 ee ba db ad 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 0d 08 21 68 71 6b result (GMM INFO) = 70 PROCESSING DETACH REQ from 0x01020304:1111 00 00 10 02 01 c0 de ad 03 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 1d 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb aa cc a3 CALLBACK, event 0, msg length 44, bvci 0x1002 00 00 10 02 01 c0 de ad 03 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 1d 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb aa cc a3 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 44 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 48 00 00 10 02 01 e0 54 32 10 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 15 01 c0 1d 08 05 01 18 05 f4 e0 54 32 10 19 03 b9 97 cb ea 6d af result (DETACH REQ) = 48 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 14 RAID patched (SGSN): 6 APN patched : 1 TLLI patched (BSS ): 13 TLLI patched (SGSN): 10 P-TMSI patched (BSS ): 1 P-TMSI patched (SGSN): 4 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 RoutingArea Update Request count: 2 RoutingArea Update Accept count : 2 RoutingArea Update Compltd count: 1 Detach Request count : 1 PDP Activation Request count : 1 TLLI from SGSN unknown : 2 TLLI cache size : 1 TLLI-Cache: 1 TLLI c0dead03 -> e0543210, IMSI 12131415161718, AGE 0 PROCESSING DETACH ACC from 0x05060708:32000 00 00 10 02 00 e0 54 32 10 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 19 08 06 00 04 ff 52 CALLBACK, event 0, msg length 67, bvci 0x1002 00 00 10 02 00 e0 54 32 10 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 19 08 06 00 04 ff 52 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 67 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 71 00 00 10 02 00 c0 de ad 03 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 19 08 06 00 04 ff 52 result (DETACH ACC) = 71 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 14 RAID patched (SGSN): 6 APN patched : 1 TLLI patched (BSS ): 13 TLLI patched (SGSN): 11 P-TMSI patched (BSS ): 1 P-TMSI patched (SGSN): 4 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 RoutingArea Update Request count: 2 RoutingArea Update Accept count : 2 RoutingArea Update Compltd count: 1 Detach Request count : 1 Detach Accept count : 1 PDP Activation Request count : 1 TLLI from SGSN unknown : 2 TLLI-Cache: 0 Gbproxy global: Invalid Routing Area Identifier : 1 Patch error: no peer : 1 === test_gbproxy_ptmsi_patching_bad_cases === --- Initialise SGSN --- MESSAGE to SGSN at 0x05060708:32000, msg length 12 02 00 81 01 01 82 01 01 04 82 01 00 PROCESSING RESET_ACK from 0x05060708:32000 03 01 82 01 01 04 82 01 00 MESSAGE to SGSN at 0x05060708:32000, msg length 1 0a result (RESET_ACK) = 1 PROCESSING ALIVE_ACK from 0x05060708:32000 0b MESSAGE to SGSN at 0x05060708:32000, msg length 1 06 result (ALIVE_ACK) = 1 PROCESSING UNBLOCK_ACK from 0x05060708:32000 07 ==> got signal NS_UNBLOCK, NS-VC 0x0101/5.6.7.8:32000 result (UNBLOCK_ACK) = 0 PROCESSING ALIVE from 0x05060708:32000 0a MESSAGE to SGSN at 0x05060708:32000, msg length 1 0b result (ALIVE) = 1 --- Initialise BSS 1 --- Setup NS-VC: remote 0x01020304:1111, NSVCI 0x1001(4097), NSEI 0x1000(4096) PROCESSING RESET from 0x01020304:1111 02 00 81 01 01 82 10 01 04 82 10 00 ==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 9 03 01 82 10 01 04 82 10 00 MESSAGE to BSS at 0x01020304:1111, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:1111 0a MESSAGE to BSS at 0x01020304:1111, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:1111 06 ==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:1111 0b result (ALIVE_ACK) = 0 Setup BSSGP: remote 0x01020304:1111, BVCI 0x1002(4098) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 10 02 07 81 08 08 88 21 63 54 40 50 60 10 00 result (BVC_RESET) = 22 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 10 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 10 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 10 02 result (BVC_RESET_ACK) = 9 Current NS-VCIs: VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111 Packets at NS Level ( In): 5 Packets at NS Level (Out): 5 Bytes at NS Level ( In): 37 Bytes at NS Level (Out): 21 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 Packets at NS Level ( In): 5 Packets at NS Level (Out): 5 Bytes at NS Level ( In): 21 Bytes at NS Level (Out): 37 NS-VC Block count : 1 Gbproxy global: Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 1 TLLI-Cache: 0 --- Send message from BSS 1 to SGSN, BVCI 0x1002 --- PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 78 de ad 00 00 00 04 08 88 21 63 54 00 63 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 result (ATTACH REQUEST) = 79 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 2 TLLI patched (BSS ): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 78dead00, IMSI (none), AGE 0 PROCESSING IDENT REQUEST from 0x05060708:32000 00 00 10 02 00 78 de ad 00 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba CALLBACK, event 0, msg length 23, bvci 0x1002 00 00 10 02 00 78 de ad 00 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 23 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 27 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba result (IDENT REQUEST) = 27 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 2 TLLI patched (BSS ): 1 TLLI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 78dead00, IMSI (none), AGE 0 PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 05 08 16 08 11 12 13 14 15 16 17 18 ad 05 28 CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 05 08 16 08 11 12 13 14 15 16 17 18 ad 05 28 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 40 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 44 00 00 10 02 01 78 de ad 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 11 01 c0 05 08 16 08 11 12 13 14 15 16 17 18 ad 05 28 result (IDENT RESPONSE) = 44 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 3 TLLI patched (BSS ): 2 TLLI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 78dead00, IMSI 12131415161718, AGE 0 PROCESSING ATTACH ACCEPT from 0x05060708:32000 00 00 10 02 00 78 de ad 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 53 62 f1 CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 78 de ad 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 53 62 f1 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 11 22 33 40 50 60 19 cd d7 08 17 16 18 05 f4 c0 de ad 01 0c 0a 29 result (ATTACH ACCEPT) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 3 RAID patched (SGSN): 1 TLLI patched (BSS ): 2 TLLI patched (SGSN): 2 P-TMSI patched (SGSN): 1 Attach Request count : 1 Attach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead/c0dead01 -> 78dead00/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH ACCEPT (duplicated) from 0x05060708:32000 00 00 10 02 00 78 de ad 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 09 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 1d 9e 24 CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 78 de ad 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 09 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 1d 9e 24 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 09 08 02 01 49 04 11 22 33 40 50 60 19 cd d7 08 17 16 18 05 f4 c0 de ad 01 42 f6 fc result (ATTACH ACCEPT (duplicated)) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 3 RAID patched (SGSN): 2 TLLI patched (BSS ): 2 TLLI patched (SGSN): 3 P-TMSI patched (SGSN): 2 Attach Request count : 1 Attach Accept count : 2 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead/c0dead01 -> 78dead00/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 c0 de ad 01 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 09 08 03 39 d7 bc CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 c0 de ad 01 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 09 08 03 39 d7 bc NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 08 01 c0 09 08 03 39 d7 bc result (ATTACH COMPLETE) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 4 RAID patched (SGSN): 2 TLLI patched (BSS ): 3 TLLI patched (SGSN): 3 P-TMSI patched (SGSN): 2 Attach Request count : 1 Attach Accept count : 2 Attach Completed count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead/c0dead01 -> 78dead00/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING GMM INFO from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 0d 08 21 68 71 6b CALLBACK, event 0, msg length 66, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 0d 08 21 68 71 6b NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 66 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 70 00 00 10 02 00 c0 de ad 01 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 0d 08 21 68 71 6b result (GMM INFO) = 70 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 4 RAID patched (SGSN): 2 TLLI patched (BSS ): 3 TLLI patched (SGSN): 4 P-TMSI patched (SGSN): 2 Attach Request count : 1 Attach Accept count : 2 Attach Completed count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH REQ from 0x01020304:1111 00 00 10 02 01 c0 de ad 01 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 0d 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 37 67 c6 CALLBACK, event 0, msg length 44, bvci 0x1002 00 00 10 02 01 c0 de ad 01 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 0d 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 37 67 c6 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 44 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 48 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 15 01 c0 0d 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 37 67 c6 result (DETACH REQ) = 48 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 5 RAID patched (SGSN): 2 TLLI patched (BSS ): 4 TLLI patched (SGSN): 4 P-TMSI patched (SGSN): 2 Attach Request count : 1 Attach Accept count : 2 Attach Completed count : 1 Detach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH ACC from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 11 08 06 00 cf 8a 58 CALLBACK, event 0, msg length 67, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 11 08 06 00 cf 8a 58 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 67 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 71 00 00 10 02 00 c0 de ad 01 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 11 08 06 00 cf 8a 58 result (DETACH ACC) = 71 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 5 RAID patched (SGSN): 2 TLLI patched (BSS ): 4 TLLI patched (SGSN): 5 P-TMSI patched (SGSN): 2 Attach Request count : 1 Attach Accept count : 2 Attach Completed count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI-Cache: 0 Gbproxy global: === test_gbproxy_imsi_acquisition === --- Initialise SGSN --- MESSAGE to SGSN at 0x05060708:32000, msg length 12 02 00 81 01 01 82 01 01 04 82 01 00 PROCESSING RESET_ACK from 0x05060708:32000 03 01 82 01 01 04 82 01 00 MESSAGE to SGSN at 0x05060708:32000, msg length 1 0a result (RESET_ACK) = 1 PROCESSING ALIVE_ACK from 0x05060708:32000 0b MESSAGE to SGSN at 0x05060708:32000, msg length 1 06 result (ALIVE_ACK) = 1 PROCESSING UNBLOCK_ACK from 0x05060708:32000 07 ==> got signal NS_UNBLOCK, NS-VC 0x0101/5.6.7.8:32000 result (UNBLOCK_ACK) = 0 PROCESSING ALIVE from 0x05060708:32000 0a MESSAGE to SGSN at 0x05060708:32000, msg length 1 0b result (ALIVE) = 1 --- Initialise BSS 1 --- Setup NS-VC: remote 0x01020304:1111, NSVCI 0x1001(4097), NSEI 0x1000(4096) PROCESSING RESET from 0x01020304:1111 02 00 81 01 01 82 10 01 04 82 10 00 ==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 9 03 01 82 10 01 04 82 10 00 MESSAGE to BSS at 0x01020304:1111, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:1111 0a MESSAGE to BSS at 0x01020304:1111, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:1111 06 ==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:1111 0b result (ALIVE_ACK) = 0 Setup BSSGP: remote 0x01020304:1111, BVCI 0x1002(4098) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 10 02 07 81 08 08 88 21 63 54 40 50 60 10 00 result (BVC_RESET) = 22 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 10 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 10 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 10 02 result (BVC_RESET_ACK) = 9 Current NS-VCIs: VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111 Packets at NS Level ( In): 5 Packets at NS Level (Out): 5 Bytes at NS Level ( In): 37 Bytes at NS Level (Out): 21 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 Packets at NS Level ( In): 5 Packets at NS Level (Out): 5 Bytes at NS Level ( In): 21 Bytes at NS Level (Out): 37 NS-VC Block count : 1 Gbproxy global: Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 1 TLLI-Cache: 0 --- Send message from BSS 1 to SGSN, BVCI 0x1002 --- PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 0e 00 09 41 c4 01 08 15 01 b7 f8 36 result (ATTACH REQUEST) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 78dead00, IMSI (none), AGE 0, STORED 1, IMSI acquisition in progress PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 05 08 16 08 11 12 13 14 15 16 17 18 ad 05 28 CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 05 08 16 08 11 12 13 14 15 16 17 18 ad 05 28 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 78 de ad 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 21 63 54 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 8e cd 32 result (IDENT RESPONSE) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 3 TLLI patched (BSS ): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 78dead00, IMSI 12131415161718, AGE 0 PROCESSING IDENT REQUEST from 0x05060708:32000 00 00 10 02 00 78 de ad 00 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba CALLBACK, event 0, msg length 23, bvci 0x1002 00 00 10 02 00 78 de ad 00 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 23 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 27 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba result (IDENT REQUEST) = 27 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 3 TLLI patched (BSS ): 1 TLLI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 78dead00, IMSI 12131415161718, AGE 0 PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 09 08 16 08 11 12 13 14 15 16 17 18 ba 14 c3 CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 09 08 16 08 11 12 13 14 15 16 17 18 ba 14 c3 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 40 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 44 00 00 10 02 01 78 de ad 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 11 01 c0 09 08 16 08 11 12 13 14 15 16 17 18 ba 14 c3 result (IDENT RESPONSE) = 44 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 4 TLLI patched (BSS ): 2 TLLI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 78dead00, IMSI 12131415161718, AGE 0 PROCESSING ATTACH ACCEPT from 0x05060708:32000 00 00 10 02 00 78 de ad 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 53 62 f1 CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 78 de ad 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 53 62 f1 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 11 22 33 40 50 60 19 cd d7 08 17 16 18 05 f4 c0 de ad 01 0c 0a 29 result (ATTACH ACCEPT) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 4 RAID patched (SGSN): 1 TLLI patched (BSS ): 2 TLLI patched (SGSN): 2 P-TMSI patched (SGSN): 1 Attach Request count : 1 Attach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead/c0dead01 -> 78dead00/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 c0 de ad 01 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 0d 08 03 55 1c ea CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 c0 de ad 01 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 0d 08 03 55 1c ea NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 08 01 c0 0d 08 03 55 1c ea result (ATTACH COMPLETE) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 5 RAID patched (SGSN): 1 TLLI patched (BSS ): 3 TLLI patched (SGSN): 2 P-TMSI patched (SGSN): 1 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead/c0dead01 -> 78dead00/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING GMM INFO from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 09 08 21 04 ba 3d CALLBACK, event 0, msg length 66, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 09 08 21 04 ba 3d NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 66 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 70 00 00 10 02 00 c0 de ad 01 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 09 08 21 04 ba 3d result (GMM INFO) = 70 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 5 RAID patched (SGSN): 1 TLLI patched (BSS ): 3 TLLI patched (SGSN): 3 P-TMSI patched (SGSN): 1 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING XID (UL) from 0x01020304:1111 00 00 10 02 01 c0 de ad 01 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 0f 41 fb 01 00 0e 00 64 11 05 16 01 90 66 b3 28 CALLBACK, event 0, msg length 38, bvci 0x1002 00 00 10 02 01 c0 de ad 01 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 0f 41 fb 01 00 0e 00 64 11 05 16 01 90 66 b3 28 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 38 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 42 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 0f 41 fb 01 00 0e 00 64 11 05 16 01 90 66 b3 28 result (XID (UL)) = 42 PROCESSING XID (DL) from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8c 41 fb 30 84 10 61 b6 64 e4 a9 1a 9e CALLBACK, event 0, msg length 70, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8c 41 fb 30 84 10 61 b6 64 e4 a9 1a 9e NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 70 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 74 00 00 10 02 00 c0 de ad 01 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8c 41 fb 30 84 10 61 b6 64 e4 a9 1a 9e result (XID (DL)) = 74 PROCESSING LL11 DNS QUERY (UL) from 0x01020304:1111 00 00 10 02 01 c0 de ad 01 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 42 0b c0 01 65 00 00 00 45 00 00 38 95 72 00 00 45 11 20 85 0a c0 07 e4 ac 10 01 0a ad ab 00 35 00 24 0e 1c 3b e0 01 00 00 01 00 00 00 00 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 47 8f 07 CALLBACK, event 0, msg length 89, bvci 0x1002 00 00 10 02 01 c0 de ad 01 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 42 0b c0 01 65 00 00 00 45 00 00 38 95 72 00 00 45 11 20 85 0a c0 07 e4 ac 10 01 0a ad ab 00 35 00 24 0e 1c 3b e0 01 00 00 01 00 00 00 00 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 47 8f 07 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 89 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 93 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 42 0b c0 01 65 00 00 00 45 00 00 38 95 72 00 00 45 11 20 85 0a c0 07 e4 ac 10 01 0a ad ab 00 35 00 24 0e 1c 3b e0 01 00 00 01 00 00 00 00 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 47 8f 07 result (LL11 DNS QUERY (UL)) = 93 PROCESSING LL11 DNS RESP (DL) from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 00 d0 4b c0 01 65 00 00 00 45 00 00 c6 00 00 40 00 3e 11 7c 69 ac 10 01 0a 0a c0 07 e4 00 35 ad ab 00 b2 74 4e 3b e0 81 80 00 01 00 01 00 05 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 c0 0c 00 01 00 01 00 00 0e 10 00 04 c1 63 90 58 c0 0e 00 02 00 01 00 00 0e 10 00 16 03 6e 73 32 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 03 6e 65 74 00 c0 0e 00 02 00 01 00 00 0e 10 00 10 02 6e 73 01 73 08 70 6c 75 73 6c 69 6e 65 c0 14 c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 0e c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 5f c0 0e 00 02 00 01 00 00 0e 10 00 12 02 6e 73 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 c0 14 aa df 31 CALLBACK, event 0, msg length 267, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 00 d0 4b c0 01 65 00 00 00 45 00 00 c6 00 00 40 00 3e 11 7c 69 ac 10 01 0a 0a c0 07 e4 00 35 ad ab 00 b2 74 4e 3b e0 81 80 00 01 00 01 00 05 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 c0 0c 00 01 00 01 00 00 0e 10 00 04 c1 63 90 58 c0 0e 00 02 00 01 00 00 0e 10 00 16 03 6e 73 32 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 03 6e 65 74 00 c0 0e 00 02 00 01 00 00 0e 10 00 10 02 6e 73 01 73 08 70 6c 75 73 6c 69 6e 65 c0 14 c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 0e c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 5f c0 0e 00 02 00 01 00 00 0e 10 00 12 02 6e 73 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 c0 14 aa df 31 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 267 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 271 00 00 10 02 00 c0 de ad 01 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 00 d0 4b c0 01 65 00 00 00 45 00 00 c6 00 00 40 00 3e 11 7c 69 ac 10 01 0a 0a c0 07 e4 00 35 ad ab 00 b2 74 4e 3b e0 81 80 00 01 00 01 00 05 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 c0 0c 00 01 00 01 00 00 0e 10 00 04 c1 63 90 58 c0 0e 00 02 00 01 00 00 0e 10 00 16 03 6e 73 32 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 03 6e 65 74 00 c0 0e 00 02 00 01 00 00 0e 10 00 10 02 6e 73 01 73 08 70 6c 75 73 6c 69 6e 65 c0 14 c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 0e c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 5f c0 0e 00 02 00 01 00 00 0e 10 00 12 02 6e 73 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 c0 14 aa df 31 result (LL11 DNS RESP (DL)) = 271 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 7 RAID patched (SGSN): 1 TLLI patched (BSS ): 5 TLLI patched (SGSN): 5 P-TMSI patched (SGSN): 1 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING LLC_DISCARDED from 0x01020304:1111 00 00 00 00 2c 1f 84 c0 de ad 01 0f 81 01 04 82 10 02 25 83 00 00 0c CALLBACK, event 0, msg length 19, bvci 0x0000 00 00 00 00 2c 1f 84 c0 de ad 01 0f 81 01 04 82 10 02 25 83 00 00 0c NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 19 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 23 00 00 00 00 2c 1f 84 ef e2 b7 00 0f 81 01 04 82 10 02 25 83 00 00 0c result (LLC_DISCARDED) = 23 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 7 RAID patched (SGSN): 1 TLLI patched (BSS ): 6 TLLI patched (SGSN): 5 P-TMSI patched (SGSN): 1 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING LLC_DISCARDED from 0x05060708:32000 00 00 00 00 2c 1f 84 ef e2 b7 00 0f 81 01 04 82 10 02 25 83 00 00 0c CALLBACK, event 0, msg length 19, bvci 0x0000 00 00 00 00 2c 1f 84 ef e2 b7 00 0f 81 01 04 82 10 02 25 83 00 00 0c NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 25 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 29 00 00 00 00 41 07 81 27 15 93 2c 1f 84 ef e2 b7 00 0f 81 01 04 82 10 02 25 83 00 00 0c result (LLC_DISCARDED) = 29 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 7 RAID patched (SGSN): 1 TLLI patched (BSS ): 6 TLLI patched (SGSN): 6 P-TMSI patched (SGSN): 1 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING BVC_SUSPEND from 0x01020304:1111 00 00 00 00 0b 1f 84 c0 de ad 01 1b 86 11 22 33 40 50 60 CALLBACK, event 0, msg length 15, bvci 0x0000 00 00 00 00 0b 1f 84 c0 de ad 01 1b 86 11 22 33 40 50 60 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 15 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 19 00 00 00 00 0b 1f 84 ef e2 b7 00 1b 86 21 63 54 40 50 60 result (BVC_SUSPEND) = 19 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 8 RAID patched (SGSN): 1 TLLI patched (BSS ): 7 TLLI patched (SGSN): 6 P-TMSI patched (SGSN): 1 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING BVC_SUSPEND_ACK from 0x05060708:32000 00 00 00 00 0c 1f 84 ef e2 b7 00 1b 86 21 63 54 40 50 60 1d 81 01 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 0c 1f 84 ef e2 b7 00 1b 86 21 63 54 40 50 60 1d 81 01 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 22 00 00 00 00 0c 1f 84 c0 de ad 01 1b 86 11 22 33 40 50 60 1d 81 01 result (BVC_SUSPEND_ACK) = 22 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 8 RAID patched (SGSN): 2 TLLI patched (BSS ): 7 TLLI patched (SGSN): 7 P-TMSI patched (SGSN): 1 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING LLC_DISCARDED from 0x01020304:1111 00 00 00 00 2c 1f 84 c0 de ad 01 0f 81 01 04 82 ee e1 25 83 00 00 0c CALLBACK, event 0, msg length 19, bvci 0x0000 00 00 00 00 2c 1f 84 c0 de ad 01 0f 81 01 04 82 ee e1 25 83 00 00 0c NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 19 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 23 00 00 00 00 2c 1f 84 ef e2 b7 00 0f 81 01 04 82 ee e1 25 83 00 00 0c result (LLC_DISCARDED) = 23 Gbproxy global: BSSGP protocol error (SGSN): 1 PROCESSING BVC_SUSPEND_ACK from 0x05060708:32000 00 00 00 00 0c 1f 84 ef e2 b7 00 1b 86 00 f1 99 00 63 60 1d 81 01 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 0c 1f 84 ef e2 b7 00 1b 86 00 f1 99 00 63 60 1d 81 01 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 24 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 28 00 00 00 00 41 07 81 21 15 92 0c 1f 84 ef e2 b7 00 1b 86 00 f1 99 00 63 60 1d 81 01 result (BVC_SUSPEND_ACK) = 28 Gbproxy global: Invalid Routing Area Identifier : 1 BSSGP protocol error (SGSN): 1 Patch error: no peer : 1 PROCESSING BVC_SUSPEND_ACK from 0x05060708:32000 00 00 00 00 0c 1f 84 ef e2 b7 00 1b 86 99 69 54 40 50 60 1d 81 01 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 0c 1f 84 ef e2 b7 00 1b 86 99 69 54 40 50 60 1d 81 01 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 22 00 00 00 00 0c 1f 84 c0 de ad 01 1b 86 11 22 33 40 50 60 1d 81 01 result (BVC_SUSPEND_ACK) = 22 Gbproxy global: Invalid Routing Area Identifier : 1 BSSGP protocol error (SGSN): 1 Patch error: no peer : 1 PROCESSING DETACH REQ from 0x01020304:1111 00 00 10 02 01 c0 de ad 01 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 11 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 6d b1 de CALLBACK, event 0, msg length 44, bvci 0x1002 00 00 10 02 01 c0 de ad 01 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 11 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 6d b1 de NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 44 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 48 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 15 01 c0 11 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 6d b1 de result (DETACH REQ) = 48 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 9 RAID patched (SGSN): 3 TLLI patched (BSS ): 9 TLLI patched (SGSN): 8 P-TMSI patched (SGSN): 1 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 Detach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH ACC from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 0d 08 06 00 aa ab ee CALLBACK, event 0, msg length 67, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 0d 08 06 00 aa ab ee NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 67 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 71 00 00 10 02 00 c0 de ad 01 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 0d 08 06 00 aa ab ee result (DETACH ACC) = 71 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 9 RAID patched (SGSN): 3 TLLI patched (BSS ): 9 TLLI patched (SGSN): 9 P-TMSI patched (SGSN): 1 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI-Cache: 0 PROCESSING RA UPD REQ from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 70 80 00 80 0e 00 3e 01 c0 15 08 08 10 11 22 33 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 96 3e 97 CALLBACK, event 0, msg length 85, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 70 80 00 80 0e 00 3e 01 c0 15 08 08 10 11 22 33 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 96 3e 97 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 0e 00 09 41 c4 01 08 15 01 b7 f8 36 result (RA UPD REQ) = 0 PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 19 08 16 08 11 12 13 14 15 16 17 18 35 23 fc CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 19 08 16 08 11 12 13 14 15 16 17 18 35 23 fc NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 85 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 89 00 00 10 02 01 78 de ad 02 00 00 04 08 88 21 63 54 00 63 60 70 80 00 80 0e 00 3e 01 c0 15 08 08 10 11 22 33 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 96 3e 97 result (IDENT RESPONSE) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 10 RAID patched (SGSN): 3 TLLI patched (BSS ): 10 TLLI patched (SGSN): 9 P-TMSI patched (SGSN): 1 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 RoutingArea Update Request count: 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 78dead02, IMSI 12131415161718, AGE 0 PROCESSING RA UDP ACC from 0x05060708:32000 00 00 10 02 00 78 de ad 02 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9d 41 c0 11 08 09 00 49 21 63 54 40 50 60 19 54 ab b3 18 05 f4 ef e2 b7 00 17 16 36 98 77 CALLBACK, event 0, msg length 87, bvci 0x1002 00 00 10 02 00 78 de ad 02 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9d 41 c0 11 08 09 00 49 21 63 54 40 50 60 19 54 ab b3 18 05 f4 ef e2 b7 00 17 16 36 98 77 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 87 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 91 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9d 41 c0 11 08 09 00 49 11 22 33 40 50 60 19 54 ab b3 18 05 f4 c0 de ad 03 17 16 6e 58 26 result (RA UDP ACC) = 91 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 10 RAID patched (SGSN): 4 TLLI patched (BSS ): 10 TLLI patched (SGSN): 10 P-TMSI patched (SGSN): 2 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 RoutingArea Update Request count: 1 RoutingArea Update Accept count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead/c0dead03 -> 78dead02/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH REQ from 0x01020304:1111 00 00 10 02 01 c0 de ad 01 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 1d 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb aa cc a3 CALLBACK, event 0, msg length 44, bvci 0x1002 00 00 10 02 01 c0 de ad 01 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 1d 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb aa cc a3 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 c0 de ad 01 00 50 20 16 82 02 58 0e 00 09 41 c4 01 08 06 00 11 f5 c0 result (DETACH REQ) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 10 RAID patched (SGSN): 4 TLLI patched (BSS ): 10 TLLI patched (SGSN): 10 P-TMSI patched (SGSN): 2 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 RoutingArea Update Request count: 1 RoutingArea Update Accept count : 1 Detach Request count : 2 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead/c0dead03 -> 78dead02/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH ACC from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 15 08 06 00 f7 35 f0 CALLBACK, event 0, msg length 67, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 15 08 06 00 f7 35 f0 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 67 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 71 00 00 10 02 00 c0 de ad 03 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 15 08 06 00 f7 35 f0 result (DETACH ACC) = 71 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 10 RAID patched (SGSN): 4 TLLI patched (BSS ): 10 TLLI patched (SGSN): 11 P-TMSI patched (SGSN): 2 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 RoutingArea Update Request count: 1 RoutingArea Update Accept count : 1 Detach Request count : 2 Detach Accept count : 2 TLLI-Cache: 0 PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 21 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 44 b6 bb CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 21 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 44 b6 bb NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 0e 00 09 41 c4 01 08 15 01 b7 f8 36 result (ATTACH REQUEST) = 0 PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 25 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 1d aa 57 CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 25 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 1d aa 57 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 0e 00 09 41 c4 05 08 15 01 8f 47 9e result (ATTACH REQUEST) = 0 PROCESSING DETACH REQ from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 29 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb d9 1d ef CALLBACK, event 0, msg length 44, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 29 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb d9 1d ef NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 0e 00 09 41 c4 09 08 06 00 da 80 ca result (DETACH REQ) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 10 RAID patched (SGSN): 4 TLLI patched (BSS ): 10 TLLI patched (SGSN): 11 P-TMSI patched (SGSN): 2 Attach Request count : 3 Attach Accept count : 1 Attach Completed count : 1 RoutingArea Update Request count: 1 RoutingArea Update Accept count : 1 Detach Request count : 3 Detach Accept count : 2 TLLI-Cache: 0 PROCESSING DETACH REQ (unknown TLLI) from 0x01020304:1111 00 00 10 02 01 80 00 be ef 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 2d 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 0d 30 0d CALLBACK, event 0, msg length 44, bvci 0x1002 00 00 10 02 01 80 00 be ef 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 2d 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 0d 30 0d NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 80 00 be ef 00 50 20 16 82 02 58 0e 00 09 41 c4 01 08 06 00 11 f5 c0 result (DETACH REQ (unknown TLLI)) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 10 RAID patched (SGSN): 4 TLLI patched (BSS ): 10 TLLI patched (SGSN): 11 P-TMSI patched (SGSN): 2 Attach Request count : 3 Attach Accept count : 1 Attach Completed count : 1 RoutingArea Update Request count: 1 RoutingArea Update Accept count : 1 Detach Request count : 4 Detach Accept count : 2 TLLI-Cache: 0 PROCESSING RA UPD REQ from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 70 80 00 80 0e 00 3e 01 c0 31 08 08 10 11 22 33 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 d8 cf d8 CALLBACK, event 0, msg length 85, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 70 80 00 80 0e 00 3e 01 c0 31 08 08 10 11 22 33 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 d8 cf d8 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 0e 00 09 41 c4 01 08 15 01 b7 f8 36 result (RA UPD REQ) = 0 PROCESSING RA UPD REQ from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 70 80 00 80 0e 00 3e 01 c0 35 08 08 10 11 22 33 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 ac 9c 37 CALLBACK, event 0, msg length 85, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 70 80 00 80 0e 00 3e 01 c0 35 08 08 10 11 22 33 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 ac 9c 37 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 0e 00 09 41 c4 05 08 15 01 8f 47 9e result (RA UPD REQ) = 0 PROCESSING DETACH REQ from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 39 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 44 b6 8a CALLBACK, event 0, msg length 44, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 39 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 44 b6 8a NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 0e 00 09 41 c4 09 08 06 00 da 80 ca result (DETACH REQ) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 10 RAID patched (SGSN): 4 TLLI patched (BSS ): 10 TLLI patched (SGSN): 11 P-TMSI patched (SGSN): 2 Attach Request count : 3 Attach Accept count : 1 Attach Completed count : 1 RoutingArea Update Request count: 3 RoutingArea Update Accept count : 1 Detach Request count : 5 Detach Accept count : 2 TLLI-Cache: 0 Gbproxy global: Invalid Routing Area Identifier : 1 BSSGP protocol error (SGSN): 1 Patch error: no peer : 1 === test_gbproxy_secondary_sgsn === --- Initialise SGSN 1 --- MESSAGE to SGSN at 0x05060708:32000, msg length 12 02 00 81 01 01 82 01 01 04 82 01 00 PROCESSING RESET_ACK from 0x05060708:32000 03 01 82 01 01 04 82 01 00 MESSAGE to SGSN at 0x05060708:32000, msg length 1 0a result (RESET_ACK) = 1 PROCESSING ALIVE_ACK from 0x05060708:32000 0b MESSAGE to SGSN at 0x05060708:32000, msg length 1 06 result (ALIVE_ACK) = 1 PROCESSING UNBLOCK_ACK from 0x05060708:32000 07 ==> got signal NS_UNBLOCK, NS-VC 0x0101/5.6.7.8:32000 result (UNBLOCK_ACK) = 0 PROCESSING ALIVE from 0x05060708:32000 0a MESSAGE to SGSN at 0x05060708:32000, msg length 1 0b result (ALIVE) = 1 --- Initialise SGSN 2 --- MESSAGE to SGSN 2 at 0x15161718:32001, msg length 12 02 00 81 01 01 82 01 03 04 82 01 02 PROCESSING RESET_ACK from 0x15161718:32001 03 01 82 01 03 04 82 01 02 MESSAGE to SGSN 2 at 0x15161718:32001, msg length 1 0a result (RESET_ACK) = 1 PROCESSING ALIVE_ACK from 0x15161718:32001 0b MESSAGE to SGSN 2 at 0x15161718:32001, msg length 1 06 result (ALIVE_ACK) = 1 PROCESSING UNBLOCK_ACK from 0x15161718:32001 07 ==> got signal NS_UNBLOCK, NS-VC 0x0103/21.22.23.24:32001 result (UNBLOCK_ACK) = 0 PROCESSING ALIVE from 0x15161718:32001 0a MESSAGE to SGSN 2 at 0x15161718:32001, msg length 1 0b result (ALIVE) = 1 --- Initialise BSS 1 --- Setup NS-VC: remote 0x01020304:1111, NSVCI 0x1001(4097), NSEI 0x1000(4096) PROCESSING RESET from 0x01020304:1111 02 00 81 01 01 82 10 01 04 82 10 00 ==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 9 03 01 82 10 01 04 82 10 00 MESSAGE to BSS at 0x01020304:1111, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:1111 0a MESSAGE to BSS at 0x01020304:1111, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:1111 06 ==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:1111 0b result (ALIVE_ACK) = 0 Setup BSSGP: remote 0x01020304:1111, BVCI 0x0000(0) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 00 00 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 00 00 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 00 00 result (BVC_RESET) = 9 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 00 00 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 00 00 result (BVC_RESET_ACK) = -2 Setup BSSGP: remote 0x01020304:1111, BVCI 0x1002(4098) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN 2, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN 2 at 0x15161718:32001, msg length 22 00 00 00 00 22 04 82 10 02 07 81 08 08 88 21 63 54 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 10 02 07 81 08 08 88 21 63 54 40 50 60 10 00 result (BVC_RESET) = 22 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 10 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 10 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 10 02 result (BVC_RESET_ACK) = 9 PROCESSING BVC_RESET_ACK from 0x15161718:32001 00 00 00 00 23 04 82 10 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 10 02 result (BVC_RESET_ACK) = 1 Current NS-VCIs: VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111 Packets at NS Level ( In): 6 Packets at NS Level (Out): 6 Bytes at NS Level ( In): 59 Bytes at NS Level (Out): 30 VCI 0x0103, NSEI 0x0102, peer 0x15161718:32001 Packets at NS Level ( In): 5 Packets at NS Level (Out): 5 Bytes at NS Level ( In): 21 Bytes at NS Level (Out): 37 NS-VC Block count : 1 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 Packets at NS Level ( In): 6 Packets at NS Level (Out): 5 Bytes at NS Level ( In): 30 Bytes at NS Level (Out): 37 NS-VC Block count : 1 Gbproxy global: Invalid BVC Identifier : 1 Patch error: no peer : 1 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 1 TLLI-Cache: 0 --- Flow control --- PROCESSING FLOW_CONTROL_BVC from 0x01020304:1111 00 00 10 02 26 1e 81 01 05 82 01 dc 03 82 02 76 01 82 00 50 1c 82 02 58 06 82 00 03 CALLBACK, event 0, msg length 24, bvci 0x1002 00 00 10 02 26 1e 81 01 05 82 01 dc 03 82 02 76 01 82 00 50 1c 82 02 58 06 82 00 03 NS UNITDATA MESSAGE to SGSN 2, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to SGSN 2 at 0x15161718:32001, msg length 28 00 00 10 02 26 1e 81 01 05 82 01 dc 03 82 02 76 01 82 00 50 1c 82 02 58 06 82 00 03 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 28 00 00 10 02 26 1e 81 01 05 82 01 dc 03 82 02 76 01 82 00 50 1c 82 02 58 06 82 00 03 result (FLOW_CONTROL_BVC) = 28 PROCESSING FLOW_CONTROL_BVC_ACK from 0x05060708:32000 00 00 10 02 27 1e 81 01 CALLBACK, event 0, msg length 4, bvci 0x1002 00 00 10 02 27 1e 81 01 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 4 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 8 00 00 10 02 27 1e 81 01 result (FLOW_CONTROL_BVC_ACK) = 8 PROCESSING FLOW_CONTROL_BVC_ACK from 0x15161718:32001 00 00 10 02 27 1e 81 01 CALLBACK, event 0, msg length 4, bvci 0x1002 00 00 10 02 27 1e 81 01 result (FLOW_CONTROL_BVC_ACK) = 0 --- Establish GPRS connection (SGSN 1) --- PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 0e 00 09 41 c4 01 08 15 01 b7 f8 36 result (ATTACH REQUEST) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 78dead00, IMSI (none), AGE 0, STORED 1, IMSI acquisition in progress, SGSN NSEI 65535 PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 05 08 16 08 11 12 13 14 15 16 17 18 ad 05 28 CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 05 08 16 08 11 12 13 14 15 16 17 18 ad 05 28 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 78 de ad 00 00 00 04 08 88 21 63 54 00 63 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 result (IDENT RESPONSE) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 2 TLLI patched (BSS ): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 78dead00, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING IDENT REQUEST from 0x05060708:32000 00 00 10 02 00 78 de ad 00 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba CALLBACK, event 0, msg length 23, bvci 0x1002 00 00 10 02 00 78 de ad 00 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 23 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 27 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba result (IDENT REQUEST) = 27 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 2 TLLI patched (BSS ): 1 TLLI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 78dead00, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 09 08 16 08 11 12 13 14 15 16 17 18 ba 14 c3 CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 09 08 16 08 11 12 13 14 15 16 17 18 ba 14 c3 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 40 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 44 00 00 10 02 01 78 de ad 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 11 01 c0 09 08 16 08 11 12 13 14 15 16 17 18 ba 14 c3 result (IDENT RESPONSE) = 44 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 3 TLLI patched (BSS ): 2 TLLI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 78dead00, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING ATTACH ACCEPT from 0x05060708:32000 00 00 10 02 00 78 de ad 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 53 62 f1 CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 78 de ad 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 53 62 f1 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 11 22 33 40 50 60 19 cd d7 08 17 16 18 05 f4 c0 de ad 01 0c 0a 29 result (ATTACH ACCEPT) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 3 RAID patched (SGSN): 1 TLLI patched (BSS ): 2 TLLI patched (SGSN): 2 P-TMSI patched (SGSN): 1 Attach Request count : 1 Attach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead/c0dead01 -> 78dead00/efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 c0 de ad 01 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 0d 08 03 55 1c ea CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 c0 de ad 01 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 0d 08 03 55 1c ea NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 08 01 c0 0d 08 03 55 1c ea result (ATTACH COMPLETE) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 4 RAID patched (SGSN): 1 TLLI patched (BSS ): 3 TLLI patched (SGSN): 2 P-TMSI patched (SGSN): 1 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead/c0dead01 -> 78dead00/efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING GMM INFO from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 09 08 21 04 ba 3d CALLBACK, event 0, msg length 66, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 09 08 21 04 ba 3d NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 66 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 70 00 00 10 02 00 c0 de ad 01 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 09 08 21 04 ba 3d result (GMM INFO) = 70 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 4 RAID patched (SGSN): 1 TLLI patched (BSS ): 3 TLLI patched (SGSN): 3 P-TMSI patched (SGSN): 1 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING XID (UL) from 0x01020304:1111 00 00 10 02 01 c0 de ad 01 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 0f 41 fb 01 00 0e 00 64 11 05 16 01 90 66 b3 28 CALLBACK, event 0, msg length 38, bvci 0x1002 00 00 10 02 01 c0 de ad 01 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 0f 41 fb 01 00 0e 00 64 11 05 16 01 90 66 b3 28 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 38 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 42 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 0f 41 fb 01 00 0e 00 64 11 05 16 01 90 66 b3 28 result (XID (UL)) = 42 PROCESSING XID (DL) from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8c 41 fb 30 84 10 61 b6 64 e4 a9 1a 9e CALLBACK, event 0, msg length 70, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8c 41 fb 30 84 10 61 b6 64 e4 a9 1a 9e NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 70 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 74 00 00 10 02 00 c0 de ad 01 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8c 41 fb 30 84 10 61 b6 64 e4 a9 1a 9e result (XID (DL)) = 74 PROCESSING LL11 DNS QUERY (UL) from 0x01020304:1111 00 00 10 02 01 c0 de ad 01 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 42 0b c0 01 65 00 00 00 45 00 00 38 95 72 00 00 45 11 20 85 0a c0 07 e4 ac 10 01 0a ad ab 00 35 00 24 0e 1c 3b e0 01 00 00 01 00 00 00 00 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 47 8f 07 CALLBACK, event 0, msg length 89, bvci 0x1002 00 00 10 02 01 c0 de ad 01 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 42 0b c0 01 65 00 00 00 45 00 00 38 95 72 00 00 45 11 20 85 0a c0 07 e4 ac 10 01 0a ad ab 00 35 00 24 0e 1c 3b e0 01 00 00 01 00 00 00 00 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 47 8f 07 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 89 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 93 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 42 0b c0 01 65 00 00 00 45 00 00 38 95 72 00 00 45 11 20 85 0a c0 07 e4 ac 10 01 0a ad ab 00 35 00 24 0e 1c 3b e0 01 00 00 01 00 00 00 00 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 47 8f 07 result (LL11 DNS QUERY (UL)) = 93 PROCESSING LL11 DNS RESP (DL) from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 00 d0 4b c0 01 65 00 00 00 45 00 00 c6 00 00 40 00 3e 11 7c 69 ac 10 01 0a 0a c0 07 e4 00 35 ad ab 00 b2 74 4e 3b e0 81 80 00 01 00 01 00 05 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 c0 0c 00 01 00 01 00 00 0e 10 00 04 c1 63 90 58 c0 0e 00 02 00 01 00 00 0e 10 00 16 03 6e 73 32 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 03 6e 65 74 00 c0 0e 00 02 00 01 00 00 0e 10 00 10 02 6e 73 01 73 08 70 6c 75 73 6c 69 6e 65 c0 14 c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 0e c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 5f c0 0e 00 02 00 01 00 00 0e 10 00 12 02 6e 73 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 c0 14 aa df 31 CALLBACK, event 0, msg length 267, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 00 d0 4b c0 01 65 00 00 00 45 00 00 c6 00 00 40 00 3e 11 7c 69 ac 10 01 0a 0a c0 07 e4 00 35 ad ab 00 b2 74 4e 3b e0 81 80 00 01 00 01 00 05 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 c0 0c 00 01 00 01 00 00 0e 10 00 04 c1 63 90 58 c0 0e 00 02 00 01 00 00 0e 10 00 16 03 6e 73 32 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 03 6e 65 74 00 c0 0e 00 02 00 01 00 00 0e 10 00 10 02 6e 73 01 73 08 70 6c 75 73 6c 69 6e 65 c0 14 c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 0e c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 5f c0 0e 00 02 00 01 00 00 0e 10 00 12 02 6e 73 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 c0 14 aa df 31 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 267 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 271 00 00 10 02 00 c0 de ad 01 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 00 d0 4b c0 01 65 00 00 00 45 00 00 c6 00 00 40 00 3e 11 7c 69 ac 10 01 0a 0a c0 07 e4 00 35 ad ab 00 b2 74 4e 3b e0 81 80 00 01 00 01 00 05 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 c0 0c 00 01 00 01 00 00 0e 10 00 04 c1 63 90 58 c0 0e 00 02 00 01 00 00 0e 10 00 16 03 6e 73 32 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 03 6e 65 74 00 c0 0e 00 02 00 01 00 00 0e 10 00 10 02 6e 73 01 73 08 70 6c 75 73 6c 69 6e 65 c0 14 c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 0e c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 5f c0 0e 00 02 00 01 00 00 0e 10 00 12 02 6e 73 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 c0 14 aa df 31 result (LL11 DNS RESP (DL)) = 271 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 6 RAID patched (SGSN): 1 TLLI patched (BSS ): 5 TLLI patched (SGSN): 5 P-TMSI patched (SGSN): 1 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING LLC_DISCARDED from 0x01020304:1111 00 00 00 00 2c 1f 84 c0 de ad 01 0f 81 01 04 82 10 02 25 83 00 00 0c CALLBACK, event 0, msg length 19, bvci 0x0000 00 00 00 00 2c 1f 84 c0 de ad 01 0f 81 01 04 82 10 02 25 83 00 00 0c NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 19 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 23 00 00 00 00 2c 1f 84 ef e2 b7 00 0f 81 01 04 82 10 02 25 83 00 00 0c result (LLC_DISCARDED) = 23 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 6 RAID patched (SGSN): 1 TLLI patched (BSS ): 6 TLLI patched (SGSN): 5 P-TMSI patched (SGSN): 1 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING LLC_DISCARDED from 0x05060708:32000 00 00 00 00 2c 1f 84 ef e2 b7 00 0f 81 01 04 82 10 02 25 83 00 00 0c CALLBACK, event 0, msg length 19, bvci 0x0000 00 00 00 00 2c 1f 84 ef e2 b7 00 0f 81 01 04 82 10 02 25 83 00 00 0c NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 25 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 29 00 00 00 00 41 07 81 27 15 93 2c 1f 84 ef e2 b7 00 0f 81 01 04 82 10 02 25 83 00 00 0c result (LLC_DISCARDED) = 29 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 6 RAID patched (SGSN): 1 TLLI patched (BSS ): 6 TLLI patched (SGSN): 6 P-TMSI patched (SGSN): 1 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING BVC_SUSPEND from 0x01020304:1111 00 00 00 00 0b 1f 84 c0 de ad 01 1b 86 11 22 33 40 50 60 CALLBACK, event 0, msg length 15, bvci 0x0000 00 00 00 00 0b 1f 84 c0 de ad 01 1b 86 11 22 33 40 50 60 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 15 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 19 00 00 00 00 0b 1f 84 ef e2 b7 00 1b 86 21 63 54 40 50 60 result (BVC_SUSPEND) = 19 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 7 RAID patched (SGSN): 1 TLLI patched (BSS ): 7 TLLI patched (SGSN): 6 P-TMSI patched (SGSN): 1 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING BVC_SUSPEND_ACK from 0x05060708:32000 00 00 00 00 0c 1f 84 ef e2 b7 00 1b 86 21 63 54 40 50 60 1d 81 01 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 0c 1f 84 ef e2 b7 00 1b 86 21 63 54 40 50 60 1d 81 01 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 22 00 00 00 00 0c 1f 84 c0 de ad 01 1b 86 11 22 33 40 50 60 1d 81 01 result (BVC_SUSPEND_ACK) = 22 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 7 RAID patched (SGSN): 2 TLLI patched (BSS ): 7 TLLI patched (SGSN): 7 P-TMSI patched (SGSN): 1 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 --- Establish GPRS connection (SGSN 2) --- PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 80 00 be ef 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 11 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 bf 00 5c CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 80 00 be ef 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 11 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 bf 00 5c NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 80 00 be ef 00 50 20 16 82 02 58 0e 00 09 41 c4 01 08 15 01 b7 f8 36 result (ATTACH REQUEST) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 7 RAID patched (SGSN): 2 TLLI patched (BSS ): 7 TLLI patched (SGSN): 7 P-TMSI patched (SGSN): 1 Attach Request count : 2 Attach Accept count : 1 Attach Completed count : 1 TLLI cache size : 2 TLLI-Cache: 2 TLLI 8000beef -> 78dead02, IMSI (none), AGE 0, STORED 1, IMSI acquisition in progress, SGSN NSEI 65535 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 80 00 be ef 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 15 08 16 08 11 12 99 99 99 16 17 18 b2 dd 58 CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 80 00 be ef 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 15 08 16 08 11 12 99 99 99 16 17 18 b2 dd 58 NS UNITDATA MESSAGE to SGSN 2, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN 2 at 0x15161718:32001, msg length 79 00 00 10 02 01 78 de ad 02 00 00 04 08 88 21 63 54 00 63 60 12 34 00 80 0e 00 34 01 c0 11 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 bf 00 5c result (IDENT RESPONSE) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 8 RAID patched (SGSN): 2 TLLI patched (BSS ): 8 TLLI patched (SGSN): 7 P-TMSI patched (SGSN): 1 Attach Request count : 2 Attach Accept count : 1 Attach Completed count : 1 TLLI cache size : 2 TLLI-Cache: 2 TLLI 8000beef -> 78dead02, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING IDENT REQUEST from 0x15161718:32001 00 00 10 02 00 78 de ad 02 00 50 20 16 82 02 58 0e 89 41 c0 0d 08 15 01 0c a6 18 CALLBACK, event 0, msg length 23, bvci 0x1002 00 00 10 02 00 78 de ad 02 00 50 20 16 82 02 58 0e 89 41 c0 0d 08 15 01 0c a6 18 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 23 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 27 00 00 10 02 00 80 00 be ef 00 50 20 16 82 02 58 0e 89 41 c0 0d 08 15 01 0c a6 18 result (IDENT REQUEST) = 27 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 8 RAID patched (SGSN): 2 TLLI patched (BSS ): 8 TLLI patched (SGSN): 8 P-TMSI patched (SGSN): 1 Attach Request count : 2 Attach Accept count : 1 Attach Completed count : 1 TLLI cache size : 2 TLLI-Cache: 2 TLLI 8000beef -> 78dead02, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 80 00 be ef 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 19 08 16 08 11 12 99 99 99 16 17 18 a5 cc b3 CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 80 00 be ef 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 19 08 16 08 11 12 99 99 99 16 17 18 a5 cc b3 NS UNITDATA MESSAGE to SGSN 2, BVCI 0x1002, msg length 40 (gprs_ns_sendmsg) MESSAGE to SGSN 2 at 0x15161718:32001, msg length 44 00 00 10 02 01 78 de ad 02 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 11 01 c0 19 08 16 08 11 12 99 99 99 16 17 18 a5 cc b3 result (IDENT RESPONSE) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 9 RAID patched (SGSN): 2 TLLI patched (BSS ): 9 TLLI patched (SGSN): 8 P-TMSI patched (SGSN): 1 Attach Request count : 2 Attach Accept count : 1 Attach Completed count : 1 TLLI cache size : 2 TLLI-Cache: 2 TLLI 8000beef -> 78dead02, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING ATTACH ACCEPT from 0x15161718:32001 00 00 10 02 00 78 de ad 02 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 9e 41 c0 11 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 e0 98 76 54 cb 1c 5b CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 78 de ad 02 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 9e 41 c0 11 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 e0 98 76 54 cb 1c 5b NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 80 00 be ef 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 9e 41 c0 11 08 02 01 49 04 11 22 33 40 50 60 19 cd d7 08 17 16 18 05 f4 c0 de ad 03 32 40 fa result (ATTACH ACCEPT) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 9 RAID patched (SGSN): 3 TLLI patched (BSS ): 9 TLLI patched (SGSN): 9 P-TMSI patched (SGSN): 2 Attach Request count : 2 Attach Accept count : 2 Attach Completed count : 1 TLLI cache size : 2 TLLI-Cache: 2 TLLI 8000beef/c0dead03 -> 78dead02/e0987654, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 c0 de ad 03 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 1d 08 03 5e 3a ea CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 c0 de ad 03 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 1d 08 03 5e 3a ea NS UNITDATA MESSAGE to SGSN 2, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN 2 at 0x15161718:32001, msg length 35 00 00 10 02 01 e0 98 76 54 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 08 01 c0 1d 08 03 5e 3a ea result (ATTACH COMPLETE) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 10 RAID patched (SGSN): 3 TLLI patched (BSS ): 10 TLLI patched (SGSN): 9 P-TMSI patched (SGSN): 2 Attach Request count : 2 Attach Accept count : 2 Attach Completed count : 2 TLLI cache size : 2 TLLI-Cache: 2 TLLI 8000beef/c0dead03 -> 78dead02/e0987654, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING GMM INFO from 0x15161718:32001 00 00 10 02 00 e0 98 76 54 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 88 41 c0 15 08 21 bb c1 c6 CALLBACK, event 0, msg length 66, bvci 0x1002 00 00 10 02 00 e0 98 76 54 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 88 41 c0 15 08 21 bb c1 c6 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 66 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 70 00 00 10 02 00 c0 de ad 03 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 88 41 c0 15 08 21 bb c1 c6 result (GMM INFO) = 70 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 10 RAID patched (SGSN): 3 TLLI patched (BSS ): 10 TLLI patched (SGSN): 10 P-TMSI patched (SGSN): 2 Attach Request count : 2 Attach Accept count : 2 Attach Completed count : 2 TLLI cache size : 2 TLLI-Cache: 2 TLLI c0dead03 -> e0987654, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING XID (UL) from 0x01020304:1111 00 00 10 02 01 c0 de ad 03 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 0f 41 fb 01 00 0e 00 64 11 05 16 01 90 66 b3 28 CALLBACK, event 0, msg length 38, bvci 0x1002 00 00 10 02 01 c0 de ad 03 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 0f 41 fb 01 00 0e 00 64 11 05 16 01 90 66 b3 28 NS UNITDATA MESSAGE to SGSN 2, BVCI 0x1002, msg length 38 (gprs_ns_sendmsg) MESSAGE to SGSN 2 at 0x15161718:32001, msg length 42 00 00 10 02 01 e0 98 76 54 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 0f 41 fb 01 00 0e 00 64 11 05 16 01 90 66 b3 28 result (XID (UL)) = 0 PROCESSING XID (DL) from 0x15161718:32001 00 00 10 02 00 e0 98 76 54 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 8c 41 fb 30 84 10 61 b6 64 e4 a9 1a 9e CALLBACK, event 0, msg length 70, bvci 0x1002 00 00 10 02 00 e0 98 76 54 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 8c 41 fb 30 84 10 61 b6 64 e4 a9 1a 9e NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 70 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 74 00 00 10 02 00 c0 de ad 03 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 8c 41 fb 30 84 10 61 b6 64 e4 a9 1a 9e result (XID (DL)) = 74 PROCESSING LL11 DNS QUERY (UL) from 0x01020304:1111 00 00 10 02 01 c0 de ad 03 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 42 0b c0 01 65 00 00 00 45 00 00 38 95 72 00 00 45 11 20 85 0a c0 07 e4 ac 10 01 0a ad ab 00 35 00 24 0e 1c 3b e0 01 00 00 01 00 00 00 00 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 47 8f 07 CALLBACK, event 0, msg length 89, bvci 0x1002 00 00 10 02 01 c0 de ad 03 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 42 0b c0 01 65 00 00 00 45 00 00 38 95 72 00 00 45 11 20 85 0a c0 07 e4 ac 10 01 0a ad ab 00 35 00 24 0e 1c 3b e0 01 00 00 01 00 00 00 00 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 47 8f 07 NS UNITDATA MESSAGE to SGSN 2, BVCI 0x1002, msg length 89 (gprs_ns_sendmsg) MESSAGE to SGSN 2 at 0x15161718:32001, msg length 93 00 00 10 02 01 e0 98 76 54 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 42 0b c0 01 65 00 00 00 45 00 00 38 95 72 00 00 45 11 20 85 0a c0 07 e4 ac 10 01 0a ad ab 00 35 00 24 0e 1c 3b e0 01 00 00 01 00 00 00 00 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 47 8f 07 result (LL11 DNS QUERY (UL)) = 0 PROCESSING LL11 DNS RESP (DL) from 0x15161718:32001 00 00 10 02 00 e0 98 76 54 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 00 d0 4b c0 01 65 00 00 00 45 00 00 c6 00 00 40 00 3e 11 7c 69 ac 10 01 0a 0a c0 07 e4 00 35 ad ab 00 b2 74 4e 3b e0 81 80 00 01 00 01 00 05 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 c0 0c 00 01 00 01 00 00 0e 10 00 04 c1 63 90 58 c0 0e 00 02 00 01 00 00 0e 10 00 16 03 6e 73 32 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 03 6e 65 74 00 c0 0e 00 02 00 01 00 00 0e 10 00 10 02 6e 73 01 73 08 70 6c 75 73 6c 69 6e 65 c0 14 c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 0e c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 5f c0 0e 00 02 00 01 00 00 0e 10 00 12 02 6e 73 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 c0 14 aa df 31 CALLBACK, event 0, msg length 267, bvci 0x1002 00 00 10 02 00 e0 98 76 54 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 00 d0 4b c0 01 65 00 00 00 45 00 00 c6 00 00 40 00 3e 11 7c 69 ac 10 01 0a 0a c0 07 e4 00 35 ad ab 00 b2 74 4e 3b e0 81 80 00 01 00 01 00 05 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 c0 0c 00 01 00 01 00 00 0e 10 00 04 c1 63 90 58 c0 0e 00 02 00 01 00 00 0e 10 00 16 03 6e 73 32 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 03 6e 65 74 00 c0 0e 00 02 00 01 00 00 0e 10 00 10 02 6e 73 01 73 08 70 6c 75 73 6c 69 6e 65 c0 14 c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 0e c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 5f c0 0e 00 02 00 01 00 00 0e 10 00 12 02 6e 73 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 c0 14 aa df 31 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 267 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 271 00 00 10 02 00 c0 de ad 03 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 00 d0 4b c0 01 65 00 00 00 45 00 00 c6 00 00 40 00 3e 11 7c 69 ac 10 01 0a 0a c0 07 e4 00 35 ad ab 00 b2 74 4e 3b e0 81 80 00 01 00 01 00 05 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 c0 0c 00 01 00 01 00 00 0e 10 00 04 c1 63 90 58 c0 0e 00 02 00 01 00 00 0e 10 00 16 03 6e 73 32 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 03 6e 65 74 00 c0 0e 00 02 00 01 00 00 0e 10 00 10 02 6e 73 01 73 08 70 6c 75 73 6c 69 6e 65 c0 14 c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 0e c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 5f c0 0e 00 02 00 01 00 00 0e 10 00 12 02 6e 73 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 c0 14 aa df 31 result (LL11 DNS RESP (DL)) = 271 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 12 RAID patched (SGSN): 3 TLLI patched (BSS ): 12 TLLI patched (SGSN): 12 P-TMSI patched (SGSN): 2 Attach Request count : 2 Attach Accept count : 2 Attach Completed count : 2 TLLI cache size : 2 TLLI-Cache: 2 TLLI c0dead03 -> e0987654, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING LLC_DISCARDED from 0x01020304:1111 00 00 00 00 2c 1f 84 c0 de ad 03 0f 81 01 04 82 10 02 25 83 00 00 0c CALLBACK, event 0, msg length 19, bvci 0x0000 00 00 00 00 2c 1f 84 c0 de ad 03 0f 81 01 04 82 10 02 25 83 00 00 0c NS UNITDATA MESSAGE to SGSN 2, BVCI 0x0000, msg length 19 (gprs_ns_sendmsg) MESSAGE to SGSN 2 at 0x15161718:32001, msg length 23 00 00 00 00 2c 1f 84 e0 98 76 54 0f 81 01 04 82 10 02 25 83 00 00 0c result (LLC_DISCARDED) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 12 RAID patched (SGSN): 3 TLLI patched (BSS ): 13 TLLI patched (SGSN): 12 P-TMSI patched (SGSN): 2 Attach Request count : 2 Attach Accept count : 2 Attach Completed count : 2 TLLI cache size : 2 TLLI-Cache: 2 TLLI c0dead03 -> e0987654, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING LLC_DISCARDED from 0x15161718:32001 00 00 00 00 2c 1f 84 e0 98 76 54 0f 81 01 04 82 10 02 25 83 00 00 0c CALLBACK, event 0, msg length 19, bvci 0x0000 00 00 00 00 2c 1f 84 e0 98 76 54 0f 81 01 04 82 10 02 25 83 00 00 0c NS UNITDATA MESSAGE to SGSN 2, BVCI 0x0000, msg length 25 (gprs_ns_sendmsg) MESSAGE to SGSN 2 at 0x15161718:32001, msg length 29 00 00 00 00 41 07 81 27 15 93 2c 1f 84 e0 98 76 54 0f 81 01 04 82 10 02 25 83 00 00 0c result (LLC_DISCARDED) = 29 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 12 RAID patched (SGSN): 3 TLLI patched (BSS ): 13 TLLI patched (SGSN): 13 P-TMSI patched (SGSN): 2 Attach Request count : 2 Attach Accept count : 2 Attach Completed count : 2 TLLI cache size : 2 TLLI-Cache: 2 TLLI c0dead03 -> e0987654, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING BVC_SUSPEND from 0x01020304:1111 00 00 00 00 0b 1f 84 c0 de ad 03 1b 86 11 22 33 40 50 60 CALLBACK, event 0, msg length 15, bvci 0x0000 00 00 00 00 0b 1f 84 c0 de ad 03 1b 86 11 22 33 40 50 60 NS UNITDATA MESSAGE to SGSN 2, BVCI 0x0000, msg length 15 (gprs_ns_sendmsg) MESSAGE to SGSN 2 at 0x15161718:32001, msg length 19 00 00 00 00 0b 1f 84 e0 98 76 54 1b 86 21 63 54 40 50 60 result (BVC_SUSPEND) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 13 RAID patched (SGSN): 3 TLLI patched (BSS ): 14 TLLI patched (SGSN): 13 P-TMSI patched (SGSN): 2 Attach Request count : 2 Attach Accept count : 2 Attach Completed count : 2 TLLI cache size : 2 TLLI-Cache: 2 TLLI c0dead03 -> e0987654, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING BVC_SUSPEND_ACK from 0x15161718:32001 00 00 00 00 0c 1f 84 e0 98 76 54 1b 86 21 63 54 40 50 60 1d 81 01 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 0c 1f 84 e0 98 76 54 1b 86 21 63 54 40 50 60 1d 81 01 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 22 00 00 00 00 0c 1f 84 c0 de ad 03 1b 86 11 22 33 40 50 60 1d 81 01 result (BVC_SUSPEND_ACK) = 22 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 13 RAID patched (SGSN): 4 TLLI patched (BSS ): 14 TLLI patched (SGSN): 14 P-TMSI patched (SGSN): 2 Attach Request count : 2 Attach Accept count : 2 Attach Completed count : 2 TLLI cache size : 2 TLLI-Cache: 2 TLLI c0dead03 -> e0987654, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 --- Establish GPRS connection (SGSN 2, P-TMSI collision) --- PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 80 00 fe ed 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 21 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 44 b6 bb CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 80 00 fe ed 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 21 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 44 b6 bb NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 80 00 fe ed 00 50 20 16 82 02 58 0e 00 09 41 c4 01 08 15 01 b7 f8 36 result (ATTACH REQUEST) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 13 RAID patched (SGSN): 4 TLLI patched (BSS ): 14 TLLI patched (SGSN): 14 P-TMSI patched (SGSN): 2 Attach Request count : 3 Attach Accept count : 2 Attach Completed count : 2 TLLI cache size : 3 TLLI-Cache: 3 TLLI 8000feed -> 78dead04, IMSI (none), AGE 0, STORED 1, IMSI acquisition in progress, SGSN NSEI 65535 TLLI c0dead03 -> e0987654, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 80 00 fe ed 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 25 08 16 08 11 12 99 99 99 26 27 28 58 c7 cb CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 80 00 fe ed 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 25 08 16 08 11 12 99 99 99 26 27 28 58 c7 cb NS UNITDATA MESSAGE to SGSN 2, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN 2 at 0x15161718:32001, msg length 79 00 00 10 02 01 78 de ad 04 00 00 04 08 88 21 63 54 00 63 60 12 34 00 80 0e 00 34 01 c0 21 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 44 b6 bb result (IDENT RESPONSE) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 14 RAID patched (SGSN): 4 TLLI patched (BSS ): 15 TLLI patched (SGSN): 14 P-TMSI patched (SGSN): 2 Attach Request count : 3 Attach Accept count : 2 Attach Completed count : 2 TLLI cache size : 3 TLLI-Cache: 3 TLLI 8000feed -> 78dead04, IMSI 12199999962728, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c0dead03 -> e0987654, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING IDENT REQUEST from 0x15161718:32001 00 00 10 02 00 78 de ad 04 00 50 20 16 82 02 58 0e 89 41 c0 19 08 15 01 a2 f2 a4 CALLBACK, event 0, msg length 23, bvci 0x1002 00 00 10 02 00 78 de ad 04 00 50 20 16 82 02 58 0e 89 41 c0 19 08 15 01 a2 f2 a4 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 23 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 27 00 00 10 02 00 80 00 fe ed 00 50 20 16 82 02 58 0e 89 41 c0 19 08 15 01 a2 f2 a4 result (IDENT REQUEST) = 27 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 14 RAID patched (SGSN): 4 TLLI patched (BSS ): 15 TLLI patched (SGSN): 15 P-TMSI patched (SGSN): 2 Attach Request count : 3 Attach Accept count : 2 Attach Completed count : 2 TLLI cache size : 3 TLLI-Cache: 3 TLLI 8000feed -> 78dead04, IMSI 12199999962728, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c0dead03 -> e0987654, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 80 00 fe ed 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 29 08 16 08 11 12 99 99 99 26 27 28 4f d6 20 CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 80 00 fe ed 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 29 08 16 08 11 12 99 99 99 26 27 28 4f d6 20 NS UNITDATA MESSAGE to SGSN 2, BVCI 0x1002, msg length 40 (gprs_ns_sendmsg) MESSAGE to SGSN 2 at 0x15161718:32001, msg length 44 00 00 10 02 01 78 de ad 04 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 11 01 c0 29 08 16 08 11 12 99 99 99 26 27 28 4f d6 20 result (IDENT RESPONSE) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 15 RAID patched (SGSN): 4 TLLI patched (BSS ): 16 TLLI patched (SGSN): 15 P-TMSI patched (SGSN): 2 Attach Request count : 3 Attach Accept count : 2 Attach Completed count : 2 TLLI cache size : 3 TLLI-Cache: 3 TLLI 8000feed -> 78dead04, IMSI 12199999962728, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c0dead03 -> e0987654, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING ATTACH ACCEPT (P-TMSI 1) from 0x15161718:32001 00 00 10 02 00 78 de ad 04 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 26 27 28 00 81 00 0e 9e 41 c0 1d 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 74 91 01 CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 78 de ad 04 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 26 27 28 00 81 00 0e 9e 41 c0 1d 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 74 91 01 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 80 00 fe ed 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 26 27 28 00 81 00 0e 9e 41 c0 1d 08 02 01 49 04 11 22 33 40 50 60 19 cd d7 08 17 16 18 05 f4 c0 de ad 05 3e 78 6e result (ATTACH ACCEPT (P-TMSI 1)) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 15 RAID patched (SGSN): 5 TLLI patched (BSS ): 16 TLLI patched (SGSN): 16 P-TMSI patched (SGSN): 3 Attach Request count : 3 Attach Accept count : 3 Attach Completed count : 2 TLLI cache size : 3 TLLI-Cache: 3 TLLI 8000feed/c0dead05 -> 78dead04/efe2b700, IMSI 12199999962728, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c0dead03 -> e0987654, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 c0 de ad 05 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 2d 08 03 43 50 ea CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 c0 de ad 05 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 2d 08 03 43 50 ea NS UNITDATA MESSAGE to SGSN 2, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN 2 at 0x15161718:32001, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 08 01 c0 2d 08 03 43 50 ea result (ATTACH COMPLETE) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 16 RAID patched (SGSN): 5 TLLI patched (BSS ): 17 TLLI patched (SGSN): 16 P-TMSI patched (SGSN): 3 Attach Request count : 3 Attach Accept count : 3 Attach Completed count : 3 TLLI cache size : 3 TLLI-Cache: 3 TLLI 8000feed/c0dead05 -> 78dead04/efe2b700, IMSI 12199999962728, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c0dead03 -> e0987654, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING GMM INFO from 0x15161718:32001 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 26 27 28 00 81 00 0e 88 41 c0 21 08 21 ca 60 90 CALLBACK, event 0, msg length 66, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 26 27 28 00 81 00 0e 88 41 c0 21 08 21 ca 60 90 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 66 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 70 00 00 10 02 00 c0 de ad 05 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 26 27 28 00 81 00 0e 88 41 c0 21 08 21 ca 60 90 result (GMM INFO) = 70 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 16 RAID patched (SGSN): 5 TLLI patched (BSS ): 17 TLLI patched (SGSN): 17 P-TMSI patched (SGSN): 3 Attach Request count : 3 Attach Accept count : 3 Attach Completed count : 3 TLLI cache size : 3 TLLI-Cache: 3 TLLI c0dead05 -> efe2b700, IMSI 12199999962728, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c0dead03 -> e0987654, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 --- Shutdown GPRS connection (SGSN 1) --- PROCESSING DETACH REQ from 0x01020304:1111 00 00 10 02 01 c0 de ad 01 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 31 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 57 e6 15 CALLBACK, event 0, msg length 44, bvci 0x1002 00 00 10 02 01 c0 de ad 01 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 31 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 57 e6 15 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 44 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 48 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 15 01 c0 31 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 57 e6 15 result (DETACH REQ) = 48 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 17 RAID patched (SGSN): 5 TLLI patched (BSS ): 18 TLLI patched (SGSN): 17 P-TMSI patched (SGSN): 3 Attach Request count : 3 Attach Accept count : 3 Attach Completed count : 3 Detach Request count : 1 TLLI cache size : 3 TLLI-Cache: 3 TLLI c0dead01 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 TLLI c0dead05 -> efe2b700, IMSI 12199999962728, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c0dead03 -> e0987654, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 PROCESSING DETACH ACC from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 25 08 06 00 4d 09 cd CALLBACK, event 0, msg length 67, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 25 08 06 00 4d 09 cd NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 67 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 71 00 00 10 02 00 c0 de ad 01 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 25 08 06 00 4d 09 cd result (DETACH ACC) = 71 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 17 RAID patched (SGSN): 5 TLLI patched (BSS ): 18 TLLI patched (SGSN): 18 P-TMSI patched (SGSN): 3 Attach Request count : 3 Attach Accept count : 3 Attach Completed count : 3 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 2 TLLI-Cache: 2 TLLI c0dead05 -> efe2b700, IMSI 12199999962728, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c0dead03 -> e0987654, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 --- Shutdown GPRS connection (SGSN 2) --- PROCESSING DETACH REQ from 0x01020304:1111 00 00 10 02 01 c0 de ad 03 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 35 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 83 cb f7 CALLBACK, event 0, msg length 44, bvci 0x1002 00 00 10 02 01 c0 de ad 03 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 35 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 83 cb f7 NS UNITDATA MESSAGE to SGSN 2, BVCI 0x1002, msg length 44 (gprs_ns_sendmsg) MESSAGE to SGSN 2 at 0x15161718:32001, msg length 48 00 00 10 02 01 e0 98 76 54 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 15 01 c0 35 08 05 01 18 05 f4 e0 98 76 54 19 03 b9 97 cb b4 31 31 result (DETACH REQ) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 18 RAID patched (SGSN): 5 TLLI patched (BSS ): 19 TLLI patched (SGSN): 18 P-TMSI patched (BSS ): 1 P-TMSI patched (SGSN): 3 Attach Request count : 3 Attach Accept count : 3 Attach Completed count : 3 Detach Request count : 2 Detach Accept count : 1 TLLI cache size : 2 TLLI-Cache: 2 TLLI c0dead03 -> e0987654, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c0dead05 -> efe2b700, IMSI 12199999962728, AGE 0, IMSI matches, SGSN NSEI 258 PROCESSING DETACH ACC from 0x15161718:32001 00 00 10 02 00 e0 98 76 54 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 89 41 c0 29 08 06 00 be c3 6f CALLBACK, event 0, msg length 67, bvci 0x1002 00 00 10 02 00 e0 98 76 54 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 89 41 c0 29 08 06 00 be c3 6f NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 67 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 71 00 00 10 02 00 c0 de ad 03 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 89 41 c0 29 08 06 00 be c3 6f result (DETACH ACC) = 71 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 18 RAID patched (SGSN): 5 TLLI patched (BSS ): 19 TLLI patched (SGSN): 19 P-TMSI patched (BSS ): 1 P-TMSI patched (SGSN): 3 Attach Request count : 3 Attach Accept count : 3 Attach Completed count : 3 Detach Request count : 2 Detach Accept count : 2 TLLI cache size : 1 TLLI-Cache: 1 TLLI c0dead05 -> efe2b700, IMSI 12199999962728, AGE 0, IMSI matches, SGSN NSEI 258 --- Shutdown GPRS connection (SGSN 2, P-TMSI 1) --- PROCESSING DETACH REQ from 0x01020304:1111 00 00 10 02 01 c0 de ad 05 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 39 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 44 b6 8a CALLBACK, event 0, msg length 44, bvci 0x1002 00 00 10 02 01 c0 de ad 05 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 39 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 44 b6 8a NS UNITDATA MESSAGE to SGSN 2, BVCI 0x1002, msg length 44 (gprs_ns_sendmsg) MESSAGE to SGSN 2 at 0x15161718:32001, msg length 48 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 15 01 c0 39 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 44 b6 8a result (DETACH REQ) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 19 RAID patched (SGSN): 5 TLLI patched (BSS ): 20 TLLI patched (SGSN): 19 P-TMSI patched (BSS ): 1 P-TMSI patched (SGSN): 3 Attach Request count : 3 Attach Accept count : 3 Attach Completed count : 3 Detach Request count : 3 Detach Accept count : 2 TLLI cache size : 1 TLLI-Cache: 1 TLLI c0dead05 -> efe2b700, IMSI 12199999962728, AGE 0, IMSI matches, SGSN NSEI 258 PROCESSING DETACH ACC from 0x15161718:32001 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 26 27 28 00 81 00 0e 89 41 c0 2d 08 06 00 86 7c c7 CALLBACK, event 0, msg length 67, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 26 27 28 00 81 00 0e 89 41 c0 2d 08 06 00 86 7c c7 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 67 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 71 00 00 10 02 00 c0 de ad 05 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 26 27 28 00 81 00 0e 89 41 c0 2d 08 06 00 86 7c c7 result (DETACH ACC) = 71 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 19 RAID patched (SGSN): 5 TLLI patched (BSS ): 20 TLLI patched (SGSN): 20 P-TMSI patched (BSS ): 1 P-TMSI patched (SGSN): 3 Attach Request count : 3 Attach Accept count : 3 Attach Completed count : 3 Detach Request count : 3 Detach Accept count : 3 TLLI-Cache: 0 Gbproxy global: Invalid BVC Identifier : 1 BSSGP protocol error (SGSN): 2 Patch error: no peer : 1 === test_gbproxy_keep_info === --- Initialise SGSN --- MESSAGE to SGSN at 0x05060708:32000, msg length 12 02 00 81 01 01 82 01 01 04 82 01 00 PROCESSING RESET_ACK from 0x05060708:32000 03 01 82 01 01 04 82 01 00 MESSAGE to SGSN at 0x05060708:32000, msg length 1 0a result (RESET_ACK) = 1 PROCESSING ALIVE_ACK from 0x05060708:32000 0b MESSAGE to SGSN at 0x05060708:32000, msg length 1 06 result (ALIVE_ACK) = 1 PROCESSING UNBLOCK_ACK from 0x05060708:32000 07 ==> got signal NS_UNBLOCK, NS-VC 0x0101/5.6.7.8:32000 result (UNBLOCK_ACK) = 0 PROCESSING ALIVE from 0x05060708:32000 0a MESSAGE to SGSN at 0x05060708:32000, msg length 1 0b result (ALIVE) = 1 --- Initialise BSS 1 --- Setup NS-VC: remote 0x01020304:1111, NSVCI 0x1001(4097), NSEI 0x1000(4096) PROCESSING RESET from 0x01020304:1111 02 00 81 01 01 82 10 01 04 82 10 00 ==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 9 03 01 82 10 01 04 82 10 00 MESSAGE to BSS at 0x01020304:1111, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:1111 0a MESSAGE to BSS at 0x01020304:1111, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:1111 06 ==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:1111 0b result (ALIVE_ACK) = 0 Setup BSSGP: remote 0x01020304:1111, BVCI 0x1002(4098) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 result (BVC_RESET) = 22 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 10 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 10 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 10 02 result (BVC_RESET_ACK) = 9 Current NS-VCIs: VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111 Packets at NS Level ( In): 5 Packets at NS Level (Out): 5 Bytes at NS Level ( In): 37 Bytes at NS Level (Out): 21 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 Packets at NS Level ( In): 5 Packets at NS Level (Out): 5 Bytes at NS Level ( In): 21 Bytes at NS Level (Out): 37 NS-VC Block count : 1 Gbproxy global: Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 --- Send message from BSS 1 to SGSN, BVCI 0x1002 --- PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 0e 00 09 41 c4 01 08 15 01 b7 f8 36 result (ATTACH REQUEST) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700 -> afe2b700, IMSI (none), AGE 0, STORED 1, IMSI acquisition in progress PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 05 08 16 08 11 12 13 14 15 16 17 18 ad 05 28 CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 05 08 16 08 11 12 13 14 15 16 17 18 ad 05 28 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 result (IDENT RESPONSE) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700 -> afe2b700, IMSI 12131415161718, AGE 0 PROCESSING IDENT REQUEST from 0x05060708:32000 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba CALLBACK, event 0, msg length 23, bvci 0x1002 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 23 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 27 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba result (IDENT REQUEST) = 27 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700 -> afe2b700, IMSI 12131415161718, AGE 0 PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 09 08 16 08 11 12 13 14 15 16 17 18 ba 14 c3 CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 09 08 16 08 11 12 13 14 15 16 17 18 ba 14 c3 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 40 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 44 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 09 08 16 08 11 12 13 14 15 16 17 18 ba 14 c3 result (IDENT RESPONSE) = 44 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700 -> afe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH ACCEPT from 0x05060708:32000 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 53 62 f1 CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 53 62 f1 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 53 62 f1 result (ATTACH ACCEPT) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 1 Attach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 0d 08 03 55 1c ea CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 0d 08 03 55 1c ea NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 0d 08 03 55 1c ea result (ATTACH COMPLETE) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING GMM INFO from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 09 08 21 04 ba 3d CALLBACK, event 0, msg length 66, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 09 08 21 04 ba 3d NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 66 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 70 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 09 08 21 04 ba 3d result (GMM INFO) = 70 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH REQ from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 11 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 6d b1 de CALLBACK, event 0, msg length 44, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 11 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 6d b1 de NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 44 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 48 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 11 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 6d b1 de result (DETACH REQ) = 48 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 Detach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH ACC from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 0d 08 06 00 aa ab ee CALLBACK, event 0, msg length 67, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 0d 08 06 00 aa ab ee NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 67 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 71 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 0d 08 06 00 aa ab ee result (DETACH ACC) = 71 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 1 Attach Accept count : 1 Attach Completed count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 00000000, IMSI 12131415161718, AGE 0, DE-REGISTERED PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 15 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 e6 71 c7 CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 15 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 e6 71 c7 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 15 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 e6 71 c7 result (ATTACH REQUEST) = 79 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 2 Attach Accept count : 1 Attach Completed count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700 -> afe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH ACCEPT from 0x05060708:32000 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 11 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 3a 6d d4 CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 11 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 3a 6d d4 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 11 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 3a 6d d4 result (ATTACH ACCEPT) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 2 Attach Accept count : 2 Attach Completed count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 19 08 03 32 f1 bc CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 19 08 03 32 f1 bc NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 19 08 03 32 f1 bc result (ATTACH COMPLETE) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 2 Attach Accept count : 2 Attach Completed count : 2 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH REQ (re-attach) from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 15 08 05 01 25 0a 67 0e 96 CALLBACK, event 0, msg length 69, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 15 08 05 01 25 0a 67 0e 96 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 69 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 73 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 15 08 05 01 25 0a 67 0e 96 result (DETACH REQ (re-attach)) = 73 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 2 Attach Accept count : 2 Attach Completed count : 2 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH ACC from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 1d 08 06 3d 1c 8b CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 1d 08 06 3d 1c 8b NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 1d 08 06 3d 1c 8b result (DETACH ACC) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 2 Attach Accept count : 2 Attach Completed count : 2 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 00000000, IMSI 12131415161718, AGE 0, DE-REGISTERED PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 21 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 44 db cc CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 21 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 44 db cc NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 21 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 44 db cc result (ATTACH REQUEST) = 79 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 3 Attach Accept count : 2 Attach Completed count : 2 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700 -> afe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH ACCEPT from 0x05060708:32000 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 19 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 27 3c 84 CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 19 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 27 3c 84 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 19 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 27 3c 84 result (ATTACH ACCEPT) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 3 Attach Accept count : 3 Attach Completed count : 2 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 25 08 03 9b c6 47 CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 25 08 03 9b c6 47 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 25 08 03 9b c6 47 result (ATTACH COMPLETE) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 3 Attach Accept count : 3 Attach Completed count : 3 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH REQ from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 1d 08 05 02 25 0a dd 56 6c CALLBACK, event 0, msg length 69, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 1d 08 05 02 25 0a dd 56 6c NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 69 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 73 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 1d 08 05 02 25 0a dd 56 6c result (DETACH REQ) = 73 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 3 Attach Accept count : 3 Attach Completed count : 3 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH ACC from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 29 08 06 4c bd dd CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 29 08 06 4c bd dd NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 29 08 06 4c bd dd result (DETACH ACC) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 3 Attach Accept count : 3 Attach Completed count : 3 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 00000000, IMSI 12131415161718, AGE 0, DE-REGISTERED PROCESSING ATTACH REQUEST (IMSI) from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 37 01 c0 2d 08 01 02 f5 e0 21 08 02 08 11 12 13 14 15 16 17 18 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 a5 85 76 CALLBACK, event 0, msg length 78, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 37 01 c0 2d 08 01 02 f5 e0 21 08 02 08 11 12 13 14 15 16 17 18 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 a5 85 76 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 78 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 82 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 37 01 c0 2d 08 01 02 f5 e0 21 08 02 08 11 12 13 14 15 16 17 18 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 a5 85 76 result (ATTACH REQUEST (IMSI)) = 82 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 4 Attach Accept count : 3 Attach Completed count : 3 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700 -> afe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH ACCEPT from 0x05060708:32000 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 21 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 cf 80 6e CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 21 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 cf 80 6e NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 21 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 cf 80 6e result (ATTACH ACCEPT) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 4 Attach Accept count : 4 Attach Completed count : 3 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 31 08 03 fc 2b 11 CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 31 08 03 fc 2b 11 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 31 08 03 fc 2b 11 result (ATTACH COMPLETE) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 4 Attach Accept count : 4 Attach Completed count : 4 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH REQ from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 25 08 05 02 25 0a 8e ee 85 CALLBACK, event 0, msg length 69, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 25 08 05 02 25 0a 8e ee 85 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 69 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 73 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 25 08 05 02 25 0a 8e ee 85 result (DETACH REQ) = 73 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 4 Attach Accept count : 4 Attach Completed count : 4 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH ACC from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 35 08 06 f3 c6 26 CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 35 08 06 f3 c6 26 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 35 08 06 f3 c6 26 result (DETACH ACC) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 4 Attach Accept count : 4 Attach Completed count : 4 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 00000000, IMSI 12131415161718, AGE 0, DE-REGISTERED PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 39 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 e4 85 12 CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 39 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 e4 85 12 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 39 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 e4 85 12 result (ATTACH REQUEST) = 79 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 5 Attach Accept count : 4 Attach Completed count : 4 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700 -> afe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH ACCEPT from 0x05060708:32000 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 29 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 d2 d1 3e CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 29 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 d2 d1 3e NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 29 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 d2 d1 3e result (ATTACH ACCEPT) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 5 Attach Accept count : 5 Attach Completed count : 4 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 3d 08 03 48 76 ea CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 3d 08 03 48 76 ea NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 3d 08 03 48 76 ea result (ATTACH COMPLETE) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 5 Attach Accept count : 5 Attach Completed count : 5 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING RA UPD REQ from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 70 80 00 80 0e 00 3e 01 c0 41 08 08 10 11 22 33 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 30 73 32 CALLBACK, event 0, msg length 85, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 70 80 00 80 0e 00 3e 01 c0 41 08 08 10 11 22 33 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 30 73 32 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 85 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 89 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 70 80 00 80 0e 00 3e 01 c0 41 08 08 10 11 22 33 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 30 73 32 result (RA UPD REQ) = 89 PROCESSING RA UDP REJ from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8a 41 c0 2d 08 0b 0a 00 41 30 a7 CALLBACK, event 0, msg length 68, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8a 41 c0 2d 08 0b 0a 00 41 30 a7 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 68 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 72 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8a 41 c0 2d 08 0b 0a 00 41 30 a7 result (RA UDP REJ) = 72 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 5 Attach Accept count : 5 Attach Completed count : 5 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 00000000, IMSI 12131415161718, AGE 0, DE-REGISTERED PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 45 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 50 cc c3 CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 45 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 50 cc c3 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 0e 00 09 41 c4 01 08 15 01 b7 f8 36 result (ATTACH REQUEST) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 6 Attach Accept count : 5 Attach Completed count : 5 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 2 TLLI-Cache: 2 TLLI afe2b700 -> afe2b700, IMSI (none), AGE 0, STORED 1, IMSI acquisition in progress TLLI 00000000, IMSI 12131415161718, AGE 0, DE-REGISTERED PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 49 08 16 08 11 12 13 14 15 16 17 18 86 ca 3f CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 49 08 16 08 11 12 13 14 15 16 17 18 86 ca 3f NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 45 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 50 cc c3 result (IDENT RESPONSE) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 6 Attach Accept count : 5 Attach Completed count : 5 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700 -> afe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH ACCEPT from 0x05060708:32000 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 31 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 f5 22 ce CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 31 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 f5 22 ce NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 31 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 f5 22 ce result (ATTACH ACCEPT) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 6 Attach Accept count : 6 Attach Completed count : 5 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 4d 08 03 79 84 ea CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 4d 08 03 79 84 ea NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 4d 08 03 79 84 ea result (ATTACH COMPLETE) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 6 Attach Accept count : 6 Attach Completed count : 6 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH REQ from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 35 08 05 02 25 0a 9b fa 57 CALLBACK, event 0, msg length 69, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 35 08 05 02 25 0a 9b fa 57 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 69 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 73 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 35 08 05 02 25 0a 9b fa 57 result (DETACH REQ) = 73 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 6 Attach Accept count : 6 Attach Completed count : 6 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH ACC from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 51 08 06 a5 d9 70 CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 51 08 06 a5 d9 70 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 51 08 06 a5 d9 70 result (DETACH ACC) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 6 Attach Accept count : 6 Attach Completed count : 6 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 00000000, IMSI 12131415161718, AGE 0, DE-REGISTERED PROCESSING ATTACH REQUEST (local TLLI) from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 55 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 f9 cc e9 CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 55 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 f9 cc e9 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 55 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 f9 cc e9 result (ATTACH REQUEST (local TLLI)) = 79 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 7 Attach Accept count : 6 Attach Completed count : 6 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH ACCEPT from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 39 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 e8 73 9e CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 39 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 e8 73 9e NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 39 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 e8 73 9e result (ATTACH ACCEPT) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 7 Attach Accept count : 7 Attach Completed count : 6 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 59 08 03 1e 69 bc CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 59 08 03 1e 69 bc NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 59 08 03 1e 69 bc result (ATTACH COMPLETE) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 7 Attach Accept count : 7 Attach Completed count : 7 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH REQ (re-attach) from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 3d 08 05 01 25 0a 21 a2 ad CALLBACK, event 0, msg length 69, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 3d 08 05 01 25 0a 21 a2 ad NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 69 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 73 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 3d 08 05 01 25 0a 21 a2 ad result (DETACH REQ (re-attach)) = 73 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 7 Attach Accept count : 7 Attach Completed count : 7 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH ACC from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 5d 08 06 11 84 8b CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 5d 08 06 11 84 8b NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 5d 08 06 11 84 8b result (DETACH ACC) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 7 Attach Accept count : 7 Attach Completed count : 7 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 00000000, IMSI 12131415161718, AGE 0, DE-REGISTERED PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 61 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 5b 66 e2 CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 61 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 5b 66 e2 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 61 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 5b 66 e2 result (ATTACH REQUEST) = 79 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 8 Attach Accept count : 7 Attach Completed count : 7 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700 -> afe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH ACCEPT from 0x05060708:32000 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 41 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 9e 50 40 CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 41 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 9e 50 40 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 41 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 9e 50 40 result (ATTACH ACCEPT) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 8 Attach Accept count : 8 Attach Completed count : 7 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 65 08 03 b7 5e 47 CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 65 08 03 b7 5e 47 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 65 08 03 b7 5e 47 result (ATTACH COMPLETE) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 8 Attach Accept count : 8 Attach Completed count : 8 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING GMM INFO from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 45 08 21 9c 7f c6 CALLBACK, event 0, msg length 66, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 45 08 21 9c 7f c6 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 66 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 70 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 45 08 21 9c 7f c6 result (GMM INFO) = 70 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 8 Attach Accept count : 8 Attach Completed count : 8 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH REQUEST (unexpected, IMSI) from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 37 01 c0 69 08 01 02 f5 e0 21 08 02 08 11 12 13 14 15 16 17 18 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 e1 11 8e CALLBACK, event 0, msg length 78, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 37 01 c0 69 08 01 02 f5 e0 21 08 02 08 11 12 13 14 15 16 17 18 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 e1 11 8e NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 78 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 82 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 37 01 c0 69 08 01 02 f5 e0 21 08 02 08 11 12 13 14 15 16 17 18 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 e1 11 8e result (ATTACH REQUEST (unexpected, IMSI)) = 82 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 9 Attach Accept count : 8 Attach Completed count : 8 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700 -> afe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH ACCEPT from 0x05060708:32000 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 49 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 83 01 10 CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 49 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 83 01 10 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 49 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 83 01 10 result (ATTACH ACCEPT) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 9 Attach Accept count : 9 Attach Completed count : 8 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 6d 08 03 6f c8 ea CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 6d 08 03 6f c8 ea NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 6d 08 03 6f c8 ea result (ATTACH COMPLETE) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 9 Attach Accept count : 9 Attach Completed count : 9 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH REQ from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 4d 08 05 02 25 0a 51 0e 1b CALLBACK, event 0, msg length 69, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 4d 08 05 02 25 0a 51 0e 1b NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 69 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 73 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 4d 08 05 02 25 0a 51 0e 1b result (DETACH REQ) = 73 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 9 Attach Accept count : 9 Attach Completed count : 9 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH ACC from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 71 08 06 b3 95 70 CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 71 08 06 b3 95 70 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 71 08 06 b3 95 70 result (DETACH ACC) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 9 Attach Accept count : 9 Attach Completed count : 9 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 00000000, IMSI 12131415161718, AGE 0, DE-REGISTERED PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 75 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 ab 17 53 CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 75 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 ab 17 53 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 75 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 ab 17 53 result (ATTACH REQUEST) = 79 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 10 Attach Accept count : 9 Attach Completed count : 9 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700 -> afe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH ACCEPT from 0x05060708:32000 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 51 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 a4 f2 e0 CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 51 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 a4 f2 e0 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 51 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 a4 f2 e0 result (ATTACH ACCEPT) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 10 Attach Accept count : 10 Attach Completed count : 9 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 79 08 03 08 25 bc CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 79 08 03 08 25 bc NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 79 08 03 08 25 bc result (ATTACH COMPLETE) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 10 Attach Accept count : 10 Attach Completed count : 10 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING GMM INFO from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 55 08 21 97 59 c6 CALLBACK, event 0, msg length 66, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 55 08 21 97 59 c6 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 66 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 70 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 55 08 21 97 59 c6 result (GMM INFO) = 70 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 10 Attach Accept count : 10 Attach Completed count : 10 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH REQUEST (unexpected) from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 7d 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 a2 24 d0 CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 7d 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 a2 24 d0 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 7d 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 a2 24 d0 result (ATTACH REQUEST (unexpected)) = 79 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 11 Attach Accept count : 10 Attach Completed count : 10 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700 -> afe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH ACCEPT from 0x05060708:32000 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 59 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 b9 a3 b0 CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 59 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 b9 a3 b0 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 59 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 b9 a3 b0 result (ATTACH ACCEPT) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 11 Attach Accept count : 11 Attach Completed count : 10 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 81 08 03 b9 71 10 CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 81 08 03 b9 71 10 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 81 08 03 b9 71 10 result (ATTACH COMPLETE) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 11 Attach Accept count : 11 Attach Completed count : 11 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH REQ from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 5d 08 05 02 25 0a 44 1a c9 CALLBACK, event 0, msg length 69, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 5d 08 05 02 25 0a 44 1a c9 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 69 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 73 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 5d 08 05 02 25 0a 44 1a c9 result (DETACH REQ) = 73 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 11 Attach Accept count : 11 Attach Completed count : 11 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH ACC from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 85 08 06 b6 9c 27 CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 85 08 06 b6 9c 27 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 85 08 06 b6 9c 27 result (DETACH ACC) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 11 Attach Accept count : 11 Attach Completed count : 11 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 00000000, IMSI 12131415161718, AGE 0, DE-REGISTERED PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 89 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 21 24 df CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 89 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 21 24 df NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 0e 00 09 41 c4 01 08 15 01 b7 f8 36 result (ATTACH REQUEST) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 12 Attach Accept count : 11 Attach Completed count : 11 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700 -> afe2b700, IMSI (none), AGE 0, STORED 1, IMSI acquisition in progress PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 8d 08 16 08 11 12 13 14 15 16 17 18 74 ac 38 CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 8d 08 16 08 11 12 13 14 15 16 17 18 74 ac 38 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 89 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 21 24 df result (IDENT RESPONSE) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 12 Attach Accept count : 11 Attach Completed count : 11 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700 -> afe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH REJECT from 0x05060708:32000 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 61 08 04 07 79 ba a5 CALLBACK, event 0, msg length 67, bvci 0x1002 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 61 08 04 07 79 ba a5 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 67 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 71 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 61 08 04 07 79 ba a5 result (ATTACH REJECT) = 71 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 12 Attach Reject count : 1 Attach Accept count : 11 Attach Completed count : 11 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 00000000, IMSI 12131415161718, AGE 0, DE-REGISTERED PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 91 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 81 7a 01 CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 91 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 81 7a 01 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 0e 00 09 41 c4 01 08 15 01 b7 f8 36 result (ATTACH REQUEST) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 13 Attach Reject count : 1 Attach Accept count : 11 Attach Completed count : 11 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 1 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700 -> afe2b700, IMSI (none), AGE 0, STORED 1, IMSI acquisition in progress PROCESSING DETACH REQ (MO) from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 95 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 9c dc fc CALLBACK, event 0, msg length 44, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 95 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 9c dc fc NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 0e 00 09 41 c4 05 08 06 00 29 4a 68 result (DETACH REQ (MO)) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 13 Attach Reject count : 1 Attach Accept count : 11 Attach Completed count : 11 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 2 Detach Accept count : 1 TLLI-Cache: 0 PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 99 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 88 49 82 CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 99 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 88 49 82 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 0e 00 09 41 c4 01 08 15 01 b7 f8 36 result (ATTACH REQUEST) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 14 Attach Reject count : 1 Attach Accept count : 11 Attach Completed count : 11 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 2 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700 -> afe2b700, IMSI (none), AGE 0, STORED 1, IMSI acquisition in progress PROCESSING DETACH REQ (MT) from 0x05060708:32000 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 65 08 05 02 25 0a 17 a2 20 CALLBACK, event 0, msg length 69, bvci 0x1002 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 65 08 05 02 25 0a 17 a2 20 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 69 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 73 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 65 08 05 02 25 0a 17 a2 20 result (DETACH REQ (MT)) = 73 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 14 Attach Reject count : 1 Attach Accept count : 11 Attach Completed count : 11 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 2 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700 -> afe2b700, IMSI 12131415161718, AGE 0, STORED 1, IMSI acquisition in progress PROCESSING DETACH ACC from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 9d 08 06 65 2c 8a CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 9d 08 06 65 2c 8a NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 99 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 88 49 82 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 9d 08 06 65 2c 8a result (DETACH ACC) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 14 Attach Reject count : 1 Attach Accept count : 11 Attach Completed count : 11 RoutingArea Update Request count: 1 RoutingArea Update Reject count : 1 Detach Request count : 2 Detach Accept count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 00000000, IMSI 12131415161718, AGE 0, DE-REGISTERED Gbproxy global: Test TLLI info expiry Test TLLI replacement: Add TLLI 1, IMSI 1 Add TLLI 2, IMSI 1 (should replace TLLI 1) Peers: NSEI 0, BVCI 20, not blocked, RAI 000-000-0-0 TLLI cache size : 1 TLLI-Cache: 1 TLLI c000162e, IMSI 03242526, AGE 0, IMSI matches Test IMSI replacement: Add TLLI 1, IMSI 1 Add TLLI 1, IMSI 2 (should replace IMSI 1) Peers: NSEI 0, BVCI 20, not blocked, RAI 000-000-0-0 TLLI cache size : 1 TLLI-Cache: 1 TLLI c00004d2, IMSI 06272829, AGE 0, IMSI matches Test TLLI expiry, max_len == 1: Add TLLI 1, IMSI 1 Add TLLI 2, IMSI 2 (should replace IMSI 1) Peers: NSEI 0, BVCI 20, not blocked, RAI 000-000-0-0 TLLI cache size : 1 TLLI-Cache: 1 TLLI c000162e, IMSI 06272829, AGE 0, IMSI matches Test TLLI expiry, max_age == 1: Add TLLI 1, IMSI 1 (should expire after timeout) Add TLLI 2, IMSI 2 (should not expire after timeout) Peers: NSEI 0, BVCI 20, not blocked, RAI 000-000-0-0 TLLI cache size : 1 TLLI-Cache: 1 TLLI c000162e, IMSI 06272829, AGE 1, IMSI matches Test TLLI expiry, max_len == 2, max_age == 1: Add TLLI 1, IMSI 1 (should expire) Add TLLI 2, IMSI 2 (should expire after timeout) Add TLLI 3, IMSI 3 (should not expire after timeout) Peers: NSEI 0, BVCI 20, not blocked, RAI 000-000-0-0 TLLI cache size : 3 TLLI-Cache: 3 TLLI c0000d80, IMSI 12345678, AGE 0, IMSI matches TLLI c000162e, IMSI 06272829, AGE 1, IMSI matches TLLI c00004d2, IMSI 03242526, AGE 2, IMSI matches Remove stale TLLIs Peers: NSEI 0, BVCI 20, not blocked, RAI 000-000-0-0 TLLI cache size : 1 TLLI-Cache: 1 TLLI c0000d80, IMSI 12345678, AGE 0, IMSI matches === test_gbproxy_stored_messages === --- Initialise SGSN --- MESSAGE to SGSN at 0x05060708:32000, msg length 12 02 00 81 01 01 82 01 01 04 82 01 00 PROCESSING RESET_ACK from 0x05060708:32000 03 01 82 01 01 04 82 01 00 MESSAGE to SGSN at 0x05060708:32000, msg length 1 0a result (RESET_ACK) = 1 PROCESSING ALIVE_ACK from 0x05060708:32000 0b MESSAGE to SGSN at 0x05060708:32000, msg length 1 06 result (ALIVE_ACK) = 1 PROCESSING UNBLOCK_ACK from 0x05060708:32000 07 ==> got signal NS_UNBLOCK, NS-VC 0x0101/5.6.7.8:32000 result (UNBLOCK_ACK) = 0 PROCESSING ALIVE from 0x05060708:32000 0a MESSAGE to SGSN at 0x05060708:32000, msg length 1 0b result (ALIVE) = 1 --- Initialise BSS 1 --- Setup NS-VC: remote 0x01020304:1111, NSVCI 0x1001(4097), NSEI 0x1000(4096) PROCESSING RESET from 0x01020304:1111 02 00 81 01 01 82 10 01 04 82 10 00 ==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 9 03 01 82 10 01 04 82 10 00 MESSAGE to BSS at 0x01020304:1111, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:1111 0a MESSAGE to BSS at 0x01020304:1111, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:1111 06 ==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:1111 0b result (ALIVE_ACK) = 0 Setup BSSGP: remote 0x01020304:1111, BVCI 0x1002(4098) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 result (BVC_RESET) = 22 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 10 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 10 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 10 02 result (BVC_RESET_ACK) = 9 Current NS-VCIs: VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111 Packets at NS Level ( In): 5 Packets at NS Level (Out): 5 Bytes at NS Level ( In): 37 Bytes at NS Level (Out): 21 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 Packets at NS Level ( In): 5 Packets at NS Level (Out): 5 Bytes at NS Level ( In): 21 Bytes at NS Level (Out): 37 NS-VC Block count : 1 Gbproxy global: Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 --- Establish first LLC connection --- PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 0e 00 09 41 c4 01 08 15 01 b7 f8 36 result (ATTACH REQUEST) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 8000dead, IMSI (none), AGE 0, STORED 1, IMSI acquisition in progress PROCESSING IDENT REQUEST from 0x05060708:32000 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba CALLBACK, event 0, msg length 23, bvci 0x1002 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 23 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 27 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba result (IDENT REQUEST) = 27 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 8000dead, IMSI (none), AGE 0, STORED 1, IMSI acquisition in progress PROCESSING DETACH ACCEPT from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 09 01 c0 05 08 06 00 f8 92 41 CALLBACK, event 0, msg length 32, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 09 01 c0 05 08 06 00 f8 92 41 result (DETACH ACCEPT) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 8000dead, IMSI (none), AGE 0, STORED 2, IMSI acquisition in progress PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 09 08 16 08 11 12 13 14 15 16 17 18 ba 14 c3 CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 09 08 16 08 11 12 13 14 15 16 17 18 ba 14 c3 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 result (IDENT RESPONSE) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 1 TLLI-Cache: 0 Gbproxy global: ===== GbProxy test END osmo-sgsn-1.3.0/tests/gprs/000077500000000000000000000000001327264017000155575ustar00rootroot00000000000000osmo-sgsn-1.3.0/tests/gprs/Makefile.am000066400000000000000000000005051327264017000176130ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOABIS_CFLAGS) EXTRA_DIST = gprs_test.ok noinst_PROGRAMS = gprs_test gprs_test_SOURCES = gprs_test.c $(top_srcdir)/src/gprs/gprs_utils.c gprs_test_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) osmo-sgsn-1.3.0/tests/gprs/gprs_test.c000066400000000000000000000074521327264017000177450ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #define ASSERT_FALSE(x) if (x) { printf("Should have returned false.\n"); abort(); } #define ASSERT_TRUE(x) if (!x) { printf("Should have returned true.\n"); abort(); } /** * GSM 04.64 8.4.2 Receipt of unacknowledged information */ static int nu_is_retransmission(uint16_t nu, uint16_t vur) { int ret = gprs_llc_is_retransmit(nu, vur); printf("N(U) = %d, V(UR) = %d => %s\n", nu, vur, ret == 1 ? "retransmit" : "new"); return ret; } static void test_8_4_2() { printf("Testing gprs_llc_is_retransmit.\n"); ASSERT_FALSE(nu_is_retransmission(0, 0)); ASSERT_TRUE (nu_is_retransmission(0, 1)); /* expect 1... check for retransmissions */ ASSERT_TRUE (nu_is_retransmission(0, 1)); ASSERT_TRUE (nu_is_retransmission(511, 1)); ASSERT_TRUE (nu_is_retransmission(483, 1)); ASSERT_TRUE (nu_is_retransmission(482, 1)); ASSERT_FALSE(nu_is_retransmission(481, 1)); /* expect 511... check for retransmissions */ ASSERT_FALSE(nu_is_retransmission(0, 240)); // ahead ASSERT_FALSE(nu_is_retransmission(0, 511)); // ahead ASSERT_FALSE(nu_is_retransmission(1, 511)); // ahead ASSERT_FALSE(nu_is_retransmission(511, 511)); // same ASSERT_TRUE (nu_is_retransmission(510, 511)); // behind ASSERT_TRUE (nu_is_retransmission(481, 511)); // behind ASSERT_FALSE(nu_is_retransmission(479, 511)); // wrapped } static void test_gprs_timer_enc_dec(void) { int i, u, secs, tmr; const int upper_secs_test_limit = 12000; int dec_secs, last_dec_secs = -1; printf("Test GPRS timer decoding/encoding\n"); /* Check gprs_tmr_to_secs with all 256 encoded values */ for (u = 0; u <= GPRS_TMR_DEACTIVATED; u += 32) { fprintf(stderr, "Testing decoding with timer value unit %u\n", u / 32); for (i = 0; i < 32; i++) { switch (u) { case GPRS_TMR_2SECONDS: OSMO_ASSERT(gprs_tmr_to_secs(u + i) == 2 * i); break; default: case GPRS_TMR_MINUTE: OSMO_ASSERT(gprs_tmr_to_secs(u + i) == 60 * i); break; case GPRS_TMR_6MINUTE: OSMO_ASSERT(gprs_tmr_to_secs(u + i) == 360 * i); break; case GPRS_TMR_DEACTIVATED: OSMO_ASSERT(gprs_tmr_to_secs(u + i) == -1); break; } OSMO_ASSERT(gprs_tmr_to_secs(u + i) < upper_secs_test_limit); } } /* Check gprs_secs_to_tmr_floor for secs that can exactly be * represented as GPRS timer values */ for (i = 0; i < GPRS_TMR_DEACTIVATED; i++) { int j; secs = gprs_tmr_to_secs(i); tmr = gprs_secs_to_tmr_floor(secs); OSMO_ASSERT(secs == gprs_tmr_to_secs(tmr)); /* Check that the highest resolution is used */ for (j = 0; j < tmr; j++) OSMO_ASSERT(secs != gprs_tmr_to_secs(j)); } OSMO_ASSERT(GPRS_TMR_DEACTIVATED == gprs_secs_to_tmr_floor(-1)); /* Check properties of gprs_secs_to_tmr_floor */ for (secs = 0; secs <= upper_secs_test_limit; secs++) { int tmr = gprs_secs_to_tmr_floor(secs); int delta_secs = gprs_tmr_to_secs((tmr & ~0x1f) | 1); dec_secs = gprs_tmr_to_secs(tmr); /* Check floor */ OSMO_ASSERT(dec_secs <= secs); /* Check monotonicity */ OSMO_ASSERT(dec_secs >= last_dec_secs); /* Check max distance (<= resolution) */ OSMO_ASSERT(dec_secs - last_dec_secs <= delta_secs); last_dec_secs = dec_secs; } } const struct log_info_cat default_categories[] = { [DGPRS] = { .name = "DGPRS", .description = "GPRS Packet Service", .enabled = 0, .loglevel = LOGL_DEBUG, }, }; static struct log_info info = { .cat = default_categories, .num_cat = ARRAY_SIZE(default_categories), }; int main(int argc, char **argv) { void *ctx = talloc_named_const(NULL, 0, "gprs_test"); osmo_init_logging2(ctx, &info); test_8_4_2(); test_gprs_timer_enc_dec(); printf("Done.\n"); return EXIT_SUCCESS; } osmo-sgsn-1.3.0/tests/gprs/gprs_test.ok000066400000000000000000000010211327264017000201160ustar00rootroot00000000000000Testing gprs_llc_is_retransmit. N(U) = 0, V(UR) = 0 => new N(U) = 0, V(UR) = 1 => retransmit N(U) = 0, V(UR) = 1 => retransmit N(U) = 511, V(UR) = 1 => retransmit N(U) = 483, V(UR) = 1 => retransmit N(U) = 482, V(UR) = 1 => retransmit N(U) = 481, V(UR) = 1 => new N(U) = 0, V(UR) = 240 => new N(U) = 0, V(UR) = 511 => new N(U) = 1, V(UR) = 511 => new N(U) = 511, V(UR) = 511 => new N(U) = 510, V(UR) = 511 => retransmit N(U) = 481, V(UR) = 511 => retransmit N(U) = 479, V(UR) = 511 => new Test GPRS timer decoding/encoding Done. osmo-sgsn-1.3.0/tests/gtphub/000077500000000000000000000000001327264017000160755ustar00rootroot00000000000000osmo-sgsn-1.3.0/tests/gtphub/Makefile.am000066400000000000000000000012231327264017000201270ustar00rootroot00000000000000AM_CPPFLAGS = \ $(all_includes) \ -I$(top_srcdir)/include \ $(NULL) AM_CFLAGS = \ -Wall \ -ggdb3 \ $(LIBOSMOCORE_CFLAGS) \ $(LIBOSMOABIS_CFLAGS) \ $(LIBOSMOGSM_CFLAGS) \ $(LIBGTP_CFLAGS) \ $(NULL) EXTRA_DIST = \ gtphub_test.ok \ $(NULL) noinst_PROGRAMS = \ gtphub_test \ $(NULL) gtphub_test_SOURCES = \ gtphub_test.c \ $(NULL) gtphub_test_LDFLAGS = \ -Wl,--wrap=gtphub_resolve_ggsn_addr \ -Wl,--wrap=gtphub_ares_init \ -Wl,--wrap=gtphub_write \ $(NULL) gtphub_test_LDADD = \ $(top_builddir)/src/gprs/gtphub.o \ $(top_builddir)/src/gprs/gprs_utils.o \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOGSM_LIBS) \ $(LIBGTP_LIBS) \ -lrt \ $(NULL) osmo-sgsn-1.3.0/tests/gtphub/gtphub_test.c000066400000000000000000001441731327264017000206030ustar00rootroot00000000000000/* Test the GTP hub */ /* (C) 2015 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 #include #include #include #include #define ZERO_STRUCT(struct_pointer) memset(struct_pointer, '\0', \ sizeof(*(struct_pointer))) #define LVL2_ASSERT(exp) LVL2_ASSERT_R(exp, return 0) #define LVL2_ASSERT_R(exp, ret) \ if (!(exp)) { \ fprintf(stderr, "LVL2 Assert failed %s %s:%d\n", #exp, \ __FILE__, __LINE__); \ osmo_generate_backtrace(); \ ret; \ } /* Convenience makro, note: only within this C file. */ #define LOG(label) \ { fprintf(stderr, "\n" label "\n"); \ printf(label "\n"); } void gtphub_init(struct gtphub *hub); void gtphub_free(struct gtphub *hub); void *osmo_gtphub_ctx; static void nr_mapping_free(struct expiring_item *e) { struct nr_mapping *m = container_of(e, struct nr_mapping, expiry_entry); nr_mapping_del(m); talloc_free(m); } static struct nr_mapping *nr_mapping_alloc(void) { struct nr_mapping *m; m = talloc(osmo_gtphub_ctx, struct nr_mapping); nr_mapping_init(m); m->expiry_entry.del_cb = nr_mapping_free; return m; } static struct nr_mapping *nr_map_have(struct nr_map *map, void *origin, nr_t orig, time_t now) { struct nr_mapping *mapping; mapping = nr_map_get(map, origin, orig); if (!mapping) { mapping = nr_mapping_alloc(); mapping->origin = origin; mapping->orig = orig; nr_map_add(map, mapping, now); } return mapping; } static nr_t nr_map_verify(const struct nr_map *map, void *origin, nr_t orig, nr_t expect_repl) { struct nr_mapping *m; m = nr_map_get(map, origin, orig); if (!m) { printf("mapping not found for %p %d\n", origin, orig); return 0; } if (m->repl != expect_repl) { printf("mapping found, but nr mismatches: expect %d, got %d\n", (int)expect_repl, (int)m->repl); return 0; } return 1; } static int nr_map_verify_inv(const struct nr_map *map, nr_t repl, void *expect_origin, nr_t expect_orig) { struct nr_mapping *m; m = nr_map_get_inv(map, repl); if (!m) { printf("mapping not found for %d\n", (int)repl); return 0; } if (m->origin != expect_origin) { printf("mapping found, but origin mismatches:" " expect %p, got %p\n", expect_origin, m->origin); return 0; } if (m->orig != expect_orig) { printf("mapping found, but nr mismatches: expect %d, got %d\n", (int)expect_orig, (int)m->orig); return 0; } return 1; } static void test_nr_map_basic(void) { struct nr_pool _pool; struct nr_pool *pool = &_pool; struct nr_map _map; struct nr_map *map = &_map; nr_pool_init(pool, 1, 1000); nr_map_init(map, pool, NULL); OSMO_ASSERT(llist_empty(&map->mappings)); #define TEST_N_HALF 100 #define TEST_N (2*TEST_N_HALF) #define TEST_I 123 uint32_t i, check_i; uint32_t m[TEST_N]; struct nr_mapping *mapping; /* create half of TEST_N mappings from one origin */ void *origin1 = (void*)0x1234; for (i = 0; i < TEST_N_HALF; i++) { nr_t orig = TEST_I + i; mapping = nr_map_have(map, origin1, orig, 0); m[i] = mapping->repl; OSMO_ASSERT(m[i] != 0); OSMO_ASSERT(llist_count(&map->mappings) == (i+1)); for (check_i = 0; check_i < i; check_i++) OSMO_ASSERT(m[check_i] != m[i]); } OSMO_ASSERT(llist_count(&map->mappings) == TEST_N_HALF); /* create another TEST_N mappings with the same original numbers, but * from a different origin */ void *origin2 = (void*)0x5678; for (i = 0; i < TEST_N_HALF; i++) { int i2 = TEST_N_HALF + i; nr_t orig = TEST_I + i; mapping = nr_map_have(map, origin2, orig, 0); m[i2] = mapping->repl; OSMO_ASSERT(m[i2] != 0); OSMO_ASSERT(llist_count(&map->mappings) == (i2+1)); for (check_i = 0; check_i < i2; check_i++) OSMO_ASSERT(m[check_i] != m[i2]); } OSMO_ASSERT(llist_count(&map->mappings) == TEST_N); /* verify mappings */ for (i = 0; i < TEST_N_HALF; i++) { nr_t orig = TEST_I + i; { OSMO_ASSERT(nr_map_verify(map, origin1, orig, m[i])); OSMO_ASSERT(nr_map_verify_inv(map, m[i], origin1, orig)); } { int i2 = TEST_N_HALF + i; OSMO_ASSERT(nr_map_verify(map, origin2, orig, m[i2])); OSMO_ASSERT(nr_map_verify_inv(map, m[i2], origin2, orig)); } } /* remove all mappings */ for (i = 0; i < TEST_N_HALF; i++) { OSMO_ASSERT(llist_count(&map->mappings) == (TEST_N - 2*i)); nr_t orig = TEST_I + i; nr_mapping_del(nr_map_get(map, origin1, orig)); nr_mapping_del(nr_map_get(map, origin2, orig)); } OSMO_ASSERT(llist_empty(&map->mappings)); #undef TEST_N #undef TEST_I } static int nr_map_is(struct nr_map *map, const char *str) { static char buf[4096]; char *pos = buf; size_t len = sizeof(buf); struct nr_mapping *m; llist_for_each_entry(m, &map->mappings, entry) { size_t wrote = snprintf(pos, len, "(%u->%u@%d), ", m->orig, m->repl, (int)m->expiry_entry.expiry); OSMO_ASSERT(wrote < len); pos += wrote; len -= wrote; } *pos = '\0'; if (strncmp(buf, str, sizeof(buf)) != 0) { printf("FAILURE: nr_map_is() mismatches expected value:\n" "expected: \"%s\"\n" "is: \"%s\"\n", str, buf); return 0; } return 1; } static int test_nr_map_wrap_with(nr_t nr_min, nr_t nr_max, nr_t repl_last, nr_t orig_start, int orig_n, const char *expect) { struct nr_pool _pool; struct nr_pool *pool = &_pool; struct nr_map _map; struct nr_map *map = &_map; nr_pool_init(pool, nr_min, nr_max); nr_map_init(map, pool, NULL); pool->last_nr = repl_last; void *origin = (void*)0x1234; int i; for (i = 0; i < orig_n; i++) LVL2_ASSERT(nr_map_have(map, origin, orig_start + i, 0)); LVL2_ASSERT(nr_map_is(map, expect)); nr_map_clear(map); return 1; } static void test_nr_map_wrap(void) { OSMO_ASSERT(test_nr_map_wrap_with( 0, UINT_MAX, UINT_MAX - 2, 1, 5, "(1->4294967294@0), " "(2->4294967295@0), " "(3->0@0), " "(4->1@0), " "(5->2@0), " )); OSMO_ASSERT(test_nr_map_wrap_with( 5, 10, 8, 1, 5, "(1->9@0), (2->10@0), (3->5@0), (4->6@0), (5->7@0), " )); } static void test_expiry(void) { struct expiry expiry; struct nr_pool pool; struct nr_map map; int i; expiry_init(&expiry, 30); nr_pool_init(&pool, 1, 1000); nr_map_init(&map, &pool, &expiry); OSMO_ASSERT(nr_map_is(&map, "")); /* tick on empty map */ OSMO_ASSERT(expiry_tick(&expiry, 10000) == 0); OSMO_ASSERT(nr_map_is(&map, "")); #define MAP1 \ "(10->1@10040), " \ "" #define MAP2 \ "(20->2@10050), " \ "(21->3@10051), " \ "(22->4@10052), " \ "(23->5@10053), " \ "(24->6@10054), " \ "(25->7@10055), " \ "(26->8@10056), " \ "(27->9@10057), " \ "" #define MAP3 \ "(420->10@10072), " \ "(421->11@10072), " \ "(422->12@10072), " \ "(423->13@10072), " \ "(424->14@10072), " \ "(425->15@10072), " \ "(426->16@10072), " \ "(427->17@10072), " \ "" /* add mapping at time 10010. */ nr_map_have(&map, 0, 10, 10010); OSMO_ASSERT(nr_map_is(&map, MAP1)); /* tick on unexpired item. */ OSMO_ASSERT(expiry_tick(&expiry, 10010) == 0); OSMO_ASSERT(expiry_tick(&expiry, 10011) == 0); OSMO_ASSERT(nr_map_is(&map, MAP1)); /* Spread mappings at 10020, 10021, ... 10027. */ for (i = 0; i < 8; i++) nr_map_have(&map, 0, 20 + i, 10020 + i); OSMO_ASSERT(nr_map_is(&map, MAP1 MAP2)); /* tick on unexpired items. */ OSMO_ASSERT(expiry_tick(&expiry, 10030) == 0); OSMO_ASSERT(expiry_tick(&expiry, 10039) == 0); OSMO_ASSERT(nr_map_is(&map, MAP1 MAP2)); /* expire the first item (from 10010). */ OSMO_ASSERT(expiry_tick(&expiry, 10010 + 30) == 1); OSMO_ASSERT(nr_map_is(&map, MAP2)); /* again nothing to expire */ OSMO_ASSERT(expiry_tick(&expiry, 10041) == 0); OSMO_ASSERT(nr_map_is(&map, MAP2)); /* Mappings all at the same time. */ for (i = 0; i < 8; i++) nr_map_have(&map, 0, 420 + i, 10042); OSMO_ASSERT(nr_map_is(&map, MAP2 MAP3)); /* Eight to expire, were added further above to be chronologically * correct, at 10020..10027. */ OSMO_ASSERT(expiry_tick(&expiry, 10027 + 30) == 8); OSMO_ASSERT(nr_map_is(&map, MAP3)); /* again nothing to expire */ OSMO_ASSERT(expiry_tick(&expiry, 10027 + 30) == 0); OSMO_ASSERT(nr_map_is(&map, MAP3)); /* Eight to expire, from 10042. Now at 10042 + 30: */ OSMO_ASSERT(expiry_tick(&expiry, 10042 + 30) == 8); OSMO_ASSERT(nr_map_is(&map, "")); #undef MAP1 #undef MAP2 #undef MAP3 } char resolve_ggsn_got_imsi[GSM23003_IMSI_MAX_DIGITS+1]; char resolve_ggsn_got_ni[GSM_APN_LENGTH]; struct osmo_sockaddr resolved_ggsn_addr; static int resolve_to_ggsn(const char *addr, uint16_t port) { LVL2_ASSERT(osmo_sockaddr_init_udp(&resolved_ggsn_addr, addr, port) == 0); return 1; } struct osmo_sockaddr resolved_sgsn_addr; static int resolve_to_sgsn(const char *addr, uint16_t port) { LVL2_ASSERT(osmo_sockaddr_init_udp(&resolved_sgsn_addr, addr, port) == 0); return 1; } struct osmo_sockaddr sgsn_sender; static int send_from_sgsn(const char *addr, uint16_t port) { LVL2_ASSERT(osmo_sockaddr_init_udp(&sgsn_sender, addr, port) == 0); return 1; } struct osmo_sockaddr ggsn_sender; static int send_from_ggsn(const char *addr, uint16_t port) { LVL2_ASSERT(osmo_sockaddr_init_udp(&ggsn_sender, addr, port) == 0); return 1; } /* override, requires '-Wl,--wrap=gtphub_resolve_ggsn_addr' */ struct gtphub_peer_port *__real_gtphub_resolve_ggsn_addr(struct gtphub *hub, const char *imsi_str, const char *apn_ni_str); struct gtphub_peer_port *__wrap_gtphub_resolve_ggsn_addr(struct gtphub *hub, const char *imsi_str, const char *apn_ni_str) { struct gsn_addr resolved_gsna; uint16_t resolved_port; OSMO_ASSERT(gsn_addr_from_sockaddr(&resolved_gsna, &resolved_port, &resolved_ggsn_addr) == 0); struct gtphub_peer_port *pp; pp = gtphub_port_have(hub, &hub->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL], &resolved_gsna, resolved_port); printf("- __wrap_gtphub_resolve_ggsn_addr():\n" " returning GGSN addr from imsi %s ni %s: %s\n", imsi_str, apn_ni_str, gtphub_port_str(pp)); if (!imsi_str) imsi_str = "(null)"; osmo_strlcpy(resolve_ggsn_got_imsi, imsi_str, sizeof(resolve_ggsn_got_imsi)); if (!apn_ni_str) apn_ni_str = "(null)"; osmo_strlcpy(resolve_ggsn_got_ni, apn_ni_str, sizeof(resolve_ggsn_got_ni)); return pp; } #define was_resolved_for(IMSI,NI) _was_resolved_for(IMSI, NI, __FILE__, __LINE__) static int _was_resolved_for(const char *imsi, const char *ni, const char *file, int line) { int cmp0 = strncmp(imsi, resolve_ggsn_got_imsi, sizeof(resolve_ggsn_got_imsi)); if (cmp0 != 0) { printf("\n%s:%d: was_resolved_for(): MISMATCH for IMSI\n" " expecting: '%s'\n" " got: '%s'\n\n", file, line, imsi, resolve_ggsn_got_imsi); } int cmp1 = strncmp(ni, resolve_ggsn_got_ni, sizeof(resolve_ggsn_got_ni)); if (cmp1 != 0) { printf("\n%s:%d: was_resolved_for(): MISMATCH for NI\n" " expecting: '%s'\n" " got: '%s'\n\n", file, line, ni, resolve_ggsn_got_ni); } return (cmp0 == 0) && (cmp1 == 0); } /* override, requires '-Wl,--wrap=gtphub_ares_init' */ int __real_gtphub_ares_init(struct gtphub *hub); int __wrap_gtphub_ares_init(struct gtphub *hub) { /* Do nothing. */ return 0; } /* override, requires '-Wl,--wrap=gtphub_write' */ int __real_gtphub_write(const struct osmo_fd *to, const struct osmo_sockaddr *to_addr, const uint8_t *buf, size_t buf_len); int __wrap_gtphub_write(const struct osmo_fd *to, const struct osmo_sockaddr *to_addr, const uint8_t *buf, size_t buf_len) { printf("Out-of-band gtphub_write(%d):\n" "to %s\n" "%s\n", (int)buf_len, osmo_sockaddr_to_str(to_addr), osmo_hexdump(buf, buf_len)); return 0; } #define buf_len 1024 static uint8_t buf[buf_len]; static uint8_t *reply_buf; static unsigned int msg(const char *hex) { unsigned int l = osmo_hexparse(hex, buf, buf_len); OSMO_ASSERT(l > 0); return l; } /* Compare static buf to given string constant. The amount of bytes is obtained * from parsing the GTP header in buf. hex must match an osmo_hexdump() of the * desired message. Return 1 if size and content match. */ #define reply_is(MSG) _reply_is(MSG, __FILE__, __LINE__) static int _reply_is(const char *hex, const char *file, int line) { struct gtp1_header_long *h = (void*)reply_buf; int len = ntoh16(h->length) + 8; const char *dump = osmo_hexdump_nospc(reply_buf, len); int cmp = strcmp(dump, hex); if (cmp != 0) { printf("\n%s:%d: reply_is(): MISMATCH\n" " expecting:\n'%s'\n" " got:\n'%s'\n\n", file, line, hex, dump); int i; int l = strlen(hex); int m = strlen(dump); if (m < l) l = m; for (i = 0; i < l; i++) { if (hex[i] != dump[i]) { printf("First mismatch at position %d:\n" " %s\n %s\n", i, hex + i, dump + i); break; } } } return cmp == 0; } #define same_addr(GOT, EXPECTED) _same_addr((GOT),(EXPECTED), __FILE__, __LINE__) static int _same_addr(const struct osmo_sockaddr *got, const struct osmo_sockaddr *expected, const char *file, int line) { int cmp = osmo_sockaddr_cmp(got, expected); if (!cmp) return 1; char buf[256]; printf("\n%s:%d: addr_is(): MISMATCH\n" " expecting: '%s'\n" " got: '%s'\n\n", file, line, osmo_sockaddr_to_str(expected), osmo_sockaddr_to_strb(got, buf, sizeof(buf))); return 0; } time_t now; static struct gtphub _hub; static struct gtphub *hub = &_hub; static int setup_test_hub() { /* Not really needed, but to make 100% sure... */ ZERO_STRUCT(hub); gtphub_init(hub); /* Tell this mock gtphub its local address for this test. */ LVL2_ASSERT(gsn_addr_from_str(&hub->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].local_addr, "127.0.1.1") == 0); LVL2_ASSERT(gsn_addr_from_str(&hub->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_USER].local_addr, "127.0.1.2") == 0); LVL2_ASSERT(gsn_addr_from_str(&hub->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].local_addr, "127.0.2.1") == 0); LVL2_ASSERT(gsn_addr_from_str(&hub->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_USER].local_addr, "127.0.2.2") == 0); hub->restart_counter = 0x23; now = 345; LVL2_ASSERT(send_from_sgsn("192.168.42.23", 423)); LVL2_ASSERT(resolve_to_ggsn("192.168.43.34", 2123)); LVL2_ASSERT(send_from_ggsn("192.168.43.34", 434)); LVL2_ASSERT(resolve_to_sgsn("192.168.42.23", 2123)); #define GGSNS_CTRL_FD 1 #define GGSNS_USER_FD 2 #define SGSNS_CTRL_FD 3 #define SGSNS_USER_FD 4 hub->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].ofd.priv_nr = GGSNS_CTRL_FD; hub->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_USER].ofd.priv_nr = GGSNS_USER_FD; hub->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].ofd.priv_nr = SGSNS_CTRL_FD; hub->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_USER].ofd.priv_nr = SGSNS_USER_FD; return 1; } static int clear_test_hub() { /* expire all */ gtphub_gc(hub, now + (60 * GTPH_EXPIRE_SLOWLY_MINUTES) + 1); int plane_idx; plane_idx = GTPH_PLANE_CTRL; LVL2_ASSERT(llist_empty(&hub->to_gsns[GTPH_SIDE_GGSN][plane_idx].peers)); LVL2_ASSERT(llist_empty(&hub->to_gsns[GTPH_SIDE_SGSN][plane_idx].peers)); plane_idx = GTPH_PLANE_USER; LVL2_ASSERT(llist_empty(&hub->to_gsns[GTPH_SIDE_GGSN][plane_idx].peers)); LVL2_ASSERT(llist_empty(&hub->to_gsns[GTPH_SIDE_SGSN][plane_idx].peers)); LVL2_ASSERT(llist_empty(&hub->tunnels)); LVL2_ASSERT(llist_empty(&hub->pending_deletes)); LVL2_ASSERT(llist_empty(&hub->ggsn_lookups)); LVL2_ASSERT(llist_empty(&hub->resolved_ggsns)); gtphub_free(hub); return 1; } static int tunnels_are(const char *expect) { static char buf[4096]; char *pos = buf; size_t len = sizeof(buf); struct gtphub_tunnel *t; llist_for_each_entry(t, &hub->tunnels, entry) { size_t wrote = snprintf(pos, len, "%s @%d\n", gtphub_tunnel_str(t), (int)t->expiry_entry.expiry); LVL2_ASSERT(wrote < len); pos += wrote; len -= wrote; } *pos = '\0'; if (strncmp(buf, expect, sizeof(buf)) != 0) { fprintf(stderr, "FAILURE: tunnels_are() mismatches expected value:\n" "EXPECTED:\n%s\n" "IS:\n%s\n", expect, buf); LVL2_ASSERT("tunnels do not match expected listing."); return 0; } return 1; } static void test_echo(void) { LOG("test_echo"); OSMO_ASSERT(setup_test_hub()); now = 123; struct osmo_fd *to_ofd; struct osmo_sockaddr to_addr; struct gtphub_peer_port *pp; int send; const char *gtp_ping_from_sgsn = "32" /* 0b001'1 0010: version 1, protocol GTP, with seq nr */ "01" /* type 01: Echo request */ "0004" /* length of 4 after header TEI */ "00000000" /* header TEI == 0 in Echo */ "abcd" /* some 2 octet sequence nr */ "0000" /* N-PDU 0, no extension header (why is this here?) */ ; const char *gtp_pong_to_sgsn = "32" "02" /* type 02: Echo response */ "0006" /* length of 6 after header TEI */ "00000000" /* header TEI == 0 in Echo */ "abcd" /* same sequence nr */ "0000" "0e23" /* Recovery with restart counter */ ; to_ofd = NULL; ZERO_STRUCT(&to_addr); send = gtphub_handle_buf(hub, GTPH_SIDE_SGSN, GTPH_PLANE_CTRL, &sgsn_sender, buf, msg(gtp_ping_from_sgsn), now, &reply_buf, &to_ofd, &to_addr); OSMO_ASSERT(send > 0); OSMO_ASSERT(to_addr.l); OSMO_ASSERT(same_addr(&to_addr, &sgsn_sender)); OSMO_ASSERT(to_ofd && (to_ofd->priv_nr == SGSNS_CTRL_FD)); OSMO_ASSERT(reply_is(gtp_pong_to_sgsn)); pp = gtphub_port_find_sa(&hub->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL], &sgsn_sender); /* We don't record Echo peers. */ OSMO_ASSERT(!pp); const char *gtp_ping_from_ggsn = "32" /* 0b001'1 0010: version 1, protocol GTP, with seq nr */ "01" /* type 01: Echo request */ "0004" /* length of 4 after header TEI */ "00000000" /* header TEI == 0 in Echo */ "cdef" /* some 2 octet sequence nr */ "0000" /* N-PDU 0, no extension header (why is this here?) */ ; const char *gtp_pong_to_ggsn = "32" "02" /* type 02: Echo response */ "0006" /* length of 6 after header TEI */ "00000000" /* header TEI == 0 in Echo */ "cdef" /* same sequence nr */ "0000" "0e23" /* Recovery with restart counter */ ; to_ofd = NULL; ZERO_STRUCT(&to_addr); send = gtphub_handle_buf(hub, GTPH_SIDE_GGSN, GTPH_PLANE_CTRL, &ggsn_sender, buf, msg(gtp_ping_from_ggsn), now, &reply_buf, &to_ofd, &to_addr); OSMO_ASSERT(send > 0); OSMO_ASSERT(same_addr(&to_addr, &ggsn_sender)); OSMO_ASSERT(to_ofd && (to_ofd->priv_nr == GGSNS_CTRL_FD)); OSMO_ASSERT(reply_is(gtp_pong_to_ggsn)); pp = gtphub_port_find_sa(&hub->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL], &sgsn_sender); OSMO_ASSERT(!pp); /* And all the same on the user plane. */ to_ofd = NULL; ZERO_STRUCT(&to_addr); send = gtphub_handle_buf(hub, GTPH_SIDE_SGSN, GTPH_PLANE_USER, &sgsn_sender, buf, msg(gtp_ping_from_sgsn), now, &reply_buf, &to_ofd, &to_addr); OSMO_ASSERT(send > 0); OSMO_ASSERT(to_addr.l); OSMO_ASSERT(same_addr(&to_addr, &sgsn_sender)); OSMO_ASSERT(to_ofd && (to_ofd->priv_nr == SGSNS_USER_FD)); OSMO_ASSERT(reply_is(gtp_pong_to_sgsn)); pp = gtphub_port_find_sa(&hub->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_USER], &sgsn_sender); OSMO_ASSERT(!pp); to_ofd = NULL; ZERO_STRUCT(&to_addr); send = gtphub_handle_buf(hub, GTPH_SIDE_GGSN, GTPH_PLANE_USER, &ggsn_sender, buf, msg(gtp_ping_from_ggsn), now, &reply_buf, &to_ofd, &to_addr); OSMO_ASSERT(send > 0); OSMO_ASSERT(same_addr(&to_addr, &ggsn_sender)); OSMO_ASSERT(to_ofd && (to_ofd->priv_nr == GGSNS_USER_FD)); OSMO_ASSERT(reply_is(gtp_pong_to_ggsn)); pp = gtphub_port_find_sa(&hub->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_USER], &sgsn_sender); OSMO_ASSERT(!pp); OSMO_ASSERT(clear_test_hub()); } #define MSG_PDP_CTX_REQ(len, seq, restart, imsi, tei_u, tei_c, apn, gsn_c, gsn_u) \ "32" /* 0b001'1 0010: version 1, protocol GTP, with seq nr. */ \ "10" /* type 16: Create PDP Context Request */ \ len /* msg length = 8 + len (2 octets) */ \ "00000000" /* No TEI yet */ \ seq /* Sequence nr (2 octets) */ \ "00" /* N-PDU 0 */ \ "00" /* No extensions */ \ /* IEs */ \ "0e" restart /* 14: Recovery (restart counter: 1 octet) */ \ "02" /* 2 = IMSI */ \ imsi /* (8 octets) */ \ "0f01" /* 15: Selection mode = MS provided APN, subscription not verified*/ \ "10" /* 16: TEI Data I */ \ tei_u /* (4 octets) */ \ "11" /* 17: TEI Control Plane */ \ tei_c /* (4 octets) */ \ "1400" /* 20: NSAPI = 0*/ \ "1a" /* 26: Charging Characteristics */ \ "0800" \ "80" /* 128: End User Address */ \ "0002" /* length = 2: empty PDP Address */ \ "f121" /* spare 0xf0, PDP organization 1, PDP type number 0x21 = 33 */ \ "83" /* 131: Access Point Name */ \ apn /* (2 octets length, N octets encoded APN-NI) */ \ "84" /* 132: Protocol Configuration Options */ \ "0015" /* length = 21 */ \ "80c0231101010011036d69670868656d6d656c6967" \ "85" /* 133: GSN Address */ \ gsn_c /* (2 octets length, N octets addr) */ \ "85" /* 133: GSN Address (second entry) */ \ gsn_u /* (2 octets length, N octets addr) */ \ "86" /* 134: MS International PSTN/ISDN Number (MSISDN) */ \ "0007" /* length */ \ "916407123254f6" /* 1946702123456(f) */ \ "87" /* 135: Quality of Service (QoS) Profile */ \ "0004" /* length */ \ "00" /* priority */ \ "0b921f" /* QoS profile data */ #define MSG_PDP_CTX_RSP(len, tei_h, seq, restart, tei_u, tei_c, gsn_c, gsn_u) \ "32" \ "11" /* Create PDP Context Response */ \ len /* msg length = 8 + len (2 octets) */ \ tei_h /* destination TEI (sent in req above) */ \ seq /* mapped seq */ \ "00" "00" \ /* IEs */ \ "01" /* 1: Cause */ \ "80" /* value = 0b10000000 = response, no rejection. */ \ "08" /* 8: Reordering Required */ \ "00" /* not required. */ \ "0e" restart /* 14: Recovery */ \ "10" /* 16: TEI Data I */ \ tei_u \ "11" /* 17: TEI Control */ \ tei_c \ "7f" /* 127: Charging ID */ \ "00000001" \ "80" /* 128: End User Address */ \ "0006" /* length = 6 */ \ "f121" /* spare 0xf0, PDP organization 1, PDP type number 0x21 = 33 */ \ "7f000002" \ "84" /* 132: Protocol Configuration Options */ \ "0014" /* len = 20 */ \ "8080211002000010810608080808830600000000" \ "85" /* 133: GSN Address (Ctrl) */ \ gsn_c \ "85" /* 133: GSN Address (User) */ \ gsn_u \ "87" /* 135: Quality of Service (QoS) Profile */ \ "0004" /* length */ \ "00" /* priority */ \ "0b921f" /* QoS profile data */ #define msg_from_sgsn_c(A,B,C,D) msg_from_sgsn(GTPH_PLANE_CTRL, A,B,C,D) #define msg_from_sgsn_u(A,B,C,D) msg_from_sgsn(GTPH_PLANE_USER, A,B,C,D) static int msg_from_sgsn(int plane_idx, struct osmo_sockaddr *_sgsn_sender, struct osmo_sockaddr *ggsn_receiver, const char *hex_from_sgsn, const char *hex_to_ggsn) { struct osmo_fd *ggsn_ofd = NULL; struct osmo_sockaddr ggsn_addr; int send; send = gtphub_handle_buf(hub, GTPH_SIDE_SGSN, plane_idx, _sgsn_sender, buf, msg(hex_from_sgsn), now, &reply_buf, &ggsn_ofd, &ggsn_addr); LVL2_ASSERT(send > 0); LVL2_ASSERT(same_addr(&ggsn_addr, ggsn_receiver)); LVL2_ASSERT(reply_is(hex_to_ggsn)); return 1; } #define msg_from_ggsn_c(A,B,C,D) msg_from_ggsn(GTPH_PLANE_CTRL, A,B,C,D) #define msg_from_ggsn_u(A,B,C,D) msg_from_ggsn(GTPH_PLANE_USER, A,B,C,D) static int msg_from_ggsn(int plane_idx, struct osmo_sockaddr *ggsn_sender, struct osmo_sockaddr *sgsn_receiver, const char *msg_from_ggsn, const char *msg_to_sgsn) { struct osmo_fd *sgsn_ofd; struct osmo_sockaddr sgsn_addr; int send; send = gtphub_handle_buf(hub, GTPH_SIDE_GGSN, plane_idx, ggsn_sender, buf, msg(msg_from_ggsn), now, &reply_buf, &sgsn_ofd, &sgsn_addr); if (*msg_to_sgsn) { LVL2_ASSERT(send > 0); LVL2_ASSERT(same_addr(&sgsn_addr, sgsn_receiver)); LVL2_ASSERT(reply_is(msg_to_sgsn)); } else LVL2_ASSERT(send == 0); return 1; } static int create_pdp_ctx() { const char *gtp_req_from_sgsn = MSG_PDP_CTX_REQ("0068", "abcd", "60", "42000121436587f9", "00000123", "00000321", "0009""08696e7465726e6574", /* "(8)internet" */ "0004""c0a82a17", /* same as default sgsn_sender */ "0004""c0a82a17" ); const char *gtp_req_to_ggsn = MSG_PDP_CTX_REQ("0068", "6d31", /* mapped seq ("abcd") */ "23", "42000121436587f9", "00000001", /* Data I: tunnel's TEI */ "00000001", /* Control: tunnel's TEI */ "0009""08696e7465726e6574", "0004""7f000201", /* replaced with gtphub's ggsn ctrl */ "0004""7f000202" /* replaced with gtphub's ggsn user */ ); LVL2_ASSERT(msg_from_sgsn_c(&sgsn_sender, &resolved_ggsn_addr, gtp_req_from_sgsn, gtp_req_to_ggsn)); LVL2_ASSERT(was_resolved_for("240010123456789", "internet")); LVL2_ASSERT(tunnels_are( "TEI=1:" " 192.168.42.23 (TEI C=321 U=123)" " <-> 192.168.43.34/(uninitialized) (TEI C=0 U=0)" " @21945\n")); const char *gtp_resp_from_ggsn = MSG_PDP_CTX_RSP("004e", "00000001", /* destination TEI (sent in req above) */ "6d31", /* mapped seq */ "01", /* restart */ "00000567", /* TEI U */ "00000765", /* TEI C */ "0004""c0a82b22", /* GSN addresses */ "0004""c0a82b22" /* (== resolved_ggsn_addr) */ ); const char *gtp_resp_to_sgsn = MSG_PDP_CTX_RSP("004e", "00000321", /* unmapped TEI ("001") */ "abcd", /* unmapped seq ("6d31") */ "23", "00000001", /* mapped TEI from GGSN ("567") */ "00000001", /* mapped TEI from GGSN ("765") */ "0004""7f000101", /* gtphub's address towards SGSNs (Ctrl) */ "0004""7f000102" /* gtphub's address towards SGSNs (User) */ ); /* The response should go back to whichever port the request came from * (unmapped by sequence nr) */ LVL2_ASSERT(msg_from_ggsn_c(&resolved_ggsn_addr, &sgsn_sender, gtp_resp_from_ggsn, gtp_resp_to_sgsn)); return 1; } #define MSG_DEL_PDP_CTX_REQ(tei, seq) \ "32" /* 0b001'1 0010: version 1, protocol GTP, with seq nr. */ \ "14" /* type 20: Delete PDP Context Request */ \ "0008" /* msg length = 8 + len (2 octets) */ \ tei /* TEI Ctrl */ \ seq /* Sequence nr (2 octets) */ \ "00" /* N-PDU 0 */ \ "00" /* No extensions */ \ /* IEs */ \ "13fe" /* 19: Teardown ind = 0 */ \ "1400" /* 20: NSAPI = 0*/ \ #define MSG_DEL_PDP_CTX_RSP(tei, seq) \ "32" /* 0b001'1 0010: version 1, protocol GTP, with seq nr. */ \ "15" /* type 21: Delete PDP Context Response */ \ "0006" /* msg length = 8 + len (2 octets) */ \ tei /* TEI Ctrl */ \ seq /* Sequence nr (2 octets) */ \ "00" /* N-PDU 0 */ \ "00" /* No extensions */ \ /* IEs */ \ "01" /* 1: Cause */ \ "80" /* value = 0b10000000 = response, no rejection. */ \ static int delete_pdp_ctx_from_sgsn(void) { now += GTPH_EXPIRE_QUICKLY_SECS + 1; gtphub_gc(hub, now); LVL2_ASSERT(tunnels_are( "TEI=1:" " 192.168.42.23 (TEI C=321 U=123)" " <-> 192.168.43.34 (TEI C=765 U=567)" " @21945\n")); /* TEI Ctrl from above and next sequence after abcd. */ const char *gtp_req_from_sgsn = MSG_DEL_PDP_CTX_REQ("00000001", "abce"); const char *gtp_req_to_ggsn = MSG_DEL_PDP_CTX_REQ("00000765", "6d32"); LVL2_ASSERT(msg_from_sgsn_c(&sgsn_sender, &resolved_ggsn_addr, gtp_req_from_sgsn, gtp_req_to_ggsn)); /* 21945 + 31 = 21976 */ LVL2_ASSERT(tunnels_are( "TEI=1:" " 192.168.42.23 (TEI C=321 U=123)" " <-> 192.168.43.34 (TEI C=765 U=567)" " @21976\n")); const char *gtp_resp_from_ggsn = MSG_DEL_PDP_CTX_RSP("00000001", "6d32"); const char *gtp_resp_to_sgsn = MSG_DEL_PDP_CTX_RSP("00000321", "abce"); /* The response should go back to whichever port the request came from * (unmapped by sequence nr) */ LVL2_ASSERT(msg_from_ggsn_c(&resolved_ggsn_addr, &sgsn_sender, gtp_resp_from_ggsn, gtp_resp_to_sgsn)); LVL2_ASSERT(tunnels_are("")); return 1; } static int delete_pdp_ctx_from_ggsn(void) { now += GTPH_EXPIRE_QUICKLY_SECS + 1; gtphub_gc(hub, now); LVL2_ASSERT(tunnels_are( "TEI=1:" " 192.168.42.23 (TEI C=321 U=123)" " <-> 192.168.43.34 (TEI C=765 U=567)" " @21945\n")); /* TEI Ctrl from above and next sequence after abcd. */ const char *gtp_req_from_ggsn = MSG_DEL_PDP_CTX_REQ("00000001", "5432"); const char *gtp_req_to_sgsn = MSG_DEL_PDP_CTX_REQ("00000321", "6d31"); LVL2_ASSERT(msg_from_ggsn_c(&ggsn_sender, &resolved_sgsn_addr, gtp_req_from_ggsn, gtp_req_to_sgsn)); /* 21945 + 31 = 21976 */ LVL2_ASSERT(tunnels_are( "TEI=1:" " 192.168.42.23 (TEI C=321 U=123)" " <-> 192.168.43.34 (TEI C=765 U=567)" " @21976\n")); const char *gtp_resp_from_sgsn = MSG_DEL_PDP_CTX_RSP("00000001", "6d31"); const char *gtp_resp_to_ggsn = MSG_DEL_PDP_CTX_RSP("00000765", "5432"); /* The response should go back to whichever port the request came from * (unmapped by sequence nr) */ LVL2_ASSERT(msg_from_sgsn_c(&resolved_sgsn_addr, &ggsn_sender, gtp_resp_from_sgsn, gtp_resp_to_ggsn)); LVL2_ASSERT(tunnels_are("")); return 1; } static void test_one_pdp_ctx(int del_from_side) { if (del_from_side == GTPH_SIDE_SGSN) LOG("test_one_pdp_ctx (del from SGSN)") else LOG("test_one_pdp_ctx (del from GGSN)"); OSMO_ASSERT(setup_test_hub()); OSMO_ASSERT(create_pdp_ctx()); struct gtphub_peer_port *ggsn_port = gtphub_port_find_sa(&hub->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL], &resolved_ggsn_addr); OSMO_ASSERT(ggsn_port); struct gtphub_peer *ggsn = ggsn_port->peer_addr->peer; /* now == 345; now + 30 == 375. * seq mapping from above: * 0xabcd == 43981 (sent in the packet) * 0x6d31 == 27953 (harcoded seq mapping start val) */ OSMO_ASSERT(nr_map_is(&ggsn->seq_map, "(43981->27953@375), ")); /* now == 345; now + (6 * 60 * 60) == 21600 + 345 == 21945. * 0x00000321 == 801 (TEI from SGSN Ctrl) * 0x00000123 == 291 (TEI from SGSN User) * 0x00000765 == 1893 (TEI from GGSN Ctrl) * 0x00000567 == 1383 (TEI from GGSN User) * Mapped TEIs should be 1 and 2. */ OSMO_ASSERT(tunnels_are( "TEI=1:" " 192.168.42.23 (TEI C=321 U=123)" " <-> 192.168.43.34 (TEI C=765 U=567)" " @21945\n")); if (del_from_side == GTPH_SIDE_SGSN) { OSMO_ASSERT(delete_pdp_ctx_from_sgsn()); } else { OSMO_ASSERT(delete_pdp_ctx_from_ggsn()); } OSMO_ASSERT(tunnels_are("")); OSMO_ASSERT(clear_test_hub()); } static void test_user_data(void) { LOG("test_user_data"); OSMO_ASSERT(setup_test_hub()); OSMO_ASSERT(create_pdp_ctx()); /* now == 345; now + (6 * 60 * 60) == 21600 + 345 == 21945. */ OSMO_ASSERT(tunnels_are( "TEI=1:" " 192.168.42.23 (TEI C=321 U=123)" " <-> 192.168.43.34 (TEI C=765 U=567)" " @21945\n")); LOG("- user data starts"); /* Now expect default port numbers for User plane. */ resolve_to_ggsn("192.168.43.34", 2152); resolve_to_sgsn("192.168.42.23", 2152); /* 10 minutes later */ now += 600; const char *u_from_ggsn = "32" /* 0b001'1 0010: version 1, protocol GTP, with seq nr */ "ff" /* type 255: G-PDU */ "0058" /* length: 88 + 8 octets == 96 */ "00000001" /* mapped TEI for SGSN from create_pdp_ctx() */ "0070" /* seq */ "0000" /* No extensions */ /* User data (ICMP packet), 96 - 12 = 84 octets */ "45000054daee40004001f7890a172a010a172a02080060d23f590071e3f8" "4156000000007241010000000000101112131415161718191a1b1c1d1e1f" "202122232425262728292a2b2c2d2e2f3031323334353637" ; const char *u_to_sgsn = "32" /* 0b001'1 0010: version 1, protocol GTP, with seq nr */ "ff" /* type 255: G-PDU */ "0058" /* length: 88 + 8 octets == 96 */ "00000123" /* unmapped User TEI */ "6d31" /* new mapped seq */ "0000" "45000054daee40004001f7890a172a010a172a02080060d23f590071e3f8" "4156000000007241010000000000101112131415161718191a1b1c1d1e1f" "202122232425262728292a2b2c2d2e2f3031323334353637" ; /* This depends on create_pdp_ctx() sending resolved_sgsn_addr as GSN * Address IEs in the GGSN's Create PDP Ctx Response. */ OSMO_ASSERT(msg_from_ggsn_u(&ggsn_sender, &resolved_sgsn_addr, u_from_ggsn, u_to_sgsn)); /* Make sure the user plane messages have refreshed the TEI mapping * timeouts: 21945 + 600 == 22545. */ OSMO_ASSERT(tunnels_are( "TEI=1:" " 192.168.42.23 (TEI C=321 U=123)" " <-> 192.168.43.34 (TEI C=765 U=567)" " @22545\n")); const char *u_from_sgsn = "32" /* 0b001'1 0010: version 1, protocol GTP, with seq nr */ "ff" /* type 255: G-PDU */ "0058" /* length: 88 + 8 octets == 96 */ "00000001" /* mapped User TEI for GGSN from create_pdp_ctx() */ "1234" /* unknown seq */ "0000" /* No extensions */ /* User data (ICMP packet), 96 - 12 = 84 octets */ "45000054daee40004001f7890a172a010a172a02080060d23f590071e3f8" "4156000000007241010000000000101112131415161718191a1b1c1d1e1f" "202122232425262728292a2b2c2d2e2f3031323334353637" ; const char *u_to_ggsn = "32" /* 0b001'1 0010: version 1, protocol GTP, with seq nr */ "ff" /* type 255: G-PDU */ "0058" /* length: 88 + 8 octets == 96 */ "00000567" /* unmapped User TEI */ "6d31" /* unmapped seq */ "0000" "45000054daee40004001f7890a172a010a172a02080060d23f590071e3f8" "4156000000007241010000000000101112131415161718191a1b1c1d1e1f" "202122232425262728292a2b2c2d2e2f3031323334353637" ; OSMO_ASSERT(msg_from_sgsn_u(&sgsn_sender, &resolved_ggsn_addr, u_from_sgsn, u_to_ggsn)); /* Make sure the user plane messages have refreshed the TEI mapping * timeouts: 21945 + 600 == 22545. Both timeouts refreshed: */ OSMO_ASSERT(tunnels_are( "TEI=1:" " 192.168.42.23 (TEI C=321 U=123)" " <-> 192.168.43.34 (TEI C=765 U=567)" " @22545\n")); OSMO_ASSERT(clear_test_hub()); } static void test_reused_tei(void) { LOG("test_reused_tei"); OSMO_ASSERT(setup_test_hub()); OSMO_ASSERT(create_pdp_ctx()); const char *gtp_req_from_sgsn = MSG_PDP_CTX_REQ("0068", "abce", /* Next seq */ "60", "42000121436587f9", "00000123", /* Same TEIs as before */ "00000321", "0009""08696e7465726e6574", /* "(8)internet" */ "0004""c0a82a17", /* same as default sgsn_sender */ "0004""c0a82a17" ); const char *gtp_req_to_ggsn = MSG_PDP_CTX_REQ("0068", "6d32", /* mapped seq ("abce") */ "23", "42000121436587f9", "00000002", /* mapped TEI Data I ("123") */ "00000002", /* mapped TEI Control ("321") */ "0009""08696e7465726e6574", "0004""7f000201", /* replaced with gtphub's ggsn ctrl */ "0004""7f000202" /* replaced with gtphub's ggsn user */ ); OSMO_ASSERT(msg_from_sgsn_c(&sgsn_sender, &resolved_ggsn_addr, gtp_req_from_sgsn, gtp_req_to_ggsn)); OSMO_ASSERT(was_resolved_for("240010123456789", "internet")); OSMO_ASSERT(tunnels_are( "TEI=2:" " 192.168.42.23 (TEI C=321 U=123)" " <-> 192.168.43.34/(uninitialized) (TEI C=0 U=0)" " @21945\n")); const char *gtp_resp_from_ggsn = MSG_PDP_CTX_RSP("004e", "00000002", /* destination TEI (sent in req above) */ "6d32", /* mapped seq */ "01", /* restart */ "00000567", /* TEI U */ "00000765", /* TEI C */ "0004""c0a82b22", /* GSN addresses */ "0004""c0a82b22" /* (== resolved_ggsn_addr) */ ); const char *gtp_resp_to_sgsn = MSG_PDP_CTX_RSP("004e", "00000321", /* unmapped TEI ("001") */ "abce", /* unmapped seq ("6d32") */ "23", "00000002", /* mapped TEI from GGSN ("567") */ "00000002", /* mapped TEI from GGSN ("765") */ "0004""7f000101", /* gtphub's address towards SGSNs (Ctrl) */ "0004""7f000102" /* gtphub's address towards SGSNs (User) */ ); /* The response should go back to whichever port the request came from * (unmapped by sequence nr) */ OSMO_ASSERT(msg_from_ggsn_c(&resolved_ggsn_addr, &sgsn_sender, gtp_resp_from_ggsn, gtp_resp_to_sgsn)); OSMO_ASSERT(clear_test_hub()); } static void test_peer_restarted(void) { LOG("test_peer_restarted"); OSMO_ASSERT(setup_test_hub()); OSMO_ASSERT(create_pdp_ctx()); now += 10; const char *gtp_req_from_sgsn = MSG_PDP_CTX_REQ("0068", "1234", /* brand new seq */ "61", /* DIFFERING restart counter */ "42000121436587f9", "00000abc", "00000cba", "0009""08696e7465726e6574", /* "(8)internet" */ "0004""c0a82a17", /* same as default sgsn_sender */ "0004""c0a82a17" ); const char *gtp_req_to_ggsn = MSG_PDP_CTX_REQ("0068", "6d33", /* mapped seq ("1234") */ "23", "42000121436587f9", "00000002", /* mapped TEI Data I ("123") */ "00000002", /* mapped TEI Control ("321") */ "0009""08696e7465726e6574", "0004""7f000201", /* replaced with gtphub's ggsn ctrl */ "0004""7f000202" /* replaced with gtphub's ggsn user */ ); OSMO_ASSERT(msg_from_sgsn_c(&sgsn_sender, &resolved_ggsn_addr, gtp_req_from_sgsn, gtp_req_to_ggsn)); OSMO_ASSERT(was_resolved_for("240010123456789", "internet")); OSMO_ASSERT(tunnels_are( "TEI=2:" " 192.168.42.23 (TEI C=cba U=abc)" " <-> 192.168.43.34/(uninitialized) (TEI C=0 U=0)" " @21955\n" "TEI=1:" " (uninitialized) (TEI C=321 U=123)" " <-> 192.168.43.34 (TEI C=765 U=567)" " @21945\n" )); const char *gtp_resp_from_ggsn = MSG_PDP_CTX_RSP("004e", "00000002", /* destination TEI (sent in req above) */ "6d33", /* mapped seq */ "01", /* restart */ "00000def", /* TEI U */ "00000fde", /* TEI C */ "0004""c0a82b22", /* GSN addresses */ "0004""c0a82b22" /* (== resolved_ggsn_addr) */ ); const char *gtp_resp_to_sgsn = MSG_PDP_CTX_RSP("004e", "00000cba", /* unmapped TEI ("005") */ "1234", /* unmapped seq ("6d32") */ "23", "00000002", /* mapped TEI from GGSN ("567") */ "00000002", /* mapped TEI from GGSN ("765") */ "0004""7f000101", /* gtphub's address towards SGSNs (Ctrl) */ "0004""7f000102" /* gtphub's address towards SGSNs (User) */ ); /* The response should go back to whichever port the request came from * (unmapped by sequence nr) */ OSMO_ASSERT(msg_from_ggsn_c(&resolved_ggsn_addr, &sgsn_sender, gtp_resp_from_ggsn, gtp_resp_to_sgsn)); OSMO_ASSERT(clear_test_hub()); } static void test_peer_restarted_reusing_tei(void) { LOG("test_peer_restarted_reusing_tei"); OSMO_ASSERT(setup_test_hub()); OSMO_ASSERT(create_pdp_ctx()); now += 10; const char *gtp_req_from_sgsn = MSG_PDP_CTX_REQ("0068", "1234", /* brand new seq */ "61", /* DIFFERING restart counter */ "42000121436587f9", "00000123", /* SAME TEI */ "00000321", "0009""08696e7465726e6574", /* "(8)internet" */ "0004""c0a82a17", /* same as default sgsn_sender */ "0004""c0a82a17" ); const char *gtp_req_to_ggsn = MSG_PDP_CTX_REQ("0068", "6d33", /* seq 6d31 + 2, after "out-of-band" Delete PDP Ctx due to differing restart counter. */ "23", "42000121436587f9", "00000002", /* mapped TEI Data I ("123") */ "00000002", /* mapped TEI Control ("321") */ "0009""08696e7465726e6574", "0004""7f000201", /* replaced with gtphub's ggsn ctrl */ "0004""7f000202" /* replaced with gtphub's ggsn user */ ); OSMO_ASSERT(msg_from_sgsn_c(&sgsn_sender, &resolved_ggsn_addr, gtp_req_from_sgsn, gtp_req_to_ggsn)); OSMO_ASSERT(was_resolved_for("240010123456789", "internet")); OSMO_ASSERT(tunnels_are( "TEI=2:" /* being established after restart */ " 192.168.42.23 (TEI C=321 U=123)" " <-> 192.168.43.34/(uninitialized) (TEI C=0 U=0)" " @21955\n" "TEI=1:" /* invalidated due to restart */ " (uninitialized) (TEI C=321 U=123)" " <-> 192.168.43.34 (TEI C=765 U=567)" " @21945\n" )); /* An "out-of-band" delete request should have been sent to the GGSN * (checked by expected log output in gtphub_test.ok), and the GGSN * will (usually) send a Delete Response like this: */ const char *gtp_del_resp_from_ggsn = MSG_DEL_PDP_CTX_RSP("00000001", "6d32"); /* For this response (due to peer restart) we expect no forwarded * message. */ OSMO_ASSERT(msg_from_ggsn_c(&resolved_ggsn_addr, &sgsn_sender, gtp_del_resp_from_ggsn, "")); OSMO_ASSERT(tunnels_are( "TEI=2:" /* still being established after restart */ " 192.168.42.23 (TEI C=321 U=123)" " <-> 192.168.43.34/(uninitialized) (TEI C=0 U=0)" " @21955\n" )); const char *gtp_resp_from_ggsn = MSG_PDP_CTX_RSP("004e", "00000002", /* destination TEI (sent in req above) */ "6d33", /* mapped seq */ "01", /* restart */ "00000def", /* TEI U */ "00000fde", /* TEI C */ "0004""c0a82b22", /* GSN addresses */ "0004""c0a82b22" /* (== resolved_ggsn_addr) */ ); const char *gtp_resp_to_sgsn = MSG_PDP_CTX_RSP("004e", "00000321", /* unmapped TEI ("005") */ "1234", /* unmapped seq ("6d33") */ "23", "00000002", /* mapped TEI from GGSN ("567") */ "00000002", /* mapped TEI from GGSN ("765") */ "0004""7f000101", /* gtphub's address towards SGSNs (Ctrl) */ "0004""7f000102" /* gtphub's address towards SGSNs (User) */ ); /* The response should go back to whichever port the request came from * (unmapped by sequence nr) */ OSMO_ASSERT(msg_from_ggsn_c(&resolved_ggsn_addr, &sgsn_sender, gtp_resp_from_ggsn, gtp_resp_to_sgsn)); OSMO_ASSERT(tunnels_are( "TEI=2:" /* still being established after restart */ " 192.168.42.23 (TEI C=321 U=123)" " <-> 192.168.43.34 (TEI C=fde U=def)" " @21955\n" )); OSMO_ASSERT(clear_test_hub()); } static void test_sgsn_behind_nat(void) { LOG("test_user_data"); OSMO_ASSERT(setup_test_hub()); hub->sgsn_use_sender = 1; /* <-- Main difference to test_user_data() */ resolve_to_sgsn("192.168.42.23", 423); /* Same as sender */ OSMO_ASSERT(create_pdp_ctx()); /* now == 345; now + (6 * 60 * 60) == 21600 + 345 == 21945. */ OSMO_ASSERT(tunnels_are( "TEI=1:" " 192.168.42.23 (TEI C=321 U=123)" " <-> 192.168.43.34 (TEI C=765 U=567)" " @21945\n")); LOG("- user data starts"); /* Now expect default port numbers for User plane -- except SGSN. */ resolve_to_ggsn("192.168.43.34", 2152); /* 10 minutes later */ now += 600; const char *u_from_ggsn = "32" /* 0b001'1 0010: version 1, protocol GTP, with seq nr */ "ff" /* type 255: G-PDU */ "0058" /* length: 88 + 8 octets == 96 */ "00000001" /* mapped User TEI for SGSN from create_pdp_ctx() */ "0070" /* seq */ "0000" /* No extensions */ /* User data (ICMP packet), 96 - 12 = 84 octets */ "45000054daee40004001f7890a172a010a172a02080060d23f590071e3f8" "4156000000007241010000000000101112131415161718191a1b1c1d1e1f" "202122232425262728292a2b2c2d2e2f3031323334353637" ; const char *u_to_sgsn = "32" /* 0b001'1 0010: version 1, protocol GTP, with seq nr */ "ff" /* type 255: G-PDU */ "0058" /* length: 88 + 8 octets == 96 */ "00000123" /* unmapped User TEI */ "6d31" /* new mapped seq */ "0000" "45000054daee40004001f7890a172a010a172a02080060d23f590071e3f8" "4156000000007241010000000000101112131415161718191a1b1c1d1e1f" "202122232425262728292a2b2c2d2e2f3031323334353637" ; /* This depends on create_pdp_ctx() sending resolved_sgsn_addr as GSN * Address IEs in the GGSN's Create PDP Ctx Response. */ OSMO_ASSERT(msg_from_ggsn_u(&ggsn_sender, &resolved_sgsn_addr, u_from_ggsn, u_to_sgsn)); /* Make sure the user plane messages have refreshed the TEI mapping * timeouts: 21945 + 600 == 22545. */ OSMO_ASSERT(tunnels_are( "TEI=1:" " 192.168.42.23 (TEI C=321 U=123)" " <-> 192.168.43.34 (TEI C=765 U=567)" " @22545\n")); const char *u_from_sgsn = "32" /* 0b001'1 0010: version 1, protocol GTP, with seq nr */ "ff" /* type 255: G-PDU */ "0058" /* length: 88 + 8 octets == 96 */ "00000001" /* mapped User TEI for GGSN from create_pdp_ctx() */ "1234" /* unknown seq */ "0000" /* No extensions */ /* User data (ICMP packet), 96 - 12 = 84 octets */ "45000054daee40004001f7890a172a010a172a02080060d23f590071e3f8" "4156000000007241010000000000101112131415161718191a1b1c1d1e1f" "202122232425262728292a2b2c2d2e2f3031323334353637" ; const char *u_to_ggsn = "32" /* 0b001'1 0010: version 1, protocol GTP, with seq nr */ "ff" /* type 255: G-PDU */ "0058" /* length: 88 + 8 octets == 96 */ "00000567" /* unmapped User TEI */ "6d31" /* unmapped seq */ "0000" "45000054daee40004001f7890a172a010a172a02080060d23f590071e3f8" "4156000000007241010000000000101112131415161718191a1b1c1d1e1f" "202122232425262728292a2b2c2d2e2f3031323334353637" ; OSMO_ASSERT(msg_from_sgsn_u(&sgsn_sender, &resolved_ggsn_addr, u_from_sgsn, u_to_ggsn)); /* Make sure the user plane messages have refreshed the TEI mapping * timeouts: 21945 + 600 == 22545. Both timeouts refreshed: */ OSMO_ASSERT(tunnels_are( "TEI=1:" " 192.168.42.23 (TEI C=321 U=123)" " <-> 192.168.43.34 (TEI C=765 U=567)" " @22545\n")); OSMO_ASSERT(clear_test_hub()); } void test_parallel_context_creation(void) { LOG("test_parallel_context_creation"); OSMO_ASSERT(setup_test_hub()); const char *gtp_req_from_sgsn1 = MSG_PDP_CTX_REQ("0068", "abcd", "60", "42000121436587f9", "00000123", "00000321", "0009""08696e7465726e6574", /* "(8)internet" */ "0004""c0a82a17", /* same as default sgsn_sender */ "0004""c0a82a17" ); const char *gtp_req_to_ggsn1 = MSG_PDP_CTX_REQ("0068", "6d31", /* mapped seq ("abcd") */ "23", "42000121436587f9", "00000001", /* mapped TEI Data I ("123") */ "00000001", /* mapped TEI Control ("321") */ "0009""08696e7465726e6574", "0004""7f000201", /* replaced with gtphub's ggsn ctrl */ "0004""7f000202" /* replaced with gtphub's ggsn user */ ); OSMO_ASSERT(msg_from_sgsn_c(&sgsn_sender, &resolved_ggsn_addr, gtp_req_from_sgsn1, gtp_req_to_ggsn1)); OSMO_ASSERT(tunnels_are( "TEI=1:" " 192.168.42.23 (TEI C=321 U=123)" " <-> 192.168.43.34/(uninitialized) (TEI C=0 U=0)" " @21945\n")); now ++; const char *gtp_req_from_sgsn2 = MSG_PDP_CTX_REQ("0068", "abce", "60", "42000121436588f9", "00000124", "00000322", "0009""08696e7465726e6574", /* "(8)internet" */ "0004""c0a82a17", /* same as default sgsn_sender */ "0004""c0a82a17" ); const char *gtp_req_to_ggsn2 = MSG_PDP_CTX_REQ("0068", "6d32", /* mapped seq ("abce") */ "23", "42000121436588f9", "00000002", /* mapped TEI Data I ("124") */ "00000002", /* mapped TEI Control ("322") */ "0009""08696e7465726e6574", "0004""7f000201", /* replaced with gtphub's ggsn ctrl */ "0004""7f000202" /* replaced with gtphub's ggsn user */ ); OSMO_ASSERT(msg_from_sgsn_c(&sgsn_sender, &resolved_ggsn_addr, gtp_req_from_sgsn2, gtp_req_to_ggsn2)); OSMO_ASSERT(tunnels_are( "TEI=2:" " 192.168.42.23 (TEI C=322 U=124)" " <-> 192.168.43.34/(uninitialized) (TEI C=0 U=0)" " @21946\n" "TEI=1:" " 192.168.42.23 (TEI C=321 U=123)" " <-> 192.168.43.34/(uninitialized) (TEI C=0 U=0)" " @21945\n" )); now ++; const char *gtp_resp_from_ggsn1 = MSG_PDP_CTX_RSP("004e", "00000001", /* destination TEI (sent in req above) */ "6d31", /* mapped seq */ "01", /* restart */ "00000567", /* TEI U */ "00000765", /* TEI C */ "0004""c0a82b22", /* GSN addresses */ "0004""c0a82b22" /* (== resolved_ggsn_addr) */ ); const char *gtp_resp_to_sgsn1 = MSG_PDP_CTX_RSP("004e", "00000321", /* unmapped TEI ("001") */ "abcd", /* unmapped seq ("6d31") */ "23", "00000001", /* mapped TEI from GGSN ("567") */ "00000001", /* mapped TEI from GGSN ("765") */ "0004""7f000101", /* gtphub's address towards SGSNs (Ctrl) */ "0004""7f000102" /* gtphub's address towards SGSNs (User) */ ); OSMO_ASSERT(msg_from_ggsn_c(&resolved_ggsn_addr, &sgsn_sender, gtp_resp_from_ggsn1, gtp_resp_to_sgsn1)); OSMO_ASSERT(tunnels_are( "TEI=2:" " 192.168.42.23 (TEI C=322 U=124)" " <-> 192.168.43.34/(uninitialized) (TEI C=0 U=0)" " @21946\n" "TEI=1:" " 192.168.42.23 (TEI C=321 U=123)" " <-> 192.168.43.34 (TEI C=765 U=567)" " @21947\n" )); now ++; const char *gtp_resp_from_ggsn2 = MSG_PDP_CTX_RSP("004e", "00000002", /* destination TEI (sent in req above) */ "6d32", /* mapped seq */ "01", /* restart */ "00000568", /* TEI U */ "00000766", /* TEI C */ "0004""c0a82b22", /* GSN addresses */ "0004""c0a82b22" /* (== resolved_ggsn_addr) */ ); const char *gtp_resp_to_sgsn2 = MSG_PDP_CTX_RSP("004e", "00000322", /* unmapped TEI ("001") */ "abce", /* unmapped seq ("6d31") */ "23", "00000002", /* mapped TEI from GGSN ("567") */ "00000002", /* mapped TEI from GGSN ("765") */ "0004""7f000101", /* gtphub's address towards SGSNs (Ctrl) */ "0004""7f000102" /* gtphub's address towards SGSNs (User) */ ); OSMO_ASSERT(msg_from_ggsn_c(&resolved_ggsn_addr, &sgsn_sender, gtp_resp_from_ggsn2, gtp_resp_to_sgsn2)); OSMO_ASSERT(tunnels_are( "TEI=2:" " 192.168.42.23 (TEI C=322 U=124)" " <-> 192.168.43.34 (TEI C=766 U=568)" " @21948\n" "TEI=1:" " 192.168.42.23 (TEI C=321 U=123)" " <-> 192.168.43.34 (TEI C=765 U=567)" " @21947\n" )); OSMO_ASSERT(clear_test_hub()); } static struct log_info_cat gtphub_categories[] = { [DGTPHUB] = { .name = "DGTPHUB", .description = "GTP Hub", .color = "\033[1;33m", .enabled = 1, .loglevel = LOGL_DEBUG, }, }; static struct log_info info = { .cat = gtphub_categories, .num_cat = ARRAY_SIZE(gtphub_categories), }; int main(int argc, char **argv) { osmo_gtphub_ctx = talloc_named_const(NULL, 0, "osmo_gtphub"); void *log_ctx = talloc_named_const(osmo_gtphub_ctx, 0, "log"); osmo_init_logging2(log_ctx, &info); test_nr_map_basic(); test_nr_map_wrap(); test_expiry(); test_echo(); test_one_pdp_ctx(GTPH_SIDE_SGSN); test_one_pdp_ctx(GTPH_SIDE_GGSN); test_user_data(); test_reused_tei(); test_peer_restarted(); test_peer_restarted_reusing_tei(); test_sgsn_behind_nat(); test_parallel_context_creation(); printf("Done\n"); talloc_report_full(osmo_gtphub_ctx, stderr); talloc_free(log_ctx); OSMO_ASSERT(talloc_total_blocks(osmo_gtphub_ctx) == 1); talloc_free(osmo_gtphub_ctx); return 0; } osmo-sgsn-1.3.0/tests/gtphub/gtphub_test.ok000066400000000000000000000036041327264017000207630ustar00rootroot00000000000000test_echo test_one_pdp_ctx (del from SGSN) - __wrap_gtphub_resolve_ggsn_addr(): returning GGSN addr from imsi 240010123456789 ni internet: 192.168.43.34 port 2123 test_one_pdp_ctx (del from GGSN) - __wrap_gtphub_resolve_ggsn_addr(): returning GGSN addr from imsi 240010123456789 ni internet: 192.168.43.34 port 2123 test_user_data - __wrap_gtphub_resolve_ggsn_addr(): returning GGSN addr from imsi 240010123456789 ni internet: 192.168.43.34 port 2123 - user data starts test_reused_tei - __wrap_gtphub_resolve_ggsn_addr(): returning GGSN addr from imsi 240010123456789 ni internet: 192.168.43.34 port 2123 - __wrap_gtphub_resolve_ggsn_addr(): returning GGSN addr from imsi 240010123456789 ni internet: 192.168.43.34 port 2123 test_peer_restarted - __wrap_gtphub_resolve_ggsn_addr(): returning GGSN addr from imsi 240010123456789 ni internet: 192.168.43.34 port 2123 Out-of-band gtphub_write(16): to 192.168.43.34 port 2123 32 14 00 08 00 00 07 65 6d 32 00 00 13 ff 14 00 - __wrap_gtphub_resolve_ggsn_addr(): returning GGSN addr from imsi 240010123456789 ni internet: 192.168.43.34 port 2123 test_peer_restarted_reusing_tei - __wrap_gtphub_resolve_ggsn_addr(): returning GGSN addr from imsi 240010123456789 ni internet: 192.168.43.34 port 2123 Out-of-band gtphub_write(16): to 192.168.43.34 port 2123 32 14 00 08 00 00 07 65 6d 32 00 00 13 ff 14 00 - __wrap_gtphub_resolve_ggsn_addr(): returning GGSN addr from imsi 240010123456789 ni internet: 192.168.43.34 port 2123 test_user_data - __wrap_gtphub_resolve_ggsn_addr(): returning GGSN addr from imsi 240010123456789 ni internet: 192.168.43.34 port 2123 - user data starts test_parallel_context_creation - __wrap_gtphub_resolve_ggsn_addr(): returning GGSN addr from imsi 240010123456789 ni internet: 192.168.43.34 port 2123 - __wrap_gtphub_resolve_ggsn_addr(): returning GGSN addr from imsi 240010123456889 ni internet: 192.168.43.34 port 2123 Done osmo-sgsn-1.3.0/tests/oap/000077500000000000000000000000001327264017000153635ustar00rootroot00000000000000osmo-sgsn-1.3.0/tests/oap/Makefile.am000066400000000000000000000007661327264017000174300ustar00rootroot00000000000000AM_CPPFLAGS = \ $(all_includes) \ -I$(top_srcdir)/include \ $(NULL) AM_CFLAGS = \ -Wall \ -ggdb3 \ $(LIBOSMOCORE_CFLAGS) \ $(LIBOSMOGSM_CFLAGS) \ $(NULL) EXTRA_DIST = \ oap_client_test.ok \ oap_client_test.err \ $(NULL) noinst_PROGRAMS = \ oap_client_test \ $(NULL) oap_client_test_SOURCES = \ oap_client_test.c \ $(NULL) oap_client_test_LDADD = \ $(top_builddir)/src/gprs/gprs_utils.o \ $(top_builddir)/src/gprs/oap_client.o \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOGSM_LIBS) \ -lrt osmo-sgsn-1.3.0/tests/oap/oap_client_test.c000066400000000000000000000207151327264017000207100ustar00rootroot00000000000000/* Test Osmocom Authentication Protocol */ /* * (C) 2015 by sysmocom s.f.m.c. GmbH * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 void test_oap_api(void) { printf("Testing OAP API\n"); struct oap_client_config _config; struct oap_client_config *config = &_config; struct oap_client_state _state; struct oap_client_state *state = &_state; struct osmo_oap_message oap_rx; struct msgb *msg_rx; struct osmo_oap_message oap_tx; struct msgb *msg_tx; memset(config, 0, sizeof(*config)); memset(state, 0, sizeof(*state)); OSMO_ASSERT(osmo_hexparse("0102030405060708090a0b0c0d0e0f10", config->secret_k, 16) == 16); OSMO_ASSERT(osmo_hexparse("1112131415161718191a1b1c1d1e1f20", config->secret_opc, 16) == 16); fprintf(stderr, "- make sure filling with zeros means uninitialized\n"); OSMO_ASSERT(state->state == OAP_UNINITIALIZED); fprintf(stderr, "- reject messages in uninitialized state\n"); memset(&oap_rx, 0, sizeof(oap_rx)); state->client_id = 1; oap_rx.message_type = OAP_MSGT_REGISTER_ERROR; msg_rx = oap_client_encoded(&oap_rx); OSMO_ASSERT(oap_client_handle(state, msg_rx, &msg_tx) < 0); OSMO_ASSERT(state->state == OAP_UNINITIALIZED); msgb_free(msg_rx); OSMO_ASSERT(!msg_tx); fprintf(stderr, "- NULL config should disable\n"); OSMO_ASSERT( oap_client_init(NULL, state) == 0 ); OSMO_ASSERT(state->state == OAP_DISABLED); fprintf(stderr, "- reject messages in disabled state\n"); memset(state, 0, sizeof(*state)); memset(&oap_rx, 0, sizeof(oap_rx)); state->state = OAP_DISABLED; state->client_id = 1; oap_rx.message_type = OAP_MSGT_REGISTER_ERROR; msg_rx = oap_client_encoded(&oap_rx); OSMO_ASSERT(oap_client_handle(state, msg_rx, &msg_tx) < 0); OSMO_ASSERT(state->state == OAP_DISABLED); msgb_free(msg_rx); OSMO_ASSERT(!msg_tx); fprintf(stderr, "- invalid client_id and shared secret\n"); memset(state, 0, sizeof(*state)); config->client_id = 0; config->secret_k_present = 0; config->secret_opc_present = 0; OSMO_ASSERT( oap_client_init(config, state) == 0 ); OSMO_ASSERT(state->state == OAP_DISABLED); fprintf(stderr, "- reset state\n"); memset(state, 0, sizeof(*state)); fprintf(stderr, "- only client_id is invalid\n"); config->client_id = 0; config->secret_k_present = 1; config->secret_opc_present = 1; OSMO_ASSERT( oap_client_init(config, state) == 0 ); OSMO_ASSERT(state->state == OAP_DISABLED); memset(state, 0, sizeof(*state)); fprintf(stderr, "- valid id, but omitted shared_secret (1/2)\n"); config->client_id = 12345; config->secret_k_present = 0; config->secret_opc_present = 1; OSMO_ASSERT( oap_client_init(config, state) == 0 ); OSMO_ASSERT(state->state == OAP_DISABLED); memset(state, 0, sizeof(*state)); fprintf(stderr, "- valid id, but omitted shared_secret (2/2)\n"); config->client_id = 12345; config->secret_k_present = 1; config->secret_opc_present = 0; OSMO_ASSERT( oap_client_init(config, state) == 0 ); OSMO_ASSERT(state->state == OAP_DISABLED); memset(state, 0, sizeof(*state)); fprintf(stderr, "- mint configuration\n"); config->client_id = 12345; config->secret_k_present = 1; config->secret_opc_present = 1; /*config->secret_* buffers are still set from the top */ OSMO_ASSERT( oap_client_init(config, state) == 0 ); OSMO_ASSERT(state->state == OAP_INITIALIZED); fprintf(stderr, "- Missing challenge data\n"); memset(&oap_rx, 0, sizeof(oap_rx)); oap_rx.message_type = OAP_MSGT_CHALLENGE_REQUEST; oap_rx.rand_present = 0; oap_rx.autn_present = 0; msg_rx = oap_client_encoded(&oap_rx); OSMO_ASSERT(oap_client_handle(state, msg_rx, &msg_tx) == -2); msgb_free(msg_rx); OSMO_ASSERT(!msg_tx); fprintf(stderr, "- AUTN missing\n"); osmo_hexparse("0102030405060708090a0b0c0d0e0f10", oap_rx.rand, 16); oap_rx.rand_present = 1; msg_rx = oap_client_encoded(&oap_rx); OSMO_ASSERT(oap_client_handle(state, msg_rx, &msg_tx) == -2); msgb_free(msg_rx); OSMO_ASSERT(!msg_tx); fprintf(stderr, "- RAND missing\n"); oap_rx.rand_present = 0; osmo_hexparse("cec4e3848a33000086781158ca40f136", oap_rx.autn, 16); oap_rx.autn_present = 1; msg_rx = oap_client_encoded(&oap_rx); OSMO_ASSERT(oap_client_handle(state, msg_rx, &msg_tx) == -2); msgb_free(msg_rx); OSMO_ASSERT(!msg_tx); fprintf(stderr, "- wrong autn (by one bit)\n"); osmo_hexparse("0102030405060708090a0b0c0d0e0f10", oap_rx.rand, 16); osmo_hexparse("dec4e3848a33000086781158ca40f136", oap_rx.autn, 16); oap_rx.rand_present = 1; oap_rx.autn_present = 1; msg_rx = oap_client_encoded(&oap_rx); OSMO_ASSERT(oap_client_handle(state, msg_rx, &msg_tx) == -2); msgb_free(msg_rx); OSMO_ASSERT(!msg_tx); fprintf(stderr, "- all data correct\n"); osmo_hexparse("cec4e3848a33000086781158ca40f136", oap_rx.autn, 16); msg_rx = oap_client_encoded(&oap_rx); fprintf(stderr, "- but refuse to evaluate in uninitialized state\n"); OSMO_ASSERT(state->state == OAP_INITIALIZED); state->state = OAP_UNINITIALIZED; OSMO_ASSERT(oap_client_handle(state, msg_rx, &msg_tx) < 0); OSMO_ASSERT(!msg_tx); state->state = OAP_DISABLED; OSMO_ASSERT(oap_client_handle(state, msg_rx, &msg_tx) < 0); OSMO_ASSERT(!msg_tx); state->state = OAP_INITIALIZED; fprintf(stderr, "- now everything is correct\n"); /* a successful return value here indicates correct autn */ OSMO_ASSERT(oap_client_handle(state, msg_rx, &msg_tx) == 0); msgb_free(msg_rx); fprintf(stderr, "- Expect the challenge response in msg_tx\n"); OSMO_ASSERT(msg_tx); OSMO_ASSERT(osmo_oap_decode(&oap_tx, msg_tx->data, msg_tx->len) == 0); OSMO_ASSERT(oap_tx.message_type == OAP_MSGT_CHALLENGE_RESULT); OSMO_ASSERT(strcmp("e2d05b598c61d9ba", osmo_hexdump_nospc(oap_tx.xres, sizeof(oap_tx.xres))) == 0); OSMO_ASSERT(state->state == OAP_SENT_CHALLENGE_RESULT); msgb_free(msg_tx); msg_tx = 0; struct oap_client_state saved_state = _state; fprintf(stderr, "- Receive registration error for the first time.\n"); memset(&oap_rx, 0, sizeof(oap_rx)); oap_rx.message_type = OAP_MSGT_REGISTER_ERROR; oap_rx.cause = GMM_CAUSE_PROTO_ERR_UNSPEC; msg_rx = oap_client_encoded(&oap_rx); OSMO_ASSERT(state->registration_failures == 0); OSMO_ASSERT(oap_client_handle(state, msg_rx, &msg_tx) == 0); OSMO_ASSERT(state->registration_failures == 1); OSMO_ASSERT(msg_tx); OSMO_ASSERT(osmo_oap_decode(&oap_tx, msg_tx->data, msg_tx->len) == 0); OSMO_ASSERT(oap_tx.message_type == OAP_MSGT_REGISTER_REQUEST); OSMO_ASSERT(state->state == OAP_REQUESTED_CHALLENGE); msgb_free(msg_tx); msg_tx = 0; fprintf(stderr, "- Receive registration error for the Nth time.\n"); state->registration_failures = 999; OSMO_ASSERT(oap_client_handle(state, msg_rx, &msg_tx) == -11); OSMO_ASSERT(!msg_tx); OSMO_ASSERT(state->state == OAP_INITIALIZED); msgb_free(msg_tx); msg_tx = 0; msgb_free(msg_rx); fprintf(stderr, "- Registration success\n"); _state = saved_state; memset(&oap_rx, 0, sizeof(oap_rx)); oap_rx.message_type = OAP_MSGT_REGISTER_RESULT; msg_rx = oap_client_encoded(&oap_rx); OSMO_ASSERT(oap_client_handle(state, msg_rx, &msg_tx) == 0); OSMO_ASSERT(!msg_tx); OSMO_ASSERT(state->state == OAP_REGISTERED); msgb_free(msg_rx); } static struct log_info_cat oap_client_test_categories[] = { }; static struct log_info info = { .cat = oap_client_test_categories, .num_cat = ARRAY_SIZE(oap_client_test_categories), }; int main(int argc, char **argv) { void *ctx = talloc_named_const(NULL, 0, "oap_client_test"); msgb_talloc_ctx_init(ctx, 0); osmo_init_logging2(ctx, &info); OSMO_ASSERT(osmo_stderr_target); log_set_use_color(osmo_stderr_target, 0); log_set_print_timestamp(osmo_stderr_target, 0); log_set_print_filename(osmo_stderr_target, 0); log_set_print_category(osmo_stderr_target, 1); log_parse_category_mask(osmo_stderr_target, "DLOAP,1"); test_oap_api(); printf("Done\n"); return 0; } osmo-sgsn-1.3.0/tests/oap/oap_client_test.err000066400000000000000000000026471327264017000212620ustar00rootroot00000000000000- make sure filling with zeros means uninitialized - reject messages in uninitialized state DLOAP Received OAP message 5, but the OAP client is not initialized - NULL config should disable - reject messages in disabled state DLOAP Received OAP message 5, but the OAP client is disabled - invalid client_id and shared secret - reset state - only client_id is invalid - valid id, but omitted shared_secret (1/2) DLOAP OAP: client ID set, but secret K missing. - valid id, but omitted shared_secret (2/2) DLOAP OAP: client ID set, but secret OPC missing. - mint configuration - Missing challenge data DLOAP OAP challenge incomplete (rand_present: 0, autn_present: 0) - AUTN missing DLOAP OAP challenge incomplete (rand_present: 1, autn_present: 0) - RAND missing DLOAP OAP challenge incomplete (rand_present: 0, autn_present: 1) - wrong autn (by one bit) DLOAP OAP: AUTN mismatch! DLOAP OAP: AUTN from server: dec4e3848a33000086781158ca40f136 DLOAP OAP: AUTN expected: cec4e3848a33000086781158ca40f136 - all data correct - but refuse to evaluate in uninitialized state DLOAP Received OAP message 8, but the OAP client is not initialized DLOAP Received OAP message 8, but the OAP client is disabled - now everything is correct - Expect the challenge response in msg_tx - Receive registration error for the first time. DLOAP OAP registration failed - Receive registration error for the Nth time. DLOAP OAP registration failed - Registration success osmo-sgsn-1.3.0/tests/oap/oap_client_test.ok000066400000000000000000000000251327264017000210670ustar00rootroot00000000000000Testing OAP API Done osmo-sgsn-1.3.0/tests/sgsn/000077500000000000000000000000001327264017000155565ustar00rootroot00000000000000osmo-sgsn-1.3.0/tests/sgsn/Makefile.am000066400000000000000000000036631327264017000176220ustar00rootroot00000000000000AM_CPPFLAGS = \ $(all_includes) \ -I$(top_srcdir)/include \ $(NULL) AM_CFLAGS = \ -Wall \ -ggdb3 \ $(LIBOSMOCORE_CFLAGS) \ $(LIBOSMOABIS_CFLAGS) \ $(LIBOSMOGSM_CFLAGS) \ $(LIBCARES_CFLAGS) \ $(LIBGTP_CFLAGS) \ $(NULL) if BUILD_IU AM_CFLAGS += \ $(LIBASN1C_CFLAGS) \ $(LIBOSMOSIGTRAN_CFLAGS) \ $(LIBOSMORANAP_CFLAGS) \ $(NULL) endif EXTRA_DIST = \ sgsn_test.ok \ $(NULL) noinst_PROGRAMS = \ sgsn_test \ $(NULL) sgsn_test_SOURCES = \ sgsn_test.c \ $(NULL) sgsn_test_LDFLAGS = \ -Wl,--wrap=osmo_get_rand_id \ -Wl,--wrap=sgsn_update_subscriber_data \ -Wl,--wrap=gprs_subscr_request_update_location \ -Wl,--wrap=gprs_subscr_request_auth_info \ -Wl,--wrap=gsup_client_send \ $(NULL) sgsn_test_LDADD = \ $(top_builddir)/src/gprs/gprs_llc_parse.o \ $(top_builddir)/src/gprs/gprs_llc.o \ $(top_builddir)/src/gprs/crc24.o \ $(top_builddir)/src/gprs/gprs_sndcp.o \ $(top_builddir)/src/gprs/gprs_gmm.o \ $(top_builddir)/src/gprs/gprs_sgsn.o \ $(top_builddir)/src/gprs/sgsn_vty.o \ $(top_builddir)/src/gprs/sgsn_libgtp.o \ $(top_builddir)/src/gprs/sgsn_auth.o \ $(top_builddir)/src/gprs/sgsn_ares.o \ $(top_builddir)/src/gprs/gprs_utils.o \ $(top_builddir)/src/gprs/gprs_subscriber.o \ $(top_builddir)/src/gprs/gprs_gb_parse.o \ $(top_builddir)/src/gprs/gprs_llc_xid.o \ $(top_builddir)/src/gprs/gprs_sndcp_xid.o \ $(top_builddir)/src/gprs/slhc.o \ $(top_builddir)/src/gprs/gprs_sndcp_comp.o \ $(top_builddir)/src/gprs/gprs_sndcp_pcomp.o \ $(top_builddir)/src/gprs/v42bis.o \ $(top_builddir)/src/gprs/gprs_sndcp_dcomp.o \ $(top_builddir)/src/gprs/gsup_client.o \ $(top_builddir)/src/gprs/oap_client.o \ $(LIBOSMOABIS_LIBS) \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOGSM_LIBS) \ $(LIBOSMOGB_LIBS) \ $(LIBCARES_LIBS) \ $(LIBGTP_LIBS) \ -lrt \ -lm \ $(NULL) if BUILD_IU sgsn_test_LDADD += \ $(LIBOSMORANAP_LIBS) \ $(LIBOSMOSIGTRAN_LIBS) \ $(LIBASN1C_LIBS) \ $(NULL) endif osmo-sgsn-1.3.0/tests/sgsn/sgsn_test.c000066400000000000000000002155011327264017000177370ustar00rootroot00000000000000/* Test the SGSN */ /* * (C) 2014 by Holger Hans Peter Freyther * (C) 2014 by sysmocom s.f.m.c. GmbH * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void *tall_bsc_ctx; static struct sgsn_instance sgsn_inst = { .config_file = "osmo_sgsn.cfg", .cfg = { .gtp_statedir = "./", .auth_policy = SGSN_AUTH_POLICY_CLOSED, }, }; struct sgsn_instance *sgsn = &sgsn_inst; unsigned sgsn_tx_counter = 0; struct msgb *last_msg = NULL; struct gprs_gb_parse_context last_dl_parse_ctx; static void reset_last_msg() { if (last_msg) msgb_free(last_msg); last_msg = NULL; memset(&last_dl_parse_ctx, 0, sizeof(last_dl_parse_ctx)); } static void cleanup_test() { reset_last_msg(); } static uint32_t get_new_ptmsi(const struct gprs_gb_parse_context *parse_ctx) { uint32_t new_ptmsi = GSM_RESERVED_TMSI; if (parse_ctx->new_ptmsi_enc) gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_ptmsi); return new_ptmsi; } /* override */ int bssgp_tx_dl_ud(struct msgb *msg, uint16_t pdu_lifetime, struct bssgp_dl_ud_par *dup) { int rc; reset_last_msg(); last_msg = msg; OSMO_ASSERT(msgb_data(last_msg) != NULL); rc = gprs_gb_parse_llc(msgb_data(last_msg), msgb_length(last_msg), &last_dl_parse_ctx); fprintf(stderr, "Got DL LLC message: %s\n", gprs_gb_message_name(&last_dl_parse_ctx, "UNKNOWN")); OSMO_ASSERT(rc > 0); sgsn_tx_counter += 1; return 0; } /* override, requires '-Wl,--wrap=osmo_get_rand_id' */ int __real_osmo_get_rand_id(uint8_t *data, size_t len); int mock_osmo_get_rand_id(uint8_t *data, size_t len); int (*osmo_get_rand_id_cb)(uint8_t *, size_t) = &mock_osmo_get_rand_id; int __wrap_osmo_get_rand_id(uint8_t *buf, size_t num) { return (*osmo_get_rand_id_cb)(buf, num); } /* make results of A&C ref predictable */ int mock_osmo_get_rand_id(uint8_t *buf, size_t num) { if (num > 1) return __real_osmo_get_rand_id(buf, num); buf[0] = 0; return 1; } /* override, requires '-Wl,--wrap=sgsn_update_subscriber_data' */ void __real_sgsn_update_subscriber_data(struct sgsn_mm_ctx *); void (*update_subscriber_data_cb)(struct sgsn_mm_ctx *) = &__real_sgsn_update_subscriber_data; void __wrap_sgsn_update_subscriber_data(struct sgsn_mm_ctx *mmctx) { (*update_subscriber_data_cb)(mmctx); } /* override, requires '-Wl,--wrap=gprs_subscr_request_update_location' */ int __real_gprs_subscr_request_update_location(struct sgsn_mm_ctx *mmctx); int (*subscr_request_update_location_cb)(struct sgsn_mm_ctx *mmctx) = &__real_gprs_subscr_request_update_location; int __wrap_gprs_subscr_request_update_location(struct sgsn_mm_ctx *mmctx) { return (*subscr_request_update_location_cb)(mmctx); }; /* override, requires '-Wl,--wrap=gprs_subscr_request_auth_info' */ int __real_gprs_subscr_request_auth_info(struct sgsn_mm_ctx *mmctx, const uint8_t *auts, const uint8_t *auts_rand); int (*subscr_request_auth_info_cb)(struct sgsn_mm_ctx *mmctx, const uint8_t *auts, const uint8_t *auts_rand) = &__real_gprs_subscr_request_auth_info; int __wrap_gprs_subscr_request_auth_info(struct sgsn_mm_ctx *mmctx, const uint8_t *auts, const uint8_t *auts_rand) { return (*subscr_request_auth_info_cb)(mmctx, auts, auts_rand); }; /* override, requires '-Wl,--wrap=gsup_client_send' */ int __real_gsup_client_send(struct gsup_client *gsupc, struct msgb *msg); int (*gsup_client_send_cb)(struct gsup_client *gsupc, struct msgb *msg) = &__real_gsup_client_send; int __wrap_gsup_client_send(struct gsup_client *gsupc, struct msgb *msg) { return (*gsup_client_send_cb)(gsupc, msg); }; static int count(struct llist_head *head) { struct llist_head *cur; int count = 0; llist_for_each(cur, head) count += 1; return count; } static struct msgb *create_msg(const uint8_t *data, size_t len) { struct msgb *msg = msgb_alloc(len + 8, "test message"); msg->l1h = msgb_put(msg, 8); msg->l2h = msgb_put(msg, len); memcpy(msg->l2h, data, len); msgb_bcid(msg) = msg->l1h; msgb_gmmh(msg) = msg->l2h; return msg; } /* * Create a context and search for it */ static struct sgsn_mm_ctx *alloc_mm_ctx(uint32_t tlli, struct gprs_ra_id *raid) { struct sgsn_mm_ctx *ctx, *ictx; struct gprs_llc_lle *lle; int old_count = count(gprs_llme_list()); lle = gprs_lle_get_or_create(tlli, 3); ctx = sgsn_mm_ctx_alloc_gb(tlli, raid); ctx->gmm_state = GMM_REGISTERED_NORMAL; ctx->gb.llme = lle->llme; ictx = sgsn_mm_ctx_by_tlli(tlli, raid); OSMO_ASSERT(ictx == ctx); OSMO_ASSERT(count(gprs_llme_list()) == old_count + 1); return ctx; } static void send_0408_message(struct gprs_llc_llme *llme, uint32_t tlli, const struct gprs_ra_id *bssgp_raid, const uint8_t *data, size_t data_len) { struct msgb *msg; reset_last_msg(); sgsn_tx_counter = 0; msg = create_msg(data, data_len); msgb_tlli(msg) = tlli; bssgp_create_cell_id(msgb_bcid(msg), bssgp_raid, 0); gsm0408_gprs_rcvmsg_gb(msg, llme, false); msgb_free(msg); } static void test_llme(void) { struct gprs_llc_lle *lle, *lle_copy; uint32_t local_tlli; printf("Testing LLME allocations\n"); local_tlli = gprs_tmsi2tlli(0x234, TLLI_LOCAL); /* initial state */ OSMO_ASSERT(count(gprs_llme_list()) == 0); /* Create a new entry */ lle = gprs_lle_get_or_create(local_tlli, 3); OSMO_ASSERT(lle); OSMO_ASSERT(count(gprs_llme_list()) == 1); /* No new entry is created */ lle_copy = gprs_lle_get_or_create(local_tlli, 3); OSMO_ASSERT(lle == lle_copy); OSMO_ASSERT(count(gprs_llme_list()) == 1); /* unassign which should delete it*/ gprs_llgmm_unassign(lle->llme); /* Check that everything was cleaned up */ OSMO_ASSERT(count(gprs_llme_list()) == 0); cleanup_test(); } struct gprs_subscr *last_updated_subscr = NULL; void my_dummy_sgsn_update_subscriber_data(struct sgsn_mm_ctx *mmctx) { OSMO_ASSERT(mmctx); fprintf(stderr, "Called %s, mmctx = %p, subscr = %p\n", __func__, mmctx, mmctx->subscr); last_updated_subscr = mmctx->subscr; } static void assert_subscr(const struct gprs_subscr *subscr, const char *imsi) { struct gprs_subscr *sfound; OSMO_ASSERT(subscr); OSMO_ASSERT(strcmp(subscr->imsi, imsi) == 0); sfound = gprs_subscr_get_by_imsi(imsi); OSMO_ASSERT(sfound == subscr); gprs_subscr_put(sfound); } static void show_subscrs(FILE *out) { struct gprs_subscr *subscr; llist_for_each_entry(subscr, gprs_subscribers, entry) { fprintf(out, " Subscriber: %s, " "use count: %d\n", subscr->imsi, subscr->use_count); } } static void assert_no_subscrs() { show_subscrs(stdout); fflush(stdout); OSMO_ASSERT(llist_empty(gprs_subscribers)); } #define VERBOSE_ASSERT(val, expect_op, fmt) \ do { \ printf(#val " == " fmt "\n", (val)); \ OSMO_ASSERT((val) expect_op); \ } while (0); static void test_subscriber(void) { struct gprs_subscr *s1, *s2, *s3; const char *imsi1 = "1234567890"; const char *imsi2 = "9876543210"; const char *imsi3 = "5656565656"; update_subscriber_data_cb = my_dummy_sgsn_update_subscriber_data; printf("Testing core subscriber data API\n"); /* Check for emptiness */ OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi1) == NULL); OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi2) == NULL); OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi3) == NULL); VERBOSE_ASSERT(llist_count(gprs_subscribers), == 0, "%d"); /* Allocate entry 1 */ s1 = gprs_subscr_get_or_create(imsi1); VERBOSE_ASSERT(llist_count(gprs_subscribers), == 1, "%d"); s1->flags |= GPRS_SUBSCRIBER_FIRST_CONTACT; assert_subscr(s1, imsi1); VERBOSE_ASSERT(llist_count(gprs_subscribers), == 1, "%d"); OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi2) == NULL); /* Allocate entry 2 */ s2 = gprs_subscr_get_or_create(imsi2); VERBOSE_ASSERT(llist_count(gprs_subscribers), == 2, "%d"); s2->flags |= GPRS_SUBSCRIBER_FIRST_CONTACT; /* Allocate entry 3 */ s3 = gprs_subscr_get_or_create(imsi3); VERBOSE_ASSERT(llist_count(gprs_subscribers), == 3, "%d"); /* Check entries */ assert_subscr(s1, imsi1); assert_subscr(s2, imsi2); assert_subscr(s3, imsi3); /* Update entry 1 */ last_updated_subscr = NULL; gprs_subscr_update(s1); OSMO_ASSERT(last_updated_subscr == NULL); OSMO_ASSERT(s1->sgsn_data->mm == NULL); OSMO_ASSERT((s1->flags & GPRS_SUBSCRIBER_FIRST_CONTACT) == 0); /* There is no subscriber cache. Verify it */ gprs_subscr_cleanup(s1); gprs_subscr_put(s1); s1 = NULL; VERBOSE_ASSERT(llist_count(gprs_subscribers), == 2, "%d"); OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi1) == NULL); assert_subscr(s2, imsi2); assert_subscr(s3, imsi3); /* Free entry 2 (GPRS_SUBSCRIBER_FIRST_CONTACT is set) */ gprs_subscr_cleanup(s2); gprs_subscr_put(s2); s2 = NULL; VERBOSE_ASSERT(llist_count(gprs_subscribers), == 1, "%d"); OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi1) == NULL); OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi2) == NULL); assert_subscr(s3, imsi3); /* Try to delete entry 3 */ gprs_subscr_cleanup(s3); gprs_subscr_put(s3); s3 = NULL; VERBOSE_ASSERT(llist_count(gprs_subscribers), == 0, "%d"); OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi3) == NULL); OSMO_ASSERT(llist_empty(gprs_subscribers)); update_subscriber_data_cb = __real_sgsn_update_subscriber_data; cleanup_test(); } static void test_auth_triplets(void) { struct gprs_subscr *s1, *s1found; const char *imsi1 = "1234567890"; struct gsm_auth_tuple *at; struct sgsn_mm_ctx *ctx; struct gprs_ra_id raid = { 0, }; uint32_t local_tlli = 0xffeeddcc; printf("Testing authentication triplet handling\n"); /* Check for emptiness */ OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi1) == NULL); /* Allocate entry 1 */ s1 = gprs_subscr_get_or_create(imsi1); s1->flags |= GPRS_SUBSCRIBER_FIRST_CONTACT; s1found = gprs_subscr_get_by_imsi(imsi1); OSMO_ASSERT(s1found == s1); gprs_subscr_put(s1found); /* Create a context */ OSMO_ASSERT(count(gprs_llme_list()) == 0); ctx = alloc_mm_ctx(local_tlli, &raid); /* Attach s1 to ctx */ ctx->subscr = gprs_subscr_get(s1); ctx->subscr->sgsn_data->mm = ctx; /* Try to get auth tuple */ at = sgsn_auth_get_tuple(ctx, GSM_KEY_SEQ_INVAL); OSMO_ASSERT(at == NULL); /* Add triplets */ s1->sgsn_data->auth_triplets[0].key_seq = 0; s1->sgsn_data->auth_triplets[1].key_seq = 1; s1->sgsn_data->auth_triplets[2].key_seq = 2; /* Try to get auth tuple */ at = sgsn_auth_get_tuple(ctx, GSM_KEY_SEQ_INVAL); OSMO_ASSERT(at != NULL); OSMO_ASSERT(at->key_seq == 0); OSMO_ASSERT(at->use_count == 1); at = sgsn_auth_get_tuple(ctx, at->key_seq); OSMO_ASSERT(at != NULL); OSMO_ASSERT(at->key_seq == 1); OSMO_ASSERT(at->use_count == 1); at = sgsn_auth_get_tuple(ctx, at->key_seq); OSMO_ASSERT(at != NULL); OSMO_ASSERT(at->key_seq == 2); OSMO_ASSERT(at->use_count == 1); at = sgsn_auth_get_tuple(ctx, at->key_seq); OSMO_ASSERT(at == NULL); /* Free MM context and subscriber */ gprs_subscr_put(s1); sgsn_mm_ctx_cleanup_free(ctx); s1found = gprs_subscr_get_by_imsi(imsi1); OSMO_ASSERT(s1found == NULL); cleanup_test(); } #define TEST_GSUP_IMSI1_IE 0x01, 0x05, 0x21, 0x43, 0x65, 0x87, 0x09 static int rx_gsup_message(const uint8_t *data, size_t data_len) { struct msgb *msg; int rc; msg = msgb_alloc(1024, __func__); msg->l2h = msgb_put(msg, data_len); OSMO_ASSERT(msg->l2h != NULL); memcpy(msg->l2h, data, data_len); rc = gprs_subscr_rx_gsup_message(msg); msgb_free(msg); return rc; } static void test_subscriber_gsup(void) { struct gprs_subscr *s1, *s1found; const char *imsi1 = "1234567890"; struct sgsn_mm_ctx *ctx; struct gprs_ra_id raid = { 0, }; uint32_t local_tlli = 0xffeeddcc; struct sgsn_subscriber_pdp_data *pdpd; int rc; static const uint8_t send_auth_info_res[] = { 0x0a, TEST_GSUP_IMSI1_IE, 0x03, 0x22, /* Auth tuple */ 0x20, 0x10, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x21, 0x04, 0x21, 0x22, 0x23, 0x24, 0x22, 0x08, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x03, 0x22, /* Auth tuple */ 0x20, 0x10, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x21, 0x04, 0xa1, 0xa2, 0xa3, 0xa4, 0x22, 0x08, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, }; static const uint8_t send_auth_info_err[] = { 0x09, TEST_GSUP_IMSI1_IE, 0x02, 0x01, 0x07 /* GPRS not allowed */ }; #define MSISDN 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 static const uint8_t s1_msisdn[] = { MSISDN }; static const uint8_t update_location_res[] = { 0x06, TEST_GSUP_IMSI1_IE, 0x08, 0x09, MSISDN, 0x04, 0x00, /* PDP info complete */ 0x05, 0x12, 0x10, 0x01, 0x01, 0x11, 0x02, 0xf1, 0x21, /* IPv4 */ 0x12, 0x09, 0x04, 't', 'e', 's', 't', 0x03, 'a', 'p', 'n', 0x05, 0x11, 0x10, 0x01, 0x02, 0x11, 0x02, 0xf1, 0x21, /* IPv4 */ 0x12, 0x08, 0x03, 'f', 'o', 'o', 0x03, 'a', 'p', 'n', }; #undef MSISDN static const uint8_t update_location_err[] = { 0x05, TEST_GSUP_IMSI1_IE, 0x02, 0x01, 0x07 /* GPRS not allowed */ }; static const uint8_t location_cancellation_req[] = { 0x1c, TEST_GSUP_IMSI1_IE, 0x06, 0x01, 0x00, }; static const uint8_t location_cancellation_req_withdraw[] = { 0x1c, TEST_GSUP_IMSI1_IE, 0x06, 0x01, 0x01, }; static const uint8_t location_cancellation_req_other[] = { 0x1c, 0x01, 0x05, 0x11, 0x11, 0x11, 0x11, 0x01, 0x06, 0x01, 0x00, }; static const uint8_t purge_ms_err[] = { 0x0d, TEST_GSUP_IMSI1_IE, 0x02, 0x01, 0x02, /* IMSI unknown in HLR */ }; static const uint8_t purge_ms_err_no_cause[] = { 0x0d, TEST_GSUP_IMSI1_IE, }; static const uint8_t purge_ms_res[] = { 0x0e, TEST_GSUP_IMSI1_IE, 0x07, 0x00, }; static const uint8_t insert_data_req[] = { 0x10, TEST_GSUP_IMSI1_IE, 0x05, 0x11, 0x10, 0x01, 0x03, 0x11, 0x02, 0xf1, 0x21, /* IPv4 */ 0x12, 0x08, 0x03, 'b', 'a', 'r', 0x03, 'a', 'p', 'n', }; static const uint8_t delete_data_req[] = { 0x14, TEST_GSUP_IMSI1_IE, 0x10, 0x01, 0x03, }; printf("Testing subscriber GSUP handling\n"); update_subscriber_data_cb = my_dummy_sgsn_update_subscriber_data; /* Check for emptiness */ OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi1) == NULL); /* Allocate entry 1 */ s1 = gprs_subscr_get_or_create(imsi1); s1->flags |= GPRS_SUBSCRIBER_FIRST_CONTACT; s1found = gprs_subscr_get_by_imsi(imsi1); OSMO_ASSERT(s1found == s1); gprs_subscr_put(s1found); /* Create a context */ OSMO_ASSERT(count(gprs_llme_list()) == 0); ctx = alloc_mm_ctx(local_tlli, &raid); /* Attach s1 to ctx */ ctx->subscr = gprs_subscr_get(s1); ctx->subscr->sgsn_data->mm = ctx; /* Inject SendAuthInfoReq GSUP message */ rc = rx_gsup_message(send_auth_info_res, sizeof(send_auth_info_res)); OSMO_ASSERT(rc >= 0); OSMO_ASSERT(last_updated_subscr == s1); /* Check triplets */ OSMO_ASSERT(s1->sgsn_data->auth_triplets[0].key_seq == 0); OSMO_ASSERT(s1->sgsn_data->auth_triplets[1].key_seq == 1); OSMO_ASSERT(s1->sgsn_data->auth_triplets[2].key_seq == GSM_KEY_SEQ_INVAL); /* Inject SendAuthInfoErr GSUP message */ rc = rx_gsup_message(send_auth_info_err, sizeof(send_auth_info_err)); OSMO_ASSERT(rc == -GMM_CAUSE_GPRS_NOTALLOWED); OSMO_ASSERT(last_updated_subscr == s1); OSMO_ASSERT(s1->sgsn_data->error_cause == GMM_CAUSE_GPRS_NOTALLOWED); /* Check triplets */ OSMO_ASSERT(s1->sgsn_data->auth_triplets[0].key_seq == GSM_KEY_SEQ_INVAL); OSMO_ASSERT(s1->sgsn_data->auth_triplets[1].key_seq == GSM_KEY_SEQ_INVAL); OSMO_ASSERT(s1->sgsn_data->auth_triplets[2].key_seq == GSM_KEY_SEQ_INVAL); /* Inject UpdateLocRes GSUP message */ rc = rx_gsup_message(update_location_res, sizeof(update_location_res)); OSMO_ASSERT(rc >= 0); OSMO_ASSERT(last_updated_subscr == s1); OSMO_ASSERT(s1->flags & GPRS_SUBSCRIBER_ENABLE_PURGE); OSMO_ASSERT(s1->sgsn_data->error_cause == SGSN_ERROR_CAUSE_NONE); OSMO_ASSERT(s1->sgsn_data->msisdn_len == sizeof(s1_msisdn)); OSMO_ASSERT(memcmp(s1->sgsn_data->msisdn, s1_msisdn, sizeof(s1_msisdn)) == 0); OSMO_ASSERT(!llist_empty(&s1->sgsn_data->pdp_list)); pdpd = llist_entry(s1->sgsn_data->pdp_list.next, struct sgsn_subscriber_pdp_data, list); OSMO_ASSERT(strcmp(pdpd->apn_str, "test.apn") == 0); pdpd = llist_entry(pdpd->list.next, struct sgsn_subscriber_pdp_data, list); OSMO_ASSERT(strcmp(pdpd->apn_str, "foo.apn") == 0); /* Check authorization */ OSMO_ASSERT(s1->authorized == 1); /* Inject UpdateLocErr GSUP message */ rc = rx_gsup_message(update_location_err, sizeof(update_location_err)); OSMO_ASSERT(rc == -GMM_CAUSE_GPRS_NOTALLOWED); OSMO_ASSERT(last_updated_subscr == s1); OSMO_ASSERT(s1->sgsn_data->error_cause == GMM_CAUSE_GPRS_NOTALLOWED); /* Check authorization */ OSMO_ASSERT(s1->authorized == 0); /* Inject InsertSubscrData GSUP message */ last_updated_subscr = NULL; rc = rx_gsup_message(insert_data_req, sizeof(insert_data_req)); OSMO_ASSERT(rc == -ENOTSUP); /* not connected */ OSMO_ASSERT(last_updated_subscr == s1); /* Inject DeleteSubscrData GSUP message */ last_updated_subscr = NULL; rc = rx_gsup_message(delete_data_req, sizeof(delete_data_req)); if (rc != -GMM_CAUSE_SEM_INCORR_MSG) printf("Unexpected response to DSD: %d\n", rc); OSMO_ASSERT(last_updated_subscr == NULL); /* Inject wrong LocCancelReq GSUP message */ last_updated_subscr = NULL; rc = rx_gsup_message(location_cancellation_req_other, sizeof(location_cancellation_req_other)); OSMO_ASSERT(rc == -GMM_CAUSE_IMSI_UNKNOWN); OSMO_ASSERT(last_updated_subscr == NULL); /* Check cancellation result */ OSMO_ASSERT(!(s1->flags & GPRS_SUBSCRIBER_CANCELLED)); OSMO_ASSERT(s1->sgsn_data->mm != NULL); /* Inject LocCancelReq GSUP message */ rc = rx_gsup_message(location_cancellation_req, sizeof(location_cancellation_req)); OSMO_ASSERT(rc >= 0); OSMO_ASSERT(last_updated_subscr == s1); OSMO_ASSERT(s1->sgsn_data->error_cause == SGSN_ERROR_CAUSE_NONE); /* Check cancellation result */ OSMO_ASSERT(s1->flags & GPRS_SUBSCRIBER_CANCELLED); OSMO_ASSERT(s1->sgsn_data->mm == NULL); /* Inject LocCancelReq(withdraw) GSUP message */ rc = rx_gsup_message(location_cancellation_req_withdraw, sizeof(location_cancellation_req_withdraw)); OSMO_ASSERT(rc >= 0); OSMO_ASSERT(s1->sgsn_data->error_cause == GMM_CAUSE_IMPL_DETACHED); /* Inject PurgeMsRes GSUP message */ rc = rx_gsup_message(purge_ms_res, sizeof(purge_ms_res)); OSMO_ASSERT(rc >= 0); OSMO_ASSERT(!(s1->flags & GPRS_SUBSCRIBER_ENABLE_PURGE)); /* Free MM context and subscriber */ OSMO_ASSERT(ctx->subscr == NULL); sgsn_mm_ctx_cleanup_free(ctx); gprs_subscr_put(s1); s1found = gprs_subscr_get_by_imsi(imsi1); OSMO_ASSERT(s1found == NULL); /* Inject PurgeMsRes GSUP message */ rc = rx_gsup_message(purge_ms_res, sizeof(purge_ms_res)); OSMO_ASSERT(rc >= 0); /* Inject PurgeMsErr(IMSI unknown in HLR) GSUP message */ rc = rx_gsup_message(purge_ms_err, sizeof(purge_ms_err)); OSMO_ASSERT(rc == -GMM_CAUSE_IMSI_UNKNOWN); /* Inject PurgeMsErr() GSUP message */ rc = rx_gsup_message(purge_ms_err_no_cause, sizeof(purge_ms_err_no_cause)); OSMO_ASSERT(rc == -GMM_CAUSE_NET_FAIL); /* Inject InsertSubscrData GSUP message (unknown IMSI) */ last_updated_subscr = NULL; rc = rx_gsup_message(insert_data_req, sizeof(insert_data_req)); OSMO_ASSERT(rc == -GMM_CAUSE_IMSI_UNKNOWN); OSMO_ASSERT(last_updated_subscr == NULL); /* Inject DeleteSubscrData GSUP message (unknown IMSI) */ rc = rx_gsup_message(delete_data_req, sizeof(delete_data_req)); OSMO_ASSERT(rc == -GMM_CAUSE_IMSI_UNKNOWN); OSMO_ASSERT(last_updated_subscr == NULL); /* Inject LocCancelReq GSUP message (unknown IMSI) */ rc = rx_gsup_message(location_cancellation_req, sizeof(location_cancellation_req)); OSMO_ASSERT(rc == -GMM_CAUSE_IMSI_UNKNOWN); OSMO_ASSERT(last_updated_subscr == NULL); update_subscriber_data_cb = __real_sgsn_update_subscriber_data; cleanup_test(); } int my_gsup_client_send_dummy(struct gsup_client *gsupc, struct msgb *msg) { msgb_free(msg); return 0; }; /* * Test that a GMM Detach will remove the MMCTX and the * associated LLME. */ static void test_gmm_detach(void) { struct gprs_ra_id raid = { 0, }; struct sgsn_mm_ctx *ctx, *ictx; uint32_t local_tlli; printf("Testing GMM detach\n"); /* DTAP - Detach Request (MO) */ /* normal detach, power_off = 0 */ static const unsigned char detach_req[] = { 0x08, 0x05, 0x01, 0x18, 0x05, 0xf4, 0xef, 0xe2, 0xb7, 0x00, 0x19, 0x03, 0xb9, 0x97, 0xcb }; local_tlli = gprs_tmsi2tlli(0x23, TLLI_LOCAL); /* Create a context */ OSMO_ASSERT(count(gprs_llme_list()) == 0); ctx = alloc_mm_ctx(local_tlli, &raid); /* inject the detach */ send_0408_message(ctx->gb.llme, local_tlli, &raid, detach_req, ARRAY_SIZE(detach_req)); /* verify that a single message (hopefully the Detach Accept) has been * sent by the SGSN */ OSMO_ASSERT(sgsn_tx_counter == 1); /* verify that things are gone */ OSMO_ASSERT(count(gprs_llme_list()) == 0); ictx = sgsn_mm_ctx_by_tlli(local_tlli, &raid); OSMO_ASSERT(!ictx); cleanup_test(); } /* * Test that a GMM Detach will remove the MMCTX and the associated LLME but * will not sent a Detach Accept message (power_off = 1) */ static void test_gmm_detach_power_off(void) { struct gprs_ra_id raid = { 0, }; struct sgsn_mm_ctx *ctx, *ictx; uint32_t local_tlli; printf("Testing GMM detach (power off)\n"); /* DTAP - Detach Request (MO) */ /* normal detach, power_off = 1 */ static const unsigned char detach_req[] = { 0x08, 0x05, 0x09, 0x18, 0x05, 0xf4, 0xef, 0xe2, 0xb7, 0x00, 0x19, 0x03, 0xb9, 0x97, 0xcb }; local_tlli = gprs_tmsi2tlli(0x23, TLLI_LOCAL); /* Create a context */ OSMO_ASSERT(count(gprs_llme_list()) == 0); ctx = alloc_mm_ctx(local_tlli, &raid); /* inject the detach */ send_0408_message(ctx->gb.llme, local_tlli, &raid, detach_req, ARRAY_SIZE(detach_req)); /* verify that no message (and therefore no Detach Accept) has been * sent by the SGSN */ OSMO_ASSERT(sgsn_tx_counter == 0); /* verify that things are gone */ OSMO_ASSERT(count(gprs_llme_list()) == 0); ictx = sgsn_mm_ctx_by_tlli(local_tlli, &raid); OSMO_ASSERT(!ictx); cleanup_test(); } /* * Test that a GMM Detach will remove the associated LLME if there is no MMCTX. */ static void test_gmm_detach_no_mmctx(void) { struct gprs_ra_id raid = { 0, }; struct gprs_llc_lle *lle; uint32_t local_tlli; printf("Testing GMM detach (no MMCTX)\n"); /* DTAP - Detach Request (MO) */ /* normal detach, power_off = 0 */ static const unsigned char detach_req[] = { 0x08, 0x05, 0x01, 0x18, 0x05, 0xf4, 0xef, 0xe2, 0xb7, 0x00, 0x19, 0x03, 0xb9, 0x97, 0xcb }; /* Create an LLME */ OSMO_ASSERT(count(gprs_llme_list()) == 0); local_tlli = gprs_tmsi2tlli(0x23, TLLI_LOCAL); lle = gprs_lle_get_or_create(local_tlli, 3); OSMO_ASSERT(count(gprs_llme_list()) == 1); /* inject the detach */ send_0408_message(lle->llme, local_tlli, &raid, detach_req, ARRAY_SIZE(detach_req)); /* verify that the LLME is gone */ OSMO_ASSERT(count(gprs_llme_list()) == 0); cleanup_test(); } /* * Test that a single GMM Detach Accept message will not cause the SGSN to send * any message or leave an MM context at the SGSN. */ static void test_gmm_detach_accept_unexpected(void) { struct gprs_ra_id raid = { 0, }; struct gprs_llc_lle *lle; uint32_t local_tlli; printf("Testing GMM detach accept (unexpected)\n"); /* DTAP - Detach Accept (MT) */ /* normal detach */ static const unsigned char detach_acc[] = { 0x08, 0x06 }; /* Create an LLME */ OSMO_ASSERT(count(gprs_llme_list()) == 0); local_tlli = gprs_tmsi2tlli(0x23, TLLI_LOCAL); lle = gprs_lle_get_or_create(local_tlli, 3); /* inject the detach */ send_0408_message(lle->llme, local_tlli, &raid, detach_acc, ARRAY_SIZE(detach_acc)); /* verify that no message (and therefore no Status or XID reset) has been * sent by the SGSN */ OSMO_ASSERT(sgsn_tx_counter == 0); /* verify that things are gone */ OSMO_ASSERT(count(gprs_llme_list()) == 0); cleanup_test(); } /* * Test that a GMM Status will remove the associated LLME if there is no MMCTX. */ static void test_gmm_status_no_mmctx(void) { struct gprs_ra_id raid = { 0, }; struct gprs_llc_lle *lle; uint32_t local_tlli; printf("Testing GMM Status (no MMCTX)\n"); /* DTAP - GMM Status, protocol error */ static const unsigned char gmm_status[] = { 0x08, 0x20, 0x6f }; /* Create an LLME */ OSMO_ASSERT(count(gprs_llme_list()) == 0); local_tlli = gprs_tmsi2tlli(0x23, TLLI_LOCAL); lle = gprs_lle_get_or_create(local_tlli, 3); OSMO_ASSERT(count(gprs_llme_list()) == 1); /* inject the detach */ send_0408_message(lle->llme, local_tlli, &raid, gmm_status, ARRAY_SIZE(gmm_status)); /* verify that no message has been sent by the SGSN */ OSMO_ASSERT(sgsn_tx_counter == 0); /* verify that the LLME is gone */ OSMO_ASSERT(count(gprs_llme_list()) == 0); cleanup_test(); } /* * Test the GMM Attach procedure */ static void test_gmm_attach(int retry) { struct gprs_ra_id raid = { 0, }; struct sgsn_mm_ctx *ctx = NULL; struct sgsn_mm_ctx *ictx; uint32_t ptmsi1; uint32_t foreign_tlli; uint32_t local_tlli = 0; struct gprs_llc_lle *lle; /* DTAP - Attach Request */ /* The P-TMSI is not known by the SGSN */ static const unsigned char attach_req[] = { 0x08, 0x01, 0x02, 0xf5, 0xe0, 0x21, 0x08, 0x02, 0x05, 0xf4, 0xfb, 0xc5, 0x46, 0x79, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, 0x19, 0x18, 0xb3, 0x43, 0x2b, 0x25, 0x96, 0x62, 0x00, 0x60, 0x80, 0x9a, 0xc2, 0xc6, 0x62, 0x00, 0x60, 0x80, 0xba, 0xc8, 0xc6, 0x62, 0x00, 0x60, 0x80, 0x00 }; /* DTAP - Identity Response IMEI */ static const unsigned char ident_resp_imei[] = { 0x08, 0x16, 0x08, 0x9a, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, 0x56 }; /* DTAP - Identity Response IMSI */ static const unsigned char ident_resp_imsi[] = { 0x08, 0x16, 0x08, 0x19, 0x32, 0x54, 0x76, 0x98, 0x10, 0x32, 0x54 }; /* DTAP - Authentication and Ciphering Resp */ static const unsigned char auth_ciph_resp[] = { 0x08, 0x13, 0x00, 0x22, 0x51, 0xe5, 0x51, 0xe5, 0x23, 0x09, 0x9a, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x01 }; /* DTAP - Attach Complete */ static const unsigned char attach_compl[] = { 0x08, 0x03 }; /* DTAP - Detach Request (MO) */ /* normal detach, power_off = 0 */ static const unsigned char detach_req[] = { 0x08, 0x05, 0x01, 0x18, 0x05, 0xf4, 0xeb, 0x8b, 0x45, 0x67, 0x19, 0x03, 0xb9, 0x97, 0xcb }; printf("Testing GMM attach%s\n", retry ? " with retry" : ""); foreign_tlli = gprs_tmsi2tlli(0xc0000023, TLLI_FOREIGN); /* Create a LLE/LLME */ OSMO_ASSERT(count(gprs_llme_list()) == 0); lle = gprs_lle_get_or_create(foreign_tlli, 3); OSMO_ASSERT(count(gprs_llme_list()) == 1); /* inject the attach request */ send_0408_message(lle->llme, foreign_tlli, &raid, attach_req, ARRAY_SIZE(attach_req)); ctx = sgsn_mm_ctx_by_tlli(foreign_tlli, &raid); OSMO_ASSERT(ctx != NULL); OSMO_ASSERT(ctx->gmm_state == GMM_COMMON_PROC_INIT); /* we expect an identity request (IMEI) */ OSMO_ASSERT(sgsn_tx_counter == 1); /* inject the identity response (IMEI) */ send_0408_message(ctx->gb.llme, foreign_tlli, &raid, ident_resp_imei, ARRAY_SIZE(ident_resp_imei)); /* we expect an identity request (IMSI) */ OSMO_ASSERT(sgsn_tx_counter == 1); /* inject the identity response (IMSI) */ send_0408_message(ctx->gb.llme, foreign_tlli, &raid, ident_resp_imsi, ARRAY_SIZE(ident_resp_imsi)); /* check that the MM context has not been removed due to a failed * authorization */ OSMO_ASSERT(ctx == sgsn_mm_ctx_by_tlli(foreign_tlli, &raid)); OSMO_ASSERT(ctx->gmm_state == GMM_COMMON_PROC_INIT); retry_attach_req: if (retry && sgsn_tx_counter == 0) { fprintf(stderr, "Retrying attach request\n"); /* re-inject the attach request */ send_0408_message(lle->llme, foreign_tlli, &raid, attach_req, ARRAY_SIZE(attach_req)); } if (ctx->auth_state == SGSN_AUTH_AUTHENTICATE && sgsn_tx_counter == 1) { /* we got an auth & ciph request */ /* inject the auth & ciph response */ send_0408_message(ctx->gb.llme, foreign_tlli, &raid, auth_ciph_resp, ARRAY_SIZE(auth_ciph_resp)); /* check that the MM context has not been removed due to a * failed authorization */ OSMO_ASSERT(ctx == sgsn_mm_ctx_by_tlli(foreign_tlli, &raid)); if (ctx->subscr && ctx->subscr->sgsn_data->msisdn_len > 0) OSMO_ASSERT(strcmp(ctx->msisdn, "+49166213323") == 0); } if (retry && sgsn_tx_counter == 0) goto retry_attach_req; /* we expect an attach accept/reject */ OSMO_ASSERT(sgsn_tx_counter == 1); ptmsi1 = get_new_ptmsi(&last_dl_parse_ctx); OSMO_ASSERT(ptmsi1 != GSM_RESERVED_TMSI); /* this has been randomly assigned by the SGSN */ local_tlli = gprs_tmsi2tlli(ptmsi1, TLLI_LOCAL); /* inject the attach complete */ send_0408_message(ctx->gb.llme, local_tlli, &raid, attach_compl, ARRAY_SIZE(attach_compl)); OSMO_ASSERT(ctx->gmm_state == GMM_REGISTERED_NORMAL); /* we don't expect a response */ OSMO_ASSERT(sgsn_tx_counter == 0); /* inject the detach */ send_0408_message(ctx->gb.llme, local_tlli, &raid, detach_req, ARRAY_SIZE(detach_req)); /* verify that things are gone */ OSMO_ASSERT(count(gprs_llme_list()) == 0); ictx = sgsn_mm_ctx_by_tlli(local_tlli, &raid); OSMO_ASSERT(!ictx); cleanup_test(); } static void test_gmm_attach_acl(void) { const enum sgsn_auth_policy saved_auth_policy = sgsn->cfg.auth_policy; sgsn_inst.cfg.auth_policy = SGSN_AUTH_POLICY_CLOSED; sgsn_acl_add("123456789012345", &sgsn->cfg); printf("Auth policy 'closed': "); test_gmm_attach(0); sgsn_acl_del("123456789012345", &sgsn->cfg); sgsn->cfg.auth_policy = saved_auth_policy; cleanup_test(); } int my_subscr_request_update_location(struct sgsn_mm_ctx *mmctx) { int rc; rc = __real_gprs_subscr_request_update_location(mmctx); if (rc == -ENOTSUP) { OSMO_ASSERT(mmctx->subscr); gprs_subscr_update(mmctx->subscr); } return rc; } int my_subscr_request_auth_info(struct sgsn_mm_ctx *mmctx, const uint8_t *auts, const uint8_t *auts_rand) { gprs_subscr_update(mmctx->subscr); return 0; } static void test_gmm_attach_subscr(void) { const enum sgsn_auth_policy saved_auth_policy = sgsn->cfg.auth_policy; struct gprs_subscr *subscr; sgsn_inst.cfg.auth_policy = SGSN_AUTH_POLICY_REMOTE; subscr_request_update_location_cb = my_subscr_request_update_location; subscr_request_auth_info_cb = my_subscr_request_auth_info; subscr = gprs_subscr_get_or_create("123456789012345"); subscr->authorized = 1; printf("Auth policy 'remote': "); test_gmm_attach(0); gprs_subscr_put(subscr); assert_no_subscrs(); sgsn->cfg.auth_policy = saved_auth_policy; subscr_request_update_location_cb = __real_gprs_subscr_request_update_location; subscr_request_auth_info_cb = __real_gprs_subscr_request_auth_info; cleanup_test(); } int my_subscr_request_auth_info_fake_auth(struct sgsn_mm_ctx *mmctx, const uint8_t *auts, const uint8_t *auts_rand) { /* Fake an authentication */ OSMO_ASSERT(mmctx->subscr); mmctx->sec_ctx = OSMO_AUTH_TYPE_GSM; gprs_subscr_update_auth_info(mmctx->subscr); return 0; } static void test_gmm_attach_subscr_fake_auth(void) { const enum sgsn_auth_policy saved_auth_policy = sgsn->cfg.auth_policy; struct gprs_subscr *subscr; sgsn_inst.cfg.auth_policy = SGSN_AUTH_POLICY_REMOTE; subscr_request_update_location_cb = my_subscr_request_update_location; subscr_request_auth_info_cb = my_subscr_request_auth_info_fake_auth; subscr = gprs_subscr_get_or_create("123456789012345"); subscr->authorized = 1; sgsn->cfg.require_authentication = 1; sgsn->cfg.require_update_location = 1; printf("Auth policy 'remote', auth faked: "); test_gmm_attach(0); gprs_subscr_put(subscr); assert_no_subscrs(); sgsn->cfg.auth_policy = saved_auth_policy; subscr_request_update_location_cb = __real_gprs_subscr_request_update_location; subscr_request_auth_info_cb = __real_gprs_subscr_request_auth_info; cleanup_test(); } int my_subscr_request_auth_info_real_auth(struct sgsn_mm_ctx *mmctx, const uint8_t *auts, const uint8_t *auts_rand) { struct gsm_auth_tuple at = { .vec.sres = {0x51, 0xe5, 0x51, 0xe5}, .vec.auth_types = OSMO_AUTH_TYPE_GSM, .key_seq = 0 }; /* Fake an authentication */ OSMO_ASSERT(mmctx->subscr); mmctx->subscr->sgsn_data->auth_triplets[0] = at; gprs_subscr_update_auth_info(mmctx->subscr); return 0; } static void test_gmm_attach_subscr_real_auth(void) { const enum sgsn_auth_policy saved_auth_policy = sgsn->cfg.auth_policy; struct gprs_subscr *subscr; sgsn_inst.cfg.auth_policy = SGSN_AUTH_POLICY_REMOTE; subscr_request_update_location_cb = my_subscr_request_update_location; subscr_request_auth_info_cb = my_subscr_request_auth_info_real_auth; subscr = gprs_subscr_get_or_create("123456789012345"); subscr->authorized = 1; sgsn->cfg.require_authentication = 1; sgsn->cfg.require_update_location = 1; printf("Auth policy 'remote', triplet based auth: "); test_gmm_attach(0); gprs_subscr_put(subscr); assert_no_subscrs(); sgsn->cfg.auth_policy = saved_auth_policy; subscr_request_update_location_cb = __real_gprs_subscr_request_update_location; subscr_request_auth_info_cb = __real_gprs_subscr_request_auth_info; cleanup_test(); } #define TEST_GSUP_IMSI_LONG_IE 0x01, 0x08, \ 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0xf5 static int auth_info_skip = 0; static int upd_loc_skip = 0; int my_subscr_request_auth_info_gsup_auth(struct sgsn_mm_ctx *mmctx, const uint8_t *auts, const uint8_t *auts_rand) { static const uint8_t send_auth_info_res[] = { 0x0a, TEST_GSUP_IMSI_LONG_IE, 0x03, 0x22, /* Auth tuple */ 0x20, 0x10, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x21, 0x04, 0x51, 0xe5, 0x51, 0xe5, 0x22, 0x08, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, }; OSMO_ASSERT(!mmctx || mmctx->subscr); if (auth_info_skip > 0) { auth_info_skip -= 1; return -EAGAIN; } /* Fake an SendAuthInfoRes */ rx_gsup_message(send_auth_info_res, sizeof(send_auth_info_res)); return 0; }; int my_subscr_request_update_gsup_auth(struct sgsn_mm_ctx *mmctx) { static const uint8_t update_location_res[] = { 0x06, TEST_GSUP_IMSI_LONG_IE, 0x04, 0x00, /* PDP info complete */ 0x05, 0x12, 0x10, 0x01, 0x01, 0x11, 0x02, 0xf1, 0x21, /* IPv4 */ 0x12, 0x09, 0x04, 't', 'e', 's', 't', 0x03, 'a', 'p', 'n', 0x08, 0x07, /* MSISDN 49166213323 encoded */ 0x91, 0x94, 0x61, 0x26, 0x31, 0x23, 0xF3, 0x09, 0x07, /* MSISDN 38166213323 encoded */ 0x91, 0x83, 0x61, 0x26, 0x31, 0x23, 0xF3, }; OSMO_ASSERT(!mmctx || mmctx->subscr); if (upd_loc_skip > 0) { upd_loc_skip -= 1; return -EAGAIN; } /* Fake an UpdateLocRes */ return rx_gsup_message(update_location_res, sizeof(update_location_res)); }; static void test_gmm_attach_subscr_gsup_auth(int retry) { const enum sgsn_auth_policy saved_auth_policy = sgsn->cfg.auth_policy; struct gprs_subscr *subscr; sgsn_inst.cfg.auth_policy = SGSN_AUTH_POLICY_REMOTE; subscr_request_update_location_cb = my_subscr_request_update_gsup_auth; subscr_request_auth_info_cb = my_subscr_request_auth_info_gsup_auth; if (retry) { upd_loc_skip = 3; auth_info_skip = 3; } subscr = gprs_subscr_get_or_create("123456789012345"); subscr->authorized = 1; sgsn->cfg.require_authentication = 1; sgsn->cfg.require_update_location = 1; gprs_subscr_put(subscr); printf("Auth policy 'remote', GSUP based auth: "); test_gmm_attach(retry); assert_no_subscrs(); sgsn->cfg.auth_policy = saved_auth_policy; subscr_request_update_location_cb = __real_gprs_subscr_request_update_location; subscr_request_auth_info_cb = __real_gprs_subscr_request_auth_info; upd_loc_skip = 0; auth_info_skip = 0; cleanup_test(); } int my_gsup_client_send(struct gsup_client *gsupc, struct msgb *msg) { struct osmo_gsup_message to_peer = {0}; struct osmo_gsup_message from_peer = {0}; struct msgb *reply_msg; int rc; /* Simulate the GSUP peer */ rc = osmo_gsup_decode(msgb_data(msg), msgb_length(msg), &to_peer); OSMO_ASSERT(rc >= 0); OSMO_ASSERT(to_peer.imsi[0] != 0); osmo_strlcpy(from_peer.imsi, to_peer.imsi, sizeof(from_peer.imsi)); /* This invalidates the pointers in to_peer */ msgb_free(msg); switch (to_peer.message_type) { case OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST: /* Send UPDATE_LOCATION_RESULT */ return my_subscr_request_update_gsup_auth(NULL); case OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST: /* Send SEND_AUTH_INFO_RESULT */ return my_subscr_request_auth_info_gsup_auth(NULL, NULL, NULL); case OSMO_GSUP_MSGT_PURGE_MS_REQUEST: from_peer.message_type = OSMO_GSUP_MSGT_PURGE_MS_RESULT; break; default: if ((to_peer.message_type & 0b00000011) == 0) { /* Unhandled request */ /* Send error(NOT_IMPL) */ from_peer.message_type = to_peer.message_type + 1; from_peer.cause = GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL; break; } /* Ignore it */ return 0; } reply_msg = gsup_client_msgb_alloc(); reply_msg->l2h = reply_msg->data; osmo_gsup_encode(reply_msg, &from_peer); gprs_subscr_rx_gsup_message(reply_msg); msgb_free(reply_msg); return 0; }; static void test_gmm_attach_subscr_real_gsup_auth(int retry) { const enum sgsn_auth_policy saved_auth_policy = sgsn->cfg.auth_policy; struct gprs_subscr *subscr; sgsn_inst.cfg.auth_policy = SGSN_AUTH_POLICY_REMOTE; gsup_client_send_cb = my_gsup_client_send; sgsn->gsup_client = talloc_zero(tall_bsc_ctx, struct gsup_client); if (retry) { upd_loc_skip = 3; auth_info_skip = 3; } printf("Auth policy 'remote', real GSUP based auth: "); test_gmm_attach(retry); subscr = gprs_subscr_get_by_imsi("123456789012345"); OSMO_ASSERT(subscr == NULL); assert_no_subscrs(); sgsn->cfg.auth_policy = saved_auth_policy; gsup_client_send_cb = __real_gsup_client_send; upd_loc_skip = 0; auth_info_skip = 0; talloc_free(sgsn->gsup_client); sgsn->gsup_client = NULL; cleanup_test(); } /* * Test the GMM Rejects */ static void test_gmm_reject(void) { struct gprs_ra_id raid = { 0, }; struct sgsn_mm_ctx *ctx = NULL; uint32_t foreign_tlli; struct gprs_llc_lle *lle; int idx; /* DTAP - Attach Request */ /* Invalid MI length */ static const unsigned char attach_req_inv_mi_len[] = { 0x08, 0x01, 0x02, 0xf5, 0xe0, 0x21, 0x08, 0x02, 0x09, 0xf4, 0xfb, 0xc5, 0x46, 0x79, 0xff, 0xff, 0xff, 0xff, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, 0x19, 0x18, 0xb3, 0x43, 0x2b, 0x25, 0x96, 0x62, 0x00, 0x60, 0x80, 0x9a, 0xc2, 0xc6, 0x62, 0x00, 0x60, 0x80, 0xba, 0xc8, 0xc6, 0x62, 0x00, 0x60, 0x80, 0x00 }; /* DTAP - Attach Request */ /* Invalid MI type (IMEI) */ static const unsigned char attach_req_inv_mi_type[] = { 0x08, 0x01, 0x02, 0xf5, 0xe0, 0x21, 0x08, 0x02, 0x05, 0xf2, 0xfb, 0xc5, 0x46, 0x79, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, 0x19, 0x18, 0xb3, 0x43, 0x2b, 0x25, 0x96, 0x62, 0x00, 0x60, 0x80, 0x9a, 0xc2, 0xc6, 0x62, 0x00, 0x60, 0x80, 0xba, 0xc8, 0xc6, 0x62, 0x00, 0x60, 0x80, 0x00 }; /* DTAP - Routing Area Update Request */ static const unsigned char dtap_ra_upd_req[] = { 0x08, 0x08, 0x10, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, 0x1d, 0x19, 0x13, 0x42, 0x33, 0x57, 0x2b, 0xf7, 0xc8, 0x48, 0x02, 0x13, 0x48, 0x50, 0xc8, 0x48, 0x02, 0x14, 0x48, 0x50, 0xc8, 0x48, 0x02, 0x17, 0x49, 0x10, 0xc8, 0x48, 0x02, 0x00, 0x19, 0x8b, 0xb2, 0x92, 0x17, 0x16, 0x27, 0x07, 0x04, 0x31, 0x02, 0xe5, 0xe0, 0x32, 0x02, 0x20, 0x00 }; /* DTAP - Routing Area Update Request */ /* Invalid type: GPRS_UPD_T_RA_LA_IMSI_ATT */ static const unsigned char dtap_ra_upd_req_inv_type[] = { 0x08, 0x08, 0x12, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, 0x1d, 0x19, 0x13, 0x42, 0x33, 0x57, 0x2b, 0xf7, 0xc8, 0x48, 0x02, 0x13, 0x48, 0x50, 0xc8, 0x48, 0x02, 0x14, 0x48, 0x50, 0xc8, 0x48, 0x02, 0x17, 0x49, 0x10, 0xc8, 0x48, 0x02, 0x00, 0x19, 0x8b, 0xb2, 0x92, 0x17, 0x16, 0x27, 0x07, 0x04, 0x31, 0x02, 0xe5, 0xe0, 0x32, 0x02, 0x20, 0x00 }; /* DTAP - Routing Area Update Request */ /* Invalid cap length */ static const unsigned char dtap_ra_upd_req_inv_cap_len[] = { 0x08, 0x08, 0x10, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, 0x3d, 0x19, 0x13, 0x42, 0x33, 0x57, 0x2b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xc8, 0x48, 0x02, 0x13, 0x48, 0x50, 0xc8, 0x48, 0x02, 0x14, 0x48, 0x50, 0xc8, 0x48, 0x02, 0x17, 0x49, 0x10, 0xc8, 0x48, 0x02, 0x00, 0x19, 0x8b, 0xb2, 0x92, 0x17, 0x16, 0x27, 0x07, 0x04, 0x31, 0x02, 0xe5, 0xe0, 0x32, 0x02, 0x20, 0x00 }; struct test { const char *title; const unsigned char *msg; unsigned msg_len; unsigned num_resp; }; static struct test tests[] = { { .title = "Attach Request (invalid MI length)", .msg = attach_req_inv_mi_len, .msg_len = sizeof(attach_req_inv_mi_len), .num_resp = 1 /* Reject */ }, { .title = "Attach Request (invalid MI type)", .msg = attach_req_inv_mi_type, .msg_len = sizeof(attach_req_inv_mi_type), .num_resp = 1 /* Reject */ }, { .title = "Routing Area Update Request (valid)", .msg = dtap_ra_upd_req, .msg_len = sizeof(dtap_ra_upd_req), .num_resp = 2 /* XID Reset + Reject */ }, { .title = "Routing Area Update Request (invalid type)", .msg = dtap_ra_upd_req_inv_type, .msg_len = sizeof(dtap_ra_upd_req_inv_type), .num_resp = 1 /* Reject */ }, { .title = "Routing Area Update Request (invalid CAP length)", .msg = dtap_ra_upd_req_inv_cap_len, .msg_len = sizeof(dtap_ra_upd_req_inv_cap_len), .num_resp = 1 /* Reject */ }, }; printf("Testing GMM reject\n"); /* reset the PRNG used by sgsn_alloc_ptmsi */ srand(1); foreign_tlli = gprs_tmsi2tlli(0xc0000023, TLLI_FOREIGN); OSMO_ASSERT(count(gprs_llme_list()) == 0); for (idx = 0; idx < ARRAY_SIZE(tests); idx++) { const struct test *test = &tests[idx]; printf(" - %s\n", test->title); /* Create a LLE/LLME */ lle = gprs_lle_get_or_create(foreign_tlli, 3); OSMO_ASSERT(count(gprs_llme_list()) == 1); /* Inject the Request message */ send_0408_message(lle->llme, foreign_tlli, &raid, test->msg, test->msg_len); /* We expect a Reject message */ fprintf(stderr, "sgsn_tx_counter = %d (expected %d)\n", sgsn_tx_counter, test->num_resp); OSMO_ASSERT(sgsn_tx_counter == test->num_resp); /* verify that LLME/MM are removed */ ctx = sgsn_mm_ctx_by_tlli(foreign_tlli, &raid); OSMO_ASSERT(ctx == NULL); OSMO_ASSERT(count(gprs_llme_list()) == 0); } cleanup_test(); } /* * Test cancellation of attached MM contexts */ static void test_gmm_cancel(void) { struct gprs_ra_id raid = { 0, }; struct sgsn_mm_ctx *ctx = NULL; struct sgsn_mm_ctx *ictx; uint32_t ptmsi1; uint32_t foreign_tlli; uint32_t local_tlli = 0; struct gprs_llc_lle *lle; const enum sgsn_auth_policy saved_auth_policy = sgsn->cfg.auth_policy; /* DTAP - Attach Request */ /* The P-TMSI is not known by the SGSN */ static const unsigned char attach_req[] = { 0x08, 0x01, 0x02, 0xf5, 0xe0, 0x21, 0x08, 0x02, 0x05, 0xf4, 0xfb, 0xc5, 0x46, 0x79, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, 0x19, 0x18, 0xb3, 0x43, 0x2b, 0x25, 0x96, 0x62, 0x00, 0x60, 0x80, 0x9a, 0xc2, 0xc6, 0x62, 0x00, 0x60, 0x80, 0xba, 0xc8, 0xc6, 0x62, 0x00, 0x60, 0x80, 0x00 }; /* DTAP - Identity Response IMEI */ static const unsigned char ident_resp_imei[] = { 0x08, 0x16, 0x08, 0x9a, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, 0x56 }; /* DTAP - Identity Response IMSI */ static const unsigned char ident_resp_imsi[] = { 0x08, 0x16, 0x08, 0x19, 0x32, 0x54, 0x76, 0x98, 0x10, 0x32, 0x54 }; /* DTAP - Attach Complete */ static const unsigned char attach_compl[] = { 0x08, 0x03 }; printf("Testing cancellation\n"); sgsn_inst.cfg.auth_policy = SGSN_AUTH_POLICY_OPEN; foreign_tlli = gprs_tmsi2tlli(0xc0000023, TLLI_FOREIGN); /* Create a LLE/LLME */ OSMO_ASSERT(count(gprs_llme_list()) == 0); lle = gprs_lle_get_or_create(foreign_tlli, 3); OSMO_ASSERT(count(gprs_llme_list()) == 1); /* inject the attach request */ send_0408_message(lle->llme, foreign_tlli, &raid, attach_req, ARRAY_SIZE(attach_req)); ctx = sgsn_mm_ctx_by_tlli(foreign_tlli, &raid); OSMO_ASSERT(ctx != NULL); OSMO_ASSERT(ctx->gmm_state == GMM_COMMON_PROC_INIT); /* we expect an identity request (IMEI) */ OSMO_ASSERT(sgsn_tx_counter == 1); /* inject the identity response (IMEI) */ send_0408_message(ctx->gb.llme, foreign_tlli, &raid, ident_resp_imei, ARRAY_SIZE(ident_resp_imei)); /* we expect an identity request (IMSI) */ OSMO_ASSERT(sgsn_tx_counter == 1); /* inject the identity response (IMSI) */ send_0408_message(ctx->gb.llme, foreign_tlli, &raid, ident_resp_imsi, ARRAY_SIZE(ident_resp_imsi)); /* check that the MM context has not been removed due to a failed * authorization */ OSMO_ASSERT(ctx == sgsn_mm_ctx_by_tlli(foreign_tlli, &raid)); OSMO_ASSERT(ctx->gmm_state == GMM_COMMON_PROC_INIT); /* we expect an attach accept/reject */ OSMO_ASSERT(sgsn_tx_counter == 1); ptmsi1 = get_new_ptmsi(&last_dl_parse_ctx); OSMO_ASSERT(ptmsi1 != GSM_RESERVED_TMSI); /* this has been randomly assigned by the SGSN */ local_tlli = gprs_tmsi2tlli(ptmsi1, TLLI_LOCAL); /* inject the attach complete */ send_0408_message(ctx->gb.llme, foreign_tlli, &raid, attach_compl, ARRAY_SIZE(attach_compl)); OSMO_ASSERT(ctx->gmm_state == GMM_REGISTERED_NORMAL); /* we don't expect a response */ OSMO_ASSERT(sgsn_tx_counter == 0); /* cancel */ gsm0408_gprs_access_cancelled(ctx, 0); /* verify that things are gone */ OSMO_ASSERT(count(gprs_llme_list()) == 0); ictx = sgsn_mm_ctx_by_tlli(local_tlli, &raid); OSMO_ASSERT(!ictx); sgsn->cfg.auth_policy = saved_auth_policy; cleanup_test(); } /* * Test the dynamic allocation of P-TMSIs */ static void test_gmm_ptmsi_allocation(void) { struct gprs_ra_id raid = { .mnc=332, .mcc=112, .lac=16464, .rac=96}; struct sgsn_mm_ctx *ctx = NULL; struct sgsn_mm_ctx *ictx; uint32_t foreign_tlli; uint32_t ptmsi1; uint32_t ptmsi2; uint32_t received_ptmsi; uint32_t old_ptmsi; uint32_t local_tlli = 0; struct gprs_llc_lle *lle; const enum sgsn_auth_policy saved_auth_policy = sgsn->cfg.auth_policy; /* DTAP - Attach Request (IMSI 12131415161718) */ static const unsigned char attach_req[] = { 0x08, 0x01, 0x02, 0xf5, 0xe0, 0x21, 0x08, 0x02, 0x08, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, 0x19, 0x18, 0xb3, 0x43, 0x2b, 0x25, 0x96, 0x62, 0x00, 0x60, 0x80, 0x9a, 0xc2, 0xc6, 0x62, 0x00, 0x60, 0x80, 0xba, 0xc8, 0xc6, 0x62, 0x00, 0x60, 0x80, 0x00, }; /* DTAP - Identity Response IMEI */ static const unsigned char ident_resp_imei[] = { 0x08, 0x16, 0x08, 0x9a, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, 0x56 }; /* DTAP - Attach Complete */ static const unsigned char attach_compl[] = { 0x08, 0x03 }; /* DTAP - Routing Area Update Request */ static const unsigned char ra_upd_req[] = { 0x08, 0x08, 0x10, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, 0x1d, 0x19, 0x13, 0x42, 0x33, 0x57, 0x2b, 0xf7, 0xc8, 0x48, 0x02, 0x13, 0x48, 0x50, 0xc8, 0x48, 0x02, 0x14, 0x48, 0x50, 0xc8, 0x48, 0x02, 0x17, 0x49, 0x10, 0xc8, 0x48, 0x02, 0x00, 0x19, 0x8b, 0xb2, 0x92, 0x17, 0x16, 0x27, 0x07, 0x04, 0x31, 0x02, 0xe5, 0xe0, 0x32, 0x02, 0x20, 0x00 }; /* DTAP - Routing Area Update Complete */ static const unsigned char ra_upd_complete[] = { 0x08, 0x0a }; /* DTAP - Detach Request (MO) */ /* normal detach, power_off = 1 */ static const unsigned char detach_req[] = { 0x08, 0x05, 0x09, 0x18, 0x05, 0xf4, 0xef, 0xe2, 0xb7, 0x00, 0x19, 0x03, 0xb9, 0x97, 0xcb }; sgsn->cfg.auth_policy = SGSN_AUTH_POLICY_OPEN; printf("Testing P-TMSI allocation\n"); printf(" - sgsn_alloc_ptmsi\n"); /* reset the PRNG used by sgsn_alloc_ptmsi */ srand(1); ptmsi1 = sgsn_alloc_ptmsi(); OSMO_ASSERT(ptmsi1 != GSM_RESERVED_TMSI); ptmsi2 = sgsn_alloc_ptmsi(); OSMO_ASSERT(ptmsi2 != GSM_RESERVED_TMSI); OSMO_ASSERT(ptmsi1 != ptmsi2); ptmsi1 = ptmsi2 = GSM_RESERVED_TMSI; printf(" - Repeated Attach Request\n"); foreign_tlli = gprs_tmsi2tlli(0xc0000023, TLLI_FOREIGN); /* Create a LLE/LLME */ OSMO_ASSERT(count(gprs_llme_list()) == 0); lle = gprs_lle_get_or_create(foreign_tlli, 3); OSMO_ASSERT(count(gprs_llme_list()) == 1); /* inject the attach request */ send_0408_message(lle->llme, foreign_tlli, &raid, attach_req, ARRAY_SIZE(attach_req)); ctx = sgsn_mm_ctx_by_tlli(foreign_tlli, &raid); OSMO_ASSERT(ctx != NULL); OSMO_ASSERT(ctx->gmm_state == GMM_COMMON_PROC_INIT); OSMO_ASSERT(ctx->p_tmsi != GSM_RESERVED_TMSI); ptmsi1 = ctx->p_tmsi; old_ptmsi = ctx->p_tmsi_old; /* we expect an identity request (IMEI) */ OSMO_ASSERT(sgsn_tx_counter == 1); /* inject the identity response (IMEI) */ send_0408_message(ctx->gb.llme, foreign_tlli, &raid, ident_resp_imei, ARRAY_SIZE(ident_resp_imei)); /* check that the MM context has not been removed due to a failed * authorization */ OSMO_ASSERT(ctx == sgsn_mm_ctx_by_tlli(foreign_tlli, &raid)); OSMO_ASSERT(ctx->gmm_state == GMM_COMMON_PROC_INIT); OSMO_ASSERT(ctx->p_tmsi == ptmsi1); /* we expect an attach accept */ OSMO_ASSERT(sgsn_tx_counter == 1); received_ptmsi = get_new_ptmsi(&last_dl_parse_ctx); OSMO_ASSERT(received_ptmsi == ptmsi1); /* we ignore this and send the attach again */ send_0408_message(lle->llme, foreign_tlli, &raid, attach_req, ARRAY_SIZE(attach_req)); /* the allocated P-TMSI should be the same */ ctx = sgsn_mm_ctx_by_tlli(foreign_tlli, &raid); OSMO_ASSERT(ctx != NULL); OSMO_ASSERT(ctx->gmm_state == GMM_COMMON_PROC_INIT); OSMO_ASSERT(ctx->p_tmsi_old == old_ptmsi); OSMO_ASSERT(ctx->p_tmsi == ptmsi1); /* we expect an attach accept */ OSMO_ASSERT(sgsn_tx_counter == 1); received_ptmsi = get_new_ptmsi(&last_dl_parse_ctx); OSMO_ASSERT(received_ptmsi == ptmsi1); /* inject the attach complete */ local_tlli = gprs_tmsi2tlli(ptmsi1, TLLI_LOCAL); send_0408_message(ctx->gb.llme, local_tlli, &raid, attach_compl, ARRAY_SIZE(attach_compl)); /* we don't expect a response */ OSMO_ASSERT(sgsn_tx_counter == 0); OSMO_ASSERT(ctx->gmm_state == GMM_REGISTERED_NORMAL); OSMO_ASSERT(ctx->p_tmsi_old == 0); OSMO_ASSERT(ctx->p_tmsi == ptmsi1); printf(" - Repeated RA Update Request\n"); /* inject the RA update request */ send_0408_message(ctx->gb.llme, local_tlli, &raid, ra_upd_req, ARRAY_SIZE(ra_upd_req)); /* we expect an RA update accept */ OSMO_ASSERT(sgsn_tx_counter == 1); OSMO_ASSERT(ctx->gmm_state == GMM_COMMON_PROC_INIT); OSMO_ASSERT(ctx->p_tmsi_old == ptmsi1); OSMO_ASSERT(ctx->p_tmsi != GSM_RESERVED_TMSI); OSMO_ASSERT(ctx->p_tmsi != ptmsi1); ptmsi2 = ctx->p_tmsi; /* repeat the RA update request */ send_0408_message(ctx->gb.llme, local_tlli, &raid, ra_upd_req, ARRAY_SIZE(ra_upd_req)); /* we expect an RA update accept */ OSMO_ASSERT(sgsn_tx_counter == 1); received_ptmsi = get_new_ptmsi(&last_dl_parse_ctx); OSMO_ASSERT(received_ptmsi == ptmsi2); OSMO_ASSERT(ctx->gmm_state == GMM_COMMON_PROC_INIT); OSMO_ASSERT(ctx->p_tmsi_old == ptmsi1); OSMO_ASSERT(ctx->p_tmsi == ptmsi2); /* inject the RA update complete */ local_tlli = gprs_tmsi2tlli(ptmsi2, TLLI_LOCAL); send_0408_message(ctx->gb.llme, local_tlli, &raid, ra_upd_complete, ARRAY_SIZE(ra_upd_complete)); /* we don't expect a response */ OSMO_ASSERT(sgsn_tx_counter == 0); OSMO_ASSERT(ctx->gmm_state == GMM_REGISTERED_NORMAL); OSMO_ASSERT(ctx->p_tmsi_old == 0); OSMO_ASSERT(ctx->p_tmsi == ptmsi2); /* inject the detach */ send_0408_message(ctx->gb.llme, local_tlli, &raid, detach_req, ARRAY_SIZE(detach_req)); /* verify that things are gone */ OSMO_ASSERT(count(gprs_llme_list()) == 0); ictx = sgsn_mm_ctx_by_tlli(local_tlli, &raid); OSMO_ASSERT(!ictx); sgsn->cfg.auth_policy = saved_auth_policy; cleanup_test(); } /* * Test changing of routing areas */ static void test_gmm_routing_areas(void) { struct gprs_ra_id raid1 = { .mnc=332, .mcc=112, .lac=16464, .rac=96}; struct gprs_ra_id raid2 = { .mnc=332, .mcc=112, .lac=16464, .rac=97}; struct sgsn_mm_ctx *ctx = NULL; struct sgsn_mm_ctx *ictx; uint32_t ptmsi1; uint32_t received_ptmsi; uint32_t ms_tlli = 0; struct gprs_llc_lle *lle; const enum sgsn_auth_policy saved_auth_policy = sgsn->cfg.auth_policy; /* DTAP - Attach Request (IMSI 12131415161718) */ static const unsigned char attach_req[] = { 0x08, 0x01, 0x02, 0xf5, 0xe0, 0x21, 0x08, 0x02, 0x08, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, 0x19, 0x18, 0xb3, 0x43, 0x2b, 0x25, 0x96, 0x62, 0x00, 0x60, 0x80, 0x9a, 0xc2, 0xc6, 0x62, 0x00, 0x60, 0x80, 0xba, 0xc8, 0xc6, 0x62, 0x00, 0x60, 0x80, 0x00, }; /* DTAP - Attach Request (IMSI 12131415161718) (RA 2) */ static const unsigned char attach_req2[] = { 0x08, 0x01, 0x02, 0xf5, 0xe0, 0x21, 0x08, 0x02, 0x08, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x11, 0x22, 0x33, 0x40, 0x50, 0x61, 0x19, 0x18, 0xb3, 0x43, 0x2b, 0x25, 0x96, 0x62, 0x00, 0x60, 0x80, 0x9a, 0xc2, 0xc6, 0x62, 0x00, 0x60, 0x80, 0xba, 0xc8, 0xc6, 0x62, 0x00, 0x60, 0x80, 0x00, }; /* DTAP - Identity Response IMEI */ static const unsigned char ident_resp_imei[] = { 0x08, 0x16, 0x08, 0x9a, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, 0x56 }; /* DTAP - Attach Complete */ static const unsigned char attach_compl[] = { 0x08, 0x03 }; /* DTAP - Routing Area Update Request (coming from RA 1) */ static const unsigned char ra_upd_req1[] = { 0x08, 0x08, 0x10, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, 0x1d, 0x19, 0x13, 0x42, 0x33, 0x57, 0x2b, 0xf7, 0xc8, 0x48, 0x02, 0x13, 0x48, 0x50, 0xc8, 0x48, 0x02, 0x14, 0x48, 0x50, 0xc8, 0x48, 0x02, 0x17, 0x49, 0x10, 0xc8, 0x48, 0x02, 0x00, 0x19, 0x8b, 0xb2, 0x92, 0x17, 0x16, 0x27, 0x07, 0x04, 0x31, 0x02, 0xe5, 0xe0, 0x32, 0x02, 0x20, 0x00 }; /* DTAP - Routing Area Update Request (coming from RA 2) */ static const unsigned char ra_upd_req2[] = { 0x08, 0x08, 0x10, 0x11, 0x22, 0x33, 0x40, 0x50, 0x61, 0x1d, 0x19, 0x13, 0x42, 0x33, 0x57, 0x2b, 0xf7, 0xc8, 0x48, 0x02, 0x13, 0x48, 0x50, 0xc8, 0x48, 0x02, 0x14, 0x48, 0x50, 0xc8, 0x48, 0x02, 0x17, 0x49, 0x10, 0xc8, 0x48, 0x02, 0x00, 0x19, 0x8b, 0xb2, 0x92, 0x17, 0x16, 0x27, 0x07, 0x04, 0x31, 0x02, 0xe5, 0xe0, 0x32, 0x02, 0x20, 0x00 }; /* DTAP - Routing Area Update Request (coming from RA other) */ /* raid_other = {443, 223, 16464, 98}; */ static const unsigned char ra_upd_req_other[] = { 0x08, 0x08, 0x10, 0x22, 0x33, 0x44, 0x40, 0x50, 0x62, 0x1d, 0x19, 0x13, 0x42, 0x33, 0x57, 0x2b, 0xf7, 0xc8, 0x48, 0x02, 0x13, 0x48, 0x50, 0xc8, 0x48, 0x02, 0x14, 0x48, 0x50, 0xc8, 0x48, 0x02, 0x17, 0x49, 0x10, 0xc8, 0x48, 0x02, 0x00, 0x19, 0x8b, 0xb2, 0x92, 0x17, 0x16, 0x27, 0x07, 0x04, 0x31, 0x02, 0xe5, 0xe0, 0x32, 0x02, 0x20, 0x00 }; /* DTAP - Routing Area Update Complete */ static const unsigned char ra_upd_complete[] = { 0x08, 0x0a }; /* DTAP - Detach Request (MO) */ /* normal detach, power_off = 1 */ static const unsigned char detach_req[] = { 0x08, 0x05, 0x09, 0x18, 0x05, 0xf4, 0xef, 0xe2, 0xb7, 0x00, 0x19, 0x03, 0xb9, 0x97, 0xcb }; sgsn->cfg.auth_policy = SGSN_AUTH_POLICY_OPEN; printf("Testing routing area changes\n"); /* reset the PRNG used by sgsn_alloc_ptmsi */ srand(1); ptmsi1 = GSM_RESERVED_TMSI; printf(" - Attach Request (RA 1)\n"); ms_tlli = gprs_tmsi2tlli(0x00000023, TLLI_RANDOM); /* Create a LLE/LLME */ OSMO_ASSERT(count(gprs_llme_list()) == 0); lle = gprs_lle_get_or_create(ms_tlli, 3); OSMO_ASSERT(count(gprs_llme_list()) == 1); /* inject the attach request */ send_0408_message(lle->llme, ms_tlli, &raid1, attach_req, ARRAY_SIZE(attach_req)); ctx = sgsn_mm_ctx_by_tlli(ms_tlli, &raid1); OSMO_ASSERT(ctx != NULL); OSMO_ASSERT(ctx->gmm_state == GMM_COMMON_PROC_INIT); OSMO_ASSERT(ctx->p_tmsi != GSM_RESERVED_TMSI); /* we expect an identity request (IMEI) */ OSMO_ASSERT(sgsn_tx_counter == 1); OSMO_ASSERT(last_dl_parse_ctx.g48_hdr->msg_type == GSM48_MT_GMM_ID_REQ); OSMO_ASSERT(last_dl_parse_ctx.tlli == ms_tlli); /* inject the identity response (IMEI) */ send_0408_message(ctx->gb.llme, ms_tlli, &raid1, ident_resp_imei, ARRAY_SIZE(ident_resp_imei)); /* check that the MM context has not been removed due to a failed * authorization */ OSMO_ASSERT(ctx == sgsn_mm_ctx_by_tlli(ms_tlli, &raid1)); OSMO_ASSERT(ctx->gmm_state == GMM_COMMON_PROC_INIT); /* we expect an attach accept */ OSMO_ASSERT(sgsn_tx_counter == 1); OSMO_ASSERT(last_dl_parse_ctx.g48_hdr->msg_type == GSM48_MT_GMM_ATTACH_ACK); OSMO_ASSERT(last_dl_parse_ctx.tlli == ms_tlli); received_ptmsi = get_new_ptmsi(&last_dl_parse_ctx); OSMO_ASSERT(received_ptmsi == ctx->p_tmsi); ptmsi1 = received_ptmsi; /* inject the attach complete */ ms_tlli = gprs_tmsi2tlli(ptmsi1, TLLI_LOCAL); send_0408_message(ctx->gb.llme, ms_tlli, &raid1, attach_compl, ARRAY_SIZE(attach_compl)); /* we don't expect a response */ OSMO_ASSERT(sgsn_tx_counter == 0); OSMO_ASSERT(ctx->gmm_state == GMM_REGISTERED_NORMAL); OSMO_ASSERT(ctx->p_tmsi_old == 0); OSMO_ASSERT(ctx->p_tmsi == ptmsi1); printf(" - RA Update Request (RA 1 -> RA 1)\n"); /* inject the RA update request */ send_0408_message(ctx->gb.llme, ms_tlli, &raid1, ra_upd_req1, ARRAY_SIZE(ra_upd_req1)); /* we expect an RA update accept */ OSMO_ASSERT(sgsn_tx_counter == 1); OSMO_ASSERT(last_dl_parse_ctx.g48_hdr->msg_type == GSM48_MT_GMM_RA_UPD_ACK); // OSMO_ASSERT(last_dl_parse_ctx.tlli == ms_tlli); OSMO_ASSERT(ctx->gmm_state == GMM_COMMON_PROC_INIT); OSMO_ASSERT(ctx->p_tmsi_old == ptmsi1); OSMO_ASSERT(ctx->p_tmsi != GSM_RESERVED_TMSI); OSMO_ASSERT(ctx->p_tmsi != ptmsi1); received_ptmsi = get_new_ptmsi(&last_dl_parse_ctx); OSMO_ASSERT(received_ptmsi == ctx->p_tmsi); ptmsi1 = received_ptmsi; /* inject the RA update complete */ ms_tlli = gprs_tmsi2tlli(ptmsi1, TLLI_LOCAL); send_0408_message(ctx->gb.llme, ms_tlli, &raid1, ra_upd_complete, ARRAY_SIZE(ra_upd_complete)); /* we don't expect a response */ OSMO_ASSERT(sgsn_tx_counter == 0); OSMO_ASSERT(ctx->gmm_state == GMM_REGISTERED_NORMAL); OSMO_ASSERT(ctx->p_tmsi_old == 0); OSMO_ASSERT(ctx->p_tmsi == ptmsi1); OSMO_ASSERT(ctx->gb.tlli == ms_tlli); printf(" - RA Update Request (RA 1 -> RA 2)\n"); /* inject the RA update request */ ms_tlli = gprs_tmsi2tlli(ptmsi1, TLLI_FOREIGN); /* It is coming from RA 1 => ra_upd_req1 */ send_0408_message(ctx->gb.llme, ms_tlli, &raid2, ra_upd_req1, ARRAY_SIZE(ra_upd_req1)); /* we expect an RA update accept */ OSMO_ASSERT(sgsn_tx_counter == 1); OSMO_ASSERT(last_dl_parse_ctx.g48_hdr->msg_type == GSM48_MT_GMM_RA_UPD_ACK); printf(" - RA Update Request (RA other -> RA 2)\n"); /* inject the RA update request */ ms_tlli = gprs_tmsi2tlli(0x12345678, TLLI_FOREIGN); /* It is coming from RA 1 => ra_upd_req1 */ send_0408_message(ctx->gb.llme, ms_tlli, &raid2, ra_upd_req_other, ARRAY_SIZE(ra_upd_req_other)); /* we expect an RA update reject (and a LLC XID RESET) */ OSMO_ASSERT(sgsn_tx_counter == 2); OSMO_ASSERT(last_dl_parse_ctx.g48_hdr->msg_type == GSM48_MT_GMM_RA_UPD_REJ); /* this has killed the LLE/LLME */ printf(" - Attach Request (RA 2)\n"); /* Create a LLE/LLME */ OSMO_ASSERT(count(gprs_llme_list()) == 1); lle = gprs_lle_get_or_create(ms_tlli, 3); OSMO_ASSERT(count(gprs_llme_list()) == 1); /* inject the attach request */ send_0408_message(lle->llme, ms_tlli, &raid2, attach_req2, ARRAY_SIZE(attach_req2)); ctx = sgsn_mm_ctx_by_tlli(ms_tlli, &raid2); OSMO_ASSERT(ctx != NULL); OSMO_ASSERT(ctx->gmm_state == GMM_COMMON_PROC_INIT); OSMO_ASSERT(ctx->p_tmsi != GSM_RESERVED_TMSI); /* we expect an attach accept */ OSMO_ASSERT(sgsn_tx_counter == 1); OSMO_ASSERT(last_dl_parse_ctx.g48_hdr->msg_type == GSM48_MT_GMM_ATTACH_ACK); received_ptmsi = get_new_ptmsi(&last_dl_parse_ctx); OSMO_ASSERT(received_ptmsi == ctx->p_tmsi); ptmsi1 = received_ptmsi; /* inject the attach complete */ ms_tlli = gprs_tmsi2tlli(ptmsi1, TLLI_LOCAL); ictx = sgsn_mm_ctx_by_tlli(ms_tlli, &raid2); OSMO_ASSERT(ictx != NULL); OSMO_ASSERT(ictx == ctx); send_0408_message(ctx->gb.llme, ms_tlli, &raid2, attach_compl, ARRAY_SIZE(attach_compl)); /* we don't expect a response */ OSMO_ASSERT(sgsn_tx_counter == 0); OSMO_ASSERT(ctx->gmm_state == GMM_REGISTERED_NORMAL); OSMO_ASSERT(ctx->p_tmsi_old == 0); OSMO_ASSERT(ctx->p_tmsi == ptmsi1); printf(" - RA Update Request (RA 2 -> RA 2)\n"); /* inject the RA update request */ send_0408_message(ctx->gb.llme, ms_tlli, &raid2, ra_upd_req2, ARRAY_SIZE(ra_upd_req2)); /* we expect an RA update accept */ OSMO_ASSERT(sgsn_tx_counter == 1); OSMO_ASSERT(last_dl_parse_ctx.g48_hdr->msg_type == GSM48_MT_GMM_RA_UPD_ACK); OSMO_ASSERT(ctx->gmm_state == GMM_COMMON_PROC_INIT); OSMO_ASSERT(ctx->p_tmsi_old == ptmsi1); OSMO_ASSERT(ctx->p_tmsi != GSM_RESERVED_TMSI); OSMO_ASSERT(ctx->p_tmsi != ptmsi1); received_ptmsi = get_new_ptmsi(&last_dl_parse_ctx); OSMO_ASSERT(received_ptmsi == ctx->p_tmsi); ptmsi1 = received_ptmsi; /* inject the RA update complete */ ms_tlli = gprs_tmsi2tlli(ptmsi1, TLLI_LOCAL); send_0408_message(ctx->gb.llme, ms_tlli, &raid2, ra_upd_complete, ARRAY_SIZE(ra_upd_complete)); /* we don't expect a response */ OSMO_ASSERT(sgsn_tx_counter == 0); OSMO_ASSERT(ctx->gmm_state == GMM_REGISTERED_NORMAL); OSMO_ASSERT(ctx->p_tmsi_old == 0); OSMO_ASSERT(ctx->p_tmsi == ptmsi1); OSMO_ASSERT(ctx->gb.tlli == ms_tlli); /* inject the detach */ send_0408_message(ctx->gb.llme, ms_tlli, &raid2, detach_req, ARRAY_SIZE(detach_req)); /* verify that things are gone */ OSMO_ASSERT(count(gprs_llme_list()) == 0); ictx = sgsn_mm_ctx_by_tlli(ms_tlli, &raid2); OSMO_ASSERT(!ictx); sgsn->cfg.auth_policy = saved_auth_policy; cleanup_test(); } static void test_apn_matching(void) { struct apn_ctx *actx, *actxs[9]; printf("Testing APN matching\n"); actxs[0] = sgsn_apn_ctx_find_alloc("*.test", ""); actxs[1] = sgsn_apn_ctx_find_alloc("*.def.test", ""); actxs[2] = sgsn_apn_ctx_find_alloc("abc.def.test", ""); actxs[3] = NULL; actxs[4] = sgsn_apn_ctx_find_alloc("abc.def.test", "456"); actxs[5] = sgsn_apn_ctx_find_alloc("abc.def.test", "456123"); actxs[6] = sgsn_apn_ctx_find_alloc("*.def.test", "456"); actxs[7] = sgsn_apn_ctx_find_alloc("*.def.test", "456123"); actxs[8] = sgsn_apn_ctx_find_alloc("ghi.def.test", "456"); actx = sgsn_apn_ctx_match("abc.def.test", "12345678"); OSMO_ASSERT(actx == actxs[2]); actx = sgsn_apn_ctx_match("aBc.dEf.test", "12345678"); OSMO_ASSERT(actx == actxs[2]); actx = sgsn_apn_ctx_match("xyz.def.test", "12345678"); OSMO_ASSERT(actx == actxs[1]); actx = sgsn_apn_ctx_match("xyz.dEf.test", "12345678"); OSMO_ASSERT(actx == actxs[1]); actx = sgsn_apn_ctx_match("xyz.uvw.test", "12345678"); OSMO_ASSERT(actx == actxs[0]); actx = sgsn_apn_ctx_match("xyz.uvw.foo", "12345678"); OSMO_ASSERT(actx == NULL); actxs[3] = sgsn_apn_ctx_find_alloc("*", ""); actx = sgsn_apn_ctx_match("xyz.uvw.foo", "12345678"); OSMO_ASSERT(actx == actxs[3]); actx = sgsn_apn_ctx_match("abc.def.test", "45699900"); OSMO_ASSERT(actx == actxs[4]); actx = sgsn_apn_ctx_match("xyz.def.test", "45699900"); OSMO_ASSERT(actx == actxs[6]); actx = sgsn_apn_ctx_match("abc.def.test", "45612300"); OSMO_ASSERT(actx == actxs[5]); actx = sgsn_apn_ctx_match("xyz.def.test", "45612300"); OSMO_ASSERT(actx == actxs[7]); actx = sgsn_apn_ctx_match("ghi.def.test", "45699900"); OSMO_ASSERT(actx == actxs[8]); actx = sgsn_apn_ctx_match("ghi.def.test", "45612300"); OSMO_ASSERT(actx == actxs[7]); /* Free APN contexts and check how the matching changes */ sgsn_apn_ctx_free(actxs[7]); actx = sgsn_apn_ctx_match("ghi.def.test", "45612300"); OSMO_ASSERT(actx == actxs[8]); sgsn_apn_ctx_free(actxs[8]); actx = sgsn_apn_ctx_match("ghi.def.test", "45612300"); OSMO_ASSERT(actx == actxs[6]); sgsn_apn_ctx_free(actxs[6]); actx = sgsn_apn_ctx_match("ghi.def.test", "45612300"); OSMO_ASSERT(actx == actxs[1]); sgsn_apn_ctx_free(actxs[5]); actx = sgsn_apn_ctx_match("abc.def.test", "45612300"); OSMO_ASSERT(actx == actxs[4]); sgsn_apn_ctx_free(actxs[4]); actx = sgsn_apn_ctx_match("abc.def.test", "45612300"); OSMO_ASSERT(actx == actxs[2]); sgsn_apn_ctx_free(actxs[2]); actx = sgsn_apn_ctx_match("abc.def.test", "12345678"); OSMO_ASSERT(actx == actxs[1]); sgsn_apn_ctx_free(actxs[1]); actx = sgsn_apn_ctx_match("abc.def.test", "12345678"); OSMO_ASSERT(actx == actxs[0]); sgsn_apn_ctx_free(actxs[0]); actx = sgsn_apn_ctx_match("abc.def.test", "12345678"); OSMO_ASSERT(actx == actxs[3]); sgsn_apn_ctx_free(actxs[3]); actx = sgsn_apn_ctx_match("abc.def.test", "12345678"); OSMO_ASSERT(actx == NULL); cleanup_test(); } struct sgsn_subscriber_pdp_data* sgsn_subscriber_pdp_data_alloc( struct sgsn_subscriber_data *sdata); static void test_ggsn_selection(void) { struct apn_ctx *actxs[4]; struct sgsn_ggsn_ctx *ggc, *ggcs[3]; struct gprs_subscr *s1; const char *imsi1 = "1234567890"; struct sgsn_mm_ctx *ctx; struct gprs_ra_id raid = { 0, }; uint32_t local_tlli = 0xffeeddcc; enum gsm48_gsm_cause gsm_cause; struct tlv_parsed tp; uint8_t apn_enc[GSM_APN_LENGTH + 10]; struct sgsn_subscriber_pdp_data *pdp_data; char apn_str[GSM_APN_LENGTH]; printf("Testing GGSN selection\n"); gsup_client_send_cb = my_gsup_client_send_dummy; /* Check for emptiness */ OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi1) == NULL); /* Create a context */ OSMO_ASSERT(count(gprs_llme_list()) == 0); ctx = alloc_mm_ctx(local_tlli, &raid); osmo_strlcpy(ctx->imsi, imsi1, sizeof(ctx->imsi)); /* Allocate and attach a subscriber */ s1 = gprs_subscr_get_or_create_by_mmctx(ctx); assert_subscr(s1, imsi1); tp.lv[GSM48_IE_GSM_APN].len = 0; tp.lv[GSM48_IE_GSM_APN].val = apn_enc; /* TODO: Add PDP info entries to s1 */ ggcs[0] = sgsn_ggsn_ctx_find_alloc(0); ggcs[1] = sgsn_ggsn_ctx_find_alloc(1); ggcs[2] = sgsn_ggsn_ctx_find_alloc(2); actxs[0] = sgsn_apn_ctx_find_alloc("test.apn", "123456"); actxs[0]->ggsn = ggcs[0]; actxs[1] = sgsn_apn_ctx_find_alloc("*.apn", "123456"); actxs[1]->ggsn = ggcs[1]; actxs[2] = sgsn_apn_ctx_find_alloc("*", "456789"); actxs[2]->ggsn = ggcs[2]; pdp_data = sgsn_subscriber_pdp_data_alloc(s1->sgsn_data); pdp_data->context_id = 1; pdp_data->pdp_type = 0x0121; osmo_strlcpy(pdp_data->apn_str, "*", sizeof(pdp_data->apn_str)); /* Resolve GGSNs */ tp.lv[GSM48_IE_GSM_APN].len = gprs_str_to_apn(apn_enc, sizeof(apn_enc), "Test.Apn"); ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause, apn_str); OSMO_ASSERT(ggc != NULL); OSMO_ASSERT(ggc->id == 0); OSMO_ASSERT(strcmp(apn_str, "Test.Apn") == 0); tp.lv[GSM48_IE_GSM_APN].len = gprs_str_to_apn(apn_enc, sizeof(apn_enc), "Other.Apn"); ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause, apn_str); OSMO_ASSERT(ggc != NULL); OSMO_ASSERT(ggc->id == 1); OSMO_ASSERT(strcmp(apn_str, "Other.Apn") == 0); tp.lv[GSM48_IE_GSM_APN].len = 0; tp.lv[GSM48_IE_GSM_APN].val = NULL; ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause, apn_str); OSMO_ASSERT(ggc != NULL); OSMO_ASSERT(ggc->id == 0); OSMO_ASSERT(strcmp(apn_str, "") == 0); actxs[3] = sgsn_apn_ctx_find_alloc("*", "123456"); actxs[3]->ggsn = ggcs[2]; ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause, apn_str); OSMO_ASSERT(ggc != NULL); OSMO_ASSERT(ggc->id == 2); OSMO_ASSERT(strcmp(apn_str, "") == 0); sgsn_apn_ctx_free(actxs[3]); tp.lv[GSM48_IE_GSM_APN].val = apn_enc; tp.lv[GSM48_IE_GSM_APN].len = gprs_str_to_apn(apn_enc, sizeof(apn_enc), "Foo.Bar"); ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause, apn_str); OSMO_ASSERT(ggc == NULL); OSMO_ASSERT(gsm_cause == GSM_CAUSE_MISSING_APN); OSMO_ASSERT(strcmp(apn_str, "Foo.Bar") == 0); tp.lv[GSM48_IE_GSM_APN].len = sizeof(apn_enc); ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause, apn_str); OSMO_ASSERT(ggc == NULL); OSMO_ASSERT(gsm_cause == GSM_CAUSE_INV_MAND_INFO); /* Add PDP data entry to subscriber */ osmo_strlcpy(pdp_data->apn_str, "Test.Apn", sizeof(pdp_data->apn_str)); tp.lv[GSM48_IE_GSM_APN].len = gprs_str_to_apn(apn_enc, sizeof(apn_enc), "Test.Apn"); ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause, apn_str); OSMO_ASSERT(ggc != NULL); OSMO_ASSERT(ggc->id == 0); OSMO_ASSERT(strcmp(apn_str, "Test.Apn") == 0); tp.lv[GSM48_IE_GSM_APN].len = gprs_str_to_apn(apn_enc, sizeof(apn_enc), "Other.Apn"); ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause, apn_str); OSMO_ASSERT(ggc == NULL); OSMO_ASSERT(gsm_cause == GSM_CAUSE_REQ_SERV_OPT_NOTSUB); OSMO_ASSERT(strcmp(apn_str, "") == 0); /* Cleanup */ gprs_subscr_put(s1); sgsn_mm_ctx_cleanup_free(ctx); assert_no_subscrs(); sgsn_apn_ctx_free(actxs[0]); sgsn_apn_ctx_free(actxs[1]); sgsn_apn_ctx_free(actxs[2]); sgsn_ggsn_ctx_free(ggcs[0]); sgsn_ggsn_ctx_free(ggcs[1]); sgsn_ggsn_ctx_free(ggcs[2]); gsup_client_send_cb = __real_gsup_client_send; cleanup_test(); } static struct log_info_cat gprs_categories[] = { [DMM] = { .name = "DMM", .description = "Layer3 Mobility Management (MM)", .color = "\033[1;33m", .enabled = 1, .loglevel = LOGL_DEBUG, }, [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, }, [DREF] = { .name = "DREF", .description = "Reference Counting", .enabled = 0, .loglevel = LOGL_NOTICE, }, [DGPRS] = { .name = "DGPRS", .description = "GPRS Packet Service", .enabled = 1, .loglevel = LOGL_DEBUG, }, [DNS] = { .name = "DNS", .description = "GPRS Network Service (NS)", .enabled = 1, .loglevel = LOGL_INFO, }, [DBSSGP] = { .name = "DBSSGP", .description = "GPRS BSS Gateway Protocol (BSSGP)", .enabled = 1, .loglevel = LOGL_DEBUG, }, [DLLC] = { .name = "DLLC", .description = "GPRS Logical Link Control Protocol (LLC)", .enabled = 1, .loglevel = LOGL_DEBUG, }, [DSNDCP] = { .name = "DSNDCP", .description = "GPRS Sub-Network Dependent Control Protocol (SNDCP)", .enabled = 1, .loglevel = LOGL_DEBUG, }, }; static struct log_info info = { .cat = gprs_categories, .num_cat = ARRAY_SIZE(gprs_categories), }; int main(int argc, char **argv) { void *osmo_sgsn_ctx; void *msgb_ctx; osmo_sgsn_ctx = talloc_named_const(NULL, 0, "osmo_sgsn"); osmo_init_logging2(osmo_sgsn_ctx, &info); tall_bsc_ctx = talloc_named_const(osmo_sgsn_ctx, 0, "bsc"); msgb_ctx = msgb_talloc_ctx_init(osmo_sgsn_ctx, 0); sgsn_rate_ctr_init(); sgsn_auth_init(); gprs_subscr_init(sgsn); test_llme(); test_subscriber(); test_auth_triplets(); test_subscriber_gsup(); test_gmm_detach(); test_gmm_detach_power_off(); test_gmm_detach_no_mmctx(); test_gmm_detach_accept_unexpected(); test_gmm_status_no_mmctx(); test_gmm_attach_acl(); test_gmm_attach_subscr(); test_gmm_attach_subscr_fake_auth(); test_gmm_attach_subscr_real_auth(); test_gmm_attach_subscr_gsup_auth(0); test_gmm_attach_subscr_gsup_auth(1); test_gmm_attach_subscr_real_gsup_auth(0); test_gmm_reject(); test_gmm_cancel(); test_gmm_ptmsi_allocation(); test_gmm_routing_areas(); test_apn_matching(); test_ggsn_selection(); printf("Done\n"); talloc_report_full(osmo_sgsn_ctx, stderr); OSMO_ASSERT(talloc_total_blocks(msgb_ctx) == 1); OSMO_ASSERT(talloc_total_blocks(tall_bsc_ctx) == 2); return 0; } /* stubs */ struct osmo_prim_hdr; int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx) { abort(); } osmo-sgsn-1.3.0/tests/sgsn/sgsn_test.ok000066400000000000000000000030661327264017000201270ustar00rootroot00000000000000Testing LLME allocations Testing core subscriber data API llist_count(gprs_subscribers) == 0 llist_count(gprs_subscribers) == 1 llist_count(gprs_subscribers) == 1 llist_count(gprs_subscribers) == 2 llist_count(gprs_subscribers) == 3 llist_count(gprs_subscribers) == 2 llist_count(gprs_subscribers) == 1 llist_count(gprs_subscribers) == 0 Testing authentication triplet handling Testing subscriber GSUP handling Testing GMM detach Testing GMM detach (power off) Testing GMM detach (no MMCTX) Testing GMM detach accept (unexpected) Testing GMM Status (no MMCTX) Auth policy 'closed': Testing GMM attach Auth policy 'remote': Testing GMM attach Auth policy 'remote', auth faked: Testing GMM attach Auth policy 'remote', triplet based auth: Testing GMM attach Auth policy 'remote', GSUP based auth: Testing GMM attach Auth policy 'remote', GSUP based auth: Testing GMM attach with retry Auth policy 'remote', real GSUP based auth: Testing GMM attach Testing GMM reject - Attach Request (invalid MI length) - Attach Request (invalid MI type) - Routing Area Update Request (valid) - Routing Area Update Request (invalid type) - Routing Area Update Request (invalid CAP length) Testing cancellation Testing P-TMSI allocation - sgsn_alloc_ptmsi - Repeated Attach Request - Repeated RA Update Request Testing routing area changes - Attach Request (RA 1) - RA Update Request (RA 1 -> RA 1) - RA Update Request (RA 1 -> RA 2) - RA Update Request (RA other -> RA 2) - Attach Request (RA 2) - RA Update Request (RA 2 -> RA 2) Testing APN matching Testing GGSN selection Done osmo-sgsn-1.3.0/tests/slhc/000077500000000000000000000000001327264017000155355ustar00rootroot00000000000000osmo-sgsn-1.3.0/tests/slhc/Makefile.am000066400000000000000000000004631327264017000175740ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBCARES_CFLAGS) EXTRA_DIST = slhc_test.ok noinst_PROGRAMS = slhc_test slhc_test_SOURCES = slhc_test.c slhc_test_LDADD = \ $(top_builddir)/src/gprs/slhc.o \ $(LIBOSMOCORE_LIBS) osmo-sgsn-1.3.0/tests/slhc/slhc_test.c000066400000000000000000000224021327264017000176710ustar00rootroot00000000000000/* Test SLHC/RFC1144 TCP/IP Header compression/decompression */ /* (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 /* Number of compression slots (S0-1) */ #define SLOTS 8 /* Maximum packet bytes to display */ #define DISP_MAX_BYTES 100 /* Sample packets to test with */ #define PACKETS_LEN 15 char *packets[] = { /* With TCP Option 10 (Timestamps) in place (forces UNCOMPRESSED_TCP) */ "4510004046dd40004006a9a7c0a8646ec0a864640017ad8b81980100f3ac984d801800e32a1600000101080a000647de06d1bf5efffd18fffd20fffd23fffd27", "4510005b46de40004006a98bc0a8646ec0a864640017ad8b8198010cf3ac984d801800e3867500000101080a000647df06d1bf61fffb03fffd1ffffd21fffe22fffb05fffa2001fff0fffa2301fff0fffa2701fff0fffa1801fff0", "4510003746df40004006a9aec0a8646ec0a864640017ad8b81980133f3ac989f801800e35fd700000101080a000647e106d1bf63fffd01", "4510003746e040004006a9adc0a8646ec0a864640017ad8b81980136f3ac98a2801800e35fd200000101080a000647e106d1bf64fffb01", "4510007446e140004006a96fc0a8646ec0a864640017ad8b81980139f3ac98a5801800e37b9b00000101080a000647e206d1bf640d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a57656c6c636f6d6520746f20706f6c6c75780d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a0d0a", "4510004246e240004006a9a0c0a8646ec0a864640017ad8b81980179f3ac98a5801800e3dab000000101080a000647ec06d1bf6f706f6c6c7578206c6f67696e3a20", /* Regular TCP packets (COMPRESSED_TCP) */ "4510003446dd40004006a9b3c0a8646ec0a864640017ad8b81980100f3ac984d501800e371410000fffd18fffd20fffd23fffd27", "4510004f46de40004006a997c0a8646ec0a864640017ad8b8198010cf3ac984d501800e3cda40000fffb03fffd1ffffd21fffe22fffb05fffa2001fff0fffa2301fff0fffa2701fff0fffa1801fff0", "4510002b46df40004006a9bac0a8646ec0a864640017ad8b81980133f3ac989f501800e3a70a0000fffd01", "4510002b46e040004006a9b9c0a8646ec0a864640017ad8b81980136f3ac98a2501800e3a7060000fffb01", "4510006846e140004006a97bc0a8646ec0a864640017ad8b81980139f3ac98a5501800e3c2d000000d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a57656c6c636f6d6520746f20706f6c6c75780d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a0d0a", "4510003646e240004006a9acc0a8646ec0a864640017ad8b81980179f3ac98a5501800e321fb0000706f6c6c7578206c6f67696e3a20", /* UDP packets (TYPE_IP */ "450000396e0b40004011a0310a0901650a09170105da003500255489a60f01000001000000000000076f736d6f636f6d036f72670000010001", "450000dc9eeb00004011aeae0a0917010a090165003505da00c83fbaa60f81800001000100030004076f736d6f636f6d036f72670000010001c00c00010001000079be0004904c2b4cc00c000200010000173d00130773756e6265616d08676e756d6f6e6b73c014c00c000200010000173d000603646e73c041c00c000200010000173d000a0767616e65736861c041c058000100010000173d0004d55f2e45c058001c00010000173d0010200107800045f0460000000000690001c06a0001000100006a710004d55f1b78c039000100010000173d000453ecb2cb", "45000037652340004011a91b0a0901650a091701ef1b0035002376a2c3910100000100000000000006676f6f676c650264650000010001", "0050b6162c10000db93a3ff908004500004726a6000038114083080808080a0901650035ef1b00338a8cc3918180000100010000000006676f6f676c650264650000010001c00c000100010000012b0004d83ad503", }; /* Compress a packet using Van Jacobson RFC1144 header compression */ static int compress(uint8_t *data_o, uint8_t *data_i, int len, struct slcompress *comp) { uint8_t *comp_ptr; /* Not used */ int compr_len; /* Create a working copy of the incoming data */ memcpy(data_o, data_i, len); /* Run compressor */ compr_len = slhc_compress(comp, data_i, len, data_o, &comp_ptr, 0); return compr_len; } /* Expand a packet using Van Jacobson RFC1144 header compression */ static int expand(uint8_t *data_o, uint8_t *data_i, int len, struct slcompress *comp) { int data_decompressed_len; /* Create a working copy of the incoming data */ memcpy(data_o, data_i, len); /* Handle an uncompressed packet (learn header information */ if ((data_i[0] & SL_TYPE_UNCOMPRESSED_TCP) == SL_TYPE_UNCOMPRESSED_TCP) { data_o[0] &= 0x4F; data_decompressed_len = slhc_remember(comp, data_o, len); return data_decompressed_len; } /* Uncompress compressed packets */ else if (data_o[0] & SL_TYPE_COMPRESSED_TCP) { data_decompressed_len = slhc_uncompress(comp, data_o, len); return data_decompressed_len; } /* Regular or unknown packets will not be touched */ return len; } /* Calculate IP Header checksum */ static uint16_t calc_ip_csum(uint8_t *data, int len) { int i; uint32_t accumulator = 0; uint16_t *pointer = (uint16_t *) data; for (i = len; i > 1; i -= 2) { accumulator += *pointer; pointer++; } if (len % 2) accumulator += *pointer; accumulator = (accumulator & 0xffff) + ((accumulator >> 16) & 0xffff); accumulator += (accumulator >> 16) & 0xffff; return (~accumulator); } /* Calculate TCP/IP checksum */ static uint16_t calc_tcpip_csum(const void *ctx, uint8_t *packet, int len) { uint8_t *buf; uint16_t csum; buf = talloc_zero_size(ctx, len); memset(buf, 0, len); memcpy(buf, packet + 12, 8); buf[9] = packet[9]; buf[11] = (len - 20) & 0xFF; buf[10] = (len - 20) >> 8 & 0xFF; memcpy(buf + 12, packet + 20, len - 20); csum = calc_ip_csum(buf, len - 20 + 12); talloc_free(buf); return csum; } /* Check TCP/IP packet */ static void check_packet(const void *ctx, uint8_t *packet, int len) { /* Check IP header */ OSMO_ASSERT(len > 20); OSMO_ASSERT(calc_ip_csum(packet, 20) == 0); printf("packet[9]=%02x\n", packet[9]); /* Check TCP packet */ if (packet[9] != 0x06) return; OSMO_ASSERT(len > 40); OSMO_ASSERT(calc_tcpip_csum(ctx, packet, len) == 0); } /* Compress / Decompress packets */ static void test_slhc(const void *ctx) { char packet_ascii[2048]; int i; struct slcompress *comp; uint8_t packet[1024]; int packet_len; uint8_t packet_compr[1024]; int packet_compr_len; uint8_t packet_decompr[1024]; int packet_decompr_len; printf("Allocating compression state...\n"); comp = slhc_init(ctx, SLOTS, SLOTS); OSMO_ASSERT(comp); for (i = 0; i < PACKETS_LEN; i++) { printf("Testing with packet No. %d\n", i); /* Read input file */ memset(packet_ascii, 0, sizeof(packet_ascii)); memset(packet, 0, sizeof(packet)); memset(packet_compr, 0, sizeof(packet_compr)); memset(packet_decompr, 0, sizeof(packet_decompr)); OSMO_ASSERT(strlen(packets[i]) < sizeof(packet_ascii)); strcpy(packet_ascii, packets[i]); packet_len = osmo_hexparse(packet_ascii, packet, sizeof(packet)); check_packet(ctx, packet, packet_len); /* Run compression/decompression algorithm */ printf("Compressing...\n"); packet_compr_len = compress(packet_compr, packet, packet_len, comp); printf("Decompressing...\n"); packet_decompr_len = expand(packet_decompr, packet_compr, packet_compr_len, comp); OSMO_ASSERT(packet_decompr_len == packet_len); check_packet(ctx, packet_decompr, packet_decompr_len); /* Display results */ printf("Results:\n"); if (packet_compr_len > DISP_MAX_BYTES) packet_compr_len = DISP_MAX_BYTES; if (packet_len > DISP_MAX_BYTES) packet_len = DISP_MAX_BYTES; if (packet_decompr_len > DISP_MAX_BYTES) packet_decompr_len = DISP_MAX_BYTES; printf("Original Packet: (%i bytes) %s\n", packet_len, osmo_hexdump_nospc(packet, packet_len)); printf("DecompressedPacket: (%i bytes) %s\n", packet_decompr_len, osmo_hexdump_nospc(packet_decompr, packet_decompr_len)); printf("CompressedPacket: (%i bytes) %s\n", packet_compr_len, osmo_hexdump_nospc(packet_compr, packet_compr_len)); slhc_o_status(comp); slhc_o_status(comp); printf("\n"); } printf("Freeing compression state...\n"); slhc_free(comp); printf("\n"); } static struct log_info_cat gprs_categories[] = { [DSNDCP] = { .name = "DSNDCP", .description = "GPRS Sub-Network Dependent Control Protocol (SNDCP)", .enabled = 1,.loglevel = LOGL_DEBUG, }, [DSLHC] = { .name = "DSLHC", .description = "Van Jacobson RFC1144 TCP/IP header compression (SLHC)", .enabled = 1,.loglevel = LOGL_DEBUG, } }; static struct log_info info = { .cat = gprs_categories, .num_cat = ARRAY_SIZE(gprs_categories), }; int main(int argc, char **argv) { void *ctx; void *log_ctx; ctx = talloc_named_const(NULL, 0, "slhc_ctx"); log_ctx = talloc_named_const(ctx, 0, "log"); osmo_init_logging2(log_ctx, &info); test_slhc(ctx); printf("Done\n"); talloc_report_full(ctx, stderr); talloc_free(log_ctx); OSMO_ASSERT(talloc_total_blocks(ctx) == 1); talloc_free(ctx); return 0; } /* stubs */ struct osmo_prim_hdr; int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx) { abort(); } osmo-sgsn-1.3.0/tests/slhc/slhc_test.ok000066400000000000000000000207301327264017000200620ustar00rootroot00000000000000Allocating compression state... Testing with packet No. 0 packet[9]=06 Compressing... Decompressing... packet[9]=06 Results: Original Packet: (64 bytes) 4510004046dd40004006a9a7c0a8646ec0a864640017ad8b81980100f3ac984d801800e32a1600000101080a000647de06d1bf5efffd18fffd20fffd23fffd27 DecompressedPacket: (64 bytes) 4510004046dd40004006a9a7c0a8646ec0a864640017ad8b81980100f3ac984d801800e32a1600000101080a000647de06d1bf5efffd18fffd20fffd23fffd27 CompressedPacket: (64 bytes) 7510004046dd40004000a9a7c0a8646ec0a864640017ad8b81980100f3ac984d801800e32a1600000101080a000647de06d1bf5efffd18fffd20fffd23fffd27 Testing with packet No. 1 packet[9]=06 Compressing... Decompressing... packet[9]=06 Results: Original Packet: (91 bytes) 4510005b46de40004006a98bc0a8646ec0a864640017ad8b8198010cf3ac984d801800e3867500000101080a000647df06d1bf61fffb03fffd1ffffd21fffe22fffb05fffa2001fff0fffa2301fff0fffa2701fff0fffa1801fff0 DecompressedPacket: (91 bytes) 4510005b46de40004006a98bc0a8646ec0a864640017ad8b8198010cf3ac984d801800e3867500000101080a000647df06d1bf61fffb03fffd1ffffd21fffe22fffb05fffa2001fff0fffa2301fff0fffa2701fff0fffa1801fff0 CompressedPacket: (91 bytes) 7510005b46de40004000a98bc0a8646ec0a864640017ad8b8198010cf3ac984d801800e3867500000101080a000647df06d1bf61fffb03fffd1ffffd21fffe22fffb05fffa2001fff0fffa2301fff0fffa2701fff0fffa1801fff0 Testing with packet No. 2 packet[9]=06 Compressing... Decompressing... packet[9]=06 Results: Original Packet: (55 bytes) 4510003746df40004006a9aec0a8646ec0a864640017ad8b81980133f3ac989f801800e35fd700000101080a000647e106d1bf63fffd01 DecompressedPacket: (55 bytes) 4510003746df40004006a9aec0a8646ec0a864640017ad8b81980133f3ac989f801800e35fd700000101080a000647e106d1bf63fffd01 CompressedPacket: (55 bytes) 7510003746df40004000a9aec0a8646ec0a864640017ad8b81980133f3ac989f801800e35fd700000101080a000647e106d1bf63fffd01 Testing with packet No. 3 packet[9]=06 Compressing... Decompressing... packet[9]=06 Results: Original Packet: (55 bytes) 4510003746e040004006a9adc0a8646ec0a864640017ad8b81980136f3ac98a2801800e35fd200000101080a000647e106d1bf64fffb01 DecompressedPacket: (55 bytes) 4510003746e040004006a9adc0a8646ec0a864640017ad8b81980136f3ac98a2801800e35fd200000101080a000647e106d1bf64fffb01 CompressedPacket: (55 bytes) 7510003746e040004000a9adc0a8646ec0a864640017ad8b81980136f3ac98a2801800e35fd200000101080a000647e106d1bf64fffb01 Testing with packet No. 4 packet[9]=06 Compressing... Decompressing... packet[9]=06 Results: Original Packet: (100 bytes) 4510007446e140004006a96fc0a8646ec0a864640017ad8b81980139f3ac98a5801800e37b9b00000101080a000647e206d1bf640d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a57656c6c636f6d6520746f20706f6c6c75780d0a2d2d2d2d2d2d DecompressedPacket: (100 bytes) 4510007446e140004006a96fc0a8646ec0a864640017ad8b81980139f3ac98a5801800e37b9b00000101080a000647e206d1bf640d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a57656c6c636f6d6520746f20706f6c6c75780d0a2d2d2d2d2d2d CompressedPacket: (100 bytes) 7510007446e140004000a96fc0a8646ec0a864640017ad8b81980139f3ac98a5801800e37b9b00000101080a000647e206d1bf640d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a57656c6c636f6d6520746f20706f6c6c75780d0a2d2d2d2d2d2d Testing with packet No. 5 packet[9]=06 Compressing... Decompressing... packet[9]=06 Results: Original Packet: (66 bytes) 4510004246e240004006a9a0c0a8646ec0a864640017ad8b81980179f3ac98a5801800e3dab000000101080a000647ec06d1bf6f706f6c6c7578206c6f67696e3a20 DecompressedPacket: (66 bytes) 4510004246e240004006a9a0c0a8646ec0a864640017ad8b81980179f3ac98a5801800e3dab000000101080a000647ec06d1bf6f706f6c6c7578206c6f67696e3a20 CompressedPacket: (66 bytes) 7510004246e240004000a9a0c0a8646ec0a864640017ad8b81980179f3ac98a5801800e3dab000000101080a000647ec06d1bf6f706f6c6c7578206c6f67696e3a20 Testing with packet No. 6 packet[9]=06 Compressing... Decompressing... packet[9]=06 Results: Original Packet: (52 bytes) 4510003446dd40004006a9b3c0a8646ec0a864640017ad8b81980100f3ac984d501800e371410000fffd18fffd20fffd23fffd27 DecompressedPacket: (52 bytes) 4510003446dd40004006a9b3c0a8646ec0a864640017ad8b81980100f3ac984d501800e371410000fffd18fffd20fffd23fffd27 CompressedPacket: (52 bytes) 7510003446dd40004000a9b3c0a8646ec0a864640017ad8b81980100f3ac984d501800e371410000fffd18fffd20fffd23fffd27 Testing with packet No. 7 packet[9]=06 Compressing... Decompressing... packet[9]=06 Results: Original Packet: (79 bytes) 4510004f46de40004006a997c0a8646ec0a864640017ad8b8198010cf3ac984d501800e3cda40000fffb03fffd1ffffd21fffe22fffb05fffa2001fff0fffa2301fff0fffa2701fff0fffa1801fff0 DecompressedPacket: (79 bytes) 4510004f46de40004006a997c0a8646ec0a864640017ad8b8198010cf3ac984d501800e3cda40000fffb03fffd1ffffd21fffe22fffb05fffa2001fff0fffa2301fff0fffa2701fff0fffa1801fff0 CompressedPacket: (43 bytes) df00cda4fffb03fffd1ffffd21fffe22fffb05fffa2001fff0fffa2301fff0fffa2701fff0fffa1801fff0 Testing with packet No. 8 packet[9]=06 Compressing... Decompressing... packet[9]=06 Results: Original Packet: (43 bytes) 4510002b46df40004006a9bac0a8646ec0a864640017ad8b81980133f3ac989f501800e3a70a0000fffd01 DecompressedPacket: (43 bytes) 4510002b46df40004006a9bac0a8646ec0a864640017ad8b81980133f3ac989f501800e3a70a0000fffd01 CompressedPacket: (9 bytes) dc00a70a5227fffd01 Testing with packet No. 9 packet[9]=06 Compressing... Decompressing... packet[9]=06 Results: Original Packet: (43 bytes) 4510002b46e040004006a9b9c0a8646ec0a864640017ad8b81980136f3ac98a2501800e3a7060000fffb01 DecompressedPacket: (43 bytes) 4510002b46e040004006a9b9c0a8646ec0a864640017ad8b81980136f3ac98a2501800e3a7060000fffb01 CompressedPacket: (7 bytes) db00a706fffb01 Testing with packet No. 10 packet[9]=06 Compressing... Decompressing... packet[9]=06 Results: Original Packet: (100 bytes) 4510006846e140004006a97bc0a8646ec0a864640017ad8b81980139f3ac98a5501800e3c2d000000d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a57656c6c636f6d6520746f20706f6c6c75780d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d DecompressedPacket: (100 bytes) 4510006846e140004006a97bc0a8646ec0a864640017ad8b81980139f3ac98a5501800e3c2d000000d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a57656c6c636f6d6520746f20706f6c6c75780d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d CompressedPacket: (68 bytes) db00c2d00d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a57656c6c636f6d6520746f20706f6c6c75780d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a0d0a Testing with packet No. 11 packet[9]=06 Compressing... Decompressing... packet[9]=06 Results: Original Packet: (54 bytes) 4510003646e240004006a9acc0a8646ec0a864640017ad8b81980179f3ac98a5501800e321fb0000706f6c6c7578206c6f67696e3a20 DecompressedPacket: (54 bytes) 4510003646e240004006a9acc0a8646ec0a864640017ad8b81980179f3ac98a5501800e321fb0000706f6c6c7578206c6f67696e3a20 CompressedPacket: (18 bytes) df0021fb706f6c6c7578206c6f67696e3a20 Testing with packet No. 12 packet[9]=11 Compressing... Decompressing... packet[9]=11 Results: Original Packet: (57 bytes) 450000396e0b40004011a0310a0901650a09170105da003500255489a60f01000001000000000000076f736d6f636f6d036f72670000010001 DecompressedPacket: (57 bytes) 450000396e0b40004011a0310a0901650a09170105da003500255489a60f01000001000000000000076f736d6f636f6d036f72670000010001 CompressedPacket: (57 bytes) 450000396e0b40004011a0310a0901650a09170105da003500255489a60f01000001000000000000076f736d6f636f6d036f72670000010001 Testing with packet No. 13 packet[9]=11 Compressing... Decompressing... packet[9]=11 Results: Original Packet: (100 bytes) 450000dc9eeb00004011aeae0a0917010a090165003505da00c83fbaa60f81800001000100030004076f736d6f636f6d036f72670000010001c00c00010001000079be0004904c2b4cc00c000200010000173d00130773756e6265616d08676e756d6f6e DecompressedPacket: (100 bytes) 450000dc9eeb00004011aeae0a0917010a090165003505da00c83fbaa60f81800001000100030004076f736d6f636f6d036f72670000010001c00c00010001000079be0004904c2b4cc00c000200010000173d00130773756e6265616d08676e756d6f6e CompressedPacket: (100 bytes) 450000dc9eeb00004011aeae0a0917010a090165003505da00c83fbaa60f81800001000100030004076f736d6f636f6d036f72670000010001c00c00010001000079be0004904c2b4cc00c000200010000173d00130773756e6265616d08676e756d6f6e Testing with packet No. 14 packet[9]=11 Compressing... Decompressing... packet[9]=11 Results: Original Packet: (55 bytes) 45000037652340004011a91b0a0901650a091701ef1b0035002376a2c3910100000100000000000006676f6f676c650264650000010001 DecompressedPacket: (55 bytes) 45000037652340004011a91b0a0901650a091701ef1b0035002376a2c3910100000100000000000006676f6f676c650264650000010001 CompressedPacket: (55 bytes) 45000037652340004011a91b0a0901650a091701ef1b0035002376a2c3910100000100000000000006676f6f676c650264650000010001 Freeing compression state... Done osmo-sgsn-1.3.0/tests/sndcp_xid/000077500000000000000000000000001327264017000165575ustar00rootroot00000000000000osmo-sgsn-1.3.0/tests/sndcp_xid/Makefile.am000066400000000000000000000007121327264017000206130ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBCARES_CFLAGS) EXTRA_DIST = sndcp_xid_test.ok noinst_PROGRAMS = sndcp_xid_test sndcp_xid_test_SOURCES = sndcp_xid_test.c sndcp_xid_test_LDADD = \ $(top_builddir)/src/gprs/gprs_sndcp_xid.o \ $(LIBOSMOABIS_LIBS) \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOGSM_LIBS) \ $(LIBOSMOGB_LIBS) \ $(LIBCARES_LIBS) \ $(LIBGTP_LIBS) \ -lrt -lm osmo-sgsn-1.3.0/tests/sndcp_xid/sndcp_xid_test.c000066400000000000000000000215531327264017000217430ustar00rootroot00000000000000/* Test SNDCP-XID Encoding/Decoding */ /* (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 /* Test SNDCP-XID decoding with a real world sample */ static void test_xid_decode_realworld(const void *ctx) { struct llist_head *comp_fields; int rc; printf("Testing SNDCP XID-Decoder/Encoder (real world data)\n"); /* Example of a real world SNDCP-XID message */ uint8_t xid[] = { 0x00, 0x01, 0x00, 0x02, 0x31, 0x82, 0x02, 0x27, 0x89, 0xff, 0xe0, 0x00, 0x0f, 0x00, 0xa8, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x02, 0x01, 0x02, 0x00, 0x03, 0x01, 0x03, 0x00, 0x04, 0x01, 0x04, 0x00, 0x05, 0x01, 0x05, 0x00, 0x06, 0x00, 0x07, 0x01, 0x07, 0x00, 0x08, 0x01, 0x08, 0x80, 0x00, 0x04, 0x12, 0x00, 0x40, 0x07 }; uint8_t xid_r[512]; /* Parse and show contained comp fields */ comp_fields = gprs_sndcp_parse_xid(NULL, ctx, xid, sizeof(xid), NULL); OSMO_ASSERT(comp_fields); printf("Decoded:\n"); gprs_sndcp_dump_comp_fields(comp_fields, DSNDCP); /* Encode comp-fields again */ rc = gprs_sndcp_compile_xid(xid_r,sizeof(xid_r), comp_fields, DEFAULT_SNDCP_VERSION); printf("Result length=%i\n",rc); printf("Encoded: %s\n", osmo_hexdump_nospc(xid, sizeof(xid))); printf("Rencoded: %s\n", osmo_hexdump_nospc(xid_r, rc)); OSMO_ASSERT(rc == 54); OSMO_ASSERT(memcmp(xid, xid_r, sizeof(xid)) == 0); /* Free comp fields */ talloc_free(comp_fields); printf("\n"); } /* Encode and decode test with artificial test data */ static void test_xid_encode_decode(const void *ctx) { printf("Testing SNDCP XID-Encoder/Decoder\n"); LLIST_HEAD(comp_fields); struct gprs_sndcp_pcomp_rfc1144_params rfc1144_params; struct gprs_sndcp_comp_field rfc1144_comp_field; struct gprs_sndcp_pcomp_rfc2507_params rfc2507_params; struct gprs_sndcp_comp_field rfc2507_comp_field; struct gprs_sndcp_pcomp_rohc_params rohc_params; struct gprs_sndcp_comp_field rohc_comp_field; struct gprs_sndcp_dcomp_v42bis_params v42bis_params; struct gprs_sndcp_comp_field v42bis_comp_field; struct gprs_sndcp_dcomp_v44_params v44_params; struct gprs_sndcp_comp_field v44_comp_field; struct llist_head *comp_fields_dec; uint8_t xid[512]; unsigned int xid_len = sizeof(xid); int rc; memset(&rfc1144_comp_field, 0, sizeof(struct gprs_sndcp_comp_field)); memset(&rfc2507_comp_field, 0, sizeof(struct gprs_sndcp_comp_field)); memset(&rohc_comp_field, 0, sizeof(struct gprs_sndcp_comp_field)); memset(&v42bis_comp_field, 0, sizeof(struct gprs_sndcp_comp_field)); memset(&v44_comp_field, 0, sizeof(struct gprs_sndcp_comp_field)); /* Setup which NSAPIs shall make use of rfc1144 */ rfc1144_params.nsapi[0] = 5; rfc1144_params.nsapi_len = 1; /* Setup rfc1144 operating parameters */ rfc1144_params.s01 = 7; /* Setup rfc1144 compression field */ rfc1144_comp_field.p = 1; rfc1144_comp_field.entity = 0; rfc1144_comp_field.algo = RFC_1144; rfc1144_comp_field.comp[RFC1144_PCOMP1] = 1; rfc1144_comp_field.comp[RFC1144_PCOMP2] = 2; rfc1144_comp_field.comp_len = RFC1144_PCOMP_NUM; rfc1144_comp_field.rfc1144_params = &rfc1144_params; /* Setup which NSAPIs shall make use of rfc1144 */ rfc2507_params.nsapi[0] = 6; rfc2507_params.nsapi_len = 1; /* Setup rfc2507 operating parameters */ rfc2507_params.f_max_period = 256; rfc2507_params.f_max_time = 5; rfc2507_params.max_header = 168; rfc2507_params.tcp_space = 15; rfc2507_params.non_tcp_space = 15; /* Setup rfc2507 compression field */ rfc2507_comp_field.p = 1; rfc2507_comp_field.entity = 1; rfc2507_comp_field.algo = RFC_2507; rfc2507_comp_field.comp[RFC2507_PCOMP1] = 3; rfc2507_comp_field.comp[RFC2507_PCOMP2] = 4; rfc2507_comp_field.comp[RFC2507_PCOMP3] = 5; rfc2507_comp_field.comp[RFC2507_PCOMP4] = 6; rfc2507_comp_field.comp[RFC2507_PCOMP5] = 7; rfc2507_comp_field.comp_len = RFC2507_PCOMP_NUM; rfc2507_comp_field.rfc2507_params = &rfc2507_params; /* Setup which NSAPIs shall make use of ROHC */ rohc_params.nsapi[0] = 5; rohc_params.nsapi[1] = 6; rohc_params.nsapi[2] = 7; rohc_params.nsapi[3] = 8; rohc_params.nsapi[4] = 9; rohc_params.nsapi[5] = 10; rohc_params.nsapi[6] = 11; rohc_params.nsapi[7] = 12; rohc_params.nsapi[8] = 13; rohc_params.nsapi[9] = 14; rohc_params.nsapi[10] = 15; rohc_params.nsapi_len = 11; /* Setup ROHC operating parameters */ rohc_params.max_cid = 15; /* default */ rohc_params.max_header = 168; /* default */ rohc_params.profile[0] = ROHC_UNCOMPRESSED; rohc_params.profile[1] = ROHC_RTP; rohc_params.profile[2] = ROHCV2_RTP; rohc_params.profile[3] = ROHC_UDP; rohc_params.profile[4] = ROHCv2_UDP; rohc_params.profile[5] = ROHC_ESP; rohc_params.profile[6] = ROHCV2_ESP; rohc_params.profile[7] = ROHC_IP; rohc_params.profile[8] = ROHCV2_IP; rohc_params.profile[9] = ROHC_LLA; rohc_params.profile[10] = ROHC_LLA_WITH_R_MODE; rohc_params.profile[11] = ROHC_TCP; rohc_params.profile[12] = ROHC_RTP_UDP_LITE; rohc_params.profile[13] = ROHCV2_RTP_UDP_LITE; rohc_params.profile[14] = ROHC_UDP_LITE; rohc_params.profile[15] = ROHCV2_UDP_LITE; rohc_params.profile_len = 16; /* Setup ROHC compression field */ rohc_comp_field.p = 1; rohc_comp_field.entity = 2; rohc_comp_field.algo = ROHC; rohc_comp_field.comp[ROHC_PCOMP1] = 8; rohc_comp_field.comp[ROHC_PCOMP2] = 9; rohc_comp_field.comp_len = ROHC_PCOMP_NUM; rohc_comp_field.rohc_params = &rohc_params; /* Setup which NSAPIs shall make use of v42bis */ v42bis_params.nsapi[0] = 5; v42bis_params.nsapi_len = 1; /* Setup v42bis operating parameters */ v42bis_params.p0 = 3; v42bis_params.p1 = 2048; v42bis_params.p2 = 20; /* Setup v42bis compression field */ v42bis_comp_field.p = 1; v42bis_comp_field.entity = 3; v42bis_comp_field.algo = V42BIS; v42bis_comp_field.comp[V42BIS_DCOMP1] = 10; v42bis_comp_field.comp_len = V42BIS_DCOMP_NUM; v42bis_comp_field.v42bis_params = &v42bis_params; /* Setup which NSAPIs shall make use of v44 */ v44_params.nsapi[0] = 5; v44_params.nsapi_len = 1; /* Setup v44 operating parameters */ v44_params.c0 = 0x80; v44_params.p0 = 3; v44_params.p1t = 300; v44_params.p1r = 300; v44_params.p3t = 600; v44_params.p3r = 600; /* Setup v44 compression field */ v44_comp_field.p = 1; v44_comp_field.entity = 3; v44_comp_field.algo = V44; v44_comp_field.comp[V44_DCOMP1] = 10; v44_comp_field.comp[V44_DCOMP2] = 11; v44_comp_field.comp_len = V44_DCOMP_NUM; v44_comp_field.v44_params = &v44_params; /* Add compression field(s) to list */ llist_add(&v44_comp_field.list, &comp_fields); llist_add(&v42bis_comp_field.list, &comp_fields); llist_add(&rfc1144_comp_field.list, &comp_fields); llist_add(&rfc2507_comp_field.list, &comp_fields); llist_add(&rohc_comp_field.list, &comp_fields); printf("Test input data:\n"); gprs_sndcp_dump_comp_fields(&comp_fields, DSNDCP); /* Encode SNDCP-XID fields */ rc = gprs_sndcp_compile_xid(xid, xid_len, &comp_fields, DEFAULT_SNDCP_VERSION); OSMO_ASSERT(rc > 0); printf("Encoded: %s (%i bytes)\n", osmo_hexdump_nospc(xid, rc), rc); /* Parse and show contained comp fields */ comp_fields_dec = gprs_sndcp_parse_xid(NULL, ctx, xid, rc, NULL); OSMO_ASSERT(comp_fields_dec); printf("Decoded:\n"); gprs_sndcp_dump_comp_fields(comp_fields_dec, DSNDCP); /* Free comp fields */ talloc_free(comp_fields_dec); } static struct log_info_cat gprs_categories[] = { [DSNDCP] = { .name = "DSNDCP", .description = "GPRS Sub-Network Dependent Control Protocol (SNDCP)", .enabled = 1,.loglevel = LOGL_DEBUG, } }; static struct log_info info = { .cat = gprs_categories, .num_cat = ARRAY_SIZE(gprs_categories), }; int main(int argc, char **argv) { void *xid_ctx; void *log_ctx; xid_ctx = talloc_named_const(NULL, 0, "xid_ctx"); log_ctx = talloc_named_const(xid_ctx, 0, "log"); osmo_init_logging2(log_ctx, &info); test_xid_decode_realworld(xid_ctx); test_xid_encode_decode(xid_ctx); printf("Done\n"); talloc_report_full(xid_ctx, stderr); talloc_free(log_ctx); OSMO_ASSERT(talloc_total_blocks(xid_ctx) == 1); talloc_free(xid_ctx); return 0; } /* stubs */ struct osmo_prim_hdr; int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx) { abort(); } osmo-sgsn-1.3.0/tests/sndcp_xid/sndcp_xid_test.ok000066400000000000000000000011261327264017000221240ustar00rootroot00000000000000Testing SNDCP XID-Decoder/Encoder (real world data) Decoded: Result length=54 Encoded: 000100023182022789ffe0000f00a8000000010101000201020003010300040104000501050006000701070008010880000412004007 Rencoded: 000100023182022789ffe0000f00a8000000010101000201020003010300040104000501050006000701070008010880000412004007 Testing SNDCP XID-Encoder/Decoder Test input data: Encoded: 000100011a83010dab00208003012c012c02580258830007a000200308001402408000041200200781010c3456700040010005a80f000f82022789ffe0000f00a80000000101010002010200030103000401040005010500060007010700080108 (97 bytes) Decoded: Done osmo-sgsn-1.3.0/tests/testsuite.at000066400000000000000000000037741327264017000171760ustar00rootroot00000000000000AT_INIT AT_BANNER([Regression tests.]) AT_SETUP([gprs]) AT_KEYWORDS([gprs]) cat $abs_srcdir/gprs/gprs_test.ok > expout AT_CHECK([$abs_top_builddir/tests/gprs/gprs_test], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([gbproxy]) AT_KEYWORDS([gbproxy]) cat $abs_srcdir/gbproxy/gbproxy_test.ok > expout AT_CHECK([$abs_top_builddir/tests/gbproxy/gbproxy_test], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([sgsn]) AT_KEYWORDS([sgsn]) AT_CHECK([test "$enable_sgsn_test" != no || exit 77]) cat $abs_srcdir/sgsn/sgsn_test.ok > expout AT_CHECK([$abs_top_builddir/tests/sgsn/sgsn_test], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([oap]) AT_KEYWORDS([oap]) AT_CHECK([test "$enable_oap_test" != no || exit 77]) cat $abs_srcdir/oap/oap_client_test.ok > expout cat $abs_srcdir/oap/oap_client_test.err > experr AT_CHECK([$abs_top_builddir/tests/oap/oap_client_test], [], [expout], [experr]) AT_CLEANUP AT_SETUP([gtphub]) AT_KEYWORDS([gtphub]) AT_CHECK([test "$enable_gtphub_test" != no || exit 77]) cat $abs_srcdir/gtphub/gtphub_test.ok > expout AT_CHECK([$abs_top_builddir/tests/gtphub/gtphub_test], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([xid]) AT_KEYWORDS([xid]) AT_CHECK([test "$enable_sgsn_test" != no || exit 77]) cat $abs_srcdir/xid/xid_test.ok > expout AT_CHECK([$abs_top_builddir/tests/xid/xid_test], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([sndcp_xid]) AT_KEYWORDS([sndcp_xid]) AT_CHECK([test "$enable_sgsn_test" != no || exit 77]) cat $abs_srcdir/sndcp_xid/sndcp_xid_test.ok > expout AT_CHECK([$abs_top_builddir/tests/sndcp_xid/sndcp_xid_test], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([slhc]) AT_KEYWORDS([slhc]) AT_CHECK([test "$enable_sgsn_test" != no || exit 77]) cat $abs_srcdir/slhc/slhc_test.ok > expout AT_CHECK([$abs_top_builddir/tests/slhc/slhc_test], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([v42bis]) AT_KEYWORDS([v42bis]) AT_CHECK([test "$enable_sgsn_test" != no || exit 77]) cat $abs_srcdir/v42bis/v42bis_test.ok > expout AT_CHECK([$abs_top_builddir/tests/v42bis/v42bis_test], [], [expout], [ignore]) AT_CLEANUP osmo-sgsn-1.3.0/tests/v42bis/000077500000000000000000000000001327264017000157155ustar00rootroot00000000000000osmo-sgsn-1.3.0/tests/v42bis/Makefile.am000066400000000000000000000004771327264017000177610ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBCARES_CFLAGS) EXTRA_DIST = v42bis_test.ok noinst_PROGRAMS = v42bis_test v42bis_test_SOURCES = v42bis_test.c v42bis_test_LDADD = \ $(top_builddir)/src/gprs/v42bis.o \ $(LIBOSMOCORE_LIBS) osmo-sgsn-1.3.0/tests/v42bis/v42bis_test.c000066400000000000000000001406411327264017000202370ustar00rootroot00000000000000/* Test v42bis Compression/Decompression */ /* (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 #include /* V.42bis compression parameters */ #define P0 3 /* Direction */ #define P1 512 /* Max number of codewords */ #define P2 20 /* Max string length */ /* V.42bis compression buffer size * (Does not affect the compression/decompression result) */ #define MAX_BLOCK_SIZE 1024 /* Compressed sample packets, sniffed from real communication */ #define COMPR_PACKETS_LEN 33 char *compr_packets[] = { /* K800i */ "4500010268000700004006cefac0a80002550d93d740000050462c7ba7e4d1753a80184000aad500000101080a0001a670084dafb4474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d6978656433005ab97a052b960d59b368d5b2ddb3e60e9c372ef610b6dbf56bd8b165030f2e7cf88dd63b68f64c3d9b76ed1cb58847b490d122e8d0a24761185913d50e1aa423f0dc49036387d6d7b169e4d0cac68e1cd93b70f0804123f7eee03372dcc801038f193b306a68b5b2864d9a3b629a30b1a2b5081b35384613357a07c6133271d4e021a3064d52347182ee81b119c69c3a72d2b079b37e4409c177e6f4902163738cdd71f8a0a903d68ec21866e4c0918185087dfb329cec9831834d951a337c4a2e1174891c3badf5e8d113a38f1c336e24520c8a65751d1844d4c7696d852c1f240e992becf8918d0c9145465441939fcc6a1950a206b7e1fca38e1145eaebc129230aeb24f57bcab011c3c68829f5efe7bfcbe4c814e731668c3042f6fef93a62d9909561e4c91123c163d0085a3a4e1c3466c6c649ea048d519d5ff3a0f95ef4280c2471269e61633ee9193469de8845a3554d9fa74199c48622e7fa7dac30ac602f9af40a9ef0236a54268247cd7f923946d0a8d1c3c68d1e35788c5002e54ad0a00100", "4500010268000900004006cef8c0a80002550d93d740000050462c7ba7e4d1753a801840007e7f00000101080a0001d1cc084db0ae474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d6978656433005ab97a052b960d59b368d5b2ddb3e60e9c372ef610b6dbf56bd8b165030f2e7cf88dd63b68f64c3d9b76ed1cb58847b490d122e8d0a24761185913d50e1aa423f0dc49036387d6d7b169e4d0cac68e1cd93b70f0804123f7eee03372dcc801038f193b306a68b5b2864d9a3b629a30b1a2b5081b35384613357a07c6133271d4e021a3064d52347182ee81b119c69c3a72d2b079b37e4409c177e6f4902163738cdd71f8a0a903d68ec21866e4c0918185087dfb329cec9831834d951a337c4a2e1174891c3badf5e8d113a38f1c336e24520c8a65751d1844d4c7696d852c1f240e992becf8918d0c9145465441939fcc6a1950a206b7e1fca38e1145eaebc129230aeb24f57bcab011c3c68829f5efe7bfcbe4c814e731668c3042f6fef93a62d9909561e4c91123c163d0085a3a4e1c3466c6c649ea048d519d5ff3a0f95ef4280c2471269e61633ee9193469de8845a3554d9fa74199c48622e7fa7dac30ac602f9af40a9ef0236a54268247cd7f923946d0a8d1c3c68d1e35788c5002e54ad0a00100", "4500010268000b00004006cef6c0a80002550d93d740000050462c7ba7e4d1753b80193fff131c00000101080a00022884084dc558474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d6978656433005ab97a052b960d59b368d5b2ddb3e60e9c372ef610b6dbf56bd8b165030f2e7cf88dd63b68f64c3d9b76ed1cb58847b490d122e8d0a24761185913d50e1aa423f0dc49036387d6d7b169e4d0cac68e1cd93b70f0804123f7eee03372dcc801038f193b306a68b5b2864d9a3b629a30b1a2b5081b35384613357a07c6133271d4e021a3064d52347182ee81b119c69c3a72d2b079b37e4409c177e6f4902163738cdd71f8a0a903d68ec21866e4c0918185087dfb329cec9831834d951a337c4a2e1174891c3badf5e8d113a38f1c336e24520c8a65751d1844d4c7696d852c1f240e992be4e8918d8c9045465441939fcc6a1950a206b7e1dca38e1145eaebb929230aeb24f579cab011c3c68829f5efe7afcbe4c814e731668c3042f6fef93a62d9909561e4c91123c163d0084a3a4e1c3466c6c649ea048dd19c5ff3a0f95ef4280c2471269e61633ee9193469de8845a3554d9fa74199c48622c7fa7dac30ac5c2f9af40a1ef0236a502682478dff913946d0a8d1c3c68d1e35788c5002e54ad0a00100", "4500010268000c00004006cef5c0a80002550d93d740000050462c7ba7e4d1753b80193fff65ab00000101080a0002d5f4084dc558474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d6978656433005ab97a052b960d59b368d5b2ddb3e60e9c372ef610b6dbf56bd8b165030f2e7cf88dd63b68f64c3d9b76ed1cb58847b490d122e8d0a24761185913d50e1aa423f0dc49036387d6d7b169e4d0cac68e1cd93b70f0804123f7eee03372dcc801038f193b306a68b5b2864d9a3b629a30b1a2b5081b35384613357a07c6133271d4e021a3064d52347182ee81b119c69c3a72d2b079b37e4409c177e6f4902163738cdd71f8a0a903d68ec21866e4c0918185087dfb329cec9831834d951a337c4a2e1174891c3badf5e8d113a38f1c336e24520c8a65751d1844d4c7696d852c1f240e992be4e8918d8c9045465441939fcc6a1950a206b7e1dca38e1145eaebb929230aeb24f579cab011c3c68829f5efe7afcbe4c814e731668c3042f6fef93a62d9909561e4c91123c163d0084a3a4e1c3466c6c649ea048dd19c5ff3a0f95ef4280c2471269e61633ee9193469de8845a3554d9fa74199c48622c7fa7dac30ac5c2f9af40a1ef0236a502682478dff913946d0a8d1c3c68d1e35788c5002e54ad0a00100", "450001022d000f00004006ac5ec0a800020a0901ab40011f4046a2f5a8e0a618025018400093480000474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e33005cbd8215bb67cd1d386f5cecd1cb766ad5ab59b7decdbbb7ef1ba877d0ec49daf56bd83960fd8e6821a3c5cd9c3b7bc230b2e6a81d343e47e0b99306c60ea8a54fd3c801958d1d39a877e0e00183c6ebd8b767e4b89103061e337660d4806a650d9b3477c4346162056a11366a7064d6c9f30e8c2764e2a8c143460d9a9f68e2dcdc0323328c3975e4a461f326fc881278efcce9214346e418b1e3f04153c7aa9dfd31ccc88123030b11f5ec6538d93163069b2a3566d0445ce2e612397646d398118347cd9a3a68f49848f12696d0756010011f67b415ad7c90fc17be5f224536322e16195105cd7b32a16540397adb06718a3a461459afe7a58c28a293acb729c3460c1b23a6ac6ffffe2d93235388c79831c288d6f6ddeb6065a355869127478cdcae79b3739c3868cc648df3d3091a9e31abe641537d674f1848e2203cc386fbcf3368d2bcc18a06aa9a3e456fde0c00", "450001022d001000004006ac5dc0a800020a0901ab40011f4046a2f5a8e0a618025018400093480000474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e33005cbd8215bb67cd1d386f5cecd1cb766ad5ab59b7decdbbb7ef1ba877d0ec49daf56bd83960fd8e6821a3c5cd9c3b7bc230b2e6a81d343e47e0b99306c60ea8a54fd3c801958d1d39a877e0e00183c6ebd8b767e4b89103061e337660d4806a650d9b3477c4346162056a11366a7064d6c9f30e8c2764e2a8c143460d9a9f68e2dcdc0323328c3975e4a461f326fc881278efcce9214346e418b1e3f04153c7aa9dfd31ccc88123030b11f5ec6538d93163069b2a3566d0445ce2e612397646d398118347cd9a3a68f49848f12696d0756010011f67b415ad7c90fc17be5f224536322e16195105cd7b32a16540397adb06718a3a461459afe7a58c28a293acb729c3460c1b23a6ac6ffffe2d93235388c79831c288d6f6ddeb6065a355869127478cdcae79b3739c3868cc648df3d3091a9e31abe641537d674f1848e2203cc386fbcf3368d2bcc18a06aa9a3e456fde0c00", "450001022d001100004006ac5cc0a800020a0901ab40011f4046a2f5a8e0a618025018400093480000474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e33005cbd8215bb67cd1d386f5cecd1cb766ad5ab59b7decdbbb7ef1ba877d0ec49daf56bd83960fd8e6821a3c5cd9c3b7bc230b2e6a81d343e47e0b99306c60ea8a54fd3c801958d1d39a877e0e00183c6ebd8b767e4b89103061e337660d4806a650d9b3477c4346162056a11366a7064d6c9f30e8c2764e2a8c143460d9a9f68e2dcdc0323328c3975e4a461f326fc881278efcce9214346e418b1e3f04153c7aa9dfd31ccc88123030b11f5ec6538d93163069b2a3566d0445ce2e612397646d398118347cd9a3a68f49848f12696d0756010011f67b415ad7c90fc17be5f224536322e16195105cd7b32a16540397adb06718a3a461459afe7a58c28a293acb729c3460c1b23a6ac6ffffe2d93235388c79831c288d6f6ddeb6065a355869127478cdcae79b3739c3868cc648df3d3091a9e31abe641537d674f1848e2203cc386fbcf3368d2bcc18a06aa9a3e456fde0c00", "450001022d001200004006ac5bc0a800020a0901ab40011f4046a2f5a8e0a618025018400093480000474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e33005cbd8215bb67cd1d386f5cecd1cb766ad5ab59b7decdbbb7ef1ba877d0ec49daf56bd83960fd8e6821a3c5cd9c3b7bc230b2e6a81d343e47e0b99306c60ea8a54fd3c801958d1d39a877e0e00183c6ebd8b767e4b89103061e337660d4806a650d9b3477c4346162056a11366a7064d6c9f30e8c2764e2a8c143460d9a9f68e2dcdc0323328c3975e4a461f326fc881278efcce9214346e418b1e3f04153c7aa9dfd31ccc88123030b11f5ec6538d93163069b2a3566d0445ce2e612397646d398118347cd9a3a68f49848f12696d0756010011f67b415ad7c90fc17be5f224536322e16195105cd7b32a16540397adb06718a3a461459afe7a58c28a293acb729c3460c1b23a6ac6ffffe2d93235388c79831c288d6f6ddeb6065a355869127478cdcae79b3739c3868cc648df3d3091a9e31abe641537d674f1848e2203cc386fbcf3368d2bcc18a06aa9a3e456fde0c00", "4500010268001300004006ceeec0a80002550d93d740000050462c7ba7e4d1753b80193fff7b4a00000101080a0003c054084dc558474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d6978656433005bbb7e0d3b964dd9b369d7b6ddb3e60e9c372ef614beeb15ac58b2660513368cf8cdd63b68f65045ab96ed9cb58947b490d1422851a34861185923d50e9aa423f0dc490363c756d8b269e4d8cac68e9cd93b70f0804143376fe13372dcc801038f193b306a6cb5b2864d9a3b629a30b1b2b5081b353848173d7a07c6133271d4e021a3068d52347184ee81c119c69c3a72d2b079c37e4489c177e6f4902183730cde71f8a0a913d6cec21866e4c091818548fdfb329cec9831834d951a337e4e2e2174891c3baef5e8d113a38f1c336e2656148a85751d1844d6c7716da52c1f240f9b2fecf8918d0c9145465441a39f0c6b1950a40ab7f1fca38e1145ecebc129234aeb24f67bcab011c3c68829f6f1ebb7cbe4c894e731668c3052163ffa3a63d9949561e4c91123c263d0105a3a4e1c3466c8c651ea04cd519d60f3a0016f14290c2471289e61735ee9193469de8c45b3554d1fa84299c88622e73afeac30ac6037aaf40a9ef0236a54268247cd7f923946d0a8d1c3c68d1e35788c5002e58a50a10100", "450001022d001400004006ac59c0a800020a0901ab40011f4046a2f5a8e0a618025018400093480000474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e33005cbd8215bb67cd1d386f5cecd1cb766ad5ab59b7decdbbb7ef1ba877d0ec49daf56bd83960fd8e6821a3c5cd9c3b7bc230b2e6a81d343e47e0b99306c60ea8a54fd3c801958d1d39a877e0e00183c6ebd8b767e4b89103061e337660d4806a650d9b3477c4346162056a11366a7064d6c9f30e8c2764e2a8c143460d9a9f68e2dcdc0323328c3975e4a461f326fc881278efcce9214346e418b1e3f04153c7aa9dfd31ccc88123030b11f5ec6538d93163069b2a3566d0445ce2e612397646d398118347cd9a3a68f49848f12696d0756010011f67b415ad7c90fc17be5f224536322e16195105cd7b32a16540397adb06718a3a461459afe7a58c28a293acb729c3460c1b23a6ac6ffffe2d93235388c79831c288d6f6ddeb6065a355869127478cdcae79b3739c3868cc648df3d3091a9e31abe641537d674f1848e2203cc386fbcf3368d2bcc18a06aa9a3e456fde0c00", "450001022d001500004006ac58c0a800020a0901ab40011f4046a2f5a8e0a618025018400093480000474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e33005cbd8215bb67cd1d386f5cecd1cb766ad5ab59b7decdbbb7ef1ba877d0ec49daf56bd83960fd8e6821a3c5cd9c3b7bc230b2e6a81d343e47e0b99306c60ea8a54fd3c801958d1d39a877e0e00183c6ebd8b767e4b89103061e337660d4806a650d9b3477c4346162056a11366a7064d6c9f30e8c2764e2a8c143460d9a9f68e2dcdc0323328c3975e4a461f326fc881278efcce9214346e418b1e3f04153c7aa9dfd31ccc88123030b11f5ec6538d93163069b2a3566d0445ce2e612397646d398118347cd9a3a68f49848f12696d0756010011f67b415ad7c90fc17be5f224536322e16195105cd7b32a16540397adb06718a3a461459afe7a58c28a293acb729c3460c1b23a6ac6ffffe2d93235388c79831c288d6f6ddeb6065a355869127478cdcae79b3739c3868cc648df3d3091a9e31abe641537d674f1848e2203cc386fbcf3368d2bcc18a06aa9a3e456fde0c00", "4500010239000500004006ac5cc0a800020a0901ab40001f90c286afa741a348cb801840007fcb0000050a41a348dc41a34a440000474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d69786564330057b36eedfa954dd8b165cfa6ddb3e60e9c372ef6049eab95ab57b062fd02164cf8cdd53b68f640256b16ed9cb38547b490d1e22791a043efc030b2c6a91d344547e0b99306c68eabad5fd3c871958d1d39b077e0e00183c6eddcbf67e4b89103061e337660d4b86a650d9b3477c4346162e56a11366a7080164d14c6133271d4e021a3068d5134717eee818119c69c3a72d2b079837e4489bf77e6f4902103738cdc71f8a0a9d3d58ec11866e4c091818548fcf9329cec9831834d951a33783e2ef173891c3bab69cc88c1a3674f1d347a6cdcf8134bea3a30889c8fb3da4a583e48162a37a891231b19208b8ca882c63e99d432a038fd6d8339471d238ac8d793534614d549e40b956123868d1153e4d3b77f97c99129cc63cc1861242c7df275beb2092bc3c89323467ef7fc693a4e1c3466c0c631ea04cdd09d5cf3a0e96e66e81d1848e2403cc366bcd13368d2bcf98ae6aa9a3e4c7ffe0c00", "450001025b000a00004006ac35c0a800020a0901ab40011f90c293b0a8af5e58be5018400072a60000474554202f72656470686f6e652e706e6720485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c33005cbd82154b368e59b46ad9ee597307ce1b177b066fedfa35ec583665010b266cf8cdd63b68f6543d9b76ed1cb58747b490d16268d1a34961185933d50e1aa523f0dc490363c7d6d7b169e4d8cac68e1cd93b70f0804123f7eee03372dcc801038f193b306a6cb5b2864d9a3b629a30b1b2b5081b3538461b457a07c6133238f190518366299a3843f7c0d80c634e1d3969d8bc513fa244e03b737ac890b139c6ee387cd0d4096b07610c3372e0c8c042647e7d194e76cc98c1a64a8d1940259718ba448e9dd63466c4e01134a80e1a3d38721c8a65751d1844d2c7696d65261f240d9923dcd8918d0c9045465441839fcc6a1950a606b7e1bca38e1145e8ebd929230aeb24f485cab011c3c68829f4ede3d7cbe4c814e731668c3032d3be1a3c75c6b2296be4c91123c1830e451d270e1a3364e32c758206694fb079d07c3f9a1406923812cfb0c1b9f40c9a346fc6a2d9aaa64fd4a175d33064b894bfff812b5bc2a421b3e60c8e32860e0d00", "4500010267001200004006ac21c0a800020a0901ab40011f90c293b0a8af5e58be80184000ee770000050aaf5e6437af5e8c230000474554202f72656470686f6e652e706e6720485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d33006fc8de41b3c72b5cb974e7cc853ca2858c164c9d42950ac3c81aae76d04c1d81e74e1a183bc8e2d64d2307593676e4ecde8183070c1ac2892b9f91e3460e1878ccd8815183ac95356cd2dc11d3848915b245d8a8c1c1fa69d43b309e90098a878c1a3454d1c461ba0706691873eac849c3e6cdfc112514df99d343860cd23188c7e183a68e5a3b126398910347061622fcfdcb70b263c60c36556acc48bab904d32572ecd8a63123060fa54a75d0e861d224532cb4ebc020223f8e6d2b3cf920b1585d62c9936c64a82c32a20a9a826468cb80c255b98deb27758c28d25f4f5119516a27e9df54868d1836464ce9ffbf20612647a65c8f316384119effd5e0a9c3968d5b234f8e1851ae94a9ec3871d098691b87aa1334518fa6cd83063d54a93090c4d978864d50aa67d0a479c3160d59357db432fd9ba66245aa0a193aac7953278d9e3f679894c1946900", "4500010236003000004006cf03c0a80002550d93d740020050c30e84a9441d06ac80184000c2f400000101080a00052df410fc31bd474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d6978656433005cbd82154b968d59b46ad9baddb3e60e9c372ef618c6fb35ecd8b26707173e9cf80dd73b68f6544dbbb6ed1cb68a47b490d16268d1a34961185933d50e1aa523f0dc49036307d7d8b369e4e0cac68e1cda3b70f080416377efe13372dcc801038f193b306a70b5b2864d9a3b629a30b1c2b5081b35384a1b457a07c6133271d4e021a306cd52347186ee81d119c69c3a72d2b079d37e4409c277e6f49021a3738cde71f8a0a923d60ec31866e4c0918185887dfc329cec9831834d951a3380522e3174891c3baff5e8d113a38f1c336e285a1c8aa5751d1844d8c7796dc52c1f24109d33f408928d8c9145465441b39f4c6b1950a60eb7011da48e1145eeebc929238aeb24f77dcab011c3c68829f7f3efbfcbe4c814e831668c3062367ffa3a64d9989561e4c91123c363d0186a3a4e1c3466cac659ea040dd29d61f3a0097f34290c24712a9e61837ee9193469de9045c3554d9fa843870600", "4500010260004500004006cec4c0a80002550d93d740030050c3134faac89c8b2980184000578d00000101080a000535c010fc34c8474554202f6e697276616e612e63737320485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d33006fcede41b3072c5dbb78e7dcad3ca2858c164ea14aa50ac3c81aaf76d0541d81e74e1a183bcef2f64d23c7593676e4fcde8183070c1ac6913b9f91e3460e187884c2a871d6ca1a3669ee8869c2c4cad9226cd4e0801d75ea1d184f7caac143460d1aab68e238dd0303358c3975e4a461f3c6fe88128fefcce9214306ea18c8e3f04153a7edd0b841e5c0918185c87f83329cec9831834d951a33967e2ee174891c3bbaf5e8d113a38f1c336e3ac2718a05771d1844eac7d16d252e1f241985563489928d8c954546544193900c6e1950bc3ab7a11da58e11450aea292a234aee240595cab011c3c68829050f2624cce4c814ed31668c3012f7a0fc3a6fd9c49561e4c91123ce63d0702a3b4e1c3466e0c6b1ea04cdd4a36cf3a0592f952a0c2471ccc839c3268e1aab67d0a479f316cd59357db83af59b062346ab0d1f46b47933e7ce9e329c3a0d00", "4500010264004600004006cebfc0a80002550d93d740040050c3135bab2189da61801840008f2d00000101080a000535c410fc34dc474554202f382d4269742f4c6162656c2e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d3300952fbf517b07cd1e9f77f3ee9da317f38816325a48a56a152b0c236bc4da419375049e3b6960ec50fb3b388d1c6ad9d891237c070e1e3068245f1e7d468e1b3960e0316307460db556d6b04973474c132656d4f254836376d5ab7760008da3060f193568b4a28923750f8cd530e6d4919386cd9bfc234a48be33a7870c19ab632c8fc3074d1db87630c63023078e0c2c440c2294e164c78c196caad498f1547409a94be4d8e9ad478f9e187de498710352a4542cbbebc020823f4e6f2b74f920e1c81da34a966c64bc2c32a20a1a866476cb802236ba0def2c758c2872500f521951782739d854868d1836464c39a89061612647a6788f31638411ba0aebd791cb86ae0c234f8e18891e838654da71e2a03133378e562768ae2a7d9b078d7bab5861208913f20c1bfa5acfa049f3462e1ab56afa80950a38cdc68d5a214aa4a873e74fa144474a951a00", "4500010264005800004006ceadc0a80002550d93d740050050c31389acaf7b26538018400075c900000101080a000537d010fc354a474554202f382d4269742f41636f726e2e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d3300942dbf497b07cd1e9f76f1ea9d93f7f28816325a48a56af50e0c236bc2da418375049e3b6960ec48eb1b388d1c69d9d891137c070e1e306820570e7d468e1b3960e0316307468db456d6b04973474c132656d2f2548343b699aa57613c2113470d1e326ad064451347ea1e18aa61cca923270d9b37f94794887c674e0f193254c7501e870f9a3a6fed608c11148e0c2c440c2294e164c78c196caad498f1347409a94be4d8e1ad478f9e187de4987103520e1ca95874d78141047f1cde56e6f241c2713bc6942bd9c87059644415340cc9e89601252c741bdd57ea1851e4a09ea432a2ec4e72d0a90c1b316c8c9872502143c34c8e4ce91e63c608237315d6af1397cd5c19469e1c31023d060da9b4e3c44163466e9cac4ed0585dea360f9af6efefc0401227e81936f4b39e4193e64d5c3469d5f4f92a15709a8d1bb342944831a7ce9e42898a942a3500", "4500010266007600004006ce8dc0a80002550d93d740060050c31431ada11fa06780184000f08e00000101080a00053b3c10fc35ef474554202f382d4269742f416d73747261642e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d33009733bf597b07cd9e9e78f5f2d5a379440b192da656bd9a1586913563eda0d13a02cf9d343076ac052e9c468eb56cecc819be03070f18349433973e23c78d1c30f098b103a3c65a2b6bd8a4b923a609132b6b77aac141db2ad63b309e9089a3060f193568b6a28933750f0cc830e6d4919386cd9bfd234a50be33a7870c199063308fc3074d9db87636c63023078e0c2c44103294e164c78c196caad49801957489a94be404ddaa478f9e187de4987133b2e4542cbcebc020a23f8e6f2b75f920f9d87d63cb976c64c82c32a20a9a876478cb803256ba8def2f758c28a2500f5319517a27512855868d1836464c51d8f061622647a67c8f31638491ba0defd799cba6ae0c234f8e18911e83c6d4db71e2a0314337ce562768b03a859b07cdfbab5961208943f20c1bfb5bcfa049f3662e9ab56afa849d3a388d478f5b2756bcf813e850a3484d4e9d1a00", "4500010264007700004006ce8ec0a80002550d93d740040050c3135ddb2189e0108018400060d600000101080a00053b4010fc35e7474554202f382d4269742f41746172692e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d3300932bbf497b07cd1e9f76f1ea9d93d7f28816325a48a56a152b0c236b84da419375049e3b6960ec48dbfb378d1c69d9d891037c070e1e30681c4ffe7c468e1b3960e0316307468db456d6b04973474c132656d2f254832376d5ab77603c2113470d1e326ad068451347ea1e18a961cca923270d9b37f84794807c674e0f193252c7481e870f9aa1da2fc63023078e0c2c440a1e94e164c78c196caad498f1147409a94be4d8d9ad478f9e187de49871e311a4542cb9ebc020723fce6e2b73f920d968e7224a956c64b42c32a20a9a856472cb8022f4b90dee2a758c286250cf511951742731c854868d1836464c31887061612647a6708f31638491b908e9d789cb66ae0c234f8e18791e8386d4d971e2a03123378e562768ae26759b070d7bab58612089f3f10c9bf95acfa049f3262e9ab46afa8095fa378d468d5a1f469c8833e7ce9f41434a951a00", "4500010264007c00004006ce89c0a80002550d93d740070050c314f3aefa37ceb18018400009f900000101080a00053e3410fc369e474554202f382d4269742f4170706c652e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d3300942dbf517b07cd1e9f76f1ea9d93f7f28816325a48a56a152b0c236bc4da419375049e3b6960ec50dbfb378d1c6ad9d891037c070e1e30681c4ffe7c468e1b3960e0316307460db556d6b04973474c132656d4f254832376d5ab77603c2113470d1e326ad068451347ea1e18a961cca923270d9ba15a4b44be33a7870c19a963248fc3074d9db7762cc63023078e0c2c400da691e164c78c196caad498f1147409a94be4d8d9ad478f9e187de49871e311a4542cb9ebc020723fce6e2b73f920d1a8dd224a956c64b42c32a20a9aa16472cb8022f6b90dee2a758c2862504f521951742731e854868d1836464c29a910cd602647a6708f31638491b908e9d789cb66ae0c234f8e18791e8386d4d971e2a03123378e562768ae2e7dc3260f1af656b1c24012e7e31936f3b59e4193e64d5c346ad5f4012bd56f9a8c19b53a84281167ce9d3f8b86942a3500", "4500010265007d00004006ce87c0a80002550d93d740050050c3138bdcaf7b296780183cec0de600000101080a00053e3410fc368e474554202f382d4269742f447261676f6e2e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d33009631bf597b07cd1e9f8df5f2d59379440b192da656bd9a1586913563eda0d13a02cf9d343076ac052e9c468eb56cecc819be03070f18349433973e23c78d1c30f098b103a3c65a2b6bd8a4b923a609132b6b79aac141db2ad63b309e9089a3068f50345bd1c499ba07c6631873eac849c3e68dfe112526df99d343868cc73198c7e183a64e5c3b1963989103470616220715ca70b263c60c36556acc804abac4d42572ecf8d6a3474f8c3e72ccb80939722a16de75601051c3dfb715a27c9074ec9e71654b363260161951054d4332bc6540192bddc6f7963a461441a847a98c28bd93208ce3d3460c1b23a6205cd89030932353bec79831c208d185f7ebcc6553d7c8932346a4c7a03175769c3868ccd08db3d5091aac4ce1e641f3fe6a561848e2883cc3c6fed63368d2bc998b66ad9a3e61a7fe4dc391e3d688132beeec0974a8519253a70600", "4500010269008000004006ce80c0a80002550d93d740060050c3143301dfa11fa3de80183c892b5d00000101080a0005412c10fc3761474554202f382d4269742f456e74657270726973652e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f6600952f67defcc66dd03d3df7f6fd3bc72fe7112d64b4b08a552b571846d6983dda75049e3b6960ec701b7c388d1c6e8dca21be03070f183496db694e63468e1b3960e0316307460db756d6b04973474c132656dcee5483c376d6ad77603c2113470d1e326ad0784513c7ea1e18ae6110959386cd1bfe234a58be33a7870c19ae63548fc347285d3b1b63989103470616220ad3c870b263c60c36556acc986aba84d52572ecfcd6a3474f8c3e72ccb81149d22a96a3756010d91fe7b715bc7c90d4f9b891a54b36326216195105cd4332476540312bdd0678973a4614a923478f531951d0dc49d299aa0c1b316c8c98d2b9e143c64c8e4c011f63c60823781be2af63970d5e19469e1c31223d060dabbce3c44163e66e1caf4ed06c853a370f1af85ab9c2401267e41936f7bd9e4193e68d5d346ed5f4216bd5701aa142bd4eac78d1e7cfa1489596b46a3500", "4500010268008100004006ce80c0a80002550d93d740040050c313600b2189e30280183d0e896b00000101080a000541d810fc379d474554202f382d4269742f436f6d6d6f646f72652e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f3300952f67defcc6ed1d347b7ceeedfb778e5fce235ac8686115ab56ae308cac316b074dd71178eea481b1c3edf0e23472b86563478ef11d3878c0a0d1fc79f519396ee48081c78c1d1835dc5a59c326cd1d314d985871cb530d8edb59b7de81f1844c1c3578c8a841e3154d1cab7b60bc8631a78e9c346cdef81f51c2f29d393d64c8781de3791c3e68ead0b5d3318699a032b01061e85086931d3366b0a95263c654d325ac2e916327b81e3d7a62f49163c64dc9a056b1fcae038348ff38c1ade0e5832424f88e2e61b29131b3c8882a682292f92d038ad9ea36c4c3d431a248433d4c6544019ea46154193662d81831a5e1c38889991c99223ec68c1146f03ed45fc72e1bbc328c3c3962a47a0c1a5671c78983c6ccdd385e9da0d9ea746e1e34f2b5728581248ec9336cf27b3d8326cd1bbb68dcaae943d62ae13420417aad78312350a1448d228523c3aad500", "4500010263008200004006ce84c0a80002550d93d740050050c3138e0daf7b2cf180183962cb2f00000101080a000542b410fc3822474554202f382d4269742f454143412e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b330096dfa4bd83668f4fbb78f5cec97b79440b192da24ead7a1586913561eda0c13a02cf9d343076a4f50d9c468eb46cecc809be03070f1834902b873e23c78d1c30f098b103a3465a2b6bd8a4b923a609132b6979aac1219baad53b309e9089a3060f193568b2a28913750f0cd530e6d4919386cd9bfc234a44be33a7870c19aa63288fc3074d9db77630c63023078e0c2c440c2294e164c78c196caad498e1347489a84be4d8e1ad478f9e187de49871035264542cbaebc020823f0e6f2b73f920e1b81da34a966c64bc2c32a20a1a866474cb801216ba8dee2c758c287250cf511951762739c854868d1836464c39a89021612647a6748f31638491b90aebd789cb66ae0c234f8e18811e8346d4d971e2a03123374e562768ac26759b074dfbaa5761208913f20c1bfa59cfa049f3262e9ab46afa7c8dfa37cdc68d59214aa4a873e7cfa04347468d1a00", "4500010262008500004006ce82c0a80002550d93d740070050c314f5defa37d29b80183c168b6100000101080a000542c810fc37e5474554202f382d4269742f4d53582e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b330095dfa0bd83668fcfba77f3cec16b79440b192da04aa56a1586913560eda0b93a02cf9d343076a0edfd9b460eb46cecc801be03070f18348e277f3e23c78d1c30f098b103a3065a2b6bd8a4b923a609132b6879aac1117b6ad53b309e9089a3060f193568b0a28903750f8cd430e6d4919386cd1bfc234a40be33a7870c19a963248fc3074d1db7762ec63023078e0c2c440a1e94e164c78c196caad498d1147409a84be4d8d9ad478f9e187de49871f33124542cb9ebc020723fce6e2b72f920d9a8fd62ca956c64b82c32a20a9a856472cb8002f6b90dee2b758c2862508f511951742731b854868d1836464c318870e1602647a6708f31638411b908e9d781cb46ae0c234f8e18791e830654d971e2a03113370e562768aa226d9b070d7baa5661208903f20c9bf958cfa049f3062e1ab46afa7885ea378d468d581f469c9853674fa0424542851a00", "4500010265009400004006ce70c0a80002550d93d740060050c3143614a11fa986801836e1f43c00000101080a000545b010fc38b7474554202f382d4269742f4d617474656c2e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d3300942dbf512b744fcfbb79f7ced17b79440b192da452b58a1586913562eda0c93a02cf9d343076a8edfd9b460eb56cecc801be03070f18348e277f3e23c78d1c30f098b103a3865a2b6bd8a4b923a609132b6a77aac111bbead53b309e9089a3060f193568b4a28923750f8cd430e6d4919386cd1bfc234a44be33a7870c19a963248fc3074d1db87630c63023078e0c2c440a1e94e164c78c196caad498f1347409a94be4d8d9ad478f9e187de498710352a4542cb9ebc020723fce6e2b74f920e1a81da34a966c64bc2c32a20a1a866472cb8022f6b90dee2c758c2862504f521951742731e854868d1836464c31889021612647a6708f31638411ba08e9d791bbd8c89323469ec7a02195769c3868cc7ca5abd5099aab4bdfe641c3de2a561848e2843cc366bed63368d2bc018a46ad9a3e60a5fe4db371a356881229eae4f97368d19152a50600", "4500010263009900004006ce6dc0a80002550d93d740040050c313623f2189e66b801839a5739a00000101080a0005464c10fc38f3474554202f382d4269742f4f7269632e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b330095dfa4bd83668f4fc578f5eab13ca2858c1651a756bd0ac3c89ab076d0601d81e74e1a183bd2fa064e23475a3676e404df8183070c1ac895439f91e3460e1878ccd8815123ad95356cd2dc11d3848995b43cd5e0904dd5ea1d184fc8c45183878c1a3459d1c489ba0706631873eac849c3e64dfe112520df99d343860cc63194c7e18326e8768c31ccc88123030b1183086538d93163069b2a356638155d22ea12397678ebd1a327461f3966dc7c0c19158bee3a3088e08fc3db8a5c3e4838dac19872251b192e8b8ca882862119dd32a084856ea3fb4a1d238a1cd483544694dd490e36956123868d11530e2a642898c99129dd63cc186144aec2fa75e0b2912bc3c8932346a0c7a01135769c3868ccc48d93d5091aab4adde641d3beea551848e2803cc3867ed63368d2bc818b26ad9a3e5fa3f64db3716356881229e6d4d91328519151a30600", "4500010269009e00004006ce62c0a80002550d93d740060050c3143845a11fab60801840003c1c00000101080a000547d810fc38b7474554202f382d4269742f526164696f536861636b2e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f330096316be6fcc6ed1d347b7a46f60b584fe7112d64b4b08a552b5718468ad6b183a6eb083c77d2c0d8e17678711a39dcb2b123c7f80e1c3c60d068febcfa8c1c3772c0c063c60e8c1a6e89b24973474c132656dcee5483e376d6ad77603c2113470d1e326ad0784513c7ea1e189361cca923270d9b37fd4794b87c674e0f193226c7781e870f9a3a74ed748c61460e1c195888307428c3c98e1933d854a93163eae9125697c8b1135c8f1e3d31fac831e3a6e449ab587ed78141847f9ce056f0f24112127cc79731d9c8a0596444153411c9fc9601c56c751be263ea1851a4a19ea732a2004fd2b0aa0c1b316c8c98d2f061c4c54c8e4c111f63c60823781fe6af6357685e234f8e18a91e838655dd71e2a03173378e572768b6469d9b074d7cad5c61208963f20c1bfc5ecfa049f3c62e1ab76afa90b55a380d48905e2b5ecc1874e8d1a44b515ab51a00", "4500010266009f00004006ce64c0a80002550d93d740050050c313903caf7b2f9580183d5ce4ef00000101080a0005481810fc3934474554202f382d4269742f5068696c6970732e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d3300952fbf597b07cd1e9f8cf5f2d58379440b192da85ac5aa1586913563eda0d93a02cf9d343076acfd1d9c468eb56cecc811be03070f1834922f8f3e23c78d1c30f098b103a3c65a2b41d3dc11d3848995b53cd5e0987d35eb1d184fc8c45183878c1a345cd1c4a1ba0786631873eac8492334ff881292efcce9214386e318cbe3f0415307b19d8b31ccc88123030b1183086538d93163069b2a3566441d5d82ea1239767aebd1a327461f3966dc7c0c4915cbee3a3088e08fd3db0a5d3e483672bf9872251b192e8b8ca882e60d1a32bb6540191bdd86f7953a461439a847a98c28bc931c7c2ac3460c1b23a61c5428f830932353bcc79831c2085d85f5ebc8654357869127478c448f4183eaed3871d098991b87ab133459990ecd83a63d56ad3090c40179860d7dae67d0a4792317cd5a357d8852059c46a346ae0f234ecca9b32750a322a9520d00", "450001049b00ae00004006cc20c0a80002550d93d740050050c31396ceaf7b396e801840009ad400000101080a0005611010fc3d6b474554202f382d4269742f6d72776f6e672e67696620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d33009733bf617b07cd9e9f8ef7f6d5a379440b192da85ac5aa1586913564eda0d93a02cf9d343076b00d3e9c460eb66cecc821be03070f183496379f3e23c78d1c30f098b103a3065b2b6b92de11d3848915b63dd5e0a87d35eb1d184fc814c543460d1aae68e250dd0303328c3975e4a461f346ff881294efcce9214306e418cde3f0415347ae1d8c31ccc88123030b1183086538d93163069b2a3566442d5d82ea1239767eebd1a327461f3966dc801449154bef3a3088e48ff3db0a513e48387ac7a892251b192f8b8ca882862199de32a0909d6e033c4b1d238a1cd4a3544614df490e3e956123868d11530e2a644898c99129e063cc186184a8423578ead06563d7c8932346a6c7a04175769c3868ccd48dc3d5099aac4ce3e641f31eab561848e2843cc3a628d73368d2bca18b86ad9a3e62a9fe4db3712357881229eadce933e8d09154a92a417265848c1d307afec489074e198678e6108d31a7e8882557ae4c9111357903abdf7f7b052b96ec4f3868d51e7e1b77ae74198ef7f63d1a76ce52bf50cf4c9d7355b7ddb290634cae7cb9b4dec05f135b76c178c41d347b7e16eeabc732db16325a74cf7a07869135647bff0e3e7c075bdad3732c6f4e7c070e1ed37dcb993e23c78d1c30f07807cfd6ca1a3669ee8869c2c40adb9e6a70a8d60ae30999a278c8e4e78a260ed53d3020c33088900d43ae25204aa46818239f8d6cbc631c3a120b11ff6964a864c9e6e58ca8a54b505d220727579d3b7d06bd48158b52a6f99f72b542940f923a13ed600c39838d8c974546fc6548a63761b2d36d8067a96344913a72f428951105cd9d248a9fcab011c3c688298ad30c444398c99129e063cc18313bce683578eae8b66be4c91123d363246f003b0e1a33d2b93a41939569dc3c68d6df8181248e193967d814e57a064d9a3774d1b055d387cd1caa7fd36cdcc835618f853a63d07473510655aa0100", /* SGSN with IAXMODEM V.42bis */ "45000101a0f3d84000400679210a0901abc0a800021f90400538e210f07a827bb1501900ed7be90000485454502f312e3020323030204f4b0d0a5365727665723a2053696d706c65485454502f302e3620507974686f6e2f322e372e360d0a446174653a205765642c203301312041756720323031362030393a32383a353220474d540d0a436f6e74656e742d747970653a20746578742f68746d6c3b20636861727365743d5554462d380d0a436f6e74656e742d4c656e6774683a203232320d0a0d0a66003f481c9162e40a97294800002068746d6c20506600588a3c6162644409183200002f5733432f2f4454442048544d4c20332e322046696e616c2f2f454e223e3c68746d66006f8234f87187cd1d006c653e4469726563746f7279206c697374696e672066016f72202f3c2f7469746c990068bcff2823e70c9fff6b6a943f9f7e7dfbf7f1e7d7bf9f5fb1c2ef6beafcc7d3fd3b7ced646aa34f03a404fa3373a4c64113630efdec27534e7509538d7e32ff657044a8f1bb0c82067f72f71e00", "45000101a0e9a54000400683540a0901abc0a800021f904004437442f17a4ab3b1501900ed04900000485454502f312e3020323030204f4b0d0a5365727665723a2053696d706c65485454502f302e3620507974686f6e2f322e372e360d0a446174653a205765642c203301312041756720323031362030393a32373a353520474d540d0a436f6e74656e742d747970653a20746578742f68746d6c3b20636861727365743d5554462d380d0a436f6e74656e742d4c656e6774683a203232320d0a0d0a66003f481c9162e40a97294800002068746d6c20506600588a3c6162644409183200002f5733432f2f4454442048544d4c20332e322046696e616c2f2f454e223e3c68746d6c3e0a3c7469746c653e4469726563746f7279206c697374696e672066016f72202f3c2f7469746c990068beff2823e70c9f816b6a9847af9ebd7bf8f2e9dbc7af5ff142f06bea0cc4e31d7cfced646aa74f03a444fa3373a4c64113634e7ded28554e7d1953cd7e320365744cb811bc8c82078376ff1e00", }; /* Uncompressed sample packets, sniffed from real communication */ #define UNCOMPR_PACKETS_LEN 11 char *uncompr_packets[] = { "45000236000700004006cf2cc0a80002550d93d7400000501e200da7c0c95a70801840002e3700000101080a000174140853d489474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a207777772e7a6f636b2e636f6d0d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a0d0a", "4510004046dd40004006a9a7c0a8646ec0a864640017ad8b81980100f3ac984d801800e32a1600000101080a000647de06d1bf5efffd18fffd20fffd23fffd27", "4510005b46de40004006a98bc0a8646ec0a864640017ad8b8198010cf3ac984d801800e3867500000101080a000647df06d1bf61fffb03fffd1ffffd21fffe22fffb05fffa2001fff0fffa2301fff0fffa2701fff0fffa1801fff0", "4510003746df40004006a9aec0a8646ec0a864640017ad8b81980133f3ac989f801800e35fd700000101080a000647e106d1bf63fffd01", "4510003746e040004006a9adc0a8646ec0a864640017ad8b81980136f3ac98a2801800e35fd200000101080a000647e106d1bf64fffb01", "4510007446e140004006a96fc0a8646ec0a864640017ad8b81980139f3ac98a5801800e37b9b00000101080a000647e206d1bf640d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a57656c6c636f6d6520746f20706f6c6c75780d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a0d0a", "4510004246e240004006a9a0c0a8646ec0a864640017ad8b81980179f3ac98a5801800e3dab000000101080a000647ec06d1bf6f706f6c6c7578206c6f67696e3a20", "450001a0b41140004006b8e80a0901abc0a800021f904002d5b860b5bab240ae501900ed861d0000485454502f312e3020323030204f4b0d0a5365727665723a2053696d706c65485454502f302e3620507974686f6e2f322e372e360d0a446174653a205475652c2033302041756720323031362030393a34333a303720474d540d0a436f6e74656e742d747970653a20746578742f68746d6c3b20636861727365743d5554462d380d0a436f6e74656e742d4c656e6774683a203232320d0a0d0a3c21444f43545950452068746d6c205055424c494320222d2f2f5733432f2f4454442048544d4c20332e322046696e616c2f2f454e223e3c68746d6c3e0a3c7469746c653e4469726563746f7279206c697374696e6720666f72202f3c2f7469746c653e0a3c626f64793e0a3c68323e4469726563746f7279206c697374696e6720666f72202f3c2f68323e0a3c68723e0a3c756c3e0a3c6c693e3c6120687265663d2272656470686f6e652e706e67223e72656470686f6e652e706e673c2f613e0a3c2f756c3e0a3c68723e0a3c2f626f64793e0a3c2f68746d6c3e0a", "450000e2971b40003706026c550d93d7c0a8000200504047217f5922c903759c8018007c4fb400000101080a1153ce39002cf6e8485454502f312e3120333034204e6f74204d6f6469666965640d0a446174653a205475652c2033302041756720323031362031363a33363a343020474d540d0a5365727665723a204170616368650d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4b6565702d416c6976653a2074696d656f75743d322c206d61783d313030300d0a455461673a2022346338613134392d3436652d34323736386138656338656330220d0a0d0a", "450000e224f1400037067496550d93d7c0a80002005040489387ebf0c904389f8018007cec5700000101080a1153cf01002cf8fc485454502f312e3120333034204e6f74204d6f6469666965640d0a446174653a205475652c2033302041756720323031362031363a33363a343020474d540d0a5365727665723a204170616368650d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4b6565702d416c6976653a2074696d656f75743d322c206d61783d313030300d0a455461673a2022346338613338302d3861362d34323736383761323236383830220d0a0d0a", "450000e2b66140003706e325550d93d7c0a8000200504049fbb679bcc9051ea48018007cebea00000101080a1153cfdc002cfdb4485454502f312e3120333034204e6f74204d6f6469666965640d0a446174653a205475652c2033302041756720323031362031363a33363a343120474d540d0a5365727665723a204170616368650d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4b6565702d416c6976653a2074696d656f75743d322c206d61783d313030300d0a455461673a2022346338313336642d3138642d34353832306530393638303430220d0a0d0a", }; /* Calculate IP Header checksum */ static uint16_t calc_ip_csum(uint8_t *data, int len) { int i; uint32_t accumulator = 0; uint16_t *pointer = (uint16_t *) data; for (i = len; i > 1; i -= 2) { accumulator += *pointer; pointer++; } if (len % 2) accumulator += *pointer; accumulator = (accumulator & 0xffff) + ((accumulator >> 16) & 0xffff); accumulator += (accumulator >> 16) & 0xffff; return (~accumulator); } /* Calculate TCP/IP checksum */ static uint16_t calc_tcpip_csum(const void *ctx, uint8_t *packet, int len) { uint8_t *buf; uint16_t csum; buf = talloc_zero_size(ctx, len); memset(buf, 0, len); memcpy(buf, packet + 12, 8); buf[9] = packet[9]; buf[11] = (len - 20) & 0xFF; buf[10] = (len - 20) >> 8 & 0xFF; memcpy(buf + 12, packet + 20, len - 20); csum = calc_ip_csum(buf, len - 20 + 12); talloc_free(buf); return csum; } /* A simple function to show the ascii content of a packet */ void show_packet(uint8_t *packet, int len) { int i; char c; for (i = 0; i < len; i++) { c = packet[i]; if (c >= 0x20 && c <= 0x7E) printf("%c", c); else printf("."); } printf("\n"); } /* A struct to capture the output data of compressor and decompressor */ struct v42bis_output_buffer { uint8_t *buf; uint8_t *buf_pointer; int len; }; /* A simple testpattern generator */ static void gen_test_pattern(uint8_t *data, int len) { int i; for (i = 0; i < len; i++) data[i] = i & 0xF0; } /* Handler to capture the output data from the compressor */ void tx_v42bis_frame_handler(void *user_data, const uint8_t *pkt, int len) { struct v42bis_output_buffer *output_buffer = (struct v42bis_output_buffer *)user_data; memcpy(output_buffer->buf_pointer, pkt, len); output_buffer->buf_pointer += len; output_buffer->len += len; return; } /* Handler to capture the output data from the decompressor */ void tx_v42bis_data_handler(void *user_data, const uint8_t *buf, int len) { /* stub, never used */ OSMO_ASSERT(false); return; } /* Handler to capture the output data from the compressor */ void rx_v42bis_frame_handler(void *user_data, const uint8_t *pkt, int len) { /* stub, never used */ OSMO_ASSERT(false); return; } /* Handler to capture the output data from the decompressor */ void rx_v42bis_data_handler(void *user_data, const uint8_t *buf, int len) { struct v42bis_output_buffer *output_buffer = (struct v42bis_output_buffer *)user_data; memcpy(output_buffer->buf_pointer, buf, len); output_buffer->buf_pointer += len; output_buffer->len += len; return; } /* Test V.42bis compression and decompression */ static void v42bis(const void *ctx, int mode, uint8_t *testvec, int len) { v42bis_state_t *tx_state; v42bis_state_t *rx_state; uint8_t *uncompressed_original; uint8_t *compressed; uint8_t *uncompressed; uncompressed_original = talloc_zero_size(ctx, len); uncompressed = talloc_zero_size(ctx, len); /* Note: We allocate double the size for the compressed buffer, * because in some cases the compression may increase the amount. * of data. */ compressed = talloc_zero_size(ctx, len * 2); int rc; int rc_sum = 0; struct v42bis_output_buffer compressed_data; struct v42bis_output_buffer uncompressed_data; /* Initalize */ tx_state = v42bis_init(ctx, NULL, P0, P1, P2, &tx_v42bis_frame_handler, NULL, MAX_BLOCK_SIZE, &tx_v42bis_data_handler, NULL, MAX_BLOCK_SIZE); OSMO_ASSERT(tx_state); rx_state = v42bis_init(ctx, NULL, P0, P1, P2, &rx_v42bis_frame_handler, NULL, MAX_BLOCK_SIZE, &rx_v42bis_data_handler, NULL, MAX_BLOCK_SIZE); OSMO_ASSERT(rx_state); v42bis_compression_control(tx_state, mode); /* Setup input data */ memcpy(uncompressed_original, testvec, len); /* Run compressor */ compressed_data.buf = compressed; compressed_data.buf_pointer = compressed; compressed_data.len = 0; tx_state->compress.user_data = (&compressed_data); rc = v42bis_compress(tx_state, uncompressed_original, len); printf("v42bis_compress() rc=%d\n", rc); OSMO_ASSERT(rc == 0); rc = v42bis_compress_flush(tx_state); printf("v42bis_compress_flush() rc=%d\n", rc); OSMO_ASSERT(rc == 0); /* Decompress again */ uncompressed_data.buf = uncompressed; uncompressed_data.buf_pointer = uncompressed; uncompressed_data.len = 0; rx_state->decompress.user_data = (&uncompressed_data); rc = v42bis_decompress(rx_state, compressed_data.buf, compressed_data.len); printf("v42bis_decompress() rc=%d\n", rc); rc = v42bis_decompress_flush(rx_state); rc_sum += rc; printf("v42bis_decompress_flush() rc=%d\n", rc); rc_sum += rc; /* Check results */ printf("Mode: %i\n", mode); printf("uncompressed_original= %s ASCII:", osmo_hexdump_nospc(uncompressed_original, len)); show_packet(uncompressed_original, len); printf("uncompressed= %s ASCII:", osmo_hexdump_nospc(uncompressed_data.buf, uncompressed_data.len)); show_packet(uncompressed_data.buf, uncompressed_data.len); printf("compressed= %s ASCII:", osmo_hexdump_nospc(compressed_data.buf, compressed_data.len)); show_packet(compressed_data.buf, compressed_data.len); rc = memcmp(uncompressed, uncompressed_original, len); printf("memcmp() rc=%d\n", rc); rc_sum += rc; OSMO_ASSERT(rc_sum == 0); /* Free buffers and exit */ v42bis_free(tx_state); v42bis_free(rx_state); talloc_free(uncompressed_original); talloc_free(compressed); talloc_free(uncompressed); printf("\n"); } /* Test V.42bis compression and decompression with generated data*/ static void test_v42bis(const void *ctx) { printf("Testing compression/decompression with generated data:\n"); uint8_t testvec[1024]; int len = sizeof(testvec); gen_test_pattern(testvec, len); v42bis(ctx, V42BIS_COMPRESSION_MODE_DYNAMIC, testvec, len); v42bis(ctx, V42BIS_COMPRESSION_MODE_ALWAYS, testvec, len); v42bis(ctx, V42BIS_COMPRESSION_MODE_NEVER, testvec, len); } /* Test V.42bis compression and decompression with some TCP/IP packets */ static void test_v42bis_tcpip(const void *ctx, int packet_id) { uint8_t *testvec; int len; printf ("Testing compression/decompression with realistic TCP/IP packets:\n"); printf("Packet No.: %i\n", packet_id); len = strlen(uncompr_packets[packet_id]); testvec = talloc_zero_size(ctx, len); len = osmo_hexparse(uncompr_packets[packet_id], testvec, len); OSMO_ASSERT(len > 0); v42bis(ctx, V42BIS_COMPRESSION_MODE_DYNAMIC, testvec, len); v42bis(ctx, V42BIS_COMPRESSION_MODE_ALWAYS, testvec, len); v42bis(ctx, V42BIS_COMPRESSION_MODE_NEVER, testvec, len); talloc_free(testvec); } /* Test V.42bis decompression with real, sniffed packets */ static void test_v42bis_tcpip_decompress(const void *ctx, int packet_id) { uint8_t *compressed; int compressed_len; uint8_t *uncompressed; v42bis_state_t *rx_state; int rc; int rc_sum = 0; int len; struct v42bis_output_buffer uncompressed_data; printf ("Testing decompression with sniffed compressed TCP/IP packets:\n"); printf("Packet No.: %i\n", packet_id); len = strlen(compr_packets[packet_id]); uncompressed = talloc_zero_size(ctx, len); compressed = talloc_zero_size(ctx, len); /* Initalize */ rx_state = v42bis_init(ctx, NULL, P0, P1, P2, &rx_v42bis_frame_handler, NULL, MAX_BLOCK_SIZE, &rx_v42bis_data_handler, NULL, MAX_BLOCK_SIZE); OSMO_ASSERT(rx_state); /* Setup input data */ compressed_len = osmo_hexparse(compr_packets[packet_id], compressed, len); /* Decompress */ uncompressed_data.buf = uncompressed; uncompressed_data.buf_pointer = uncompressed; uncompressed_data.len = 0; rx_state->decompress.user_data = (&uncompressed_data); rc = v42bis_decompress_flush(rx_state); printf("v42bis_decompress_flush() rc=%d\n", rc); rc_sum += rc; rc = v42bis_decompress(rx_state, compressed, compressed_len); printf("v42bis_decompress() rc=%d\n", rc); rc_sum += rc; rc = v42bis_decompress_flush(rx_state); printf("v42bis_decompress_flush() rc=%d\n", rc); rc_sum += rc; /* Check results */ printf("compressed= %s ASCII:", osmo_hexdump_nospc(compressed, compressed_len)); show_packet(compressed, compressed_len); printf("uncompressed= %s ASCII:", osmo_hexdump_nospc(uncompressed_data.buf, uncompressed_data.len)); show_packet(uncompressed_data.buf, uncompressed_data.len); OSMO_ASSERT(calc_ip_csum(uncompressed_data.buf, 20) == 0); OSMO_ASSERT(calc_tcpip_csum(ctx, uncompressed_data.buf, uncompressed_data.len) == 0); /* Free buffers and exit */ v42bis_free(rx_state); talloc_free(uncompressed); talloc_free(compressed); printf("\n"); } static struct log_info_cat gprs_categories[] = { [DV42BIS] = { .name = "DV42BIS", .description = "V.42bis data compression (SNDCP)", .enabled = 1,.loglevel = LOGL_DEBUG, } }; static struct log_info info = { .cat = gprs_categories, .num_cat = ARRAY_SIZE(gprs_categories), }; int main(int argc, char **argv) { void *v42bis_ctx; void *log_ctx; int i; v42bis_ctx = talloc_named_const(NULL, 0, "v42bis_ctx"); log_ctx = talloc_named_const(v42bis_ctx, 0, "log"); osmo_init_logging2(log_ctx, &info); test_v42bis(v42bis_ctx); for (i = 0; i < UNCOMPR_PACKETS_LEN; i++) test_v42bis_tcpip(v42bis_ctx, i); for (i = 0; i < COMPR_PACKETS_LEN; i++) test_v42bis_tcpip_decompress(v42bis_ctx, i); printf("Done\n"); talloc_report_full(v42bis_ctx, stderr); talloc_free(log_ctx); OSMO_ASSERT(talloc_total_blocks(v42bis_ctx) == 1); talloc_free(v42bis_ctx); return 0; } /* stubs */ struct osmo_prim_hdr; int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx) { abort(); } osmo-sgsn-1.3.0/tests/v42bis/v42bis_test.ok000066400000000000000000006227041327264017000204330ustar00rootroot00000000000000Testing compression/decompression with generated data: v42bis_compress() rc=0 v42bis_compress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 Mode: 0 uncompressed_original= 00000000000000000000000000000000101010101010101010101010101010102020202020202020202020202020202030303030303030303030303030303030404040404040404040404040404040405050505050505050505050505050505060606060606060606060606060606060707070707070707070707070707070708080808080808080808080808080808090909090909090909090909090909090a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f000000000000000000000000000000000101010101010101010101010101010102020202020202020202020202020202030303030303030303030303030303030404040404040404040404040404040405050505050505050505050505050505060606060606060606060606060606060707070707070707070707070707070708080808080808080808080808080808090909090909090909090909090909090a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f000000000000000000000000000000000101010101010101010101010101010102020202020202020202020202020202030303030303030303030303030303030404040404040404040404040404040405050505050505050505050505050505060606060606060606060606060606060707070707070707070707070707070708080808080808080808080808080808090909090909090909090909090909090a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f000000000000000000000000000000000101010101010101010101010101010102020202020202020202020202020202030303030303030303030303030303030404040404040404040404040404040405050505050505050505050505050505060606060606060606060606060606060707070707070707070707070707070708080808080808080808080808080808090909090909090909090909090909090a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0 ASCII:................................ 0000000000000000@@@@@@@@@@@@@@@@PPPPPPPPPPPPPPPP````````````````pppppppppppppppp................................................................................................................................................................ 0000000000000000@@@@@@@@@@@@@@@@PPPPPPPPPPPPPPPP````````````````pppppppppppppppp................................................................................................................................................................ 0000000000000000@@@@@@@@@@@@@@@@PPPPPPPPPPPPPPPP````````````````pppppppppppppppp................................................................................................................................................................ 0000000000000000@@@@@@@@@@@@@@@@PPPPPPPPPPPPPPPP````````````````pppppppppppppppp................................................................................................................................ uncompressed= 00000000000000000000000000000000101010101010101010101010101010102020202020202020202020202020202030303030303030303030303030303030404040404040404040404040404040405050505050505050505050505050505060606060606060606060606060606060707070707070707070707070707070708080808080808080808080808080808090909090909090909090909090909090a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f000000000000000000000000000000000101010101010101010101010101010102020202020202020202020202020202030303030303030303030303030303030404040404040404040404040404040405050505050505050505050505050505060606060606060606060606060606060707070707070707070707070707070708080808080808080808080808080808090909090909090909090909090909090a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f000000000000000000000000000000000101010101010101010101010101010102020202020202020202020202020202030303030303030303030303030303030404040404040404040404040404040405050505050505050505050505050505060606060606060606060606060606060707070707070707070707070707070708080808080808080808080808080808090909090909090909090909090909090a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f000000000000000000000000000000000101010101010101010101010101010102020202020202020202020202020202030303030303030303030303030303030404040404040404040404040404040405050505050505050505050505050505060606060606060606060606060606060707070707070707070707070707070708080808080808080808080808080808090909090909090909090909090909090a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0 ASCII:................................ 0000000000000000@@@@@@@@@@@@@@@@PPPPPPPPPPPPPPPP````````````````pppppppppppppppp................................................................................................................................................................ 0000000000000000@@@@@@@@@@@@@@@@PPPPPPPPPPPPPPPP````````````````pppppppppppppppp................................................................................................................................................................ 0000000000000000@@@@@@@@@@@@@@@@PPPPPPPPPPPPPPPP````````````````pppppppppppppppp................................................................................................................................................................ 0000000000000000@@@@@@@@@@@@@@@@PPPPPPPPPPPPPPPP````````````````pppppppppppppppp................................................................................................................................ compressed= 000100000000000000003300040b4e9870f020428409478c58b89021c38633663c7c081162c42143264ea448b1e29429172f62c49871cc988d1b3972ec3867cec78f2041861c3468e44892244b4e9a74f2244a9429478d5ab99225cb96b366bd7c091366cc61c366cea449b3e6b469376fe2c49973dcb89d3b79f2ec396fdecf9f4081062d587028c184098d226cd83029c388119942ac58f129c58c19a562ecd8b12ac79021b1822c59722bc99429bda26cd9322ccb9831c9c2ac59f32ccd9c39d5e2ecd9b32dcfa041e102254ad4e0d1a348952a5ddab4a953a850a34e9d4ad5aad5ab59b36ae5cab5ebd7af60c58a1d5bb6ac59b468d3ae5dcbd6addbb771e3ca9d3b7740ddba07efde5d9837efc3bd7b27f6ed7bf1efdf8d81037f1c3c7864e1c2270f1f5e9938f1cbc58b67366e7cf3f1e39d9123ff0c00 ASCII:..........3...N.p. B..G.X..!..3f<|..b.!C&N.H...)./b..q....9r.8g... A..4h.H.$KN.t.$J.)G.Z..%...f.|..f.a.f..I...i7o...s...;y..9o...@..-Xp(...."l.0)....B.X.)....b...*..!..,Yr+..)..l.2,..1...Y.,..9.....-..A..%J....H.*]...S.P.N.J....Y.j......`...[..Y.h..].....q...;w@.....].7...{'..{..........wb.......8....G..|.......\.xf..7.?..9... memcmp() rc=0 v42bis_compress() rc=0 v42bis_compress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 Mode: 2 uncompressed_original= 00000000000000000000000000000000101010101010101010101010101010102020202020202020202020202020202030303030303030303030303030303030404040404040404040404040404040405050505050505050505050505050505060606060606060606060606060606060707070707070707070707070707070708080808080808080808080808080808090909090909090909090909090909090a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f000000000000000000000000000000000101010101010101010101010101010102020202020202020202020202020202030303030303030303030303030303030404040404040404040404040404040405050505050505050505050505050505060606060606060606060606060606060707070707070707070707070707070708080808080808080808080808080808090909090909090909090909090909090a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f000000000000000000000000000000000101010101010101010101010101010102020202020202020202020202020202030303030303030303030303030303030404040404040404040404040404040405050505050505050505050505050505060606060606060606060606060606060707070707070707070707070707070708080808080808080808080808080808090909090909090909090909090909090a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f000000000000000000000000000000000101010101010101010101010101010102020202020202020202020202020202030303030303030303030303030303030404040404040404040404040404040405050505050505050505050505050505060606060606060606060606060606060707070707070707070707070707070708080808080808080808080808080808090909090909090909090909090909090a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0 ASCII:................................ 0000000000000000@@@@@@@@@@@@@@@@PPPPPPPPPPPPPPPP````````````````pppppppppppppppp................................................................................................................................................................ 0000000000000000@@@@@@@@@@@@@@@@PPPPPPPPPPPPPPPP````````````````pppppppppppppppp................................................................................................................................................................ 0000000000000000@@@@@@@@@@@@@@@@PPPPPPPPPPPPPPPP````````````````pppppppppppppppp................................................................................................................................................................ 0000000000000000@@@@@@@@@@@@@@@@PPPPPPPPPPPPPPPP````````````````pppppppppppppppp................................................................................................................................ uncompressed= 00000000000000000000000000000000101010101010101010101010101010102020202020202020202020202020202030303030303030303030303030303030404040404040404040404040404040405050505050505050505050505050505060606060606060606060606060606060707070707070707070707070707070708080808080808080808080808080808090909090909090909090909090909090a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f000000000000000000000000000000000101010101010101010101010101010102020202020202020202020202020202030303030303030303030303030303030404040404040404040404040404040405050505050505050505050505050505060606060606060606060606060606060707070707070707070707070707070708080808080808080808080808080808090909090909090909090909090909090a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f000000000000000000000000000000000101010101010101010101010101010102020202020202020202020202020202030303030303030303030303030303030404040404040404040404040404040405050505050505050505050505050505060606060606060606060606060606060707070707070707070707070707070708080808080808080808080808080808090909090909090909090909090909090a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f000000000000000000000000000000000101010101010101010101010101010102020202020202020202020202020202030303030303030303030303030303030404040404040404040404040404040405050505050505050505050505050505060606060606060606060606060606060707070707070707070707070707070708080808080808080808080808080808090909090909090909090909090909090a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0 ASCII:................................ 0000000000000000@@@@@@@@@@@@@@@@PPPPPPPPPPPPPPPP````````````````pppppppppppppppp................................................................................................................................................................ 0000000000000000@@@@@@@@@@@@@@@@PPPPPPPPPPPPPPPP````````````````pppppppppppppppp................................................................................................................................................................ 0000000000000000@@@@@@@@@@@@@@@@PPPPPPPPPPPPPPPP````````````````pppppppppppppppp................................................................................................................................................................ 0000000000000000@@@@@@@@@@@@@@@@PPPPPPPPPPPPPPPP````````````````pppppppppppppppp................................................................................................................................ compressed= 0001000000000000000000000000000000101010101010101010101010101010102020202020202020202020202020202030303030303030303030303030303030404040404040404040404040404040405050505050505050505050505050505060606060606060606060606060606060707070707070707070707070707070708080808080808080808080808080808090909090909090909090909090909090a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f000000000000000000000000000000000101010101010101010101010101010102020202020202020202020202020202030303030303030303030303030303030404040404040404040404040404040405050505050505050505050505050505060606060606060606060606060606060707070707070707070707070707070708080808080808080808080808080808090909090909090909090909090909090a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f000000000000000000000000000000000101010101010101010101010101010102020202020202020202020202020202030303030303030303030303030303030404040404040404040404040404040405050505050505050505050505050505060606060606060606060606060606060707070707070707070707070707070708080808080808080808080808080808090909090909090909090909090909090a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f000000000000000000000000000000000101010101010101010101010101010102020202020202020202020202020202030303030303030303030303030303030404040404040404040404040404040405050505050505050505050505050505060606060606060606060606060606060707070707070707070707070707070708080808080808080808080808080808090909090909090909090909090909090a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0 ASCII:................................. 0000000000000000@@@@@@@@@@@@@@@@PPPPPPPPPPPPPPPP````````````````pppppppppppppppp................................................................................................................................................................ 0000000000000000@@@@@@@@@@@@@@@@PPPPPPPPPPPPPPPP````````````````pppppppppppppppp................................................................................................................................................................ 0000000000000000@@@@@@@@@@@@@@@@PPPPPPPPPPPPPPPP````````````````pppppppppppppppp................................................................................................................................................................ 0000000000000000@@@@@@@@@@@@@@@@PPPPPPPPPPPPPPPP````````````````pppppppppppppppp................................................................................................................................ memcmp() rc=0 Testing compression/decompression with realistic TCP/IP packets: Packet No.: 0 v42bis_compress() rc=0 v42bis_compress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 Mode: 0 uncompressed_original= 45000236000700004006cf2cc0a80002550d93d7400000501e200da7c0c95a70801840002e3700000101080a000174140853d489474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a207777772e7a6f636b2e636f6d0d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a0d0a ASCII:E..6....@..,....U...@..P. ....Zp..@..7........t..S..GET / HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/vnd.wap.wml, */*..Accept-Charset: utf-8, utf-16, iso-8859-1, iso-10646-ucs-2, Shift_JIS, Big5..Accept-Language: en..x-wap-profile: "http://wap.sonyericsson.com/UAprof/K800iR201.xml"..Host: www.zock.com..User-Agent: SonyEricssonK800i/R2B Release/Mar-13-2007 Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1..Connection: Keep-Alive..Accept-Encoding: deflate, gzip.... uncompressed= 45000236000700004006cf2cc0a80002550d93d7400000501e200da7c0c95a70801840002e3700000101080a000174140853d489474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a207777772e7a6f636b2e636f6d0d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a0d0a ASCII:E..6....@..,....U...@..P. ....Zp..@..7........t..S..GET / HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/vnd.wap.wml, */*..Accept-Charset: utf-8, utf-16, iso-8859-1, iso-10646-ucs-2, Shift_JIS, Big5..Accept-Language: en..x-wap-profile: "http://wap.sonyericsson.com/UAprof/K800iR201.xml"..Host: www.zock.com..User-Agent: SonyEricssonK800i/R2B Release/Mar-13-2007 Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1..Connection: Keep-Alive..Accept-Encoding: deflate, gzip.... compressed= 4500010236000700004006cf2cc0a80002550d93d7400000501e200da7c0c95a70801840002e3700000101080a000174140853d489474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c6963330062d990358b562ddb3d6beec079e3620f61bb5dbf861d5b36f0e0c287df68bd8366cfd4b369d7ce518b78440b192d820e2d7a1486913551eda0413a02cf9d343076687d1d9b460ead6cecc891bd03070f183472ef0e3e23c78d1c30f098b103a386562b6bd8a4b923a609132b5a8bb05183633451a377603c2113470d1e326ad024451327e81e189b61cca923270d9b37eb4794107c674e0f193236c7d81d870f9a3a60ed288c61460e1c195888d0b72fc3c98e1933d854a931c3a7e4124197c8b1d35a8f1e3d31fac831e34622c5a05856d78141447d9cd656c8f241e290b9c28e1fd9c8105964441534f9c9ac9601256a701bce3fea1851a4be1e9c32a2b04e52bfa70c1b316c8c9852ff7efebb4c8e4c711e63c6082364ef9faf23960d5919469e1c31123c068da0a5e3c44163666c9ca44ed018d5f9350f9aef458fc2401267e21936e6939e4193e68d58345ad5f4791a346800 ASCII:E...6....@..,....U...@..P. ....Zp..@..7........t..S..GET / HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, applic3.b..5.V-.=k..y.b.a.]...[6.....h..f...i..Q.xD..-..-z...5Q..A:...40vh}..F..l........4r..>#...0......V+k...#...+Z..Q.c4Q.w`r...H1(..u`.Q.....|.8d...G62D..Q.M~2.e@......:F......(....).F..#.......#S...1.......eCV..'G...A#h.8q....'..4Fu~...{..0...x....g..y#..V5}..... memcmp() rc=0 v42bis_compress() rc=0 v42bis_compress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 Mode: 2 uncompressed_original= 45000236000700004006cf2cc0a80002550d93d7400000501e200da7c0c95a70801840002e3700000101080a000174140853d489474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a207777772e7a6f636b2e636f6d0d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a0d0a ASCII:E..6....@..,....U...@..P. ....Zp..@..7........t..S..GET / HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/vnd.wap.wml, */*..Accept-Charset: utf-8, utf-16, iso-8859-1, iso-10646-ucs-2, Shift_JIS, Big5..Accept-Language: en..x-wap-profile: "http://wap.sonyericsson.com/UAprof/K800iR201.xml"..Host: www.zock.com..User-Agent: SonyEricssonK800i/R2B Release/Mar-13-2007 Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1..Connection: Keep-Alive..Accept-Encoding: deflate, gzip.... uncompressed= 45000236000700004006cf2cc0a80002550d93d7400000501e200da7c0c95a70801840002e3700000101080a000174140853d489474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a207777772e7a6f636b2e636f6d0d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a0d0a ASCII:E..6....@..,....U...@..P. ....Zp..@..7........t..S..GET / HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/vnd.wap.wml, */*..Accept-Charset: utf-8, utf-16, iso-8859-1, iso-10646-ucs-2, Shift_JIS, Big5..Accept-Language: en..x-wap-profile: "http://wap.sonyericsson.com/UAprof/K800iR201.xml"..Host: www.zock.com..User-Agent: SonyEricssonK800i/R2B Release/Mar-13-2007 Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1..Connection: Keep-Alive..Accept-Encoding: deflate, gzip.... compressed= 4500010236000700004006cf2cc0a80002550d93d7400000501e200da7c0c95a70801840002e3700000101080a000174140853d489474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a207777772e7a6f636b2e636f6d0d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d3133012d323030372042726f777365722f4e657446726f6e742f332e332050726f6601696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a0d0a ASCII:E...6....@..,....U...@..P. ....Zp..@..7........t..S..GET / HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/vnd.wap.wml, */*..Accept-Charset: utf-8, utf-16, iso-8859-1, iso-10646-ucs-2, Shift_JIS, Big5..Accept-Language: en..x-wap-profile: "http://wap.sonyericsson.com/UAprof/K800iR201.xml"..Host: www.zock.com..User-Agent: SonyEricssonK800i/R2B Release/Mar-13.-2007 Browser/NetFront/3.3 Prof.ile/MIDP-2.0 Configuration/CLDC-1.1..Connection: Keep-Alive..Accept-Encoding: deflate, gzip.... memcmp() rc=0 Testing compression/decompression with realistic TCP/IP packets: Packet No.: 1 v42bis_compress() rc=0 v42bis_compress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 Mode: 0 uncompressed_original= 4510004046dd40004006a9a7c0a8646ec0a864640017ad8b81980100f3ac984d801800e32a1600000101080a000647de06d1bf5efffd18fffd20fffd23fffd27 ASCII:E..@F.@.@.....dn..dd...........M....*.........G....^..... ..#..' uncompressed= 4510004046dd40004006a9a7c0a8646ec0a864640017ad8b81980100f3ac984d801800e32a1600000101080a000647de06d1bf5efffd18fffd20fffd23fffd27 ASCII:E..@F.@.@.....dn..dd...........M....*.........G....^..... ..#..' compressed= 451000014046dd40004006a9a7c0a8646ec0a864640017ad8b81980100f3ac984d801800e32a1600000101080a000647de06d1bf5efffd18fffd20fffd23fffd27 ASCII:E...@F.@.@.....dn..dd...........M....*.........G....^..... ..#..' memcmp() rc=0 v42bis_compress() rc=0 v42bis_compress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 Mode: 1 uncompressed_original= 4510004046dd40004006a9a7c0a8646ec0a864640017ad8b81980100f3ac984d801800e32a1600000101080a000647de06d1bf5efffd18fffd20fffd23fffd27 ASCII:E..@F.@.@.....dn..dd...........M....*.........G....^..... ..#..' uncompressed= 4510004046dd40004006a9a7c0a8646ec0a864640017ad8b81980100f3ac984d801800e32a1600000101080a000647de06d1bf5efffd18fffd20fffd23fffd27 ASCII:E..@F.@.@.....dn..dd...........M....*.........G....^..... ..#..' compressed= 45000013060c49026e48c104ac540d5b75268ec33367066880e588d0260203ecbdda0465d08601e65a641830800081050d062450122e013561610402dc5073444d1335550400 ASCII:E.....I.nH...T.[u&..3g.h....&......e....Zd.0......$P...5aa...PsDM.5U.. memcmp() rc=0 v42bis_compress() rc=0 v42bis_compress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 Mode: 2 uncompressed_original= 4510004046dd40004006a9a7c0a8646ec0a864640017ad8b81980100f3ac984d801800e32a1600000101080a000647de06d1bf5efffd18fffd20fffd23fffd27 ASCII:E..@F.@.@.....dn..dd...........M....*.........G....^..... ..#..' uncompressed= 4510004046dd40004006a9a7c0a8646ec0a864640017ad8b81980100f3ac984d801800e32a1600000101080a000647de06d1bf5efffd18fffd20fffd23fffd27 ASCII:E..@F.@.@.....dn..dd...........M....*.........G....^..... ..#..' compressed= 451000014046dd40004006a9a7c0a8646ec0a864640017ad8b81980100f3ac984d801800e32a1600000101080a000647de06d1bf5efffd18fffd20fffd23fffd27 ASCII:E...@F.@.@.....dn..dd...........M....*.........G....^..... ..#..' memcmp() rc=0 Testing compression/decompression with realistic TCP/IP packets: Packet No.: 2 v42bis_compress() rc=0 v42bis_compress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 Mode: 0 uncompressed_original= 4510005b46de40004006a98bc0a8646ec0a864640017ad8b8198010cf3ac984d801800e3867500000101080a000647df06d1bf61fffb03fffd1ffffd21fffe22fffb05fffa2001fff0fffa2301fff0fffa2701fff0fffa1801fff0 ASCII:E..[F.@.@.....dn..dd...........M.....u........G....a........!.."..... .....#.....'......... uncompressed= 4510005b46de40004006a98bc0a8646ec0a864640017ad8b8198010cf3ac984d801800e3867500000101080a000647df06d1bf61fffb03fffd1ffffd21fffe22fffb05fffa2001fff0fffa2301fff0fffa2701fff0fffa1801fff0 ASCII:E..[F.@.@.....dn..dd...........M.....u........G....a........!.."..... .....#.....'......... compressed= 451000015b46de40004006a98bc0a8646ec0a864640017ad8b8198010cf3ac984d801800e3867500000101080a000647df06d1bf61fffb03fffd1ffffd21fffe22fffb05fffa2001fff0fffa2301fff0fffa2701fff0fffa1801fff0 ASCII:E...[F.@.@.....dn..dd...........M.....u........G....a........!.."..... .....#.....'......... memcmp() rc=0 v42bis_compress() rc=0 v42bis_compress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 Mode: 1 uncompressed_original= 4510005b46de40004006a98bc0a8646ec0a864640017ad8b8198010cf3ac984d801800e3867500000101080a000647df06d1bf61fffb03fffd1ffffd21fffe22fffb05fffa2001fff0fffa2301fff0fffa2701fff0fffa1801fff0 ASCII:E..[F.@.@.....dn..dd...........M.....u........G....a........!.."..... .....#.....'......... uncompressed= 4510005b46de40004006a98bc0a8646ec0a864640017ad8b8198010cf3ac984d801800e3867500000101080a000647df06d1bf61fffb03fffd1ffffd21fffe22fffb05fffa2001fff0fffa2301fff0fffa2701fff0fffa1801fff0 ASCII:E..[F.@.@.....dn..dd...........M.....u........G....a........!.."..... .....#.....'......... compressed= 45000013067849126ec880210958391ab6ea4c9c8767ce0cd000cb11a14d041ed87bb509caa00d03cc25c233600001020b1a0c48a0445c026ac2c808f467402040113949080c58c2260281fd461010386fa809a348fba9583a74c3d200 ASCII:E....xI.n..!.X9...L..g.......M...{.......%.3`......H.D\.j....g@ @.9I..X.&...F..8o...H..X:t... memcmp() rc=0 v42bis_compress() rc=0 v42bis_compress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 Mode: 2 uncompressed_original= 4510005b46de40004006a98bc0a8646ec0a864640017ad8b8198010cf3ac984d801800e3867500000101080a000647df06d1bf61fffb03fffd1ffffd21fffe22fffb05fffa2001fff0fffa2301fff0fffa2701fff0fffa1801fff0 ASCII:E..[F.@.@.....dn..dd...........M.....u........G....a........!.."..... .....#.....'......... uncompressed= 4510005b46de40004006a98bc0a8646ec0a864640017ad8b8198010cf3ac984d801800e3867500000101080a000647df06d1bf61fffb03fffd1ffffd21fffe22fffb05fffa2001fff0fffa2301fff0fffa2701fff0fffa1801fff0 ASCII:E..[F.@.@.....dn..dd...........M.....u........G....a........!.."..... .....#.....'......... compressed= 451000015b46de40004006a98bc0a8646ec0a864640017ad8b8198010cf3ac984d801800e3867500000101080a000647df06d1bf61fffb03fffd1ffffd21fffe22fffb05fffa2001fff0fffa2301fff0fffa2701fff0fffa1801fff0 ASCII:E...[F.@.@.....dn..dd...........M.....u........G....a........!.."..... .....#.....'......... memcmp() rc=0 Testing compression/decompression with realistic TCP/IP packets: Packet No.: 3 v42bis_compress() rc=0 v42bis_compress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 Mode: 0 uncompressed_original= 4510003746df40004006a9aec0a8646ec0a864640017ad8b81980133f3ac989f801800e35fd700000101080a000647e106d1bf63fffd01 ASCII:E..7F.@.@.....dn..dd.......3........_.........G....c... uncompressed= 4510003746df40004006a9aec0a8646ec0a864640017ad8b81980133f3ac989f801800e35fd700000101080a000647e106d1bf63fffd01 ASCII:E..7F.@.@.....dn..dd.......3........_.........G....c... compressed= 451000013746df40004006a9aec0a8646ec0a864640017ad8b8198013301f3ac989f801800e35fd700000101080a000647e106d1bf63fffd01 ASCII:E...7F.@.@.....dn..dd.......3........._.........G....c... memcmp() rc=0 v42bis_compress() rc=0 v42bis_compress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 Mode: 1 uncompressed_original= 4510003746df40004006a9aec0a8646ec0a864640017ad8b81980133f3ac989f801800e35fd700000101080a000647e106d1bf63fffd01 ASCII:E..7F.@.@.....dn..dd.......3........_.........G....c... uncompressed= 4510003746df40004006a9aec0a8646ec0a864640017ad8b81980133f3ac989f801800e35fd700000101080a000647e106d1bf63fffd01 ASCII:E..7F.@.@.....dn..dd.......3........_.........G....c... compressed= 4500001306e848226ec880210958c51ab6ea4c9c8767ce0cd000cb11a14d046cd87bb549d4a00d03cc89d136600001020b1a0c48a0845c026ac2cc0804482000 ASCII:E.....H"n..!.X....L..g.......M.l.{.I.......6`......H..\.j....H . memcmp() rc=0 v42bis_compress() rc=0 v42bis_compress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 Mode: 2 uncompressed_original= 4510003746df40004006a9aec0a8646ec0a864640017ad8b81980133f3ac989f801800e35fd700000101080a000647e106d1bf63fffd01 ASCII:E..7F.@.@.....dn..dd.......3........_.........G....c... uncompressed= 4510003746df40004006a9aec0a8646ec0a864640017ad8b81980133f3ac989f801800e35fd700000101080a000647e106d1bf63fffd01 ASCII:E..7F.@.@.....dn..dd.......3........_.........G....c... compressed= 451000013746df40004006a9aec0a8646ec0a864640017ad8b8198013301f3ac989f801800e35fd700000101080a000647e106d1bf63fffd01 ASCII:E...7F.@.@.....dn..dd.......3........._.........G....c... memcmp() rc=0 Testing compression/decompression with realistic TCP/IP packets: Packet No.: 4 v42bis_compress() rc=0 v42bis_compress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 Mode: 0 uncompressed_original= 4510003746e040004006a9adc0a8646ec0a864640017ad8b81980136f3ac98a2801800e35fd200000101080a000647e106d1bf64fffb01 ASCII:E..7F.@.@.....dn..dd.......6........_.........G....d... uncompressed= 4510003746e040004006a9adc0a8646ec0a864640017ad8b81980136f3ac98a2801800e35fd200000101080a000647e106d1bf64fffb01 ASCII:E..7F.@.@.....dn..dd.......6........_.........G....d... compressed= 451000013746e040004006a9adc0a8646ec0a864640017ad8b81980136f3ac98a2801800e35fd200000101080a000647e106d1bf64fffb01 ASCII:E...7F.@.@.....dn..dd.......6........_.........G....d... memcmp() rc=0 v42bis_compress() rc=0 v42bis_compress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 Mode: 1 uncompressed_original= 4510003746e040004006a9adc0a8646ec0a864640017ad8b81980136f3ac98a2801800e35fd200000101080a000647e106d1bf64fffb01 ASCII:E..7F.@.@.....dn..dd.......6........_.........G....d... uncompressed= 4510003746e040004006a9adc0a8646ec0a864640017ad8b81980136f3ac98a2801800e35fd200000101080a000647e106d1bf64fffb01 ASCII:E..7F.@.@.....dn..dd.......6........_.........G....d... compressed= 4500001306e848326ec880210958c11ab6ea4c9c8767ce0cd000cb11a14d0472d87bb5a9d4a00d03cc89a936600001020b1a0c48a0845c026ac2ce08f4472000 ASCII:E.....H2n..!.X....L..g.......M.r.{.........6`......H..\.j....G . memcmp() rc=0 v42bis_compress() rc=0 v42bis_compress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 Mode: 2 uncompressed_original= 4510003746e040004006a9adc0a8646ec0a864640017ad8b81980136f3ac98a2801800e35fd200000101080a000647e106d1bf64fffb01 ASCII:E..7F.@.@.....dn..dd.......6........_.........G....d... uncompressed= 4510003746e040004006a9adc0a8646ec0a864640017ad8b81980136f3ac98a2801800e35fd200000101080a000647e106d1bf64fffb01 ASCII:E..7F.@.@.....dn..dd.......6........_.........G....d... compressed= 451000013746e040004006a9adc0a8646ec0a864640017ad8b81980136f3ac98a2801800e35fd200000101080a000647e106d1bf64fffb01 ASCII:E...7F.@.@.....dn..dd.......6........_.........G....d... memcmp() rc=0 Testing compression/decompression with realistic TCP/IP packets: Packet No.: 5 v42bis_compress() rc=0 v42bis_compress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 Mode: 0 uncompressed_original= 4510007446e140004006a96fc0a8646ec0a864640017ad8b81980139f3ac98a5801800e37b9b00000101080a000647e206d1bf640d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a57656c6c636f6d6520746f20706f6c6c75780d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a0d0a ASCII:E..tF.@.@..o..dn..dd.......9........{.........G....d..------------------..Wellcome to pollux..------------------.... uncompressed= 4510007446e140004006a96fc0a8646ec0a864640017ad8b81980139f3ac98a5801800e37b9b00000101080a000647e206d1bf640d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a57656c6c636f6d6520746f20706f6c6c75780d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a0d0a ASCII:E..tF.@.@..o..dn..dd.......9........{.........G....d..------------------..Wellcome to pollux..------------------.... compressed= 451000017446e140004006a96fc0a8646ec0a864640017ad8b81980139f3ac98a5801800e37b9b00000101080a000647e206d1bf640d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d3300386d6a41f3e68d193970d08cb82367c41c3940f1ecb1b97327549e0d6c0600 ASCII:E...tF.@.@..o..dn..dd.......9........{.........G....d..----------------3.8mjA....9p...#g..9@....s'T..l.. memcmp() rc=0 v42bis_compress() rc=0 v42bis_compress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 Mode: 1 uncompressed_original= 4510007446e140004006a96fc0a8646ec0a864640017ad8b81980139f3ac98a5801800e37b9b00000101080a000647e206d1bf640d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a57656c6c636f6d6520746f20706f6c6c75780d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a0d0a ASCII:E..tF.@.@..o..dn..dd.......9........{.........G....d..------------------..Wellcome to pollux..------------------.... uncompressed= 4510007446e140004006a96fc0a8646ec0a864640017ad8b81980139f3ac98a5801800e37b9b00000101080a000647e206d1bf640d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a57656c6c636f6d6520746f20706f6c6c75780d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a0d0a ASCII:E..tF.@.@..o..dn..dd.......9........{.........G....d..------------------..Wellcome to pollux..------------------.... compressed= 4500001306dc49426ec880210958c919b6ea4c9c8767ce0cd000cb11a14d0478d87bb509d5a00d03ccf9f134600001020b1a0c48a0a45c026ac2ce40680003064e9c3973eac469530b9a376fccc8818366c41d3923e6c8018a678fcd9d3ba1f26c603300 ASCII:E.....IBn..!.X....L..g.......M.x.{.........4`......H..\.j..@h...N.9s..iS..7o....f..9#....g...;..l`3. memcmp() rc=0 v42bis_compress() rc=0 v42bis_compress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 Mode: 2 uncompressed_original= 4510007446e140004006a96fc0a8646ec0a864640017ad8b81980139f3ac98a5801800e37b9b00000101080a000647e206d1bf640d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a57656c6c636f6d6520746f20706f6c6c75780d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a0d0a ASCII:E..tF.@.@..o..dn..dd.......9........{.........G....d..------------------..Wellcome to pollux..------------------.... uncompressed= 4510007446e140004006a96fc0a8646ec0a864640017ad8b81980139f3ac98a5801800e37b9b00000101080a000647e206d1bf640d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a57656c6c636f6d6520746f20706f6c6c75780d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a0d0a ASCII:E..tF.@.@..o..dn..dd.......9........{.........G....d..------------------..Wellcome to pollux..------------------.... compressed= 451000017446e140004006a96fc0a8646ec0a864640017ad8b81980139f3ac98a5801800e37b9b00000101080a000647e206d1bf640d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a57656c6c636f6d6520746f20706f6c6c75780d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a0d0a ASCII:E...tF.@.@..o..dn..dd.......9........{.........G....d..------------------..Wellcome to pollux..------------------.... memcmp() rc=0 Testing compression/decompression with realistic TCP/IP packets: Packet No.: 6 v42bis_compress() rc=0 v42bis_compress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 Mode: 0 uncompressed_original= 4510004246e240004006a9a0c0a8646ec0a864640017ad8b81980179f3ac98a5801800e3dab000000101080a000647ec06d1bf6f706f6c6c7578206c6f67696e3a20 ASCII:E..BF.@.@.....dn..dd.......y..................G....opollux login: uncompressed= 4510004246e240004006a9a0c0a8646ec0a864640017ad8b81980179f3ac98a5801800e3dab000000101080a000647ec06d1bf6f706f6c6c7578206c6f67696e3a20 ASCII:E..BF.@.@.....dn..dd.......y..................G....opollux login: compressed= 451000014246e240004006a9a0c0a8646ec0a864640017ad8b81980179f3ac98a5801800e3dab000000101080a000647ec06d1bf6f706f6c6c7578206c6f67696e3a20 ASCII:E...BF.@.@.....dn..dd.......y..................G....opollux login: memcmp() rc=0 v42bis_compress() rc=0 v42bis_compress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 Mode: 1 uncompressed_original= 4510004246e240004006a9a0c0a8646ec0a864640017ad8b81980179f3ac98a5801800e3dab000000101080a000647ec06d1bf6f706f6c6c7578206c6f67696e3a20 ASCII:E..BF.@.@.....dn..dd.......y..................G....opollux login: uncompressed= 4510004246e240004006a9a0c0a8646ec0a864640017ad8b81980179f3ac98a5801800e3dab000000101080a000647ec06d1bf6f706f6c6c7578206c6f67696e3a20 ASCII:E..BF.@.@.....dn..dd.......y..................G....opollux login: compressed= 45000013061449526ec8802109588d1ab6ea4c9c8767ce0cd000cb11a14d04f8d87bb509d5a00d03cc759b35600001020b1a0c48a0e45d026ac2e4cc91f3e60d9e3d23dec851c3264e8f110100 ASCII:E.....IRn..!.X....L..g.......M...{.......u.5`......H..].j........=#..Q.&N.... memcmp() rc=0 v42bis_compress() rc=0 v42bis_compress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 Mode: 2 uncompressed_original= 4510004246e240004006a9a0c0a8646ec0a864640017ad8b81980179f3ac98a5801800e3dab000000101080a000647ec06d1bf6f706f6c6c7578206c6f67696e3a20 ASCII:E..BF.@.@.....dn..dd.......y..................G....opollux login: uncompressed= 4510004246e240004006a9a0c0a8646ec0a864640017ad8b81980179f3ac98a5801800e3dab000000101080a000647ec06d1bf6f706f6c6c7578206c6f67696e3a20 ASCII:E..BF.@.@.....dn..dd.......y..................G....opollux login: compressed= 451000014246e240004006a9a0c0a8646ec0a864640017ad8b81980179f3ac98a5801800e3dab000000101080a000647ec06d1bf6f706f6c6c7578206c6f67696e3a20 ASCII:E...BF.@.@.....dn..dd.......y..................G....opollux login: memcmp() rc=0 Testing compression/decompression with realistic TCP/IP packets: Packet No.: 7 v42bis_compress() rc=0 v42bis_compress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 Mode: 0 uncompressed_original= 450001a0b41140004006b8e80a0901abc0a800021f904002d5b860b5bab240ae501900ed861d0000485454502f312e3020323030204f4b0d0a5365727665723a2053696d706c65485454502f302e3620507974686f6e2f322e372e360d0a446174653a205475652c2033302041756720323031362030393a34333a303720474d540d0a436f6e74656e742d747970653a20746578742f68746d6c3b20636861727365743d5554462d380d0a436f6e74656e742d4c656e6774683a203232320d0a0d0a3c21444f43545950452068746d6c205055424c494320222d2f2f5733432f2f4454442048544d4c20332e322046696e616c2f2f454e223e3c68746d6c3e0a3c7469746c653e4469726563746f7279206c697374696e6720666f72202f3c2f7469746c653e0a3c626f64793e0a3c68323e4469726563746f7279206c697374696e6720666f72202f3c2f68323e0a3c68723e0a3c756c3e0a3c6c693e3c6120687265663d2272656470686f6e652e706e67223e72656470686f6e652e706e673c2f613e0a3c2f756c3e0a3c68723e0a3c2f626f64793e0a3c2f68746d6c3e0a ASCII:E.....@.@.............@...`...@.P.......HTTP/1.0 200 OK..Server: SimpleHTTP/0.6 Python/2.7.6..Date: Tue, 30 Aug 2016 09:43:07 GMT..Content-type: text/html; charset=UTF-8..Content-Length: 222.....Directory listing for /..

Directory listing for /

.
..
... uncompressed= 450001a0b41140004006b8e80a0901abc0a800021f904002d5b860b5bab240ae501900ed861d0000485454502f312e3020323030204f4b0d0a5365727665723a2053696d706c65485454502f302e3620507974686f6e2f322e372e360d0a446174653a205475652c2033302041756720323031362030393a34333a303720474d540d0a436f6e74656e742d747970653a20746578742f68746d6c3b20636861727365743d5554462d380d0a436f6e74656e742d4c656e6774683a203232320d0a0d0a3c21444f43545950452068746d6c205055424c494320222d2f2f5733432f2f4454442048544d4c20332e322046696e616c2f2f454e223e3c68746d6c3e0a3c7469746c653e4469726563746f7279206c697374696e6720666f72202f3c2f7469746c653e0a3c626f64793e0a3c68323e4469726563746f7279206c697374696e6720666f72202f3c2f68323e0a3c68723e0a3c756c3e0a3c6c693e3c6120687265663d2272656470686f6e652e706e67223e72656470686f6e652e706e673c2f613e0a3c2f756c3e0a3c68723e0a3c2f626f64793e0a3c2f68746d6c3e0a ASCII:E.....@.@.............@...`...@.P.......HTTP/1.0 200 OK..Server: SimpleHTTP/0.6 Python/2.7.6..Date: Tue, 30 Aug 2016 09:43:07 GMT..Content-type: text/html; charset=UTF-8..Content-Length: 222.....Directory listing for /..

Directory listing for /

.
..
... compressed= 45000101a0b41140004006b8e80a0901abc0a800021f904002d5b860b5bab240ae501900ed861d0000485454502f312e3020323030204f4b0d0a5365727665723a2053696d706c65485454502f302e3620507974686f6e2f322e372e360d0a446174653a205475652c203301302041756720323031362030393a34333a303720474d540d0a436f6e74656e742d747970653a20746578742f68746d6c3b20636861727365743d5554462d380d0a436f6e74656e742d4c656e6774683a206600356ad4c899f3078923528c5ce13205c908c58c9d6229f2848991112560c890a1050033432f2f4454442048544d4c20332e322046696e616c2f2f454e223e3c68746d6c3e0a3c7469746c653e4469726563746f7279206c697374696e672066016f72202f3c2f7469746c990068bcff2823e70c9fff6b6a943f9f7e7dfbf7f1e7d7bf9f3fb1c2ef6beafcc7d3fd3b7ced6468a34f03a404fa3373a4c64113630efdec27534e7509538d7e32ff657044a8f1bb0c82067f72f71e00 ASCII:E......@.@.............@...`...@.P.......HTTP/1.0 200 OK..Server: SimpleHTTP/0.6 Python/2.7.6..Date: Tue, 3.0 Aug 2016 09:43:07 GMT..Content-type: text/html; charset=UTF-8..Content-Length: f.5j......#R.\.2......b).....%`.....3C//DTD HTML 3.2 Final//EN">.Directory listing f.or /</titl..h..(#....kj.?.~}.......?...k.....;|.dh.O....3s..A.c...'SNu.S.~2.epD.......r... memcmp() rc=0 v42bis_compress() rc=0 v42bis_compress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 Mode: 1 uncompressed_original= 450001a0b41140004006b8e80a0901abc0a800021f904002d5b860b5bab240ae501900ed861d0000485454502f312e3020323030204f4b0d0a5365727665723a2053696d706c65485454502f302e3620507974686f6e2f322e372e360d0a446174653a205475652c2033302041756720323031362030393a34333a303720474d540d0a436f6e74656e742d747970653a20746578742f68746d6c3b20636861727365743d5554462d380d0a436f6e74656e742d4c656e6774683a203232320d0a0d0a3c21444f43545950452068746d6c205055424c494320222d2f2f5733432f2f4454442048544d4c20332e322046696e616c2f2f454e223e3c68746d6c3e0a3c7469746c653e4469726563746f7279206c697374696e6720666f72202f3c2f7469746c653e0a3c626f64793e0a3c68323e4469726563746f7279206c697374696e6720666f72202f3c2f68323e0a3c68723e0a3c756c3e0a3c6c693e3c6120687265663d2272656470686f6e652e706e67223e72656470686f6e652e706e673c2f613e0a3c2f756c3e0a3c68723e0a3c2f626f64793e0a3c2f68746d6c3e0a ASCII:E.....@.@.............@...`...@.P.......HTTP/1.0 200 OK..Server: SimpleHTTP/0.6 Python/2.7.6..Date: Tue, 30 Aug 2016 09:43:07 GMT..Content-type: text/html; charset=UTF-8..Content-Length: 222....<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><html>.<title>Directory listing for /..

Directory listing for /

.
..
... uncompressed= 450001a0b41140004006b8e80a0901abc0a800021f904002d5b860b5bab240ae501900ed861d0000485454502f312e3020323030204f4b0d0a5365727665723a2053696d706c65485454502f302e3620507974686f6e2f322e372e360d0a446174653a205475652c2033302041756720323031362030393a34333a303720474d540d0a436f6e74656e742d747970653a20746578742f68746d6c3b20636861727365743d5554462d380d0a436f6e74656e742d4c656e6774683a203232320d0a0d0a3c21444f43545950452068746d6c205055424c494320222d2f2f5733432f2f4454442048544d4c20332e322046696e616c2f2f454e223e3c68746d6c3e0a3c7469746c653e4469726563746f7279206c697374696e6720666f72202f3c2f7469746c653e0a3c626f64793e0a3c68323e4469726563746f7279206c697374696e6720666f72202f3c2f68323e0a3c68723e0a3c756c3e0a3c6c693e3c6120687265663d2272656470686f6e652e706e67223e72656470686f6e652e706e673c2f613e0a3c2f756c3e0a3c68723e0a3c2f626f64793e0a3c2f68746d6c3e0a ASCII:E.....@.@.............@...`...@.P.......HTTP/1.0 200 OK..Server: SimpleHTTP/0.6 Python/2.7.6..Date: Tue, 30 Aug 2016 09:43:07 GMT..Content-type: text/html; charset=UTF-8..Content-Length: 222.....Directory listing for /..

Directory listing for /

.
..
... compressed= 45000003088cba4561c880210976ad6bc08080ab61ab061410316948016cbbc6e0ea556b48ac291c06c04b0462c0802557ae4c914123c68c113566c894e20442032b68eae4e1d96384153670e6bc41b3b2a58c1931728c98c2e7ce1a397164d488a18369ce2364eea0017a050f9a17236cc8248247cdcc19349acee0d1e3868d1e33748c5002e54a4e2353bbc6b903e30e9f395e4774dd7347c69a3b70def81861660d993a76d0dc0182e54a12183bf4f245e317c693cf6aa202ad51a346ce9c3f481c9162e40a972948462866ec144b91274c8c8c280143860c2d368c003f72e5c88895509e908d516344123671c8bc018e244a89203f6abf09d2e0c71d36778c0639c2a60e1a3377e4d4e133e20d1b3be1e3a44dc37e848c1f32c28f47e3fd47193967f8fc5f53a3fcf9f4ebdbbf8f3fbffefdfc89157e5f53e73f9eeedfe16b27431b7d1a2025d09f9923350e9a1873e8673f9972aa4b986af493f92f8323428ddf651034f893bbf700 ASCII:E......Ea..!.v.k....a....1iH.l....UkH.)...K.b..%W.L.A#...5f....B.+h....c..6p..A.....1r......9qd....i.#d...z....#l.$.G...4........3t.P..JN#S.......9^Gt.sG..;p...af..:v.....J..;..E.....j...Q.F..?H..b...)HF(f..K.'L..(.C..-6..?r....P...QcD.6q....$J. ?j......6w..9....3w...3...;...M.~...2..G..G.9g.._S........?......~_S.?....k'C.}. %...#5...s.g?.r.K.j.../.#B..e.4..... memcmp() rc=0 v42bis_compress() rc=0 v42bis_compress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 Mode: 2 uncompressed_original= 450001a0b41140004006b8e80a0901abc0a800021f904002d5b860b5bab240ae501900ed861d0000485454502f312e3020323030204f4b0d0a5365727665723a2053696d706c65485454502f302e3620507974686f6e2f322e372e360d0a446174653a205475652c2033302041756720323031362030393a34333a303720474d540d0a436f6e74656e742d747970653a20746578742f68746d6c3b20636861727365743d5554462d380d0a436f6e74656e742d4c656e6774683a203232320d0a0d0a3c21444f43545950452068746d6c205055424c494320222d2f2f5733432f2f4454442048544d4c20332e322046696e616c2f2f454e223e3c68746d6c3e0a3c7469746c653e4469726563746f7279206c697374696e6720666f72202f3c2f7469746c653e0a3c626f64793e0a3c68323e4469726563746f7279206c697374696e6720666f72202f3c2f68323e0a3c68723e0a3c756c3e0a3c6c693e3c6120687265663d2272656470686f6e652e706e67223e72656470686f6e652e706e673c2f613e0a3c2f756c3e0a3c68723e0a3c2f626f64793e0a3c2f68746d6c3e0a ASCII:E.....@.@.............@...`...@.P.......HTTP/1.0 200 OK..Server: SimpleHTTP/0.6 Python/2.7.6..Date: Tue, 30 Aug 2016 09:43:07 GMT..Content-type: text/html; charset=UTF-8..Content-Length: 222.....Directory listing for /..

Directory listing for /

.
..
... uncompressed= 450001a0b41140004006b8e80a0901abc0a800021f904002d5b860b5bab240ae501900ed861d0000485454502f312e3020323030204f4b0d0a5365727665723a2053696d706c65485454502f302e3620507974686f6e2f322e372e360d0a446174653a205475652c2033302041756720323031362030393a34333a303720474d540d0a436f6e74656e742d747970653a20746578742f68746d6c3b20636861727365743d5554462d380d0a436f6e74656e742d4c656e6774683a203232320d0a0d0a3c21444f43545950452068746d6c205055424c494320222d2f2f5733432f2f4454442048544d4c20332e322046696e616c2f2f454e223e3c68746d6c3e0a3c7469746c653e4469726563746f7279206c697374696e6720666f72202f3c2f7469746c653e0a3c626f64793e0a3c68323e4469726563746f7279206c697374696e6720666f72202f3c2f68323e0a3c68723e0a3c756c3e0a3c6c693e3c6120687265663d2272656470686f6e652e706e67223e72656470686f6e652e706e673c2f613e0a3c2f756c3e0a3c68723e0a3c2f626f64793e0a3c2f68746d6c3e0a ASCII:E.....@.@.............@...`...@.P.......HTTP/1.0 200 OK..Server: SimpleHTTP/0.6 Python/2.7.6..Date: Tue, 30 Aug 2016 09:43:07 GMT..Content-type: text/html; charset=UTF-8..Content-Length: 222.....Directory listing for /..

Directory listing for /

.
..
... compressed= 45000101a0b41140004006b8e80a0901abc0a800021f904002d5b860b5bab240ae501900ed861d0000485454502f312e3020323030204f4b0d0a5365727665723a2053696d706c65485454502f302e3620507974686f6e2f322e372e360d0a446174653a205475652c203301302041756720323031362030393a34333a303720474d540d0a436f6e74656e742d747970653a20746578742f68746d6c3b20636861727365743d5554462d380d0a436f6e74656e742d4c656e6774683a203232320d0a0d0a3c21444f43545950452068746d6c205055424c494320222d2f2f5733432f2f4454442048544d4c20332e322046696e616c2f2f454e223e3c68746d6c3e0a3c7469746c653e4469726563746f7279206c697374696e672066016f72202f3c2f7469746c653e0a3c626f64793e0a3c68323e4469726563746f7279206c697374696e6720666f72202f3c2f68323e0a3c68723e0a3c756c3e0a3c6c693e3c6120687265663d2272656470686f6e652e706e67223e72656470686f6e652e706e673c2f613e0a3c2f756c3e0a3c68723e0a3c2f626f64793e0a3c2f68746d6c3e0a ASCII:E......@.@.............@...`...@.P.......HTTP/1.0 200 OK..Server: SimpleHTTP/0.6 Python/2.7.6..Date: Tue, 3.0 Aug 2016 09:43:07 GMT..Content-type: text/html; charset=UTF-8..Content-Length: 222.....Directory listing f.or /..

Directory listing for /

.
..
... memcmp() rc=0 Testing compression/decompression with realistic TCP/IP packets: Packet No.: 8 v42bis_compress() rc=0 v42bis_compress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 Mode: 0 uncompressed_original= 450000e2971b40003706026c550d93d7c0a8000200504047217f5922c903759c8018007c4fb400000101080a1153ce39002cf6e8485454502f312e3120333034204e6f74204d6f6469666965640d0a446174653a205475652c2033302041756720323031362031363a33363a343020474d540d0a5365727665723a204170616368650d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4b6565702d416c6976653a2074696d656f75743d322c206d61783d313030300d0a455461673a2022346338613134392d3436652d34323736386138656338656330220d0a0d0a ASCII:E.....@.7..lU........P@G!.Y"..u....|O........S.9.,..HTTP/1.1 304 Not Modified..Date: Tue, 30 Aug 2016 16:36:40 GMT..Server: Apache..Connection: Keep-Alive..Keep-Alive: timeout=2, max=1000..ETag: "4c8a149-46e-42768a8ec8ec0".... uncompressed= 450000e2971b40003706026c550d93d7c0a8000200504047217f5922c903759c8018007c4fb400000101080a1153ce39002cf6e8485454502f312e3120333034204e6f74204d6f6469666965640d0a446174653a205475652c2033302041756720323031362031363a33363a343020474d540d0a5365727665723a204170616368650d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4b6565702d416c6976653a2074696d656f75743d322c206d61783d313030300d0a455461673a2022346338613134392d3436652d34323736386138656338656330220d0a0d0a ASCII:E.....@.7..lU........P@G!.Y"..u....|O........S.9.,..HTTP/1.1 304 Not Modified..Date: Tue, 30 Aug 2016 16:36:40 GMT..Server: Apache..Connection: Keep-Alive..Keep-Alive: timeout=2, max=1000..ETag: "4c8a149-46e-42768a8ec8ec0".... compressed= 45000100e2971b40003706026c550d93d7c0a8000200504047217f5922c903759c8018007c4fb400000101080a1153ce39002cf6e8485454502f312e312033013034204e6f74204d6f646966016965640d0a446174653a205475652c2033302041756720323031362031363a33363a343020474d540d0a5365727665723a204170616368650d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4b6565702d416c6976653a2074696d656f75743d322c206d61783d313030300d0a455461673a2022346338613134392d3436652d3432373638613865633865990066669478fa3400 ASCII:E......@.7..lU........P@G!.Y"..u....|O........S.9.,..HTTP/1.1 3.04 Not Modif.ied..Date: Tue, 30 Aug 2016 16:36:40 GMT..Server: Apache..Connection: Keep-Alive..Keep-Alive: timeout=2, max=1000..ETag: "4c8a149-46e-42768a8ec8e..ff.x.4. memcmp() rc=0 v42bis_compress() rc=0 v42bis_compress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 Mode: 1 uncompressed_original= 450000e2971b40003706026c550d93d7c0a8000200504047217f5922c903759c8018007c4fb400000101080a1153ce39002cf6e8485454502f312e3120333034204e6f74204d6f6469666965640d0a446174653a205475652c2033302041756720323031362031363a33363a343020474d540d0a5365727665723a204170616368650d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4b6565702d416c6976653a2074696d656f75743d322c206d61783d313030300d0a455461673a2022346338613134392d3436652d34323736386138656338656330220d0a0d0a ASCII:E.....@.7..lU........P@G!.Y"..u....|O........S.9.,..HTTP/1.1 304 Not Modified..Date: Tue, 30 Aug 2016 16:36:40 GMT..Server: Apache..Connection: Keep-Alive..Keep-Alive: timeout=2, max=1000..ETag: "4c8a149-46e-42768a8ec8ec0".... uncompressed= 450000e2971b40003706026c550d93d7c0a8000200504047217f5922c903759c8018007c4fb400000101080a1153ce39002cf6e8485454502f312e3120333034204e6f74204d6f6469666965640d0a446174653a205475652c2033302041756720323031362031363a33363a343020474d540d0a5365727665723a204170616368650d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4b6565702d416c6976653a2074696d656f75743d322c206d61783d313030300d0a455461673a2022346338613134392d3436652d34323736386138656338656330220d0a0d0a ASCII:E.....@.7..lU........P@G!.Y"..u....|O........S.9.,..HTTP/1.1 304 Not Modified..Date: Tue, 30 Aug 2016 16:36:40 GMT..Server: Apache..Connection: Keep-Alive..Keep-Alive: timeout=2, max=1000..ETag: "4c8a149-46e-42768a8ec8ec0".... compressed= 450000030694d3e461c8001d090abcc102c192b661ab061418306588121282b894606600cfa7411b06fc91728b2001020b1a50b0128dc78017f9d62db972658a0c1a31688cb031e3c6882872ee8c8022e70c9b346cd09c81d0e008993b687a8cb88207cd0b9f334610c1a366448d1934728c20dbc3468e1e37b82a8172e5a9153475f2ccad4a640e19336bd03c3522274e1c3466eeb0015cd5091a3473601079c3a6eed3c48b1b3fae5bb5301c3472f0dc0152432b1c327b80d0983163c6532457c8a8a95ae286991d6468dce001e3460e34b76be8c8217bc760e066669478fa3400 ASCII:E.......a...........a....0e......`f...A....r. ....P........-.re...1h..1...(r..."...4l.......;hz.......3F...fD..4r. ..F..7.*.r...4u...Jd..3k.<5"'N.4f...\...4s`.y.......?.[.0.4r...RC+.2{...1c.S$W...Z....dh....F.4.k..!{.`.ff.x.4. memcmp() rc=0 v42bis_compress() rc=0 v42bis_compress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 Mode: 2 uncompressed_original= 450000e2971b40003706026c550d93d7c0a8000200504047217f5922c903759c8018007c4fb400000101080a1153ce39002cf6e8485454502f312e3120333034204e6f74204d6f6469666965640d0a446174653a205475652c2033302041756720323031362031363a33363a343020474d540d0a5365727665723a204170616368650d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4b6565702d416c6976653a2074696d656f75743d322c206d61783d313030300d0a455461673a2022346338613134392d3436652d34323736386138656338656330220d0a0d0a ASCII:E.....@.7..lU........P@G!.Y"..u....|O........S.9.,..HTTP/1.1 304 Not Modified..Date: Tue, 30 Aug 2016 16:36:40 GMT..Server: Apache..Connection: Keep-Alive..Keep-Alive: timeout=2, max=1000..ETag: "4c8a149-46e-42768a8ec8ec0".... uncompressed= 450000e2971b40003706026c550d93d7c0a8000200504047217f5922c903759c8018007c4fb400000101080a1153ce39002cf6e8485454502f312e3120333034204e6f74204d6f6469666965640d0a446174653a205475652c2033302041756720323031362031363a33363a343020474d540d0a5365727665723a204170616368650d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4b6565702d416c6976653a2074696d656f75743d322c206d61783d313030300d0a455461673a2022346338613134392d3436652d34323736386138656338656330220d0a0d0a ASCII:E.....@.7..lU........P@G!.Y"..u....|O........S.9.,..HTTP/1.1 304 Not Modified..Date: Tue, 30 Aug 2016 16:36:40 GMT..Server: Apache..Connection: Keep-Alive..Keep-Alive: timeout=2, max=1000..ETag: "4c8a149-46e-42768a8ec8ec0".... compressed= 45000100e2971b40003706026c550d93d7c0a8000200504047217f5922c903759c8018007c4fb400000101080a1153ce39002cf6e8485454502f312e312033013034204e6f74204d6f646966016965640d0a446174653a205475652c2033302041756720323031362031363a33363a343020474d540d0a5365727665723a204170616368650d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4b6565702d416c6976653a2074696d656f75743d322c206d61783d313030300d0a455461673a2022346338613134392d3436652d34323736386138656338656330220d0a0d0a ASCII:E......@.7..lU........P@G!.Y"..u....|O........S.9.,..HTTP/1.1 3.04 Not Modif.ied..Date: Tue, 30 Aug 2016 16:36:40 GMT..Server: Apache..Connection: Keep-Alive..Keep-Alive: timeout=2, max=1000..ETag: "4c8a149-46e-42768a8ec8ec0".... memcmp() rc=0 Testing compression/decompression with realistic TCP/IP packets: Packet No.: 9 v42bis_compress() rc=0 v42bis_compress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 Mode: 0 uncompressed_original= 450000e224f1400037067496550d93d7c0a80002005040489387ebf0c904389f8018007cec5700000101080a1153cf01002cf8fc485454502f312e3120333034204e6f74204d6f6469666965640d0a446174653a205475652c2033302041756720323031362031363a33363a343020474d540d0a5365727665723a204170616368650d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4b6565702d416c6976653a2074696d656f75743d322c206d61783d313030300d0a455461673a2022346338613338302d3861362d34323736383761323236383830220d0a0d0a ASCII:E...$.@.7.t.U........P@H......8....|.W.......S...,..HTTP/1.1 304 Not Modified..Date: Tue, 30 Aug 2016 16:36:40 GMT..Server: Apache..Connection: Keep-Alive..Keep-Alive: timeout=2, max=1000..ETag: "4c8a380-8a6-427687a226880".... uncompressed= 450000e224f1400037067496550d93d7c0a80002005040489387ebf0c904389f8018007cec5700000101080a1153cf01002cf8fc485454502f312e3120333034204e6f74204d6f6469666965640d0a446174653a205475652c2033302041756720323031362031363a33363a343020474d540d0a5365727665723a204170616368650d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4b6565702d416c6976653a2074696d656f75743d322c206d61783d313030300d0a455461673a2022346338613338302d3861362d34323736383761323236383830220d0a0d0a ASCII:E...$.@.7.t.U........P@H......8....|.W.......S...,..HTTP/1.1 304 Not Modified..Date: Tue, 30 Aug 2016 16:36:40 GMT..Server: Apache..Connection: Keep-Alive..Keep-Alive: timeout=2, max=1000..ETag: "4c8a380-8a6-427687a226880".... compressed= 45000100e224f1400037067496550d93d7c0a80002005040489387ebf0c904389f8018007cec5700000101080a1153cf01002cf8fc485454502f312e312033013034204e6f74204d6f646966016965640d0a446174653a205475652c2033302041756720323031362031363a33363a343020474d540d0a5365727665723a204170616368650d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4b6565702d416c6976653a2074696d656f75743d322c206d61783d313030300d0a455461673a2022346338613338302d3861362d34323736383761323236383830220d0a0d0a ASCII:E....$.@.7.t.U........P@H......8....|.W.......S...,..HTTP/1.1 3.04 Not Modif.ied..Date: Tue, 30 Aug 2016 16:36:40 GMT..Server: Apache..Connection: Keep-Alive..Keep-Alive: timeout=2, max=1000..ETag: "4c8a380-8a6-427687a226880".... memcmp() rc=0 v42bis_compress() rc=0 v42bis_compress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 Mode: 1 uncompressed_original= 450000e224f1400037067496550d93d7c0a80002005040489387ebf0c904389f8018007cec5700000101080a1153cf01002cf8fc485454502f312e3120333034204e6f74204d6f6469666965640d0a446174653a205475652c2033302041756720323031362031363a33363a343020474d540d0a5365727665723a204170616368650d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4b6565702d416c6976653a2074696d656f75743d322c206d61783d313030300d0a455461673a2022346338613338302d3861362d34323736383761323236383830220d0a0d0a ASCII:E...$.@.7.t.U........P@H......8....|.W.......S...,..HTTP/1.1 304 Not Modified..Date: Tue, 30 Aug 2016 16:36:40 GMT..Server: Apache..Connection: Keep-Alive..Keep-Alive: timeout=2, max=1000..ETag: "4c8a380-8a6-427687a226880".... uncompressed= 450000e224f1400037067496550d93d7c0a80002005040489387ebf0c904389f8018007cec5700000101080a1153cf01002cf8fc485454502f312e3120333034204e6f74204d6f6469666965640d0a446174653a205475652c2033302041756720323031362031363a33363a343020474d540d0a5365727665723a204170616368650d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4b6565702d416c6976653a2074696d656f75743d322c206d61783d313030300d0a455461673a2022346338613338302d3861362d34323736383761323236383830220d0a0d0a ASCII:E...$.@.7.t.U........P@H......8....|.W.......S...,..HTTP/1.1 304 Not Modified..Date: Tue, 30 Aug 2016 16:36:40 GMT..Server: Apache..Connection: Keep-Alive..Keep-Alive: timeout=2, max=1000..ETag: "4c8a380-8a6-427687a226880".... compressed= 4500000306943b416fc8001d09ee64c202c192b661ab0614183065c8124b8adccd63766087a8411b06fc79a7852001020b1a50b0228dc08017fbfe2db972658a0c1a31688cb031e3c6882872ee8c8022e70c9b346cd09c81d0e008993b687a8cb88207cd0b9f334610c1a366448d1934728c20dbc3468e1e37b82a8172e5a9153475f2ccad4a640e19336bd03c3522274e1c3466eeb0015cd5091a3473601079c3a6eed3c48b1b3fae5bb5301c3472f0dc0152432b1c327b80d0983163c6532457c8a8a95ae286991d646cec980143760e18376ae8c8b14307991a357cd72ef1f46900 ASCII:E.....;Ao.....d.....a....0e..K...cv`..A...y.. ....P."......-.re...1h..1...(r..."...4l.......;hz.......3F...fD..4r. ..F..7.*.r...4u...Jd..3k.<5"'N.4f...\...4s`.y.......?.[.0.4r...RC+.2{...1c.S$W...Z....dl...Cv..7j...C...5|....i. memcmp() rc=0 v42bis_compress() rc=0 v42bis_compress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 Mode: 2 uncompressed_original= 450000e224f1400037067496550d93d7c0a80002005040489387ebf0c904389f8018007cec5700000101080a1153cf01002cf8fc485454502f312e3120333034204e6f74204d6f6469666965640d0a446174653a205475652c2033302041756720323031362031363a33363a343020474d540d0a5365727665723a204170616368650d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4b6565702d416c6976653a2074696d656f75743d322c206d61783d313030300d0a455461673a2022346338613338302d3861362d34323736383761323236383830220d0a0d0a ASCII:E...$.@.7.t.U........P@H......8....|.W.......S...,..HTTP/1.1 304 Not Modified..Date: Tue, 30 Aug 2016 16:36:40 GMT..Server: Apache..Connection: Keep-Alive..Keep-Alive: timeout=2, max=1000..ETag: "4c8a380-8a6-427687a226880".... uncompressed= 450000e224f1400037067496550d93d7c0a80002005040489387ebf0c904389f8018007cec5700000101080a1153cf01002cf8fc485454502f312e3120333034204e6f74204d6f6469666965640d0a446174653a205475652c2033302041756720323031362031363a33363a343020474d540d0a5365727665723a204170616368650d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4b6565702d416c6976653a2074696d656f75743d322c206d61783d313030300d0a455461673a2022346338613338302d3861362d34323736383761323236383830220d0a0d0a ASCII:E...$.@.7.t.U........P@H......8....|.W.......S...,..HTTP/1.1 304 Not Modified..Date: Tue, 30 Aug 2016 16:36:40 GMT..Server: Apache..Connection: Keep-Alive..Keep-Alive: timeout=2, max=1000..ETag: "4c8a380-8a6-427687a226880".... compressed= 45000100e224f1400037067496550d93d7c0a80002005040489387ebf0c904389f8018007cec5700000101080a1153cf01002cf8fc485454502f312e312033013034204e6f74204d6f646966016965640d0a446174653a205475652c2033302041756720323031362031363a33363a343020474d540d0a5365727665723a204170616368650d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4b6565702d416c6976653a2074696d656f75743d322c206d61783d313030300d0a455461673a2022346338613338302d3861362d34323736383761323236383830220d0a0d0a ASCII:E....$.@.7.t.U........P@H......8....|.W.......S...,..HTTP/1.1 3.04 Not Modif.ied..Date: Tue, 30 Aug 2016 16:36:40 GMT..Server: Apache..Connection: Keep-Alive..Keep-Alive: timeout=2, max=1000..ETag: "4c8a380-8a6-427687a226880".... memcmp() rc=0 Testing compression/decompression with realistic TCP/IP packets: Packet No.: 10 v42bis_compress() rc=0 v42bis_compress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 Mode: 0 uncompressed_original= 450000e2b66140003706e325550d93d7c0a8000200504049fbb679bcc9051ea48018007cebea00000101080a1153cfdc002cfdb4485454502f312e3120333034204e6f74204d6f6469666965640d0a446174653a205475652c2033302041756720323031362031363a33363a343120474d540d0a5365727665723a204170616368650d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4b6565702d416c6976653a2074696d656f75743d322c206d61783d313030300d0a455461673a2022346338313336642d3138642d34353832306530393638303430220d0a0d0a ASCII:E....a@.7..%U........P@I..y........|.........S...,..HTTP/1.1 304 Not Modified..Date: Tue, 30 Aug 2016 16:36:41 GMT..Server: Apache..Connection: Keep-Alive..Keep-Alive: timeout=2, max=1000..ETag: "4c8136d-18d-45820e0968040".... uncompressed= 450000e2b66140003706e325550d93d7c0a8000200504049fbb679bcc9051ea48018007cebea00000101080a1153cfdc002cfdb4485454502f312e3120333034204e6f74204d6f6469666965640d0a446174653a205475652c2033302041756720323031362031363a33363a343120474d540d0a5365727665723a204170616368650d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4b6565702d416c6976653a2074696d656f75743d322c206d61783d313030300d0a455461673a2022346338313336642d3138642d34353832306530393638303430220d0a0d0a ASCII:E....a@.7..%U........P@I..y........|.........S...,..HTTP/1.1 304 Not Modified..Date: Tue, 30 Aug 2016 16:36:41 GMT..Server: Apache..Connection: Keep-Alive..Keep-Alive: timeout=2, max=1000..ETag: "4c8136d-18d-45820e0968040".... compressed= 45000100e2b66140003706e325550d93d7c0a8000200504049fbb679bcc9051ea48018007cebea00000101080a1153cfdc002cfdb4485454502f312e312033013034204e6f74204d6f646966016965640d0a446174653a205475652c2033302041756720323031362031363a33363a343120474d540d0a5365727665723a204170616368650d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4b6565702d416c6976653a2074696d656f75743d322c206d61783d313030300d0a455461673a2022346338313336642d3138642d34353832306530393638303430220d0a0d0a ASCII:E.....a@.7..%U........P@I..y........|.........S...,..HTTP/1.1 3.04 Not Modif.ied..Date: Tue, 30 Aug 2016 16:36:41 GMT..Server: Apache..Connection: Keep-Alive..Keep-Alive: timeout=2, max=1000..ETag: "4c8136d-18d-45820e0968040".... memcmp() rc=0 v42bis_compress() rc=0 v42bis_compress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 Mode: 1 uncompressed_original= 450000e2b66140003706e325550d93d7c0a8000200504049fbb679bcc9051ea48018007cebea00000101080a1153cfdc002cfdb4485454502f312e3120333034204e6f74204d6f6469666965640d0a446174653a205475652c2033302041756720323031362031363a33363a343120474d540d0a5365727665723a204170616368650d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4b6565702d416c6976653a2074696d656f75743d322c206d61783d313030300d0a455461673a2022346338313336642d3138642d34353832306530393638303430220d0a0d0a ASCII:E....a@.7..%U........P@I..y........|.........S...,..HTTP/1.1 304 Not Modified..Date: Tue, 30 Aug 2016 16:36:41 GMT..Server: Apache..Connection: Keep-Alive..Keep-Alive: timeout=2, max=1000..ETag: "4c8136d-18d-45820e0968040".... uncompressed= 450000e2b66140003706e325550d93d7c0a8000200504049fbb679bcc9051ea48018007cebea00000101080a1153cfdc002cfdb4485454502f312e3120333034204e6f74204d6f6469666965640d0a446174653a205475652c2033302041756720323031362031363a33363a343120474d540d0a5365727665723a204170616368650d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4b6565702d416c6976653a2074696d656f75743d322c206d61783d313030300d0a455461673a2022346338313336642d3138642d34353832306530393638303430220d0a0d0a ASCII:E....a@.7..%U........P@I..y........|.........S...,..HTTP/1.1 304 Not Modified..Date: Tue, 30 Aug 2016 16:36:41 GMT..Server: Apache..Connection: Keep-Alive..Keep-Alive: timeout=2, max=1000..ETag: "4c8136d-18d-45820e0968040".... compressed= 450000030694cb4566c8001d09cca1c002c192b661ab061418306508137fb9f8fc628620c4a9411b06fc71d78e2001020b1a50b022eddb8017006f2db972658a0c1a31688cb031e3c6882872ee8c8022e70c9b346cd09c81d0e008993b687a8cb88207cd0b9f334610c1a366448d1934728c20dbc3468e1e377a2a8172e5a9153475f2ccad4a640e19336bd03c3522274e1c3466eeb0015cd5091a3473601079c3a6eed3c48b1b3fae5bb5301c3472f0dc0152432b1c327b80d0983163c6532457c8a8a95ae286991d34d29e81416347ed1b387688453383478e1d40679478fa3400 ASCII:E......Ef...........a....0e......b. ..A...q.. ....P.".....o-.re...1h..1...(r..."...4l.......;hz.......3F...fD..4r. ..F..7z*.r...4u...Jd..3k.<5"'N.4f...\...4s`.y.......?.[.0.4r...RC+.2{...1c.S$W...Z....4...AcG..8v.E3.G..@g.x.4. memcmp() rc=0 v42bis_compress() rc=0 v42bis_compress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 Mode: 2 uncompressed_original= 450000e2b66140003706e325550d93d7c0a8000200504049fbb679bcc9051ea48018007cebea00000101080a1153cfdc002cfdb4485454502f312e3120333034204e6f74204d6f6469666965640d0a446174653a205475652c2033302041756720323031362031363a33363a343120474d540d0a5365727665723a204170616368650d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4b6565702d416c6976653a2074696d656f75743d322c206d61783d313030300d0a455461673a2022346338313336642d3138642d34353832306530393638303430220d0a0d0a ASCII:E....a@.7..%U........P@I..y........|.........S...,..HTTP/1.1 304 Not Modified..Date: Tue, 30 Aug 2016 16:36:41 GMT..Server: Apache..Connection: Keep-Alive..Keep-Alive: timeout=2, max=1000..ETag: "4c8136d-18d-45820e0968040".... uncompressed= 450000e2b66140003706e325550d93d7c0a8000200504049fbb679bcc9051ea48018007cebea00000101080a1153cfdc002cfdb4485454502f312e3120333034204e6f74204d6f6469666965640d0a446174653a205475652c2033302041756720323031362031363a33363a343120474d540d0a5365727665723a204170616368650d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4b6565702d416c6976653a2074696d656f75743d322c206d61783d313030300d0a455461673a2022346338313336642d3138642d34353832306530393638303430220d0a0d0a ASCII:E....a@.7..%U........P@I..y........|.........S...,..HTTP/1.1 304 Not Modified..Date: Tue, 30 Aug 2016 16:36:41 GMT..Server: Apache..Connection: Keep-Alive..Keep-Alive: timeout=2, max=1000..ETag: "4c8136d-18d-45820e0968040".... compressed= 45000100e2b66140003706e325550d93d7c0a8000200504049fbb679bcc9051ea48018007cebea00000101080a1153cfdc002cfdb4485454502f312e312033013034204e6f74204d6f646966016965640d0a446174653a205475652c2033302041756720323031362031363a33363a343120474d540d0a5365727665723a204170616368650d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4b6565702d416c6976653a2074696d656f75743d322c206d61783d313030300d0a455461673a2022346338313336642d3138642d34353832306530393638303430220d0a0d0a ASCII:E.....a@.7..%U........P@I..y........|.........S...,..HTTP/1.1 3.04 Not Modif.ied..Date: Tue, 30 Aug 2016 16:36:41 GMT..Server: Apache..Connection: Keep-Alive..Keep-Alive: timeout=2, max=1000..ETag: "4c8136d-18d-45820e0968040".... memcmp() rc=0 Testing decompression with sniffed compressed TCP/IP packets: Packet No.: 0 v42bis_decompress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 compressed= 4500010268000700004006cefac0a80002550d93d740000050462c7ba7e4d1753a80184000aad500000101080a0001a670084dafb4474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d6978656433005ab97a052b960d59b368d5b2ddb3e60e9c372ef610b6dbf56bd8b165030f2e7cf88dd63b68f64c3d9b76ed1cb58847b490d122e8d0a24761185913d50e1aa423f0dc49036387d6d7b169e4d0cac68e1cd93b70f0804123f7eee03372dcc801038f193b306a68b5b2864d9a3b629a30b1a2b5081b35384613357a07c6133271d4e021a3064d52347182ee81b119c69c3a72d2b079b37e4409c177e6f4902163738cdd71f8a0a903d68ec21866e4c0918185087dfb329cec9831834d951a337c4a2e1174891c3badf5e8d113a38f1c336e24520c8a65751d1844d4c7696d852c1f240e992becf8918d0c9145465441939fcc6a1950a206b7e1fca38e1145eaebc129230aeb24f57bcab011c3c68829f5efe7bfcbe4c814e731668c3042f6fef93a62d9909561e4c91123c163d0085a3a4e1c3466c6c649ea048d519d5ff3a0f95ef4280c2471269e61633ee9193469de8845a3554d9fa74199c48622e7fa7dac30ac602f9af40a9ef0236a54268247cd7f923946d0a8d1c3c68d1e35788c5002e54ad0a00100 ASCII:E...h....@.......U...@..PF,{...u:..@............p.M..GET / HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed3.Z.z.+..Y.h.......7......k..e...|...;h.L=.v....G..."...Ga.Y.....#..I.c....i.......;p..A#...3r......;0jh...M.;b.0.....58F.5z...2q..!..MR4q.......:r..y.~D..w...!cs..q........f......}.2...1.M..3|J..t..;........3n$R..eu..D..im.,.$..+......EFTA...j.P........E...)#..$.{......).........1f.0B...:b...a...#.c..Z:N.4f..I...Q._...^.(.$q&.ac>..4i..E.UM..A..."..}.0.`/.....#jT&.G...9F.......5x.P..J.... uncompressed= 45000268000700004006cefac0a80002550d93d740000050462c7ba7e4d1753a80184000aad500000101080a0001a670084dafb4474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a207777772e7a6f636b2e636f6d0d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a49662d4d6f6469666965642d53696e63653a205475652c2032332041756720323031362031323a33343a323920474d540d0a0d0a ASCII:E..h....@.......U...@..PF,{...u:..@............p.M..GET / HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/vnd.wap.wml, */*..Accept-Charset: utf-8, utf-16, iso-8859-1, iso-10646-ucs-2, Shift_JIS, Big5..Accept-Language: en..x-wap-profile: "http://wap.sonyericsson.com/UAprof/K800iR201.xml"..Host: www.zock.com..User-Agent: SonyEricssonK800i/R2B Release/Mar-13-2007 Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1..Connection: Keep-Alive..Accept-Encoding: deflate, gzip..If-Modified-Since: Tue, 23 Aug 2016 12:34:29 GMT.... Testing decompression with sniffed compressed TCP/IP packets: Packet No.: 1 v42bis_decompress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 compressed= 4500010268000900004006cef8c0a80002550d93d740000050462c7ba7e4d1753a801840007e7f00000101080a0001d1cc084db0ae474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d6978656433005ab97a052b960d59b368d5b2ddb3e60e9c372ef610b6dbf56bd8b165030f2e7cf88dd63b68f64c3d9b76ed1cb58847b490d122e8d0a24761185913d50e1aa423f0dc49036387d6d7b169e4d0cac68e1cd93b70f0804123f7eee03372dcc801038f193b306a68b5b2864d9a3b629a30b1a2b5081b35384613357a07c6133271d4e021a3064d52347182ee81b119c69c3a72d2b079b37e4409c177e6f4902163738cdd71f8a0a903d68ec21866e4c0918185087dfb329cec9831834d951a337c4a2e1174891c3badf5e8d113a38f1c336e24520c8a65751d1844d4c7696d852c1f240e992becf8918d0c9145465441939fcc6a1950a206b7e1fca38e1145eaebc129230aeb24f57bcab011c3c68829f5efe7bfcbe4c814e731668c3042f6fef93a62d9909561e4c91123c163d0085a3a4e1c3466c6c649ea048d519d5ff3a0f95ef4280c2471269e61633ee9193469de8845a3554d9fa74199c48622e7fa7dac30ac602f9af40a9ef0236a54268247cd7f923946d0a8d1c3c68d1e35788c5002e54ad0a00100 ASCII:E...h....@.......U...@..PF,{...u:..@.~............M..GET / HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed3.Z.z.+..Y.h.......7......k..e...|...;h.L=.v....G..."...Ga.Y.....#..I.c....i.......;p..A#...3r......;0jh...M.;b.0.....58F.5z...2q..!..MR4q.......:r..y.~D..w...!cs..q........f......}.2...1.M..3|J..t..;........3n$R..eu..D..im.,.$..+......EFTA...j.P........E...)#..$.{......).........1f.0B...:b...a...#.c..Z:N.4f..I...Q._...^.(.$q&.ac>..4i..E.UM..A..."..}.0.`/.....#jT&.G...9F.......5x.P..J.... uncompressed= 45000268000900004006cef8c0a80002550d93d740000050462c7ba7e4d1753a801840007e7f00000101080a0001d1cc084db0ae474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a207777772e7a6f636b2e636f6d0d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a49662d4d6f6469666965642d53696e63653a205475652c2032332041756720323031362031323a33343a323920474d540d0a0d0a ASCII:E..h....@.......U...@..PF,{...u:..@.~............M..GET / HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/vnd.wap.wml, */*..Accept-Charset: utf-8, utf-16, iso-8859-1, iso-10646-ucs-2, Shift_JIS, Big5..Accept-Language: en..x-wap-profile: "http://wap.sonyericsson.com/UAprof/K800iR201.xml"..Host: www.zock.com..User-Agent: SonyEricssonK800i/R2B Release/Mar-13-2007 Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1..Connection: Keep-Alive..Accept-Encoding: deflate, gzip..If-Modified-Since: Tue, 23 Aug 2016 12:34:29 GMT.... Testing decompression with sniffed compressed TCP/IP packets: Packet No.: 2 v42bis_decompress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 compressed= 4500010268000b00004006cef6c0a80002550d93d740000050462c7ba7e4d1753b80193fff131c00000101080a00022884084dc558474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d6978656433005ab97a052b960d59b368d5b2ddb3e60e9c372ef610b6dbf56bd8b165030f2e7cf88dd63b68f64c3d9b76ed1cb58847b490d122e8d0a24761185913d50e1aa423f0dc49036387d6d7b169e4d0cac68e1cd93b70f0804123f7eee03372dcc801038f193b306a68b5b2864d9a3b629a30b1a2b5081b35384613357a07c6133271d4e021a3064d52347182ee81b119c69c3a72d2b079b37e4409c177e6f4902163738cdd71f8a0a903d68ec21866e4c0918185087dfb329cec9831834d951a337c4a2e1174891c3badf5e8d113a38f1c336e24520c8a65751d1844d4c7696d852c1f240e992be4e8918d8c9045465441939fcc6a1950a206b7e1dca38e1145eaebb929230aeb24f579cab011c3c68829f5efe7afcbe4c814e731668c3042f6fef93a62d9909561e4c91123c163d0084a3a4e1c3466c6c649ea048dd19c5ff3a0f95ef4280c2471269e61633ee9193469de8845a3554d9fa74199c48622c7fa7dac30ac5c2f9af40a1ef0236a502682478dff913946d0a8d1c3c68d1e35788c5002e54ad0a00100 ASCII:E...h....@.......U...@..PF,{...u;..?...........(..M.XGET / HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed3.Z.z.+..Y.h.......7......k..e...|...;h.L=.v....G..."...Ga.Y.....#..I.c....i.......;p..A#...3r......;0jh...M.;b.0.....58F.5z...2q..!..MR4q.......:r..y.~D..w...!cs..q........f......}.2...1.M..3|J..t..;........3n$R..eu..D..im.,.$..+......EFTA...j.P........E...)#..$.y......).........1f.0B...:b...a...#.c..J:N.4f..I....._...^.(.$q&.ac>..4i..E.UM..A..."..}.0.\/.....#jP&.G...9F.......5x.P..J.... uncompressed= 45000268000b00004006cef6c0a80002550d93d740000050462c7ba7e4d1753b80193fff131c00000101080a00022884084dc558474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a207777772e7a6f636b2e636f6d0d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a49662d4d6f6469666965642d53696e63653a205475652c2032332041756720323031362031323a33343a323920474d540d0a0d0a ASCII:E..h....@.......U...@..PF,{...u;..?...........(..M.XGET / HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/vnd.wap.wml, */*..Accept-Charset: utf-8, utf-16, iso-8859-1, iso-10646-ucs-2, Shift_JIS, Big5..Accept-Language: en..x-wap-profile: "http://wap.sonyericsson.com/UAprof/K800iR201.xml"..Host: www.zock.com..User-Agent: SonyEricssonK800i/R2B Release/Mar-13-2007 Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1..Connection: Keep-Alive..Accept-Encoding: deflate, gzip..If-Modified-Since: Tue, 23 Aug 2016 12:34:29 GMT.... Testing decompression with sniffed compressed TCP/IP packets: Packet No.: 3 v42bis_decompress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 compressed= 4500010268000c00004006cef5c0a80002550d93d740000050462c7ba7e4d1753b80193fff65ab00000101080a0002d5f4084dc558474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d6978656433005ab97a052b960d59b368d5b2ddb3e60e9c372ef610b6dbf56bd8b165030f2e7cf88dd63b68f64c3d9b76ed1cb58847b490d122e8d0a24761185913d50e1aa423f0dc49036387d6d7b169e4d0cac68e1cd93b70f0804123f7eee03372dcc801038f193b306a68b5b2864d9a3b629a30b1a2b5081b35384613357a07c6133271d4e021a3064d52347182ee81b119c69c3a72d2b079b37e4409c177e6f4902163738cdd71f8a0a903d68ec21866e4c0918185087dfb329cec9831834d951a337c4a2e1174891c3badf5e8d113a38f1c336e24520c8a65751d1844d4c7696d852c1f240e992be4e8918d8c9045465441939fcc6a1950a206b7e1dca38e1145eaebb929230aeb24f579cab011c3c68829f5efe7afcbe4c814e731668c3042f6fef93a62d9909561e4c91123c163d0084a3a4e1c3466c6c649ea048dd19c5ff3a0f95ef4280c2471269e61633ee9193469de8845a3554d9fa74199c48622c7fa7dac30ac5c2f9af40a1ef0236a502682478dff913946d0a8d1c3c68d1e35788c5002e54ad0a00100 ASCII:E...h....@.......U...@..PF,{...u;..?.e............M.XGET / HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed3.Z.z.+..Y.h.......7......k..e...|...;h.L=.v....G..."...Ga.Y.....#..I.c....i.......;p..A#...3r......;0jh...M.;b.0.....58F.5z...2q..!..MR4q.......:r..y.~D..w...!cs..q........f......}.2...1.M..3|J..t..;........3n$R..eu..D..im.,.$..+......EFTA...j.P........E...)#..$.y......).........1f.0B...:b...a...#.c..J:N.4f..I....._...^.(.$q&.ac>..4i..E.UM..A..."..}.0.\/.....#jP&.G...9F.......5x.P..J.... uncompressed= 45000268000c00004006cef5c0a80002550d93d740000050462c7ba7e4d1753b80193fff65ab00000101080a0002d5f4084dc558474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a207777772e7a6f636b2e636f6d0d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a49662d4d6f6469666965642d53696e63653a205475652c2032332041756720323031362031323a33343a323920474d540d0a0d0a ASCII:E..h....@.......U...@..PF,{...u;..?.e............M.XGET / HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/vnd.wap.wml, */*..Accept-Charset: utf-8, utf-16, iso-8859-1, iso-10646-ucs-2, Shift_JIS, Big5..Accept-Language: en..x-wap-profile: "http://wap.sonyericsson.com/UAprof/K800iR201.xml"..Host: www.zock.com..User-Agent: SonyEricssonK800i/R2B Release/Mar-13-2007 Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1..Connection: Keep-Alive..Accept-Encoding: deflate, gzip..If-Modified-Since: Tue, 23 Aug 2016 12:34:29 GMT.... Testing decompression with sniffed compressed TCP/IP packets: Packet No.: 4 v42bis_decompress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 compressed= 450001022d000f00004006ac5ec0a800020a0901ab40011f4046a2f5a8e0a618025018400093480000474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e33005cbd8215bb67cd1d386f5cecd1cb766ad5ab59b7decdbbb7ef1ba877d0ec49daf56bd83960fd8e6821a3c5cd9c3b7bc230b2e6a81d343e47e0b99306c60ea8a54fd3c801958d1d39a877e0e00183c6ebd8b767e4b89103061e337660d4806a650d9b3477c4346162056a11366a7064d6c9f30e8c2764e2a8c143460d9a9f68e2dcdc0323328c3975e4a461f326fc881278efcce9214346e418b1e3f04153c7aa9dfd31ccc88123030b11f5ec6538d93163069b2a3566d0445ce2e612397646d398118347cd9a3a68f49848f12696d0756010011f67b415ad7c90fc17be5f224536322e16195105cd7b32a16540397adb06718a3a461459afe7a58c28a293acb729c3460c1b23a6ac6ffffe2d93235388c79831c288d6f6ddeb6065a355869127478cdcae79b3739c3868cc648df3d3091a9e31abe641537d674f1848e2203cc386fbcf3368d2bcc18a06aa9a3e456fde0c00 ASCII:E...-....@..^........@..@F.......P.@..H..GET / HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application3.\....g..8o\...vj..Y........w..I..k.9`..h!....;{.0....4>G........O......9.w........g......3v`..je..4w.4ab.j.6jpd.....'d...CF...h....#2.9u..a.&...x...!CF.....AS....1...#.....e8.1c..*5f.D\...9vF....G..:h..H.&..u`...g...|...._"E62...Q..{2.e@9z..q.:F.Y....(....).F..#..o..-.#S...1......`e.U..'G...y.s.8h.d......1..AS}gO.H. <....3h.......>Eo... uncompressed= 4500022d000f00004006ac5ec0a800020a0901ab40011f4046a2f5a8e0a618025018400093480000474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a2031302e392e312e3137313a383030300d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a0d0a ASCII:E..-....@..^........@..@F.......P.@..H..GET / HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/vnd.wap.wml, */*..Accept-Charset: utf-8, utf-16, iso-8859-1, iso-10646-ucs-2, Shift_JIS, Big5..Accept-Language: en..x-wap-profile: "http://wap.sonyericsson.com/UAprof/K800iR201.xml"..Host: 10.9.1.171:8000..User-Agent: SonyEricssonK800i/R2B Release/Mar-13-2007 Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1..Connection: Keep-Alive..Accept-Encoding: deflate, gzip.... Testing decompression with sniffed compressed TCP/IP packets: Packet No.: 5 v42bis_decompress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 compressed= 450001022d001000004006ac5dc0a800020a0901ab40011f4046a2f5a8e0a618025018400093480000474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e33005cbd8215bb67cd1d386f5cecd1cb766ad5ab59b7decdbbb7ef1ba877d0ec49daf56bd83960fd8e6821a3c5cd9c3b7bc230b2e6a81d343e47e0b99306c60ea8a54fd3c801958d1d39a877e0e00183c6ebd8b767e4b89103061e337660d4806a650d9b3477c4346162056a11366a7064d6c9f30e8c2764e2a8c143460d9a9f68e2dcdc0323328c3975e4a461f326fc881278efcce9214346e418b1e3f04153c7aa9dfd31ccc88123030b11f5ec6538d93163069b2a3566d0445ce2e612397646d398118347cd9a3a68f49848f12696d0756010011f67b415ad7c90fc17be5f224536322e16195105cd7b32a16540397adb06718a3a461459afe7a58c28a293acb729c3460c1b23a6ac6ffffe2d93235388c79831c288d6f6ddeb6065a355869127478cdcae79b3739c3868cc648df3d3091a9e31abe641537d674f1848e2203cc386fbcf3368d2bcc18a06aa9a3e456fde0c00 ASCII:E...-....@..]........@..@F.......P.@..H..GET / HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application3.\....g..8o\...vj..Y........w..I..k.9`..h!....;{.0....4>G........O......9.w........g......3v`..je..4w.4ab.j.6jpd.....'d...CF...h....#2.9u..a.&...x...!CF.....AS....1...#.....e8.1c..*5f.D\...9vF....G..:h..H.&..u`...g...|...._"E62...Q..{2.e@9z..q.:F.Y....(....).F..#..o..-.#S...1......`e.U..'G...y.s.8h.d......1..AS}gO.H. <....3h.......>Eo... uncompressed= 4500022d001000004006ac5dc0a800020a0901ab40011f4046a2f5a8e0a618025018400093480000474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a2031302e392e312e3137313a383030300d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a0d0a ASCII:E..-....@..]........@..@F.......P.@..H..GET / HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/vnd.wap.wml, */*..Accept-Charset: utf-8, utf-16, iso-8859-1, iso-10646-ucs-2, Shift_JIS, Big5..Accept-Language: en..x-wap-profile: "http://wap.sonyericsson.com/UAprof/K800iR201.xml"..Host: 10.9.1.171:8000..User-Agent: SonyEricssonK800i/R2B Release/Mar-13-2007 Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1..Connection: Keep-Alive..Accept-Encoding: deflate, gzip.... Testing decompression with sniffed compressed TCP/IP packets: Packet No.: 6 v42bis_decompress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 compressed= 450001022d001100004006ac5cc0a800020a0901ab40011f4046a2f5a8e0a618025018400093480000474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e33005cbd8215bb67cd1d386f5cecd1cb766ad5ab59b7decdbbb7ef1ba877d0ec49daf56bd83960fd8e6821a3c5cd9c3b7bc230b2e6a81d343e47e0b99306c60ea8a54fd3c801958d1d39a877e0e00183c6ebd8b767e4b89103061e337660d4806a650d9b3477c4346162056a11366a7064d6c9f30e8c2764e2a8c143460d9a9f68e2dcdc0323328c3975e4a461f326fc881278efcce9214346e418b1e3f04153c7aa9dfd31ccc88123030b11f5ec6538d93163069b2a3566d0445ce2e612397646d398118347cd9a3a68f49848f12696d0756010011f67b415ad7c90fc17be5f224536322e16195105cd7b32a16540397adb06718a3a461459afe7a58c28a293acb729c3460c1b23a6ac6ffffe2d93235388c79831c288d6f6ddeb6065a355869127478cdcae79b3739c3868cc648df3d3091a9e31abe641537d674f1848e2203cc386fbcf3368d2bcc18a06aa9a3e456fde0c00 ASCII:E...-....@..\........@..@F.......P.@..H..GET / HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application3.\....g..8o\...vj..Y........w..I..k.9`..h!....;{.0....4>G........O......9.w........g......3v`..je..4w.4ab.j.6jpd.....'d...CF...h....#2.9u..a.&...x...!CF.....AS....1...#.....e8.1c..*5f.D\...9vF....G..:h..H.&..u`...g...|...._"E62...Q..{2.e@9z..q.:F.Y....(....).F..#..o..-.#S...1......`e.U..'G...y.s.8h.d......1..AS}gO.H. <....3h.......>Eo... uncompressed= 4500022d001100004006ac5cc0a800020a0901ab40011f4046a2f5a8e0a618025018400093480000474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a2031302e392e312e3137313a383030300d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a0d0a ASCII:E..-....@..\........@..@F.......P.@..H..GET / HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/vnd.wap.wml, */*..Accept-Charset: utf-8, utf-16, iso-8859-1, iso-10646-ucs-2, Shift_JIS, Big5..Accept-Language: en..x-wap-profile: "http://wap.sonyericsson.com/UAprof/K800iR201.xml"..Host: 10.9.1.171:8000..User-Agent: SonyEricssonK800i/R2B Release/Mar-13-2007 Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1..Connection: Keep-Alive..Accept-Encoding: deflate, gzip.... Testing decompression with sniffed compressed TCP/IP packets: Packet No.: 7 v42bis_decompress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 compressed= 450001022d001200004006ac5bc0a800020a0901ab40011f4046a2f5a8e0a618025018400093480000474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e33005cbd8215bb67cd1d386f5cecd1cb766ad5ab59b7decdbbb7ef1ba877d0ec49daf56bd83960fd8e6821a3c5cd9c3b7bc230b2e6a81d343e47e0b99306c60ea8a54fd3c801958d1d39a877e0e00183c6ebd8b767e4b89103061e337660d4806a650d9b3477c4346162056a11366a7064d6c9f30e8c2764e2a8c143460d9a9f68e2dcdc0323328c3975e4a461f326fc881278efcce9214346e418b1e3f04153c7aa9dfd31ccc88123030b11f5ec6538d93163069b2a3566d0445ce2e612397646d398118347cd9a3a68f49848f12696d0756010011f67b415ad7c90fc17be5f224536322e16195105cd7b32a16540397adb06718a3a461459afe7a58c28a293acb729c3460c1b23a6ac6ffffe2d93235388c79831c288d6f6ddeb6065a355869127478cdcae79b3739c3868cc648df3d3091a9e31abe641537d674f1848e2203cc386fbcf3368d2bcc18a06aa9a3e456fde0c00 ASCII:E...-....@..[........@..@F.......P.@..H..GET / HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application3.\....g..8o\...vj..Y........w..I..k.9`..h!....;{.0....4>G........O......9.w........g......3v`..je..4w.4ab.j.6jpd.....'d...CF...h....#2.9u..a.&...x...!CF.....AS....1...#.....e8.1c..*5f.D\...9vF....G..:h..H.&..u`...g...|...._"E62...Q..{2.e@9z..q.:F.Y....(....).F..#..o..-.#S...1......`e.U..'G...y.s.8h.d......1..AS}gO.H. <....3h.......>Eo... uncompressed= 4500022d001200004006ac5bc0a800020a0901ab40011f4046a2f5a8e0a618025018400093480000474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a2031302e392e312e3137313a383030300d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a0d0a ASCII:E..-....@..[........@..@F.......P.@..H..GET / HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/vnd.wap.wml, */*..Accept-Charset: utf-8, utf-16, iso-8859-1, iso-10646-ucs-2, Shift_JIS, Big5..Accept-Language: en..x-wap-profile: "http://wap.sonyericsson.com/UAprof/K800iR201.xml"..Host: 10.9.1.171:8000..User-Agent: SonyEricssonK800i/R2B Release/Mar-13-2007 Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1..Connection: Keep-Alive..Accept-Encoding: deflate, gzip.... Testing decompression with sniffed compressed TCP/IP packets: Packet No.: 8 v42bis_decompress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 compressed= 4500010268001300004006ceeec0a80002550d93d740000050462c7ba7e4d1753b80193fff7b4a00000101080a0003c054084dc558474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d6978656433005bbb7e0d3b964dd9b369d7b6ddb3e60e9c372ef614beeb15ac58b2660513368cf8cdd63b68f65045ab96ed9cb58947b490d1422851a34861185923d50e9aa423f0dc490363c756d8b269e4d8cac68e9cd93b70f0804143376fe13372dcc801038f193b306a6cb5b2864d9a3b629a30b1b2b5081b353848173d7a07c6133271d4e021a3068d52347184ee81c119c69c3a72d2b079c37e4489c177e6f4902183730cde71f8a0a913d6cec21866e4c091818548fdfb329cec9831834d951a337e4e2e2174891c3baef5e8d113a38f1c336e2656148a85751d1844d6c7716da52c1f240f9b2fecf8918d0c9145465441a39f0c6b1950a40ab7f1fca38e1145ecebc129234aeb24f67bcab011c3c68829f6f1ebb7cbe4c894e731668c3052163ffa3a63d9949561e4c91123c263d0105a3a4e1c3466c8c651ea04cd519d60f3a0016f14290c2471289e61735ee9193469de8c45b3554d1fa84299c88622e73afeac30ac6037aaf40a9ef0236a54268247cd7f923946d0a8d1c3c68d1e35788c5002e58a50a10100 ASCII:E...h....@.......U...@..PF,{...u;..?.{J.........T.M.XGET / HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed3.[.~.;.M..i.......7.......X.f..6....;h.PE......G...B(Q.Ha.Y#....#..I.c.V..i.......;p..AC7o.3r......;0jl...M.;b.0.....58H.=z...2q..!...R4q.......:r..y.~D..w...!.s..q........f.....H..2...1.M..3~N.!t..;........3n&V...u..D..qm.,.$../......EFTA...k.P........E...)#J.$.{......).........1f.0R.?.:c...a...#.c..Z:N.4f..Q...Q.`...o.).$q(.as^..4i..E.UM..B...".:..0.`7.....#jT&.G...9F.......5x.P...P... uncompressed= 45000268001300004006ceeec0a80002550d93d740000050462c7ba7e4d1753b80193fff7b4a00000101080a0003c054084dc558474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a207777772e7a6f636b2e636f6d0d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a49662d4d6f6469666965642d53696e63653a205475652c2032332041756720323031362031323a33343a323920474d540d0a0d0a ASCII:E..h....@.......U...@..PF,{...u;..?.{J.........T.M.XGET / HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/vnd.wap.wml, */*..Accept-Charset: utf-8, utf-16, iso-8859-1, iso-10646-ucs-2, Shift_JIS, Big5..Accept-Language: en..x-wap-profile: "http://wap.sonyericsson.com/UAprof/K800iR201.xml"..Host: www.zock.com..User-Agent: SonyEricssonK800i/R2B Release/Mar-13-2007 Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1..Connection: Keep-Alive..Accept-Encoding: deflate, gzip..If-Modified-Since: Tue, 23 Aug 2016 12:34:29 GMT.... Testing decompression with sniffed compressed TCP/IP packets: Packet No.: 9 v42bis_decompress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 compressed= 450001022d001400004006ac59c0a800020a0901ab40011f4046a2f5a8e0a618025018400093480000474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e33005cbd8215bb67cd1d386f5cecd1cb766ad5ab59b7decdbbb7ef1ba877d0ec49daf56bd83960fd8e6821a3c5cd9c3b7bc230b2e6a81d343e47e0b99306c60ea8a54fd3c801958d1d39a877e0e00183c6ebd8b767e4b89103061e337660d4806a650d9b3477c4346162056a11366a7064d6c9f30e8c2764e2a8c143460d9a9f68e2dcdc0323328c3975e4a461f326fc881278efcce9214346e418b1e3f04153c7aa9dfd31ccc88123030b11f5ec6538d93163069b2a3566d0445ce2e612397646d398118347cd9a3a68f49848f12696d0756010011f67b415ad7c90fc17be5f224536322e16195105cd7b32a16540397adb06718a3a461459afe7a58c28a293acb729c3460c1b23a6ac6ffffe2d93235388c79831c288d6f6ddeb6065a355869127478cdcae79b3739c3868cc648df3d3091a9e31abe641537d674f1848e2203cc386fbcf3368d2bcc18a06aa9a3e456fde0c00 ASCII:E...-....@..Y........@..@F.......P.@..H..GET / HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application3.\....g..8o\...vj..Y........w..I..k.9`..h!....;{.0....4>G........O......9.w........g......3v`..je..4w.4ab.j.6jpd.....'d...CF...h....#2.9u..a.&...x...!CF.....AS....1...#.....e8.1c..*5f.D\...9vF....G..:h..H.&..u`...g...|...._"E62...Q..{2.e@9z..q.:F.Y....(....).F..#..o..-.#S...1......`e.U..'G...y.s.8h.d......1..AS}gO.H. <....3h.......>Eo... uncompressed= 4500022d001400004006ac59c0a800020a0901ab40011f4046a2f5a8e0a618025018400093480000474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a2031302e392e312e3137313a383030300d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a0d0a ASCII:E..-....@..Y........@..@F.......P.@..H..GET / HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/vnd.wap.wml, */*..Accept-Charset: utf-8, utf-16, iso-8859-1, iso-10646-ucs-2, Shift_JIS, Big5..Accept-Language: en..x-wap-profile: "http://wap.sonyericsson.com/UAprof/K800iR201.xml"..Host: 10.9.1.171:8000..User-Agent: SonyEricssonK800i/R2B Release/Mar-13-2007 Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1..Connection: Keep-Alive..Accept-Encoding: deflate, gzip.... Testing decompression with sniffed compressed TCP/IP packets: Packet No.: 10 v42bis_decompress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 compressed= 450001022d001500004006ac58c0a800020a0901ab40011f4046a2f5a8e0a618025018400093480000474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e33005cbd8215bb67cd1d386f5cecd1cb766ad5ab59b7decdbbb7ef1ba877d0ec49daf56bd83960fd8e6821a3c5cd9c3b7bc230b2e6a81d343e47e0b99306c60ea8a54fd3c801958d1d39a877e0e00183c6ebd8b767e4b89103061e337660d4806a650d9b3477c4346162056a11366a7064d6c9f30e8c2764e2a8c143460d9a9f68e2dcdc0323328c3975e4a461f326fc881278efcce9214346e418b1e3f04153c7aa9dfd31ccc88123030b11f5ec6538d93163069b2a3566d0445ce2e612397646d398118347cd9a3a68f49848f12696d0756010011f67b415ad7c90fc17be5f224536322e16195105cd7b32a16540397adb06718a3a461459afe7a58c28a293acb729c3460c1b23a6ac6ffffe2d93235388c79831c288d6f6ddeb6065a355869127478cdcae79b3739c3868cc648df3d3091a9e31abe641537d674f1848e2203cc386fbcf3368d2bcc18a06aa9a3e456fde0c00 ASCII:E...-....@..X........@..@F.......P.@..H..GET / HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application3.\....g..8o\...vj..Y........w..I..k.9`..h!....;{.0....4>G........O......9.w........g......3v`..je..4w.4ab.j.6jpd.....'d...CF...h....#2.9u..a.&...x...!CF.....AS....1...#.....e8.1c..*5f.D\...9vF....G..:h..H.&..u`...g...|...._"E62...Q..{2.e@9z..q.:F.Y....(....).F..#..o..-.#S...1......`e.U..'G...y.s.8h.d......1..AS}gO.H. <....3h.......>Eo... uncompressed= 4500022d001500004006ac58c0a800020a0901ab40011f4046a2f5a8e0a618025018400093480000474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a2031302e392e312e3137313a383030300d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a0d0a ASCII:E..-....@..X........@..@F.......P.@..H..GET / HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/vnd.wap.wml, */*..Accept-Charset: utf-8, utf-16, iso-8859-1, iso-10646-ucs-2, Shift_JIS, Big5..Accept-Language: en..x-wap-profile: "http://wap.sonyericsson.com/UAprof/K800iR201.xml"..Host: 10.9.1.171:8000..User-Agent: SonyEricssonK800i/R2B Release/Mar-13-2007 Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1..Connection: Keep-Alive..Accept-Encoding: deflate, gzip.... Testing decompression with sniffed compressed TCP/IP packets: Packet No.: 11 v42bis_decompress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 compressed= 4500010239000500004006ac5cc0a800020a0901ab40001f90c286afa741a348cb801840007fcb0000050a41a348dc41a34a440000474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d69786564330057b36eedfa954dd8b165cfa6ddb3e60e9c372ef6049eab95ab57b062fd02164cf8cdd53b68f640256b16ed9cb38547b490d1e22791a043efc030b2c6a91d344547e0b99306c68eabad5fd3c871958d1d39b077e0e00183c6eddcbf67e4b89103061e337660d4b86a650d9b3477c4346162e56a11366a7080164d14c6133271d4e021a3068d5134717eee818119c69c3a72d2b079837e4489bf77e6f4902103738cdc71f8a0a9d3d58ec11866e4c091818548fcf9329cec9831834d951a33783e2ef173891c3bab69cc88c1a3674f1d347a6cdcf8134bea3a30889c8fb3da4a583e48162a37a891231b19208b8ca882c63e99d432a038fd6d8339471d238ac8d793534614d549e40b956123868d1153e4d3b77f97c99129cc63cc1861242c7df275beb2092bc3c89323467ef7fc693a4e1c3466c0c631ea04cdd09d5cf3a0e96e66e81d1848e2403cc366bcd13368d2bcf98ae6aa9a3e4c7ffe0c00 ASCII:E...9....@..\........@.......A.H...@.......A.H.A.JD..GET / HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed3.W.n...M..e.......7.......W.b...L...;h.@%k.....G....'..C..0....4EG........_..q...9.w........g......3v`..je..4w.4ab.j.6jp..M...2q..!...Q4q~......:r..y.~D..w...!.s..q........f.....H..2...1.M..3x>..s..;.i....gO.4zl...K.:0.....JX>H.*7..#.. .....>..2.8.m.9G.#....SF..I...a#...S.......).c..a$,}.u...+...#F~..i:N.4f..1.....\...nf...H.@<.f..3h.......>L.... uncompressed= 45000239000500004006ac5cc0a800020a0901ab40001f90c286afa741a348cb801840007fcb0000050a41a348dc41a34a440000474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a2031302e392e312e3137313a383038300d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a0d0a ASCII:E..9....@..\........@.......A.H...@.......A.H.A.JD..GET / HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/vnd.wap.wml, */*..Accept-Charset: utf-8, utf-16, iso-8859-1, iso-10646-ucs-2, Shift_JIS, Big5..Accept-Language: en..x-wap-profile: "http://wap.sonyericsson.com/UAprof/K800iR201.xml"..Host: 10.9.1.171:8080..User-Agent: SonyEricssonK800i/R2B Release/Mar-13-2007 Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1..Connection: Keep-Alive..Accept-Encoding: deflate, gzip.... Testing decompression with sniffed compressed TCP/IP packets: Packet No.: 12 v42bis_decompress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 compressed= 450001025b000a00004006ac35c0a800020a0901ab40011f90c293b0a8af5e58be5018400072a60000474554202f72656470686f6e652e706e6720485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c33005cbd82154b368e59b46ad9ee597307ce1b177b066fedfa35ec583665010b266cf8cdd63b68f6543d9b76ed1cb58747b490d16268d1a34961185933d50e1aa523f0dc490363c7d6d7b169e4d8cac68e1cd93b70f0804123f7eee03372dcc801038f193b306a6cb5b2864d9a3b629a30b1b2b5081b3538461b457a07c6133238f190518366299a3843f7c0d80c634e1d3969d8bc513fa244e03b737ac890b139c6ee387cd0d4096b07610c3372e0c8c042647e7d194e76cc98c1a64a8d1940259718ba448e9dd63466c4e01134a80e1a3d38721c8a65751d1844d2c7696d65261f240d9923dcd8918d0c9045465441839fcc6a1950a606b7e1bca38e1145e8ebd929230aeb24f485cab011c3c68829f4ede3d7cbe4c814e731668c3032d3be1a3c75c6b2296be4c91123c1830e451d270e1a3364e32c758206694fb079d07c3f9a1406923812cfb0c1b9f40c9a346fc6a2d9aaa64fd4a175d33064b894bfff812b5bc2a421b3e60c8e32860e0d00 ASCII:E...[....@..5........@........^X.P.@.r...GET /redphone.png HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed,3.\...K6.Y.j..Ys....{.o..5.X6e..&l...;h.T=.v....G...bh..Ia.Y3....#..I.c....i.......;p..A#...3r......;0jl...M.;b.0.....58F.Ez...28..Q.f).8C....cN.9i..Q?.D.;sz...9..8|...k.a.3r...Bd~}.Nv....J..@%...D...4f...4...=8r..eu..D..ime&.$..#......EFTA...j.P........E...)#..$........).........1f.02.......;h.TM......G...bh..Ia.Y3....#..I.c....i.......;p..Acw..3r......;0jp...M.;b.0.....58J.Ez...2q..!...R4q.......:r..y.~D..w...!.s..q...#....f......}.2...1.M..3.R.1t..;........3n(Z...u..D..ym.,.$..3......EFTA..Lk.P........E...)#..$.}......).........1f.0b6..:d...a...#.c..j:N.4f..Y.....a....4).$q*.a.~..4i..E.UM..C... uncompressed= 45000236003000004006cf03c0a80002550d93d740020050c30e84a9441d06ac80184000c2f400000101080a00052df410fc31bd474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a207777772e7a6f636b2e636f6d0d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a0d0a ASCII:E..6.0..@.......U...@..P....D.....@...........-...1.GET / HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/vnd.wap.wml, */*..Accept-Charset: utf-8, utf-16, iso-8859-1, iso-10646-ucs-2, Shift_JIS, Big5..Accept-Language: en..x-wap-profile: "http://wap.sonyericsson.com/UAprof/K800iR201.xml"..Host: www.zock.com..User-Agent: SonyEricssonK800i/R2B Release/Mar-13-2007 Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1..Connection: Keep-Alive..Accept-Encoding: deflate, gzip.... Testing decompression with sniffed compressed TCP/IP packets: Packet No.: 15 v42bis_decompress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 compressed= 4500010260004500004006cec4c0a80002550d93d740030050c3134faac89c8b2980184000578d00000101080a000535c010fc34c8474554202f6e697276616e612e63737320485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d33006fcede41b3072c5dbb78e7dcad3ca2858c164ea14aa50ac3c81aaf76d0541d81e74e1a183bcef2f64d23c7593676e4fcde8183070c1ac6913b9f91e3460e187884c2a871d6ca1a3669ee8869c2c4cad9226cd4e0801d75ea1d184f7caac143460d1aab68e238dd0303358c3975e4a461f3c6fe88128fefcce9214306ea18c8e3f04153a7edd0b841e5c0918185c87f83329cec9831834d951a33967e2ee174891c3bbaf5e8d113a38f1c336e3ac2718a05771d1844eac7d16d252e1f241985563489928d8c954546544193900c6e1950bc3ab7a11da58e11450aea292a234aee240595cab011c3c68829050f2624cce4c814ed31668c3012f7a0fc3a6fd9c49561e4c91123ce63d0702a3b4e1c3466e0c6b1ea04cdd4a36cf3a0592f952a0c2471ccc839c3268e1aab67d0a479f316cd59357db83af59b062346ab0d1f46b47933e7ce9e329c3a0d00 ASCII:E...`.E..@.......U...@..P..O....)..@.W.........5...4.GET /nirvana.css HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtml+xm3.o..A..,].x...<....N.J......v.T...N..;...M#.Y6v..........;...F..x...q...6i..i...."l....u...O|..CF...h.8...5.9u..a.........!C......AS....A........2...1.M..3.~..t..;........3n:.q..w..D...m%..$..V4.....EFTA...n.P.:......E..)*#J.$........)..&$.....1f.0....:o...a...#.c.p*;N.4f........l..Y/.*.$q..9.&...g..y...Y5}.:...#F...F.y3...2.:.. uncompressed= 45000260004500004006cec4c0a80002550d93d740030050c3134faac89c8b2980184000578d00000101080a000535c010fc34c8474554202f6e697276616e612e63737320485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a207777772e7a6f636b2e636f6d0d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a526566657265723a20687474703a2f2f7777772e7a6f636b2e636f6d2f0d0a0d0a ASCII:E..`.E..@.......U...@..P..O....)..@.W.........5...4.GET /nirvana.css HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/vnd.wap.wml, */*..Accept-Charset: utf-8, utf-16, iso-8859-1, iso-10646-ucs-2, Shift_JIS, Big5..Accept-Language: en..x-wap-profile: "http://wap.sonyericsson.com/UAprof/K800iR201.xml"..Host: www.zock.com..User-Agent: SonyEricssonK800i/R2B Release/Mar-13-2007 Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1..Connection: Keep-Alive..Accept-Encoding: deflate, gzip..Referer: http://www.zock.com/.... Testing decompression with sniffed compressed TCP/IP packets: Packet No.: 16 v42bis_decompress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 compressed= 4500010264004600004006cebfc0a80002550d93d740040050c3135bab2189da61801840008f2d00000101080a000535c410fc34dc474554202f382d4269742f4c6162656c2e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d3300952fbf517b07cd1e9f77f3ee9da317f38816325a48a56a152b0c236bc4da419375049e3b6960ec50fb3b388d1c6ad9d891237c070e1e3068245f1e7d468e1b3960e0316307460db556d6b04973474c132656d4f254836376d5ab7760008da3060f193568b4a28923750f8cd530e6d4919386cd9bfc234a48be33a7870c19ab632c8fc3074d1db87630c63023078e0c2c440c2294e164c78c196caad498f1547409a94be4d8e9ad478f9e187de498710352a4542cbbebc020823f4e6f2b74f920e1c81da34a966c64bc2c32a20a1a866476cb802236ba0def2c758c2872500f521951782739d854868d1836464c39a89061612647a6788f31638411ba0aebd791cb86ae0c234f8e18891e838654da71e2a03133378e562768ae2a7d9b078d7bab5861208913f20c1bfa5acfa049f3462e1ab56afa80950a38cdc68d5a214aa4a873e74fa144474a951a00 ASCII:E...d.F..@.......U...@..P..[.!..a..@..-........5...4.GET /8-Bit/Label.GIF HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtm3../.Q{....w........2ZH.j.+.#k..A.u..;i`.P.;8..j...#|...0h$_.}F..9`.1c.F..V..IsGL.&V..T.cv..w`......5h...#u...0........#JH.3.....c,...M..v0.0#...,D."..d...l....Tt..K....G...}..q.R.T,... .?No+t. ....J.ld.,2....dv.."6...,u.(rP.R.Qx'9.T...6FL9..aa&G.x.1c...........#O......T.q..137.V'h.*}...{.Xa ......Z..I.F...j....8...Z!J..s.O.DGJ... uncompressed= 45000264004600004006cebfc0a80002550d93d740040050c3135bab2189da61801840008f2d00000101080a000535c410fc34dc474554202f382d4269742f4c6162656c2e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a207777772e7a6f636b2e636f6d0d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a526566657265723a20687474703a2f2f7777772e7a6f636b2e636f6d2f0d0a0d0a ASCII:E..d.F..@.......U...@..P..[.!..a..@..-........5...4.GET /8-Bit/Label.GIF HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/vnd.wap.wml, */*..Accept-Charset: utf-8, utf-16, iso-8859-1, iso-10646-ucs-2, Shift_JIS, Big5..Accept-Language: en..x-wap-profile: "http://wap.sonyericsson.com/UAprof/K800iR201.xml"..Host: www.zock.com..User-Agent: SonyEricssonK800i/R2B Release/Mar-13-2007 Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1..Connection: Keep-Alive..Accept-Encoding: deflate, gzip..Referer: http://www.zock.com/.... Testing decompression with sniffed compressed TCP/IP packets: Packet No.: 17 v42bis_decompress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 compressed= 4500010264005800004006ceadc0a80002550d93d740050050c31389acaf7b26538018400075c900000101080a000537d010fc354a474554202f382d4269742f41636f726e2e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d3300942dbf497b07cd1e9f76f1ea9d93f7f28816325a48a56af50e0c236bc2da418375049e3b6960ec48eb1b388d1c69d9d891137c070e1e306820570e7d468e1b3960e0316307468db456d6b04973474c132656d2f2548343b699aa57613c2113470d1e326ad064451347ea1e18aa61cca923270d9b37f94794887c674e0f193254c7501e870f9a3a6fed608c11148e0c2c440c2294e164c78c196caad498f1347409a94be4d8e1ad478f9e187de4987103520e1ca95874d78141047f1cde56e6f241c2713bc6942bd9c87059644415340cc9e89601252c741bdd57ea1851e4a09ea432a2ec4e72d0a90c1b316c8c9872502143c34c8e4ce91e63c608237315d6af1397cd5c19469e1c31023d060da9b4e3c44163466e9cac4ed0585dea360f9af6efefc0401227e81936f4b39e4193e64d5c3469d5f4f92a15709a8d1bb342944831a7ce9e42898a942a3500 ASCII:E...d.X..@.......U...@..P.....{&S..@.u.........7...5JGET /8-Bit/Acorn.GIF HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtm3..-.I{....v........2ZH.j...#k..A.u..;i`.H..8..i....|...0h W.}F..9`.1c.F..V..IsGL.&V..T.C...Wa#...0......Z+k...#...+kw..A.*.;0.......5h...3u...0........#JP.3.....c0...M..v6.0#...,D.2..d...l.....t..K....G...}..q3..T,... .?.o+u. ..}c..ld.,2....dx..2V.../u.(.P.S.Qz'Q(U...6FLQ..ab&G.|.1c...........#O........q..1C7.V'h.:......Ya .C....[..I.f...j...:8.G.['V....P.HMN... uncompressed= 45000266007600004006ce8dc0a80002550d93d740060050c31431ada11fa06780184000f08e00000101080a00053b3c10fc35ef474554202f382d4269742f416d73747261642e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a207777772e7a6f636b2e636f6d0d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a526566657265723a20687474703a2f2f7777772e7a6f636b2e636f6d2f0d0a0d0a ASCII:E..f.v..@.......U...@..P..1....g..@...........;<..5.GET /8-Bit/Amstrad.GIF HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/vnd.wap.wml, */*..Accept-Charset: utf-8, utf-16, iso-8859-1, iso-10646-ucs-2, Shift_JIS, Big5..Accept-Language: en..x-wap-profile: "http://wap.sonyericsson.com/UAprof/K800iR201.xml"..Host: www.zock.com..User-Agent: SonyEricssonK800i/R2B Release/Mar-13-2007 Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1..Connection: Keep-Alive..Accept-Encoding: deflate, gzip..Referer: http://www.zock.com/.... Testing decompression with sniffed compressed TCP/IP packets: Packet No.: 19 v42bis_decompress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 compressed= 4500010264007700004006ce8ec0a80002550d93d740040050c3135ddb2189e0108018400060d600000101080a00053b4010fc35e7474554202f382d4269742f41746172692e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d3300932bbf497b07cd1e9f76f1ea9d93d7f28816325a48a56a152b0c236b84da419375049e3b6960ec48dbfb378d1c69d9d891037c070e1e30681c4ffe7c468e1b3960e0316307468db456d6b04973474c132656d2f254832376d5ab77603c2113470d1e326ad068451347ea1e18a961cca923270d9b37f84794807c674e0f193252c7481e870f9aa1da2fc63023078e0c2c440a1e94e164c78c196caad498f1147409a94be4d8d9ad478f9e187de49871e311a4542cb9ebc020723fce6e2b73f920d968e7224a956c64b42c32a20a9a856472cb8022f4b90dee2a758c286250cf511951742731c854868d1836464c31887061612647a6708f31638491b908e9d789cb66ae0c234f8e18791e8386d4d971e2a03123378e562768ae26759b070d7bab58612089f3f10c9bf95acfa049f3262e9ab46afa8095fa378d468d5a1f469c8833e7ce9f41434a951a00 ASCII:E...d.w..@.......U...@..P..].!.....@.`.........;@..5.GET /8-Bit/Atari.GIF HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtm3..+.I{....v........2ZH.j.+.#k..A.u..;i`.H..7..i....|...0h.O.|F..9`.1c.F..V..IsGL.&V..T.#v..w`4..6.GET /8-Bit/Apple.GIF HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtm3..-.Q{....v........2ZH.j.+.#k..A.u..;i`.P..7..j....|...0h.O.|F..9`.1c.F..V..IsGL.&V..T.#v..w`4..6.GET /8-Bit/Apple.GIF HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/vnd.wap.wml, */*..Accept-Charset: utf-8, utf-16, iso-8859-1, iso-10646-ucs-2, Shift_JIS, Big5..Accept-Language: en..x-wap-profile: "http://wap.sonyericsson.com/UAprof/K800iR201.xml"..Host: www.zock.com..User-Agent: SonyEricssonK800i/R2B Release/Mar-13-2007 Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1..Connection: Keep-Alive..Accept-Encoding: deflate, gzip..Referer: http://www.zock.com/.... Testing decompression with sniffed compressed TCP/IP packets: Packet No.: 21 v42bis_decompress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 compressed= 4500010265007d00004006ce87c0a80002550d93d740050050c3138bdcaf7b296780183cec0de600000101080a00053e3410fc368e474554202f382d4269742f447261676f6e2e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d33009631bf597b07cd1e9f8df5f2d59379440b192da656bd9a1586913563eda0d13a02cf9d343076ac052e9c468eb56cecc819be03070f18349433973e23c78d1c30f098b103a3c65a2b6bd8a4b923a609132b6b79aac141db2ad63b309e9089a3068f50345bd1c499ba07c6631873eac849c3e68dfe112526df99d343868cc73198c7e183a64e5c3b1963989103470616220715ca70b263c60c36556acc804abac4d42572ecf8d6a3474f8c3e72ccb80939722a16de75601051c3dfb715a27c9074ec9e71654b363260161951054d4332bc6540192bddc6f7963a461441a847a98c28bd93208ce3d3460c1b23a6205cd89030932353bec79831c208d185f7ebcc6553d7c8932346a4c7a03175769c3868ccd08db3d5091aac4ce1e641f3fe6a561848e2883cc3c6fed63368d2bc998b66ad9a3e61a7fe4dc391e3d688132beeec0974a8519253a70600 ASCII:E...e.}..@.......U...@..P.....{)g..<...........>4..6.GET /8-Bit/Dragon.GIF HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtm3..1.Y{.........yD..-.V.....5c...:...40v....F..l........4.3.>#...0......Z+k...#...+ky..A.*.;0......P4[......c.s..I.....%&...C...1.....N\;.c...G.."...p.c..6Uj..J...%r....GO.>r...9r*..u`.Q.....|.t..qeK62`..Q.MC2.e@.+....:F.A.G..(.. ...F..#. \..0.#S...1.......eS...#F...1uv.8h........L..A..jV.H..<....3h....f..>a..M......+...t.Q.S... uncompressed= 45000265007d00004006ce87c0a80002550d93d740050050c3138bdcaf7b296780183cec0de600000101080a00053e3410fc368e474554202f382d4269742f447261676f6e2e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a207777772e7a6f636b2e636f6d0d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a526566657265723a20687474703a2f2f7777772e7a6f636b2e636f6d2f0d0a0d0a ASCII:E..e.}..@.......U...@..P.....{)g..<...........>4..6.GET /8-Bit/Dragon.GIF HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/vnd.wap.wml, */*..Accept-Charset: utf-8, utf-16, iso-8859-1, iso-10646-ucs-2, Shift_JIS, Big5..Accept-Language: en..x-wap-profile: "http://wap.sonyericsson.com/UAprof/K800iR201.xml"..Host: www.zock.com..User-Agent: SonyEricssonK800i/R2B Release/Mar-13-2007 Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1..Connection: Keep-Alive..Accept-Encoding: deflate, gzip..Referer: http://www.zock.com/.... Testing decompression with sniffed compressed TCP/IP packets: Packet No.: 22 v42bis_decompress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 compressed= 4500010269008000004006ce80c0a80002550d93d740060050c3143301dfa11fa3de80183c892b5d00000101080a0005412c10fc3761474554202f382d4269742f456e74657270726973652e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f6600952f67defcc66dd03d3df7f6fd3bc72fe7112d64b4b08a552b571846d6983dda75049e3b6960ec701b7c388d1c6e8dca21be03070f183496db694e63468e1b3960e0316307460db756d6b04973474c132656dcee5483c376d6ad77603c2113470d1e326ad0784513c7ea1e18ae6110959386cd1bfe234a58be33a7870c19ae63548fc347285d3b1b63989103470616220ad3c870b263c60c36556acc986aba84d52572ecfcd6a3474f8c3e72ccb81149d22a96a3756010d91fe7b715bc7c90d4f9b891a54b36326216195105cd4332476540312bdd0678973a4614a923478f531951d0dc49d299aa0c1b316c8c98d2b9e143c64c8e4c011f63c60823781be2af63970d5e19469e1c31223d060dabbce3c44163e66e1caf4ed06c853a370f1af85ab9c2401267e41936f7bd9e4193e68d5d346ed5f4216bd5701aa142bd4eac78d1e7cfa1489596b46a3500 ASCII:E...i....@.......U...@..P..3........<.+]........A,..7aGET /8-Bit/Enterprise.GIF HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/f../g...m.==...;./..-d...U+W.F..=.u..;i`.p.|8..n..!.....4..iNcF..9`.1c.F..V..IsGL.&V..T..v..w`r...I.*..u`.......|......K62b..Q..C2Ge@1+..x.:F..#G.S.Q..I.....1l.....C.L.L..c..#x...c..^.F..1"=......Ac.n..N.l.:7...Z..@.g..6...A...]4n..!k.p..B.N.x....H...j5. uncompressed= 45000269008000004006ce80c0a80002550d93d740060050c31433dfa11fa3de80183c892b5d00000101080a0005412c10fc3761474554202f382d4269742f456e74657270726973652e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a207777772e7a6f636b2e636f6d0d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a526566657265723a20687474703a2f2f7777772e7a6f636b2e636f6d2f0d0a0d0a ASCII:E..i....@.......U...@..P..3.......<.+]........A,..7aGET /8-Bit/Enterprise.GIF HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/vnd.wap.wml, */*..Accept-Charset: utf-8, utf-16, iso-8859-1, iso-10646-ucs-2, Shift_JIS, Big5..Accept-Language: en..x-wap-profile: "http://wap.sonyericsson.com/UAprof/K800iR201.xml"..Host: www.zock.com..User-Agent: SonyEricssonK800i/R2B Release/Mar-13-2007 Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1..Connection: Keep-Alive..Accept-Encoding: deflate, gzip..Referer: http://www.zock.com/.... Testing decompression with sniffed compressed TCP/IP packets: Packet No.: 23 v42bis_decompress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 compressed= 4500010268008100004006ce80c0a80002550d93d740040050c313600b2189e30280183d0e896b00000101080a000541d810fc379d474554202f382d4269742f436f6d6d6f646f72652e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f3300952f67defcc6ed1d347b7ceeedfb778e5fce235ac8686115ab56ae308cac316b074dd71178eea481b1c3edf0e23472b86563478ef11d3878c0a0d1fc79f519396ee48081c78c1d1835dc5a59c326cd1d314d985871cb530d8edb59b7de81f1844c1c3578c8a841e3154d1cab7b60bc8631a78e9c346cdef81f51c2f29d393d64c8781de3791c3e68ead0b5d3318699a032b01061e85086931d3366b0a95263c654d325ac2e916327b81e3d7a62f49163c64dc9a056b1fcae038348ff38c1ade0e5832424f88e2e61b29131b3c8882a682292f92d038ad9ea36c4c3d431a248433d4c6544019ea46154193662d81831a5e1c38889991c99223ec68c1146f03ed45fc72e1bbc328c3c3962a47a0c1a5671c78983c6ccdd385e9da0d9ea746e1e34f2b5728581248ec9336cf27b3d8326cd1bbb68dcaae943d62ae13420417aad78312350a1448d228523c3aad500 ASCII:E...h....@.......U...@..P..`.!.....=..k........A...7.GET /8-Bit/Commodore.GIF HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/3../g.....4{|...w._.#Z.ha..V.0..1k.M..x........4r.ecG...8x....y..9n.......5.ZY.&..1M.Xq.S...Y.....L.5x..A..M..{`..1...4l...Q...9=d.x..y.>h....1...2..a.P...3f..Rc.T.%...c'..=zb..c.M..V.....H.8.....$$...a..1...*h"..-....6...1.HC=LeD...aT.6b..1........">...F.>._....2.<9b.z..Vq......8^....tn.4..r..$..3l.{=.&...h...C.*.4 Az.x1#P.D.".#.... uncompressed= 45000268008100004006ce80c0a80002550d93d740040050c313600b2189e30280183d0e896b00000101080a000541d810fc379d474554202f382d4269742f436f6d6d6f646f72652e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a207777772e7a6f636b2e636f6d0d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a526566657265723a20687474703a2f2f7777772e7a6f636b2e636f6d2f0d0a0d0a ASCII:E..h....@.......U...@..P..`.!.....=..k........A...7.GET /8-Bit/Commodore.GIF HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/vnd.wap.wml, */*..Accept-Charset: utf-8, utf-16, iso-8859-1, iso-10646-ucs-2, Shift_JIS, Big5..Accept-Language: en..x-wap-profile: "http://wap.sonyericsson.com/UAprof/K800iR201.xml"..Host: www.zock.com..User-Agent: SonyEricssonK800i/R2B Release/Mar-13-2007 Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1..Connection: Keep-Alive..Accept-Encoding: deflate, gzip..Referer: http://www.zock.com/.... Testing decompression with sniffed compressed TCP/IP packets: Packet No.: 24 v42bis_decompress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 compressed= 4500010263008200004006ce84c0a80002550d93d740050050c3138e0daf7b2cf180183962cb2f00000101080a000542b410fc3822474554202f382d4269742f454143412e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b330096dfa4bd83668f4fbb78f5cec97b79440b192da24ead7a1586913561eda0c13a02cf9d343076a4f50d9c468eb46cecc809be03070f1834902b873e23c78d1c30f098b103a3465a2b6bd8a4b923a609132b6979aac1219baad53b309e9089a3060f193568b2a28913750f0cd530e6d4919386cd9bfc234a44be33a7870c19aa63288fc3074d9db77630c63023078e0c2c440c2294e164c78c196caad498e1347489a84be4d8e1ad478f9e187de49871035264542cbaebc020823f0e6f2b73f920e1b81da34a966c64bc2c32a20a1a866474cb801216ba8dee2c758c287250cf511951762739c854868d1836464c39a89021612647a6748f31638491b90aebd789cb66ae0c234f8e18811e8346d4d971e2a03123374e562768ac26759b074dfbaa5761208913f20c1bfa59cfa049f3262e9ab46afa7c8dfa37cdc68d59214aa4a873e7cfa04347468d1a00 ASCII:E...c....@.......U...@..P.....{,...9b./........B...8"GET /8-Bit/EACA.GIF HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtml+3......f.O.x...{yD..-.N.z...5a...:...40v....F..l........4.+.>#...0.....FZ+k...#...+iy..!...;0.......5h....u...0........#JD.3.....c(...M..v0.0#...,D."..d...l....4t..K....G...}..q.RdT,... .?.o+s. ....J.ld.,2....dt.......,u.(rP.Q.Qv'9.T...6FL9..!a&G.t.1c........f..#O.....F..q..1#7NV'h.&u..M..Wa ......Y..I.&...j.|..7...Y!J..s...CGF... uncompressed= 45000263008200004006ce84c0a80002550d93d740050050c3138e0daf7b2cf180183962cb2f00000101080a000542b410fc3822474554202f382d4269742f454143412e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a207777772e7a6f636b2e636f6d0d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a526566657265723a20687474703a2f2f7777772e7a6f636b2e636f6d2f0d0a0d0a ASCII:E..c....@.......U...@..P.....{,...9b./........B...8"GET /8-Bit/EACA.GIF HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/vnd.wap.wml, */*..Accept-Charset: utf-8, utf-16, iso-8859-1, iso-10646-ucs-2, Shift_JIS, Big5..Accept-Language: en..x-wap-profile: "http://wap.sonyericsson.com/UAprof/K800iR201.xml"..Host: www.zock.com..User-Agent: SonyEricssonK800i/R2B Release/Mar-13-2007 Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1..Connection: Keep-Alive..Accept-Encoding: deflate, gzip..Referer: http://www.zock.com/.... Testing decompression with sniffed compressed TCP/IP packets: Packet No.: 25 v42bis_decompress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 compressed= 4500010262008500004006ce82c0a80002550d93d740070050c314f5defa37d29b80183c168b6100000101080a000542c810fc37e5474554202f382d4269742f4d53582e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b330095dfa0bd83668fcfba77f3cec16b79440b192da04aa56a1586913560eda0b93a02cf9d343076a0edfd9b460eb46cecc801be03070f18348e277f3e23c78d1c30f098b103a3065a2b6bd8a4b923a609132b6879aac1117b6ad53b309e9089a3060f193568b0a28903750f8cd430e6d4919386cd1bfc234a40be33a7870c19a963248fc3074d1db7762ec63023078e0c2c440a1e94e164c78c196caad498d1147409a84be4d8d9ad478f9e187de49871f33124542cb9ebc020723fce6e2b72f920d9a8fd62ca956c64b82c32a20a9a856472cb8002f6b90dee2b758c2862508f511951742731b854868d1836464c318870e1602647a6708f31638411b908e9d781cb46ae0c234f8e18791e830654d971e2a03113370e562768aa226d9b070d7baa5661208903f20c9bf958cfa049f3062e1ab46afa7885ea378d468d581f469c9853674fa0424542851a00 ASCII:E...b....@.......U...@..P.....7....<..a........B...7.GET /8-Bit/MSX.GIF HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtml+3......f...w...kyD..-.J.j...5`...:...40v....F..l........4.'.>#...0......Z+k...#...+hy...{j.;0.......5h....u...0........#J@.3.....c$...M..v..0#...,D....d...l.....t..K....G...}..q.1$T,... r?.n+r. ...b..ld.,2....dr.......+u.(bP.Q.Qt'1.T...6FL1.p.`&G.p.1c........F..#O..y...T.q..1.7.V'h."m...{.Va ......X..I.....j.x..7.F.X.F..SgO.BEB... uncompressed= 45000262008500004006ce82c0a80002550d93d740070050c314f5defa37d29b80183c168b6100000101080a000542c810fc37e5474554202f382d4269742f4d53582e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a207777772e7a6f636b2e636f6d0d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a526566657265723a20687474703a2f2f7777772e7a6f636b2e636f6d2f0d0a0d0a ASCII:E..b....@.......U...@..P.....7....<..a........B...7.GET /8-Bit/MSX.GIF HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/vnd.wap.wml, */*..Accept-Charset: utf-8, utf-16, iso-8859-1, iso-10646-ucs-2, Shift_JIS, Big5..Accept-Language: en..x-wap-profile: "http://wap.sonyericsson.com/UAprof/K800iR201.xml"..Host: www.zock.com..User-Agent: SonyEricssonK800i/R2B Release/Mar-13-2007 Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1..Connection: Keep-Alive..Accept-Encoding: deflate, gzip..Referer: http://www.zock.com/.... Testing decompression with sniffed compressed TCP/IP packets: Packet No.: 26 v42bis_decompress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 compressed= 4500010265009400004006ce70c0a80002550d93d740060050c3143614a11fa986801836e1f43c00000101080a000545b010fc38b7474554202f382d4269742f4d617474656c2e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d3300942dbf512b744fcfbb79f7ced17b79440b192da452b58a1586913562eda0c93a02cf9d343076a8edfd9b460eb56cecc801be03070f18348e277f3e23c78d1c30f098b103a3865a2b6bd8a4b923a609132b6a77aac111bbead53b309e9089a3060f193568b4a28923750f8cd430e6d4919386cd1bfc234a44be33a7870c19a963248fc3074d1db87630c63023078e0c2c440a1e94e164c78c196caad498f1347409a94be4d8d9ad478f9e187de498710352a4542cb9ebc020723fce6e2b74f920e1a81da34a966c64bc2c32a20a1a866472cb8022f6b90dee2c758c2862504f521951742731e854868d1836464c31889021612647a6708f31638411ba08e9d791bbd8c89323469ec7a02195769c3868cc7ca5abd5099aab4bdfe641c3de2a561848e2843cc366bed63368d2bc018a46ad9a3e60a5fe4db371a356881229eae4f97368d19152a50600 ASCII:E...e....@..p....U...@..P..6.......6..<........E...8.GET /8-Bit/Mattel.GIF HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtm3..-.Q+tO..y...{yD..-.R.....5b...:...40v....F..l........4.'.>#...0......Z+k...#...+jw......;0.......5h...#u...0........#JD.3.....c$...M..v0.0#...,D....d...l....4t..K....G...}..q.R.T,... r?.n+t. ....J.ld.,2....dr.."....,u.(bPOR.Qt'1.T...6FL1..!a&G.p.1c...........#F...!.v.8h.|......K..A..*V.H..<.f..3h....F..>`..M.q.V..)...sh..R... uncompressed= 45000265009400004006ce70c0a80002550d93d740060050c3143614a11fa986801836e1f43c00000101080a000545b010fc38b7474554202f382d4269742f4d617474656c2e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a207777772e7a6f636b2e636f6d0d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a526566657265723a20687474703a2f2f7777772e7a6f636b2e636f6d2f0d0a0d0a ASCII:E..e....@..p....U...@..P..6.......6..<........E...8.GET /8-Bit/Mattel.GIF HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/vnd.wap.wml, */*..Accept-Charset: utf-8, utf-16, iso-8859-1, iso-10646-ucs-2, Shift_JIS, Big5..Accept-Language: en..x-wap-profile: "http://wap.sonyericsson.com/UAprof/K800iR201.xml"..Host: www.zock.com..User-Agent: SonyEricssonK800i/R2B Release/Mar-13-2007 Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1..Connection: Keep-Alive..Accept-Encoding: deflate, gzip..Referer: http://www.zock.com/.... Testing decompression with sniffed compressed TCP/IP packets: Packet No.: 27 v42bis_decompress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 compressed= 4500010263009900004006ce6dc0a80002550d93d740040050c313623f2189e66b801839a5739a00000101080a0005464c10fc38f3474554202f382d4269742f4f7269632e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b330095dfa4bd83668f4fc578f5eab13ca2858c1651a756bd0ac3c89ab076d0601d81e74e1a183bd2fa064e23475a3676e404df8183070c1ac895439f91e3460e1878ccd8815123ad95356cd2dc11d3848995b43cd5e0904dd5ea1d184fc8c45183878c1a3459d1c489ba0706631873eac849c3e64dfe112520df99d343860cc63194c7e18326e8768c31ccc88123030b1183086538d93163069b2a356638155d22ea12397678ebd1a327461f3966dc7c0c19158bee3a3088e08fc3db8a5c3e4838dac19872251b192e8b8ca882862119dd32a084856ea3fb4a1d238a1cd483544694dd490e36956123868d11530e2a642898c99129dd63cc186144aec2fa75e0b2912bc3c8932346a0c7a01135769c3868ccc48d93d5091aab4adde641d3beea551848e2803cc3867ed63368d2bc818b26ad9a3e5fa3f64db3716356881229e6d4d91328519151a30600 ASCII:E...c....@..m....U...@..P..b?!..k..9.s.........FL..8.GET /8-Bit/Oric.GIF HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtml+3......f.O.x...<....Q.V......v.`...N..;...N#GZ6v..........C...F..x...Q#..5l........<...M....O..Q....4Y......c.s..I..M..% ...C...1....&.v.1...#.....e8.1c..*5f8.]"..9vx...'F.9f.|.....:0......\>H8...r%........!..2...n..J.#....TF..I.6.a#...S.*d(...).c..aD...u...+...#F....5v.8h........J..A...U.H..<..~.3h....&..>_..M.qcV..)....(Q.Q... uncompressed= 45000263009900004006ce6dc0a80002550d93d740040050c313623f2189e66b801839a5739a00000101080a0005464c10fc38f3474554202f382d4269742f4f7269632e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a207777772e7a6f636b2e636f6d0d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a526566657265723a20687474703a2f2f7777772e7a6f636b2e636f6d2f0d0a0d0a ASCII:E..c....@..m....U...@..P..b?!..k..9.s.........FL..8.GET /8-Bit/Oric.GIF HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/vnd.wap.wml, */*..Accept-Charset: utf-8, utf-16, iso-8859-1, iso-10646-ucs-2, Shift_JIS, Big5..Accept-Language: en..x-wap-profile: "http://wap.sonyericsson.com/UAprof/K800iR201.xml"..Host: www.zock.com..User-Agent: SonyEricssonK800i/R2B Release/Mar-13-2007 Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1..Connection: Keep-Alive..Accept-Encoding: deflate, gzip..Referer: http://www.zock.com/.... Testing decompression with sniffed compressed TCP/IP packets: Packet No.: 28 v42bis_decompress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 compressed= 4500010269009e00004006ce62c0a80002550d93d740060050c3143845a11fab60801840003c1c00000101080a000547d810fc38b7474554202f382d4269742f526164696f536861636b2e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f330096316be6fcc6ed1d347b7a46f60b584fe7112d64b4b08a552b5718468ad6b183a6eb083c77d2c0d8e17678711a39dcb2b123c7f80e1c3c60d068febcfa8c1c3772c0c063c60e8c1a6e89b24973474c132656dcee5483e376d6ad77603c2113470d1e326ad0784513c7ea1e189361cca923270d9b37fd4794b87c674e0f193226c7781e870f9a3a74ed748c61460e1c195888307428c3c98e1933d854a93163eae9125697c8b1135c8f1e3d31fac831e3a6e449ab587ed78141847f9ce056f0f24112127cc79731d9c8a0596444153411c9fc9601c56c751be263ea1851a4a19ea732a2004fd2b0aa0c1b316c8c98d2f061c4c54c8e4c111f63c60823781fe6af6357685e234f8e18a91e838655dd71e2a03173378e572768b6469d9b074d7cad5c61208963f20c1bfc5ecfa049f3c62e1ab76afa90b55a380d48905e2b5ecc1874e8d1a44b515ab51a00 ASCII:E...i....@..b....U...@..P..8E...`..@.<.........G...8.GET /8-Bit/RadioShack.GIF HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/3..1k.....4{zF..XO..-d...U+W.F.......#...0......Z+A........<...}5...O..Q....4\......c.s..I#4.......!C......AS....1...#.....e8.1c..*5fD.]...9vz...'F.9f.|.I...:0......]>H6r..r%..........2.e@......:F.9.G..(...|*.F..#..T(.0.#S...1..]....eCW..'G.D.A...8q.......4Y.....=V.0...y..}.g..y#..Z5}.R..F.F..#N...'P.".R.. uncompressed= 45000266009f00004006ce64c0a80002550d93d740050050c313903caf7b2f9580183d5ce4ef00000101080a0005481810fc3934474554202f382d4269742f5068696c6970732e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a207777772e7a6f636b2e636f6d0d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a526566657265723a20687474703a2f2f7777772e7a6f636b2e636f6d2f0d0a0d0a ASCII:E..f....@..d....U...@..P...<.{/...=\..........H...94GET /8-Bit/Philips.GIF HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/vnd.wap.wml, */*..Accept-Charset: utf-8, utf-16, iso-8859-1, iso-10646-ucs-2, Shift_JIS, Big5..Accept-Language: en..x-wap-profile: "http://wap.sonyericsson.com/UAprof/K800iR201.xml"..Host: www.zock.com..User-Agent: SonyEricssonK800i/R2B Release/Mar-13-2007 Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1..Connection: Keep-Alive..Accept-Encoding: deflate, gzip..Referer: http://www.zock.com/.... Testing decompression with sniffed compressed TCP/IP packets: Packet No.: 30 v42bis_decompress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 compressed= 450001049b00ae00004006cc20c0a80002550d93d740050050c31396ceaf7b396e801840009ad400000101080a0005611010fc3d6b474554202f382d4269742f6d72776f6e672e67696620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d33009733bf617b07cd9e9f8ef7f6d5a379440b192da85ac5aa1586913564eda0d93a02cf9d343076b00d3e9c460eb66cecc821be03070f183496379f3e23c78d1c30f098b103a3065b2b6b92de11d3848915b63dd5e0a87d35eb1d184fc814c543460d1aae68e250dd0303328c3975e4a461f346ff881294efcce9214306e418cde3f0415347ae1d8c31ccc88123030b1183086538d93163069b2a3566442d5d82ea1239767eebd1a327461f3966dc801449154bef3a3088e48ff3db0a513e48387ac7a892251b192f8b8ca882862199de32a0909d6e033c4b1d238a1cd4a3544614df490e3e956123868d11530e2a644898c99129e063cc186184a8423578ead06563d7c8932346a6c7a04175769c3868ccd48dc3d5099aac4ce3e641f31eab561848e2843cc3a628d73368d2bca18b86ad9a3e62a9fe4db3712357881229eadce933e8d09154a92a417265848c1d307afec489074e198678e6108d31a7e8882557ae4c9111357903abdf7f7b052b96ec4f3868d51e7e1b77ae74198ef7f63d1a76ce52bf50cf4c9d7355b7ddb290634cae7cb9b4dec05f135b76c178c41d347b7e16eeabc732db16325a74cf7a07869135647bff0e3e7c075bdad3732c6f4e7c070e1ed37dcb993e23c78d1c30f07807cfd6ca1a3669ee8869c2c40adb9e6a70a8d60ae30999a278c8e4e78a260ed53d3020c33088900d43ae25204aa46818239f8d6cbc631c3a120b11ff6964a864c9e6e58ca8a54b505d220727579d3b7d06bd48158b52a6f99f72b542940f923a13ed600c39838d8c974546fc6548a63761b2d36d8067a96344913a72f428951105cd9d248a9fcab011c3c688298ad30c444398c99129e063cc18313bce683578eae8b66be4c91123d363246f003b0e1a33d2b93a41939569dc3c68d6df8181248e193967d814e57a064d9a3774d1b055d387cd1caa7fd36cdcc835618f853a63d07473510655aa0100 ASCII:E........@.. ....U...@..P.....{9n..@...........a...=kGET /8-Bit/mrwong.gif HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtm3..3.a{.........yD..-.Z.....5d...:...40v..>.F..l..!.....4.7.>#...0......[+k........=...}5...O...CF...h.P...2.9u..a.F.......!C......ASG...1...#.....e8.1c..*5fD-]...9v~...'F.9f...I.K.:0......Q>H8z...%../.....!..2...n..a#...S.*dH...).c..a..B5x..ec...#F...Auv.8h........L..A...V.H..<..(.3h.......>b..M.q#W..)...3...T.*Are...0z....N..x...1...%W.L..5y....{.+..O8h..~.w.t....=.v.R.P.L.sU....cL.|...._.[v.x..4{~....2..2Zt.z...5d{..>|.[..s,oN|....}..>#...0.x.....6i..i.....jp.......x....&..=0 .0...C.% J.h.#..l.c.:....id.d......KP]".'W.;}..H..R...r.B...:..`.9....EF.eH.7a..m.g.cD.:r.(.....$........)...DC...).c..1;.h5x...k...#.c$o.;..3..:A..i.Directory listing f.or /.Directory listing for /..

Directory listing for /

.
..
... Testing decompression with sniffed compressed TCP/IP packets: Packet No.: 32 v42bis_decompress_flush() rc=0 v42bis_decompress() rc=0 v42bis_decompress_flush() rc=0 compressed= 45000101a0e9a54000400683540a0901abc0a800021f904004437442f17a4ab3b1501900ed04900000485454502f312e3020323030204f4b0d0a5365727665723a2053696d706c65485454502f302e3620507974686f6e2f322e372e360d0a446174653a205765642c203301312041756720323031362030393a32373a353520474d540d0a436f6e74656e742d747970653a20746578742f68746d6c3b20636861727365743d5554462d380d0a436f6e74656e742d4c656e6774683a203232320d0a0d0a66003f481c9162e40a97294800002068746d6c20506600588a3c6162644409183200002f5733432f2f4454442048544d4c20332e322046696e616c2f2f454e223e3c68746d6c3e0a3c7469746c653e4469726563746f7279206c697374696e672066016f72202f3c2f7469746c990068beff2823e70c9f816b6a9847af9ebd7bf8f2e9dbc7af5ff142f06bea0cc4e31d7cfced646aa74f03a444fa3373a4c64113634e7ded28554e7d1953cd7e320365744cb811bc8c82078376ff1e00 ASCII:E......@.@..T..........@.CtB.zJ..P.......HTTP/1.0 200 OK..Server: SimpleHTTP/0.6 Python/2.7.6..Date: Wed, 3.1 Aug 2016 09:27:55 GMT..Content-type: text/html; charset=UTF-8..Content-Length: 222....f.?H..b...)H.. html Pf.X..Directory listing f.or /</titl..h..(#....kj.G...{......_.B.k.....|..dj.O..D.3s..A.cN}.(UN}.S.~2.etL.......v... uncompressed= 450001a0e9a54000400683540a0901abc0a800021f904004437442f17a4ab3b1501900ed04900000485454502f312e3020323030204f4b0d0a5365727665723a2053696d706c65485454502f302e3620507974686f6e2f322e372e360d0a446174653a205765642c2033312041756720323031362030393a32373a353520474d540d0a436f6e74656e742d747970653a20746578742f68746d6c3b20636861727365743d5554462d380d0a436f6e74656e742d4c656e6774683a203232320d0a0d0a3c21444f43545950452068746d6c205055424c494320222d2f2f5733432f2f4454442048544d4c20332e322046696e616c2f2f454e223e3c68746d6c3e0a3c7469746c653e4469726563746f7279206c697374696e6720666f72202f3c2f7469746c653e0a3c626f64793e0a3c68323e4469726563746f7279206c697374696e6720666f72202f3c2f68323e0a3c68723e0a3c756c3e0a3c6c693e3c6120687265663d2272656470686f6e652e706e67223e72656470686f6e652e706e673c2f613e0a3c2f756c3e0a3c68723e0a3c2f626f64793e0a3c2f68746d6c3e0a ASCII:E.....@.@..T..........@.CtB.zJ..P.......HTTP/1.0 200 OK..Server: SimpleHTTP/0.6 Python/2.7.6..Date: Wed, 31 Aug 2016 09:27:55 GMT..Content-type: text/html; charset=UTF-8..Content-Length: 222....<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><html>.<title>Directory listing for /..

Directory listing for /

.
..
... Done osmo-sgsn-1.3.0/tests/vty_test_runner.py000077500000000000000000000326261327264017000204440ustar00rootroot00000000000000#!/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 TestVTYGbproxy(TestVTYBase): def vty_command(self): return ["./src/gprs/osmo-gbproxy", "-c", "doc/examples/osmo-gbproxy/osmo-gbproxy.cfg"] def vty_app(self): return (4246, "./src/gprs/osmo-gbproxy", "OsmoGbProxy", "bsc") 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('ns', [''])) self.assertEquals(self.vty.node(), 'config-ns') self.checkForEndAndExit() self.assertTrue(self.vty.verify('exit', [''])) self.assertEquals(self.vty.node(), 'config') self.assertTrue(self.vty.verify('gbproxy', [''])) self.assertEquals(self.vty.node(), 'config-gbproxy') self.checkForEndAndExit() self.assertTrue(self.vty.verify('exit', [''])) self.assertEquals(self.vty.node(), 'config') def testVtyShow(self): res = self.vty.command("show ns") self.assert_(res.find('Encapsulation NS-UDP-IP') >= 0) res = self.vty.command("show gbproxy stats") self.assert_(res.find('GBProxy Global Statistics') >= 0) def testVtyDeletePeer(self): self.vty.enable() self.assertTrue(self.vty.verify('delete-gbproxy-peer 9999 bvci 7777', ['BVC not found'])) res = self.vty.command("delete-gbproxy-peer 9999 all dry-run") self.assert_(res.find('Not Deleted 0 BVC') >= 0) self.assert_(res.find('Not Deleted 0 NS-VC') >= 0) res = self.vty.command("delete-gbproxy-peer 9999 only-bvc dry-run") self.assert_(res.find('Not Deleted 0 BVC') >= 0) self.assert_(res.find('Not Deleted 0 NS-VC') < 0) res = self.vty.command("delete-gbproxy-peer 9999 only-nsvc dry-run") self.assert_(res.find('Not Deleted 0 BVC') < 0) self.assert_(res.find('Not Deleted 0 NS-VC') >= 0) res = self.vty.command("delete-gbproxy-peer 9999 all") self.assert_(res.find('Deleted 0 BVC') >= 0) self.assert_(res.find('Deleted 0 NS-VC') >= 0) class TestVTYSGSN(TestVTYBase): def vty_command(self): return ["./src/gprs/osmo-sgsn", "-c", "doc/examples/osmo-sgsn/osmo-sgsn-accept-all.cfg"] def vty_app(self): return (4245, "./src/gprs/osmo-sgsn", "OsmoSGSN", "sgsn") 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('ns', [''])) self.assertEquals(self.vty.node(), 'config-ns') self.checkForEndAndExit() self.assertTrue(self.vty.verify('exit', [''])) self.assertEquals(self.vty.node(), 'config') self.assertTrue(self.vty.verify('sgsn', [''])) self.assertEquals(self.vty.node(), 'config-sgsn') self.checkForEndAndExit() self.assertTrue(self.vty.verify('exit', [''])) self.assertEquals(self.vty.node(), 'config') def testVtyShow(self): res = self.vty.command("show ns") self.assert_(res.find('Encapsulation NS-UDP-IP') >= 0) self.assertTrue(self.vty.verify('show bssgp', [''])) self.assertTrue(self.vty.verify('show bssgp stats', [''])) self.assertTrue(self.vty.verify('show bssgp nsei 123', [''])) self.assertTrue(self.vty.verify('show bssgp nsei 123 stats', [''])) self.assertTrue(self.vty.verify('show sgsn', [' GSN: signalling 127.0.0.1, user traffic 127.0.0.1'])) self.assertTrue(self.vty.verify('show mm-context all', [''])) self.assertTrue(self.vty.verify('show mm-context imsi 000001234567', ['No MM context for IMSI 000001234567'])) self.assertTrue(self.vty.verify('show pdp-context all', [''])) res = self.vty.command("show sndcp") self.assert_(res.find('State of SNDCP Entities') >= 0) res = self.vty.command("show llc") self.assert_(res.find('State of LLC Entities') >= 0) def testVtyAuth(self): self.vty.enable() self.assertTrue(self.vty.verify('configure terminal', [''])) self.assertEquals(self.vty.node(), 'config') self.assertTrue(self.vty.verify('sgsn', [''])) self.assertEquals(self.vty.node(), 'config-sgsn') self.assertTrue(self.vty.verify('auth-policy accept-all', [''])) res = self.vty.command("show running-config") self.assert_(res.find('auth-policy accept-all') > 0) self.assertTrue(self.vty.verify('auth-policy acl-only', [''])) res = self.vty.command("show running-config") self.assert_(res.find('auth-policy acl-only') > 0) self.assertTrue(self.vty.verify('auth-policy closed', [''])) res = self.vty.command("show running-config") self.assert_(res.find('auth-policy closed') > 0) self.assertTrue(self.vty.verify('gsup remote-ip 127.0.0.4', [''])) self.assertTrue(self.vty.verify('gsup remote-port 2222', [''])) self.assertTrue(self.vty.verify('auth-policy remote', [''])) res = self.vty.command("show running-config") self.assert_(res.find('auth-policy remote') > 0) def testVtySubscriber(self): self.vty.enable() res = self.vty.command('show subscriber cache') self.assert_(res.find('1234567890') < 0) self.assertTrue(self.vty.verify('update-subscriber imsi 1234567890 create', [''])) res = self.vty.command('show subscriber cache') self.assert_(res.find('1234567890') >= 0) self.assert_(res.find('Authorized: 0') >= 0) self.assertTrue(self.vty.verify('update-subscriber imsi 1234567890 update-location-result ok', [''])) res = self.vty.command('show subscriber cache') self.assert_(res.find('1234567890') >= 0) self.assert_(res.find('Authorized: 1') >= 0) self.assertTrue(self.vty.verify('update-subscriber imsi 1234567890 cancel update-procedure', [''])) res = self.vty.command('show subscriber cache') self.assert_(res.find('1234567890') >= 0) self.assertTrue(self.vty.verify('update-subscriber imsi 1234567890 destroy', [''])) res = self.vty.command('show subscriber cache') self.assert_(res.find('1234567890') < 0) def testVtyGgsn(self): self.vty.enable() self.assertTrue(self.vty.verify('configure terminal', [''])) self.assertEquals(self.vty.node(), 'config') self.assertTrue(self.vty.verify('sgsn', [''])) self.assertEquals(self.vty.node(), 'config-sgsn') self.assertTrue(self.vty.verify('ggsn 0 remote-ip 127.99.99.99', [''])) self.assertTrue(self.vty.verify('ggsn 0 gtp-version 1', [''])) self.assertTrue(self.vty.verify('apn * ggsn 0', [''])) self.assertTrue(self.vty.verify('apn apn1.test ggsn 0', [''])) self.assertTrue(self.vty.verify('apn apn1.test ggsn 1', ['% a GGSN with id 1 has not been defined'])) self.assertTrue(self.vty.verify('apn apn1.test imsi-prefix 123456 ggsn 0', [''])) self.assertTrue(self.vty.verify('apn apn2.test imsi-prefix 123456 ggsn 0', [''])) res = self.vty.command("show running-config") self.assert_(res.find('ggsn 0 remote-ip 127.99.99.99') >= 0) self.assert_(res.find('ggsn 0 gtp-version 1') >= 0) self.assert_(res.find('apn * ggsn 0') >= 0) self.assert_(res.find('apn apn1.test ggsn 0') >= 0) self.assert_(res.find('apn apn1.test imsi-prefix 123456 ggsn 0') >= 0) self.assert_(res.find('apn apn2.test imsi-prefix 123456 ggsn 0') >= 0) def testVtyEasyAPN(self): self.vty.enable() self.assertTrue(self.vty.verify('configure terminal', [''])) self.assertEquals(self.vty.node(), 'config') self.assertTrue(self.vty.verify('sgsn', [''])) self.assertEquals(self.vty.node(), 'config-sgsn') res = self.vty.command("show running-config") self.assertEquals(res.find("apn internet"), -1) self.assertTrue(self.vty.verify("access-point-name internet.apn", [''])) res = self.vty.command("show running-config") self.assert_(res.find("apn internet.apn ggsn 0") >= 0) self.assertTrue(self.vty.verify("no access-point-name internet.apn", [''])) res = self.vty.command("show running-config") self.assertEquals(res.find("apn internet"), -1) def testVtyCDR(self): self.vty.enable() self.assertTrue(self.vty.verify('configure terminal', [''])) self.assertEquals(self.vty.node(), 'config') self.assertTrue(self.vty.verify('sgsn', [''])) self.assertEquals(self.vty.node(), 'config-sgsn') res = self.vty.command("show running-config") self.assert_(res.find("no cdr filename") > 0) self.vty.command("cdr filename bla.cdr") res = self.vty.command("show running-config") self.assertEquals(res.find("no cdr filename"), -1) self.assert_(res.find(" cdr filename bla.cdr") > 0) self.vty.command("no cdr filename") res = self.vty.command("show running-config") self.assert_(res.find("no cdr filename") > 0) self.assertEquals(res.find(" cdr filename bla.cdr"), -1) res = self.vty.command("show running-config") self.assert_(res.find(" cdr interval 600") > 0) self.vty.command("cdr interval 900") res = self.vty.command("show running-config") self.assert_(res.find(" cdr interval 900") > 0) self.assertEquals(res.find(" cdr interval 600"), -1) def add_gbproxy_test(suite, workdir): if not os.path.isfile(os.path.join(workdir, "src/gprs/osmo-gbproxy")): print("Skipping the Gb-Proxy test") return test = unittest.TestLoader().loadTestsFromTestCase(TestVTYGbproxy) suite.addTest(test) def add_sgsn_test(suite, workdir): if not os.path.isfile(os.path.join(workdir, "src/gprs/osmo-sgsn")): print("Skipping the SGSN test") return test = unittest.TestLoader().loadTestsFromTestCase(TestVTYSGSN) 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_gbproxy_test(suite, workdir) add_sgsn_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 osmo-sgsn-1.3.0/tests/xid/000077500000000000000000000000001327264017000153705ustar00rootroot00000000000000osmo-sgsn-1.3.0/tests/xid/Makefile.am000066400000000000000000000010171327264017000174230ustar00rootroot00000000000000AM_CPPFLAGS = \ $(all_includes) \ -I$(top_srcdir)/include \ $(NULL) AM_CFLAGS = \ -Wall \ -ggdb3 \ $(LIBOSMOCORE_CFLAGS) \ $(LIBOSMOGSM_CFLAGS) \ $(LIBCARES_CFLAGS) \ $(NULL) EXTRA_DIST = \ xid_test.ok \ $(NULL) noinst_PROGRAMS = \ xid_test \ $(NULL) xid_test_SOURCES = \ xid_test.c \ $(NULL) xid_test_LDADD = \ $(top_builddir)/src/gprs/gprs_llc_xid.o \ $(LIBOSMOABIS_LIBS) \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOGSM_LIBS) \ $(LIBOSMOGB_LIBS) \ $(LIBCARES_LIBS) \ $(LIBGTP_LIBS) \ -lrt \ -lm \ $(NULL) osmo-sgsn-1.3.0/tests/xid/xid_test.c000066400000000000000000000110061327264017000173550ustar00rootroot00000000000000/* Test LLC-XID Encoding/Decoding */ /* (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 /* Test XID encoding */ static void test_xid_encode(const void *ctx) { struct gprs_llc_xid_field xid_field_1; struct gprs_llc_xid_field xid_field_2; struct gprs_llc_xid_field xid_field_3; struct gprs_llc_xid_field xid_field_4; LLIST_HEAD(xid_fields); uint8_t xid[255]; uint8_t xid_expected[] = { 0x10, 0x8c, 0x14, 0x43, 0x43, 0x43, 0x43, 0x43, 0x0b, 0x42, 0x42, 0x42, 0x05, 0x41 }; int rc; printf("Testing LLC XID-Encoder\n"); /* Setup some simple XID data */ xid_field_1.type = 1; xid_field_2.type = 2; xid_field_3.type = 3; xid_field_4.type = 4; xid_field_1.data = (uint8_t *) "A"; xid_field_2.data = (uint8_t *) "BBB"; xid_field_3.data = (uint8_t *) "CCCCC"; xid_field_4.data = NULL; xid_field_1.data_len = 1; xid_field_2.data_len = 3; xid_field_3.data_len = 5; xid_field_4.data_len = 0; llist_add(&xid_field_4.list, &xid_fields); llist_add(&xid_field_3.list, &xid_fields); llist_add(&xid_field_2.list, &xid_fields); llist_add(&xid_field_1.list, &xid_fields); printf("Data to encode:\n"); gprs_llc_dump_xid_fields(&xid_fields, DSNDCP); /* Encode data */ rc = gprs_llc_compile_xid(xid, sizeof(xid), &xid_fields); OSMO_ASSERT(rc == 14); printf("Encoded: %s (%i bytes)\n", osmo_hexdump_nospc(xid, rc), rc); printf("Expected: %s (%i bytes)\n", osmo_hexdump_nospc(xid_expected, sizeof(xid_expected)), (int)sizeof(xid_expected)); OSMO_ASSERT(memcmp(xid_expected, xid, sizeof(xid_expected)) == 0); printf("\n"); } /* Test XID decoding */ static void test_xid_decode(const void *ctx) { struct llist_head *xid_fields; int rc; printf("Testing LLC XID-Decoder/Encoder\n"); /* Example of a real world LLC-XID message */ uint8_t xid[] = { 0x01, 0x00, 0x16, 0x05, 0xf0, 0x1a, 0x05, 0xf0, 0xac, 0xd8, 0x00, 0x01, 0x00, 0x02, 0x31, 0x82, 0x02, 0x27, 0x89, 0xff, 0xe0, 0x00, 0x0f, 0x00, 0xa8, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x02, 0x01, 0x02, 0x00, 0x03, 0x01, 0x03, 0x00, 0x04, 0x01, 0x04, 0x00, 0x05, 0x01, 0x05, 0x00, 0x06, 0x00, 0x07, 0x01, 0x07, 0x00, 0x08, 0x01, 0x08, 0x80, 0x00, 0x04, 0x12, 0x00, 0x40, 0x07 }; uint8_t xid_r[512]; /* Decode and display XID fields */ xid_fields = gprs_llc_parse_xid(ctx, xid, sizeof(xid)); OSMO_ASSERT(xid_fields); printf("Decoded:\n"); gprs_llc_dump_xid_fields(xid_fields, DSNDCP); /* Encode xid-fields again */ rc = gprs_llc_compile_xid(xid_r, sizeof(xid_r), xid_fields); printf("Result length=%i\n",rc); printf("Encoded: %s\n", osmo_hexdump_nospc(xid, sizeof(xid))); printf("Rencoded: %s\n", osmo_hexdump_nospc(xid_r, rc)); OSMO_ASSERT(rc == 64); OSMO_ASSERT(memcmp(xid, xid_r, sizeof(xid)) == 0); /* Free xid fields */ talloc_free(xid_fields); printf("\n"); } static struct log_info_cat gprs_categories[] = { [DSNDCP] = { .name = "DSNDCP", .description = "GPRS Sub-Network Dependent Control Protocol (SNDCP)", .enabled = 1,.loglevel = LOGL_DEBUG, } }; static struct log_info info = { .cat = gprs_categories, .num_cat = ARRAY_SIZE(gprs_categories), }; int main(int argc, char **argv) { void *xid_ctx; void *log_ctx; xid_ctx = talloc_named_const(NULL, 0, "xid_ctx"); log_ctx = talloc_named_const(xid_ctx, 0, "log"); osmo_init_logging2(log_ctx, &info); test_xid_decode(xid_ctx); test_xid_encode(xid_ctx); printf("Done\n"); talloc_report_full(xid_ctx, stderr); talloc_free(log_ctx); OSMO_ASSERT(talloc_total_blocks(xid_ctx) == 1); talloc_free(xid_ctx); return 0; } /* stubs */ struct osmo_prim_hdr; int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx) { abort(); } osmo-sgsn-1.3.0/tests/xid/xid_test.ok000066400000000000000000000007431327264017000175520ustar00rootroot00000000000000Testing LLC XID-Decoder/Encoder Decoded: Result length=64 Encoded: 01001605f01a05f0acd8000100023182022789ffe0000f00a8000000010101000201020003010300040104000501050006000701070008010880000412004007 Rencoded: 01001605f01a05f0acd8000100023182022789ffe0000f00a8000000010101000201020003010300040104000501050006000701070008010880000412004007 Testing LLC XID-Encoder Data to encode: Encoded: 108c1443434343430b4242420541 (14 bytes) Expected: 108c1443434343430b4242420541 (14 bytes) Done