diff options
143 files changed, 51592 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..78790884d --- /dev/null +++ b/.gitignore @@ -0,0 +1,59 @@ +debian/*.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 diff --git a/.gitreview b/.gitreview new file mode 100644 index 000000000..560ddef1e --- /dev/null +++ b/.gitreview @@ -0,0 +1,3 @@ +[gerrit] +host=gerrit.osmocom.org +project=osmo-sgsn diff --git a/.mailmap b/.mailmap new file mode 100644 index 000000000..cda405790 --- /dev/null +++ b/.mailmap @@ -0,0 +1,12 @@ +Harald Welte <laforge@gnumonks.org> +Harald Welte <laforge@gnumonks.org> <laflocal@hanuman.gnumonks.org> +Harald Welte <laforge@gnumonks.org> <laflocal@goeller.de.gnumonks.org> +Holger Hans Peter Freyther <holger@moiji-mobile.com> <zecke@selfish.org> +Holger Hans Peter Freyther <holger@moiji-mobile.com> <ich@tamarin.(none)> +Holger Hans Peter Freyther <holgre@moiji-mobile.com> <holger@freyther.de> +Andreas Eversberg <jolly@eversberg.eu> +Andreas Eversberg <jolly@eversberg.eu> <Andreas.Eversberg@versatel.de> +Andreas Eversberg <jolly@eversberg.eu> <root@nuedel.(none)> +Pablo Neira Ayuso <pablo@soleta.eu> <pablo@gnumonks.org> +Max Suraev <msuraev@sysmocom.de> +Tom Tsou <tom.tsou@ettus.com> <tom@tsou.cc> diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 000000000..91af51520 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,9 @@ +Harald Welte <laforge@gnumonks.org> +Holger Freyther <zecke@selfish.org> +Jan Luebbe <jluebbe@debian.org> +Stefan Schmidt <stefan@datenfreihafen.org> +Daniel Willmann <daniel@totalueberwachung.de> +Andreas Eversberg <Andreas.Eversberg@versatel.de> +Sylvain Munaut <246tnt@gmail.com> +Jacob Erlbeck <jerlbeck@sysmocom.de> +Neels Hofmeyr <nhofmeyr@sysmocom.de> diff --git a/COPYING b/COPYING new file mode 100644 index 000000000..dba13ed2d --- /dev/null +++ b/COPYING @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + 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. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU 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 <http://www.gnu.org/licenses/>. + +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 +<http://www.gnu.org/licenses/>. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 000000000..1883ab5fc --- /dev/null +++ b/Makefile.am @@ -0,0 +1,33 @@ +AUTOMAKE_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 \ + contrib \ + tests \ + $(NULL) + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = osmo-sgsn.pc + +BUILT_SOURCES = $(top_srcdir)/.version +EXTRA_DIST = git-version-gen osmoappdesc.py .version + +DISTCHECK_CONFIGURE_FLAGS = \ + --with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir) + +@RELMAKE@ + +$(top_srcdir)/.version: + echo $(VERSION) > $@-t && mv $@-t $@ +dist-hook: + echo $(VERSION) > $(distdir)/.tarball-version @@ -0,0 +1,17 @@ +About 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 diff --git a/README.vty-tests b/README.vty-tests new file mode 100644 index 000000000..0669ea8e8 --- /dev/null +++ b/README.vty-tests @@ -0,0 +1,11 @@ +To 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 diff --git a/configure.ac b/configure.ac new file mode 100644 index 000000000..0e4ff94cd --- /dev/null +++ b/configure.ac @@ -0,0 +1,223 @@ +dnl 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) +PKG_CHECK_MODULES(LIBOSMOGSUPCLIENT, libosmo-gsup-client >= 0.2.1) + +# 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.h> + ], [ + 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") + +# https://www.freedesktop.org/software/systemd/man/daemon.html +AC_ARG_WITH([systemdsystemunitdir], + [AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files])],, + [with_systemdsystemunitdir=auto]) +AS_IF([test "x$with_systemdsystemunitdir" = "xyes" -o "x$with_systemdsystemunitdir" = "xauto"], [ + def_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd) + + AS_IF([test "x$def_systemdsystemunitdir" = "x"], + [AS_IF([test "x$with_systemdsystemunitdir" = "xyes"], + [AC_MSG_ERROR([systemd support requested but pkg-config unable to query systemd package])]) + with_systemdsystemunitdir=no], + [with_systemdsystemunitdir="$def_systemdsystemunitdir"])]) +AS_IF([test "x$with_systemdsystemunitdir" != "xno"], + [AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])]) +AM_CONDITIONAL([HAVE_SYSTEMD], [test "x$with_systemdsystemunitdir" != "xno"]) + +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/gtphub/Makefile + tests/xid/Makefile + tests/sndcp_xid/Makefile + tests/slhc/Makefile + tests/v42bis/Makefile + doc/Makefile + doc/examples/Makefile + contrib/Makefile + contrib/systemd/Makefile + Makefile) diff --git a/contrib/Makefile.am b/contrib/Makefile.am new file mode 100644 index 000000000..3439c97be --- /dev/null +++ b/contrib/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = systemd diff --git a/contrib/gprs/gb-proxy-unblock-bug.py b/contrib/gprs/gb-proxy-unblock-bug.py new file mode 100755 index 000000000..0cd4b871f --- /dev/null +++ b/contrib/gprs/gb-proxy-unblock-bug.py @@ -0,0 +1,58 @@ +#!/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" diff --git a/contrib/gprs/gprs-bssgp-histogram.lua b/contrib/gprs/gprs-bssgp-histogram.lua new file mode 100644 index 000000000..b1ab5df7f --- /dev/null +++ b/contrib/gprs/gprs-bssgp-histogram.lua @@ -0,0 +1,78 @@ +-- 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 diff --git a/contrib/gprs/gprs-buffer-count.lua b/contrib/gprs/gprs-buffer-count.lua new file mode 100644 index 000000000..ca8864ad1 --- /dev/null +++ b/contrib/gprs/gprs-buffer-count.lua @@ -0,0 +1,80 @@ +-- 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 diff --git a/contrib/gprs/gprs-split-trace-by-tlli.lua b/contrib/gprs/gprs-split-trace-by-tlli.lua new file mode 100644 index 000000000..018c377c5 --- /dev/null +++ b/contrib/gprs/gprs-split-trace-by-tlli.lua @@ -0,0 +1,46 @@ +-- 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 diff --git a/contrib/gprs/gprs-verify-nu.lua b/contrib/gprs/gprs-verify-nu.lua new file mode 100644 index 000000000..e44fdd16f --- /dev/null +++ b/contrib/gprs/gprs-verify-nu.lua @@ -0,0 +1,59 @@ +-- 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 + diff --git a/contrib/jenkins.sh b/contrib/jenkins.sh new file mode 100755 index 000000000..64663d686 --- /dev/null +++ b/contrib/jenkins.sh @@ -0,0 +1,62 @@ +#!/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 +osmo-build-dep.sh osmo-hlr + +enable_werror="" +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 +else + enable_werror="--enable-werror" +fi + +set +x +echo +echo +echo +echo " =============================== osmo-sgsn ===============================" +echo +set -x + +cd "$base" +autoreconf --install --force +./configure --enable-sanitize $enable_werror $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_werror $IU --enable-external-tests" \ + $MAKE distcheck \ + || cat-testlogs.sh + +osmo-clean-workspace.sh diff --git a/contrib/systemd/Makefile.am b/contrib/systemd/Makefile.am new file mode 100644 index 000000000..b644f3401 --- /dev/null +++ b/contrib/systemd/Makefile.am @@ -0,0 +1,9 @@ +if HAVE_SYSTEMD +SYSTEMD_SERVICES = \ + osmo-gbproxy.service \ + osmo-gtphub.service \ + osmo-sgsn.service + +EXTRA_DIST = $(SYSTEMD_SERVICES) +systemdsystemunit_DATA = $(SYSTEMD_SERVICES) +endif diff --git a/contrib/systemd/osmo-gbproxy.service b/contrib/systemd/osmo-gbproxy.service new file mode 100644 index 000000000..a0b7829db --- /dev/null +++ b/contrib/systemd/osmo-gbproxy.service @@ -0,0 +1,12 @@ +[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 diff --git a/contrib/systemd/osmo-gtphub.service b/contrib/systemd/osmo-gtphub.service new file mode 100644 index 000000000..488178584 --- /dev/null +++ b/contrib/systemd/osmo-gtphub.service @@ -0,0 +1,12 @@ +[Unit] +Description=Osmocom GTP Hub + +[Service] +Type=simple +ExecStart=/usr/bin/osmo-gtphub -c /etc/osmocom/osmo-gtphub.cfg +Restart=always +RestartSec=2 +RestartPreventExitStatus=1 + +[Install] +WantedBy=multi-user.target diff --git a/contrib/systemd/osmo-sgsn.service b/contrib/systemd/osmo-sgsn.service new file mode 100644 index 000000000..0991c70c6 --- /dev/null +++ b/contrib/systemd/osmo-sgsn.service @@ -0,0 +1,14 @@ +[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 diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 000000000..b39abb8af --- /dev/null +++ b/debian/changelog @@ -0,0 +1,129 @@ +osmo-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 <pespin@sysmocom.de> 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 <laforge@gnumonks.org> Sat, 28 Oct 2017 19:07:48 +0200 + +osmo-sgsn (0.1.0) unstable; urgency=low + + * Initial release. + + -- Alexander Couzens <lynxis@fe80.eu> Tue, 08 Aug 2017 01:13:05 +0000 diff --git a/debian/compat b/debian/compat new file mode 100644 index 000000000..ec635144f --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +9 diff --git a/debian/control b/debian/control new file mode 100644 index 000000000..9594a0841 --- /dev/null +++ b/debian/control @@ -0,0 +1,69 @@ +Source: osmo-sgsn +Section: net +Priority: extra +Maintainer: Alexander Couzens <lynxis@fe80.eu> +Build-Depends: debhelper (>=9), + dh-autoreconf, + dh-systemd (>= 1.5), + autotools-dev, + autoconf, + automake, + libtool, + pkg-config, + libasn1c-dev, + libtalloc-dev, + libc-ares-dev, + libgtp-dev, + libosmocore-dev, + libosmo-abis-dev, + libosmo-ranap-dev, + libosmo-sccp-dev, + libosmo-sigtran-dev, + libosmo-netif-dev, + libosmo-gsup-client-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 diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 000000000..cc631f491 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,347 @@ +Format: 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 + 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/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/gtphub.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/gtphub.c + src/gprs/gtphub_main.c + src/gprs/gtphub_vty.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 + tests/gtphub/gtphub_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 <zecke@selfish.org> + 2008-2016 Harald Welte <laforge@gnumonks.org> + 2009-2015 Holger Hans Peter Freyther + 2010-2014 On-Waves + 2011-2016 sysmocom s.f.m.c. GmbH <info@sysmocom.de> + 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 <http://www.gnu.org/licenses/>. + +Files: src/gprs/gtphub_ares.c + src/gprs/gtphub_sock.c + tests/gbproxy/gbproxy_test.c +Copyright: 2013 Jacob Erlbeck <jerlbeck@sysmocom.de> + 2013 sysmocom s.f.m.c. GmbH + 2014 Holger Hans Peter Freyther + 2015 sysmocom s.f.m.c. GmbH <info@sysmocom.de> +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 <kat.obsc@gmail.com> + 2013 Jacob Erlbeck <jerlbeck@sysmocom.de> + 2013-2014 Holger Hans Peter Freyther <zecke@selfish.org> +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 <http://www.gnu.org/licenses/>. + . + This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/. + It may be run two ways: + - from a git repository in which the "git describe" command below + produces useful output (thus requiring at least one signed tag) + - from a non-git-repo directory containing a .tarball-version file, which + presumes this script is invoked like "./git-version-gen .tarball-version". + . + In order to use intra-version strings in your project, you will need two + separate generated version string files: + . + .tarball-version - present only in a distribution tarball, and not in + a checked-out repository. Created with contents that were learned at + the last time autoconf was run, and used by git-version-gen. Must not + be present in either $(srcdir) or $(builddir) for git-version-gen to + give accurate answers during normal development with a checked out tree, + but must be present in a tarball when there is no version control system. + Therefore, it cannot be used in any dependencies. GNUmakefile has + hooks to force a reconfigure at distribution time to get the value + correct, without penalizing normal development with extra reconfigures. + . + .version - present in a checked-out repository and in a distribution + tarball. Usable in dependencies, particularly for files that don't + want to depend on config.h but do want to track version changes. + Delete this file prior to any autoconf run where you want to rebuild + files to pick up a version string change; and leave it stale to + minimize rebuild time after unrelated changes to configure sources. + . + It is probably wise to add these two files to .gitignore, so that you + don't accidentally commit either generated file. + . + Use the following line in your configure.ac, so that $(VERSION) will + automatically be up-to-date each time configure is run (and note that + since configure.ac no longer includes a version string, Makefile rules + should not depend on configure.ac for version updates). + . + 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 <guidod@gmx.de> + 2011 Maarten Bosmans <mkbosmans@gmail.com> +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 <http://www.gnu.org/licenses/>. + . + 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'. diff --git a/debian/osmo-gbproxy.init b/debian/osmo-gbproxy.init new file mode 100755 index 000000000..924f32d10 --- /dev/null +++ b/debian/osmo-gbproxy.init @@ -0,0 +1,151 @@ +#!/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 <laforge@gnumonks.org> + +# 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 + +: diff --git a/debian/osmo-gbproxy.install b/debian/osmo-gbproxy.install new file mode 100644 index 000000000..a8c0dadd1 --- /dev/null +++ b/debian/osmo-gbproxy.install @@ -0,0 +1,5 @@ +etc/osmocom/osmo-gbproxy.cfg +lib/systemd/system/osmo-gbproxy.service +usr/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 diff --git a/debian/osmo-gtphub.default b/debian/osmo-gtphub.default new file mode 100644 index 000000000..6af82da39 --- /dev/null +++ b/debian/osmo-gtphub.default @@ -0,0 +1,2 @@ +CONFIG_FILE="/etc/osmocom/osmo-gtphub.cfg" + diff --git a/debian/osmo-gtphub.init b/debian/osmo-gtphub.init new file mode 100755 index 000000000..160d55b70 --- /dev/null +++ b/debian/osmo-gtphub.init @@ -0,0 +1,150 @@ +#!/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 <nhofmeyr@sysmocom.de> + +# 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 + +: diff --git a/debian/osmo-gtphub.install b/debian/osmo-gtphub.install new file mode 100644 index 000000000..078207672 --- /dev/null +++ b/debian/osmo-gtphub.install @@ -0,0 +1,5 @@ +etc/osmocom/osmo-gtphub.cfg +lib/systemd/system/osmo-gtphub.service +usr/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 diff --git a/debian/osmo-sgsn.install b/debian/osmo-sgsn.install new file mode 100644 index 000000000..b9a46abf2 --- /dev/null +++ b/debian/osmo-sgsn.install @@ -0,0 +1,5 @@ +/etc/osmocom/osmo-sgsn.cfg +lib/systemd/system/osmo-sgsn.service +usr/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 diff --git a/debian/rules b/debian/rules new file mode 100755 index 000000000..af6b5b6e4 --- /dev/null +++ b/debian/rules @@ -0,0 +1,65 @@ +#!/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 --with-systemdsystemunitdir=/lib/systemd/system +override_dh_auto_configure: + dh_auto_configure -- $(CONFIGURE_FLAGS) +# +# Do not install libtool archive, python .pyc .pyo +#override_dh_install: +# dh_install --list-missing -X.la -X.pyc -X.pyo + +# See https://www.debian.org/doc/manuals/developers-reference/best-pkging-practices.html#bpp-dbg +override_dh_strip: + dh_strip -posmo-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) diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 000000000..89ae9db8f --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/doc/Makefile.am b/doc/Makefile.am new file mode 100644 index 000000000..5a231074c --- /dev/null +++ b/doc/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = \ + examples \ + $(NULL) diff --git a/doc/examples/Makefile.am b/doc/examples/Makefile.am new file mode 100644 index 000000000..2b5781a59 --- /dev/null +++ b/doc/examples/Makefile.am @@ -0,0 +1,32 @@ +OSMOCONF_FILES = \ + osmo-gtphub/osmo-gtphub.cfg \ + osmo-sgsn/osmo-sgsn.cfg \ + osmo-gbproxy/osmo-gbproxy.cfg + +osmoconfdir = $(sysconfdir)/osmocom +osmoconf_DATA = $(OSMOCONF_FILES) + +EXTRA_DIST = $(OSMOCONF_FILES) + +CFG_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 diff --git a/doc/examples/osmo-gbproxy/osmo-gbproxy-legacy.cfg b/doc/examples/osmo-gbproxy/osmo-gbproxy-legacy.cfg new file mode 100644 index 000000000..c471c38b5 --- /dev/null +++ b/doc/examples/osmo-gbproxy/osmo-gbproxy-legacy.cfg @@ -0,0 +1,44 @@ +! +! 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 diff --git a/doc/examples/osmo-gbproxy/osmo-gbproxy.cfg b/doc/examples/osmo-gbproxy/osmo-gbproxy.cfg new file mode 100644 index 000000000..0c3917a3e --- /dev/null +++ b/doc/examples/osmo-gbproxy/osmo-gbproxy.cfg @@ -0,0 +1,25 @@ +! +! 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 diff --git a/doc/examples/osmo-gtphub/gtphub-example.txt b/doc/examples/osmo-gtphub/gtphub-example.txt new file mode 100644 index 000000000..9c65f925f --- /dev/null +++ b/doc/examples/osmo-gtphub/gtphub-example.txt @@ -0,0 +1,90 @@ +Here 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 <your-test-dir> + LD_LIBRARY_PATH=/usr/local/lib /usr/local/bin/ggsn -f -c ./ggsn.conf + +2. GTPHub: + + cd <your-test-dir> + path/to/openbsc/openbsc/src/gprs/osmo-gtphub -c gtphub.conf #-e 1 #for DEBUG level + +3. SGSN tests: + + sudo -s + cd <your-test-dir> + /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 + diff --git a/doc/examples/osmo-gtphub/osmo-gtphub-1iface.cfg b/doc/examples/osmo-gtphub/osmo-gtphub-1iface.cfg new file mode 100644 index 000000000..3913d2c3c --- /dev/null +++ b/doc/examples/osmo-gtphub/osmo-gtphub-1iface.cfg @@ -0,0 +1,25 @@ +! +! 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 diff --git a/doc/examples/osmo-gtphub/osmo-gtphub.cfg b/doc/examples/osmo-gtphub/osmo-gtphub.cfg new file mode 100644 index 000000000..0dc415047 --- /dev/null +++ b/doc/examples/osmo-gtphub/osmo-gtphub.cfg @@ -0,0 +1,25 @@ +! +! 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 diff --git a/doc/examples/osmo-sgsn/osmo-sgsn-accept-all.cfg b/doc/examples/osmo-sgsn/osmo-sgsn-accept-all.cfg new file mode 100644 index 000000000..b47878a21 --- /dev/null +++ b/doc/examples/osmo-sgsn/osmo-sgsn-accept-all.cfg @@ -0,0 +1,28 @@ +! +! 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 + ggsn 0 echo-interval 60 + 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 +! diff --git a/doc/examples/osmo-sgsn/osmo-sgsn.cfg b/doc/examples/osmo-sgsn/osmo-sgsn.cfg new file mode 100644 index 000000000..263bd00e2 --- /dev/null +++ b/doc/examples/osmo-sgsn/osmo-sgsn.cfg @@ -0,0 +1,30 @@ +! +! 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 + ggsn 0 echo-interval 60 + 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 +! diff --git a/git-version-gen b/git-version-gen new file mode 100755 index 000000000..42cf3d2bd --- /dev/null +++ b/git-version-gen @@ -0,0 +1,151 @@ +#!/bin/sh +# Print a version string. +scriptversion=2010-01-28.01 + +# Copyright (C) 2007-2010 Free Software Foundation, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/. +# It may be run two ways: +# - from a git repository in which the "git describe" command below +# produces useful output (thus requiring at least one signed tag) +# - from a non-git-repo directory containing a .tarball-version file, which +# presumes this script is invoked like "./git-version-gen .tarball-version". + +# In order to use intra-version strings in your project, you will need two +# separate generated version string files: +# +# .tarball-version - present only in a distribution tarball, and not in +# a checked-out repository. Created with contents that were learned at +# the last time autoconf was run, and used by git-version-gen. Must not +# be present in either $(srcdir) or $(builddir) for git-version-gen to +# give accurate answers during normal development with a checked out tree, +# but must be present in a tarball when there is no version control system. +# Therefore, it cannot be used in any dependencies. GNUmakefile has +# hooks to force a reconfigure at distribution time to get the value +# correct, without penalizing normal development with extra reconfigures. +# +# .version - present in a checked-out repository and in a distribution +# tarball. Usable in dependencies, particularly for files that don't +# want to depend on config.h but do want to track version changes. +# Delete this file prior to any autoconf run where you want to rebuild +# files to pick up a version string change; and leave it stale to +# minimize rebuild time after unrelated changes to configure sources. +# +# It is probably wise to add these two files to .gitignore, so that you +# don't accidentally commit either generated file. +# +# Use the following line in your configure.ac, so that $(VERSION) will +# automatically be up-to-date each time configure is run (and note that +# since configure.ac no longer includes a version string, Makefile rules +# should not depend on configure.ac for version updates). +# +# AC_INIT([GNU project], +# m4_esyscmd([build-aux/git-version-gen .tarball-version]), +# [bug-project@example]) +# +# Then use the following lines in your Makefile.am, so that .version +# will be present for dependencies, and so that .tarball-version will +# exist in distribution tarballs. +# +# BUILT_SOURCES = $(top_srcdir)/.version +# $(top_srcdir)/.version: +# echo $(VERSION) > $@-t && mv $@-t $@ +# dist-hook: +# echo $(VERSION) > $(distdir)/.tarball-version + +case $# in + 1) ;; + *) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version"; exit 1;; +esac + +tarball_version_file=$1 +nl=' +' + +# First see if there is a tarball-only version file. +# then try "git describe", then default. +if test -f $tarball_version_file +then + v=`cat $tarball_version_file` || exit 1 + case $v in + *$nl*) v= ;; # reject multi-line output + [0-9]*) ;; + *) v= ;; + esac + test -z "$v" \ + && echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2 +fi + +if test -n "$v" +then + : # use $v +elif + v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \ + || git describe --abbrev=4 HEAD 2>/dev/null` \ + && case $v in + [0-9]*) ;; + v[0-9]*) ;; + *) (exit 1) ;; + esac +then + # Is this a new git that lists number of commits since the last + # tag or the previous older version that did not? + # Newer: v6.10-77-g0f8faeb + # Older: v6.10-g0f8faeb + case $v in + *-*-*) : git describe is okay three part flavor ;; + *-*) + : git describe is older two part flavor + # Recreate the number of commits and rewrite such that the + # result is the same as if we were using the newer version + # of git describe. + vtag=`echo "$v" | sed 's/-.*//'` + numcommits=`git rev-list "$vtag"..HEAD | wc -l` + v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`; + ;; + esac + + # Change the first '-' to a '.', so version-comparing tools work properly. + # Remove the "g" in git describe's output string, to save a byte. + v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`; +else + v=UNKNOWN +fi + +v=`echo "$v" |sed 's/^v//'` + +# Don't declare a version "dirty" merely because a time stamp has changed. +git status > /dev/null 2>&1 + +dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty= +case "$dirty" in + '') ;; + *) # Append the suffix only if there isn't one already. + case $v in + *-dirty) ;; + *) v="$v-dirty" ;; + esac ;; +esac + +# Omit the trailing newline, so that m4_esyscmd can use the result directly. +echo "$v" | tr -d '\012' + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/include/Makefile.am b/include/Makefile.am new file mode 100644 index 000000000..9d963a024 --- /dev/null +++ b/include/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = \ + osmocom \ + $(NULL) diff --git a/include/osmocom/Makefile.am b/include/osmocom/Makefile.am new file mode 100644 index 000000000..09db97a95 --- /dev/null +++ b/include/osmocom/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = \ + sgsn \ + $(NULL) diff --git a/include/osmocom/sgsn/Makefile.am b/include/osmocom/sgsn/Makefile.am new file mode 100644 index 000000000..cbf7c51b6 --- /dev/null +++ b/include/osmocom/sgsn/Makefile.am @@ -0,0 +1,26 @@ +noinst_HEADERS = \ + common.h \ + crc24.h \ + debug.h \ + gb_proxy.h \ + gprs_gb_parse.h \ + gprs_gmm.h \ + gprs_gmm_attach.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 \ + gtphub.h \ + sgsn.h \ + signal.h \ + slhc.h \ + v42bis.h \ + v42bis_private.h \ + vty.h \ + $(NULL) diff --git a/include/osmocom/sgsn/common.h b/include/osmocom/sgsn/common.h new file mode 100644 index 000000000..d91b3d39e --- /dev/null +++ b/include/osmocom/sgsn/common.h @@ -0,0 +1,6 @@ +#pragma once + +enum nsap_addr_enc { + NSAP_ADDR_ENC_X213, + NSAP_ADDR_ENC_V4RAW, +}; diff --git a/include/osmocom/sgsn/crc24.h b/include/osmocom/sgsn/crc24.h new file mode 100644 index 000000000..756638c03 --- /dev/null +++ b/include/osmocom/sgsn/crc24.h @@ -0,0 +1,10 @@ +#ifndef _CRC24_H +#define _CRC24_H + +#include <stdint.h> + +#define INIT_CRC24 0xffffff + +uint32_t crc24_calc(uint32_t fcs, uint8_t *cp, unsigned int len); + +#endif diff --git a/include/osmocom/sgsn/debug.h b/include/osmocom/sgsn/debug.h new file mode 100644 index 000000000..4d0fc6987 --- /dev/null +++ b/include/osmocom/sgsn/debug.h @@ -0,0 +1,45 @@ +#pragma once + +#include <stdio.h> +#include <osmocom/core/linuxlist.h> + +#define DEBUG +#include <osmocom/core/logging.h> + +/* Debug Areas of the code */ +enum { + DRLL, + DCC, + DMM, + DRR, + DRSL, + DNM, + DMNCC, + DPAG, + DMEAS, + DSCCP, + DMSC, + DHO, + DDB, + DREF, + DGPRS, + DNS, + DBSSGP, + DLLC, + DSNDCP, + DSLHC, + DNAT, + DCTRL, + DFILTER, + DGTPHUB, + DRANAP, + DSUA, + DV42BIS, + DPCU, + DVLR, + DIUCS, + DSIGTRAN, + Debug_LastEntry, +}; + +extern const struct log_info log_info; diff --git a/include/osmocom/sgsn/gb_proxy.h b/include/osmocom/sgsn/gb_proxy.h new file mode 100644 index 000000000..7e2ae42fb --- /dev/null +++ b/include/osmocom/sgsn/gb_proxy.h @@ -0,0 +1,300 @@ +#ifndef _GB_PROXY_H +#define _GB_PROXY_H + + +#include <osmocom/core/msgb.h> +#include <osmocom/gsm/gsm23003.h> + +#include <osmocom/gprs/gprs_ns.h> +#include <osmocom/vty/command.h> + +#include <sys/types.h> +#include <regex.h> + +#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; + /* Frequency (sec) at which timer to clean stale links is fired (0 disabled) */ + unsigned int clean_stale_timer_freq; + /* If !0, Max age to consider a struct gbproxy_link_info as stale */ + int tlli_max_age; + /* If !0, Max len of gbproxy_peer->list (list of struct gbproxy_link_info) */ + int tlli_max_len; + /* If !0, Max len of gbproxy_link_info->stored_msgs (list of msgb) */ + uint32_t stored_msgs_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; + + /* Fired periodically to clean up stale links from list */ + struct osmo_timer_list clean_stale_timer; +}; + +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; + uint32_t stored_msgs_len; + 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_ctrl.c */ +int gb_ctrl_cmds_install(void); + + +/* 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 diff --git a/include/osmocom/sgsn/gprs_gb_parse.h b/include/osmocom/sgsn/gprs_gb_parse.h new file mode 100644 index 000000000..9f43faed6 --- /dev/null +++ b/include/osmocom/sgsn/gprs_gb_parse.h @@ -0,0 +1,59 @@ +#pragma once + +#include <osmocom/sgsn/gprs_llc.h> + +#include <sys/types.h> + +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); diff --git a/include/osmocom/sgsn/gprs_gmm.h b/include/osmocom/sgsn/gprs_gmm.h new file mode 100644 index 000000000..ffcebd353 --- /dev/null +++ b/include/osmocom/sgsn/gprs_gmm.h @@ -0,0 +1,50 @@ +#ifndef _GPRS_GMM_H +#define _GPRS_GMM_H + +#include <osmocom/core/msgb.h> +#include <osmocom/sgsn/gprs_sgsn.h> + +#include <stdbool.h> + +int gsm48_tx_gsm_deact_pdp_req(struct sgsn_pdp_ctx *pdp, uint8_t sm_cause, bool teardown); +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 gsm48_tx_gmm_auth_ciph_req(struct sgsn_mm_ctx *mm, + const struct osmo_auth_vector *vec, + uint8_t key_seq, bool force_standby); + +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); + +int gsm48_tx_gmm_id_req(struct sgsn_mm_ctx *mm, uint8_t id_type); +int gsm48_tx_gmm_att_rej(struct sgsn_mm_ctx *mm, + uint8_t gmm_cause); +int gsm48_tx_gmm_att_ack(struct sgsn_mm_ctx *mm); + +int gprs_gmm_attach_req_ies(struct msgb *a, struct msgb *b); + +/* TODO: move extract_subscr_* when gsm48_gmm_authorize() got removed */ +void extract_subscr_msisdn(struct sgsn_mm_ctx *ctx); +void extract_subscr_hlr(struct sgsn_mm_ctx *ctx); + +void pdp_ctx_detach_mm_ctx(struct sgsn_pdp_ctx *pdp); +#endif /* _GPRS_GMM_H */ diff --git a/include/osmocom/sgsn/gprs_gmm_attach.h b/include/osmocom/sgsn/gprs_gmm_attach.h new file mode 100644 index 000000000..0aa2123a8 --- /dev/null +++ b/include/osmocom/sgsn/gprs_gmm_attach.h @@ -0,0 +1,39 @@ +#ifndef GPRS_GMM_ATTACH_H +#define GPRS_GMM_ATTACH_H + +#include <osmocom/core/fsm.h> + +struct sgsn_mm_ctx; + +enum gmm_attach_req_fsm_states { + ST_INIT, + ST_IDENTIY, + ST_RETRIEVE_AUTH, + ST_AUTH, + ST_ASK_VLR, + ST_IU_SECURITY_CMD, + ST_ACCEPT, + ST_REJECT +}; + +enum gmm_attach_req_fsm_events { + E_ATTACH_REQ_RECV, + E_IDEN_RESP_RECV, + E_AUTH_RESP_RECV_SUCCESS, + E_AUTH_RESP_RECV_RESYNC, + E_IU_SECURITY_CMD_COMPLETE, + E_ATTACH_ACCEPTED, + E_ATTACH_ACCEPT_SENT, + E_ATTACH_COMPLETE_RECV, + E_REJECT, + E_VLR_ANSWERED, +}; + +#define GMM_DISCARD_MS_WITHOUT_REJECT -1 + +extern const struct value_string gmm_attach_req_fsm_event_names[]; +extern struct osmo_fsm gmm_attach_req_fsm; + +void gmm_att_req_free(struct sgsn_mm_ctx *mm); + +#endif // GPRS_GMM_ATTACH_H diff --git a/include/osmocom/sgsn/gprs_llc.h b/include/osmocom/sgsn/gprs_llc.h new file mode 100644 index 000000000..376ae9a1a --- /dev/null +++ b/include/osmocom/sgsn/gprs_llc.h @@ -0,0 +1,284 @@ +#ifndef _GPRS_LLC_H +#define _GPRS_LLC_H + +#include <stdint.h> +#include <stdbool.h> +#include <osmocom/sgsn/gprs_sgsn.h> +#include <osmocom/sgsn/gprs_llc_xid.h> + +/* 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 diff --git a/include/osmocom/sgsn/gprs_llc_xid.h b/include/osmocom/sgsn/gprs_llc_xid.h new file mode 100644 index 000000000..d340d40b7 --- /dev/null +++ b/include/osmocom/sgsn/gprs_llc_xid.h @@ -0,0 +1,57 @@ +/* GPRS LLC XID field encoding/decoding as per 3GPP TS 44.064 */ + +/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> + * 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 <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <stdint.h> +#include <osmocom/core/linuxlist.h> + +/* 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); + diff --git a/include/osmocom/sgsn/gprs_sgsn.h b/include/osmocom/sgsn/gprs_sgsn.h new file mode 100644 index 000000000..cf7876669 --- /dev/null +++ b/include/osmocom/sgsn/gprs_sgsn.h @@ -0,0 +1,512 @@ +#ifndef _GPRS_SGSN_H +#define _GPRS_SGSN_H + +#include <stdint.h> +#include <netinet/in.h> + +#include <osmocom/core/fsm.h> +#include <osmocom/core/timer.h> + +#include <osmocom/gsm/gsm48.h> + +#include <osmocom/crypt/gprs_cipher.h> +#include <osmocom/gsm/protocol/gsm_23_003.h> +#include <osmocom/crypt/auth.h> + +#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; + struct { + struct osmo_fsm_inst *fsm; + + /* when a second attach req arrives while in this procedure, + * the fsm needs to compare it against old to decide what to do */ + struct msgb *attach_req; + uint32_t id_type; + unsigned int auth_reattempt; /* tracking UMTS resync auth attempts */ + } gmm_att_req; + /* 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 llist_head ggsn_list; /* list_head for ggsn->pdp_list */ + 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, + struct sgsn_ggsn_ctx *ggsn, + 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 llist_head pdp_list; /* list of associated pdp ctx (struct sgsn_pdp_ctx*) */ + struct osmo_timer_list echo_timer; + int echo_interval; +}; +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); +void sgsn_ggsn_ctx_drop_pdp(struct sgsn_pdp_ctx *pctx); +int sgsn_ggsn_ctx_drop_all_pdp_except(struct sgsn_ggsn_ctx *ggsn, struct sgsn_pdp_ctx *except); +void sgsn_ggsn_ctx_add_pdp(struct sgsn_ggsn_ctx *ggc, struct sgsn_pdp_ctx *pdp); +void sgsn_ggsn_ctx_remove_pdp(struct sgsn_ggsn_ctx *ggc, struct sgsn_pdp_ctx *pdp); +void sgsn_ggsn_ctx_check_echo_timer(struct sgsn_ggsn_ctx *ggc); + +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); +struct sgsn_instance *sgsn_instance_alloc(void *talloc_ctx); +void sgsn_inst_init(struct sgsn_instance *sgsn); + +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 */ diff --git a/include/osmocom/sgsn/gprs_sndcp.h b/include/osmocom/sgsn/gprs_sndcp.h new file mode 100644 index 000000000..d970240e4 --- /dev/null +++ b/include/osmocom/sgsn/gprs_sndcp.h @@ -0,0 +1,79 @@ +#ifndef _INT_SNDCP_H +#define _INT_SNDCP_H + +#include <stdint.h> +#include <osmocom/core/linuxlist.h> + +/* 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 */ diff --git a/include/osmocom/sgsn/gprs_sndcp_comp.h b/include/osmocom/sgsn/gprs_sndcp_comp.h new file mode 100644 index 000000000..e01a9d72f --- /dev/null +++ b/include/osmocom/sgsn/gprs_sndcp_comp.h @@ -0,0 +1,82 @@ +/* GPRS SNDCP header compression entity management tools */ + +/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> + * 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 <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <stdint.h> +#include <osmocom/core/linuxlist.h> +#include <osmocom/sgsn/gprs_sndcp_xid.h> + +/* 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 */ + union gprs_sndcp_comp_algo algo; + enum gprs_sndcp_xid_param_types 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); diff --git a/include/osmocom/sgsn/gprs_sndcp_dcomp.h b/include/osmocom/sgsn/gprs_sndcp_dcomp.h new file mode 100644 index 000000000..3e851421c --- /dev/null +++ b/include/osmocom/sgsn/gprs_sndcp_dcomp.h @@ -0,0 +1,53 @@ +/* GPRS SNDCP data compression handler */ + +/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> + * 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 <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <stdint.h> +#include <osmocom/core/linuxlist.h> +#include <osmocom/sgsn/gprs_sndcp_comp.h> + +/* 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); diff --git a/include/osmocom/sgsn/gprs_sndcp_pcomp.h b/include/osmocom/sgsn/gprs_sndcp_pcomp.h new file mode 100644 index 000000000..3e3131b52 --- /dev/null +++ b/include/osmocom/sgsn/gprs_sndcp_pcomp.h @@ -0,0 +1,46 @@ +/* GPRS SNDCP header compression handler */ + +/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> + * 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 <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <stdint.h> +#include <osmocom/core/linuxlist.h> +#include <osmocom/sgsn/gprs_sndcp_comp.h> + +/* 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); diff --git a/include/osmocom/sgsn/gprs_sndcp_xid.h b/include/osmocom/sgsn/gprs_sndcp_xid.h new file mode 100644 index 000000000..0dce43e40 --- /dev/null +++ b/include/osmocom/sgsn/gprs_sndcp_xid.h @@ -0,0 +1,224 @@ +/* GPRS SNDCP XID field encoding/decoding as per 3GPP TS 44.065 */ + +/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> + * 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 <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <stdint.h> +#include <osmocom/core/linuxlist.h> + +#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.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 */ +}; + +union gprs_sndcp_comp_algo { + enum gprs_sndcp_hdr_comp_algo pcomp; + enum gprs_sndcp_data_comp_algo dcomp; +}; + +/* 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 */ + union gprs_sndcp_comp_algo 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, 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 */ + SNDCP_XID_INVALID_COMPRESSION /* Not part of the spec; this means we found an invalid value */ +}; + +/* 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?) */ +enum gprs_sndcp_xid_param_types 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); + diff --git a/include/osmocom/sgsn/gprs_subscriber.h b/include/osmocom/sgsn/gprs_subscriber.h new file mode 100644 index 000000000..d36db7500 --- /dev/null +++ b/include/osmocom/sgsn/gprs_subscriber.h @@ -0,0 +1,31 @@ +/* GPRS subscriber details for use in SGSN land */ +#pragma once + +#include <stdint.h> + +#include <osmocom/core/linuxlist.h> +#include <osmocom/gsm/protocol/gsm_23_003.h> + +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, __FILE__, __LINE__) +#define gprs_subscr_put(gsub) _gprs_subscr_put(gsub, __FILE__, __LINE__) diff --git a/include/osmocom/sgsn/gprs_utils.h b/include/osmocom/sgsn/gprs_utils.h new file mode 100644 index 000000000..eacaec7a2 --- /dev/null +++ b/include/osmocom/sgsn/gprs_utils.h @@ -0,0 +1,52 @@ +/* GPRS utility functions */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * (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 <http://www.gnu.org/licenses/>. + * + */ +#pragma once + +#include <stdint.h> +#include <sys/types.h> + +#include <osmocom/core/msgb.h> + +struct msgb; +struct gprs_ra_id; + +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); +} diff --git a/include/osmocom/sgsn/gtphub.h b/include/osmocom/sgsn/gtphub.h new file mode 100644 index 000000000..8fd9f38cf --- /dev/null +++ b/include/osmocom/sgsn/gtphub.h @@ -0,0 +1,523 @@ +/* GTP Hub Implementation */ + +/* (C) 2015 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> + * 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 <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <stdint.h> +#include <sys/socket.h> + +#include <osmocom/core/select.h> +#include <osmocom/core/timer.h> +#include <osmocom/core/rate_ctr.h> + +#include <osmocom/sgsn/gprs_sgsn.h> + + +/* support */ + +/* TODO move to osmocom/core/socket.c ? */ +#include <netdb.h> /* 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 "<ip-addr> port <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); diff --git a/include/osmocom/sgsn/sgsn.h b/include/osmocom/sgsn/sgsn.h new file mode 100644 index 000000000..3a34ff928 --- /dev/null +++ b/include/osmocom/sgsn/sgsn.h @@ -0,0 +1,203 @@ +#ifndef _SGSN_H +#define _SGSN_H + + +#include <osmocom/core/msgb.h> +#include <osmocom/crypt/gprs_cipher.h> +#include <osmocom/gprs/gprs_ns.h> +#include <osmocom/sgsn/gprs_sgsn.h> +#include <osmocom/gsm/oap_client.h> +#include <osmocom/gsupclient/gsup_client.h> +#include <osmocom/sgsn/common.h> + +#include "../../bscconfig.h" + +#if BUILD_IU +#include <osmocom/ranap/iu_client.h> +#endif + +#include <ares.h> +#include <gtp.h> + +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 osmo_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 osmo_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); +void sgsn_ggsn_echo_req(struct sgsn_ggsn_ctx *ggc); + +/* 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 diff --git a/include/osmocom/sgsn/signal.h b/include/osmocom/sgsn/signal.h new file mode 100644 index 000000000..4b6ba56f6 --- /dev/null +++ b/include/osmocom/sgsn/signal.h @@ -0,0 +1,52 @@ +/* Generic signalling/notification infrastructure */ +/* (C) 2009-2010, 2015 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2009 by Harald Welte <laforge@gnumonks.org> + * (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 <http://www.gnu.org/licenses/>. + * + */ + +#ifndef OPENBSC_SIGNAL_H +#define OPENBSC_SIGNAL_H + +#include <stdlib.h> +#include <errno.h> + +#include <osmocom/core/signal.h> + +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 diff --git a/include/osmocom/sgsn/slhc.h b/include/osmocom/sgsn/slhc.h new file mode 100644 index 000000000..cd5a47cf4 --- /dev/null +++ b/include/osmocom/sgsn/slhc.h @@ -0,0 +1,187 @@ +#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 <linux/ip.h> +#include <linux/tcp.h> + +/* 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 */ diff --git a/include/osmocom/sgsn/v42bis.h b/include/osmocom/sgsn/v42bis.h new file mode 100644 index 000000000..b64c43e69 --- /dev/null +++ b/include/osmocom/sgsn/v42bis.h @@ -0,0 +1,147 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * v42bis.h + * + * Written by Steve Underwood <steveu@coppice.org> + * + * 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 <stdint.h> + +#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 ------------------------------------------------------------*/ diff --git a/include/osmocom/sgsn/v42bis_private.h b/include/osmocom/sgsn/v42bis_private.h new file mode 100644 index 000000000..c6b675a1b --- /dev/null +++ b/include/osmocom/sgsn/v42bis_private.h @@ -0,0 +1,126 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * private/v42bis.h + * + * Written by Steve Underwood <steveu@coppice.org> + * + * 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 ------------------------------------------------------------*/ diff --git a/include/osmocom/sgsn/vty.h b/include/osmocom/sgsn/vty.h new file mode 100644 index 000000000..d2e3d9a73 --- /dev/null +++ b/include/osmocom/sgsn/vty.h @@ -0,0 +1,9 @@ +#pragma once + +#include <osmocom/vty/command.h> + +enum bsc_vty_node { + GBPROXY_NODE = _LAST_OSMOVTY_NODE + 1, + SGSN_NODE, + GTPHUB_NODE, +}; diff --git a/m4/README b/m4/README new file mode 100644 index 000000000..92eb30ba4 --- /dev/null +++ b/m4/README @@ -0,0 +1,3 @@ +We 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. diff --git a/m4/ax_check_compile_flag.m4 b/m4/ax_check_compile_flag.m4 new file mode 100644 index 000000000..ca3639715 --- /dev/null +++ b/m4/ax_check_compile_flag.m4 @@ -0,0 +1,74 @@ +# =========================================================================== +# 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 <guidod@gmx.de> +# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com> +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see <http://www.gnu.org/licenses/>. +# +# 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 diff --git a/osmo-sgsn.pc.in b/osmo-sgsn.pc.in new file mode 100644 index 000000000..45aef571b --- /dev/null +++ b/osmo-sgsn.pc.in @@ -0,0 +1,10 @@ +prefix=@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} diff --git a/osmoappdesc.py b/osmoappdesc.py new file mode 100644 index 000000000..f610e77b3 --- /dev/null +++ b/osmoappdesc.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +# (C) 2013 by Katerina Barone-Adesi <kat.obsc@gmail.com> +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/> + + +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] diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 000000000..d2dcb9fba --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = \ + gprs \ + $(NULL) diff --git a/src/gprs/.gitignore b/src/gprs/.gitignore new file mode 100644 index 000000000..7cfefbac2 --- /dev/null +++ b/src/gprs/.gitignore @@ -0,0 +1,2 @@ +gsn_restart +osmo_*.cfg* diff --git a/src/gprs/Makefile.am b/src/gprs/Makefile.am new file mode 100644 index 000000000..ba5dfd617 --- /dev/null +++ b/src/gprs/Makefile.am @@ -0,0 +1,123 @@ +AM_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) \ + $(LIBOSMOGSUPCLIENT_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_ctrl.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_attach.c \ + 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 \ + $(NULL) +osmo_sgsn_LDADD = \ + $(OSMO_LIBS) \ + $(LIBOSMOABIS_LIBS) \ + $(LIBOSMOGSUPCLIENT_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) diff --git a/src/gprs/crc24.c b/src/gprs/crc24.c new file mode 100644 index 000000000..da269b375 --- /dev/null +++ b/src/gprs/crc24.c @@ -0,0 +1,67 @@ +/* GPRS LLC CRC-24 Implementation */ + +/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU 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 <http://www.gnu.org/licenses/>. + * + */ + +#include <osmocom/sgsn/crc24.h> + +/* 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; +} diff --git a/src/gprs/gb_proxy.c b/src/gprs/gb_proxy.c new file mode 100644 index 000000000..0b5758a33 --- /dev/null +++ b/src/gprs/gb_proxy.c @@ -0,0 +1,1454 @@ +/* NS-over-IP proxy */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * (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 <http://www.gnu.org/licenses/>. + * + */ + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <errno.h> +#include <sys/fcntl.h> +#include <sys/stat.h> +#include <arpa/inet.h> +#include <time.h> + +#include <osmocom/core/talloc.h> +#include <osmocom/core/select.h> +#include <osmocom/core/rate_ctr.h> +#include <osmocom/core/stats.h> + +#include <osmocom/gprs/gprs_ns.h> +#include <osmocom/gprs/gprs_bssgp.h> + +#include <osmocom/gsm/gsm_utils.h> + +#include <osmocom/sgsn/signal.h> +#include <osmocom/sgsn/debug.h> +#include <osmocom/sgsn/gprs_gb_parse.h> +#include <osmocom/sgsn/gb_proxy.h> + +#include <osmocom/sgsn/gprs_llc.h> +#include <osmocom/gsm/protocol/gsm_04_08_gprs.h> +#include <osmocom/sgsn/gprs_utils.h> + +extern void *tall_sgsn_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; +} + +/* Got identity response with IMSI, assuming the request had + * been generated by the gbproxy */ +static int gbproxy_flush_stored_messages(struct gbproxy_peer *peer, + time_t now, + struct gbproxy_link_info* link_info) +{ + int rc; + struct msgb *stored_msg; + + /* Patch and flush stored messages towards the SGSN */ + while ((stored_msg = msgb_dequeue_count(&link_info->stored_msgs, + &link_info->stored_msgs_len))) { + 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(stored_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(stored_msg), link_info->sgsn_nsei); + + if (rc < 0) + LOGP(DLLC, LOGL_ERROR, + "NSEI=%d(BSS) failed to send stored message " + "(%s)\n", + tmp_parse_ctx.peer_nsei, + tmp_parse_ctx.llc_msg_name ? + tmp_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; + + LOGP(DLLC, LOGL_DEBUG, + "NSEI=%d(BSS) IMSI acquisition succeeded, " + "flushing stored messages\n", + msgb_nsei(msg)); + /* 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, now, link_info) < 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 */ + + /* If queue is getting too large, drop oldest msgb before adding new one */ + if (peer->cfg->stored_msgs_max_len > 0) { + int exceeded_max_len = link_info->stored_msgs_len + + 1 - peer->cfg->stored_msgs_max_len; + + for (; exceeded_max_len > 0; exceeded_max_len--) { + struct msgb *msgb_drop; + msgb_drop = msgb_dequeue_count(&link_info->stored_msgs, + &link_info->stored_msgs_len); + LOGP(DLLC, LOGL_INFO, + "NSEI=%d(BSS) Dropping stored msgb from list " + "(!acq imsi, length %d, max_len exceeded)\n", + msgb_nsei(msgb_drop), link_info->stored_msgs_len); + + msgb_free(msgb_drop); + } + } + + /* 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 = bssgp_msgb_copy(msg, "process_bssgp_ul"); + msgb_enqueue_count(&link_info->stored_msgs, stored_msg, + &link_info->stored_msgs_len); + + 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; + + + osmo_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; + + osmo_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 = bssgp_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 = bssgp_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 = bssgp_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_sgsn_ctx, &global_ctrg_desc, 0); + if (!cfg->ctrg) { + LOGP(DGPRS, LOGL_ERROR, "Cannot allocate global counter group!\n"); + return -1; + } + osmo_clock_gettime(CLOCK_REALTIME, &tp); + + return 0; +} diff --git a/src/gprs/gb_proxy_ctrl.c b/src/gprs/gb_proxy_ctrl.c new file mode 100644 index 000000000..4b7c2f430 --- /dev/null +++ b/src/gprs/gb_proxy_ctrl.c @@ -0,0 +1,98 @@ +/* Control Interface Implementation for the Gb-proxy */ +/* + * (C) 2018 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> + * All Rights Reserved + * + * Author: Daniel Willmann + * + * 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 <http://www.gnu.org/licenses/>. + * + */ + +#include <osmocom/core/talloc.h> + + +#include <osmocom/gprs/gprs_bssgp.h> +#include <osmocom/gprs/gprs_ns.h> + +#include <osmocom/ctrl/control_if.h> +#include <osmocom/ctrl/control_cmd.h> +#include <osmocom/sgsn/gb_proxy.h> +#include <osmocom/sgsn/debug.h> + +extern vector ctrl_node_vec; + +static int get_nsvc_state(struct ctrl_cmd *cmd, void *data) +{ + struct gbproxy_config *cfg = data; + struct gprs_ns_inst *nsi = cfg->nsi; + struct gprs_nsvc *nsvc; + + cmd->reply = talloc_strdup(cmd, ""); + + llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) { + if (nsvc == nsi->unknown_nsvc) + continue; + + cmd->reply = gprs_nsvc_state_append(cmd->reply, nsvc); + } + + return CTRL_CMD_REPLY; +} + +CTRL_CMD_DEFINE_RO(nsvc_state, "nsvc-state"); + +static int get_gbproxy_state(struct ctrl_cmd *cmd, void *data) +{ + struct gbproxy_config *cfg = data; + struct gbproxy_peer *peer; + + cmd->reply = talloc_strdup(cmd, ""); + + llist_for_each_entry(peer, &cfg->bts_peers, list) { + struct gprs_ra_id raid; + gsm48_parse_ra(&raid, peer->ra); + + cmd->reply = talloc_asprintf_append(cmd->reply, "%u,%u,%u,%u,%u,%u,%s\n", + peer->nsei, peer->bvci, + raid.mcc, raid.mnc, + raid.lac, raid.rac, + peer->blocked ? "BLOCKED" : "UNBLOCKED"); + } + + return CTRL_CMD_REPLY; +} + +CTRL_CMD_DEFINE_RO(gbproxy_state, "gbproxy-state"); + +static int get_num_peers(struct ctrl_cmd *cmd, void *data) +{ + struct gbproxy_config *cfg = data; + + cmd->reply = talloc_strdup(cmd, ""); + cmd->reply = talloc_asprintf_append(cmd->reply, "%u", llist_count(&cfg->bts_peers)); + + return CTRL_CMD_REPLY; +} +CTRL_CMD_DEFINE_RO(num_peers, "number-of-peers"); + +int gb_ctrl_cmds_install(void) +{ + int rc = 0; + rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_nsvc_state); + rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_gbproxy_state); + rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_num_peers); + + return rc; +} diff --git a/src/gprs/gb_proxy_main.c b/src/gprs/gb_proxy_main.c new file mode 100644 index 000000000..19fbbba8f --- /dev/null +++ b/src/gprs/gb_proxy_main.c @@ -0,0 +1,393 @@ +/* NS-over-IP proxy */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * (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 <http://www.gnu.org/licenses/>. + * + */ + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <errno.h> +#include <signal.h> +#include <sys/fcntl.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <osmocom/core/application.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/select.h> +#include <osmocom/core/rate_ctr.h> +#include <osmocom/core/stats.h> + +#include <osmocom/gprs/gprs_ns.h> +#include <osmocom/gprs/gprs_bssgp.h> + +#include <osmocom/sgsn/signal.h> +#include <osmocom/sgsn/debug.h> +#include <osmocom/sgsn/vty.h> +#include <osmocom/sgsn/gb_proxy.h> + +#include <osmocom/ctrl/control_vty.h> +#include <osmocom/ctrl/control_if.h> +#include <osmocom/ctrl/ports.h> + +#include <osmocom/vty/command.h> +#include <osmocom/vty/telnet_interface.h> +#include <osmocom/vty/logging.h> +#include <osmocom/vty/stats.h> +#include <osmocom/vty/ports.h> +#include <osmocom/vty/misc.h> + +#include "../../bscconfig.h" + +#define _GNU_SOURCE +#include <getopt.h> + +void *tall_sgsn_ctx; + +const char *openbsc_copyright = + "Copyright (C) 2010 Harald Welte and On-Waves\r\n" + "License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\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; +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_sgsn_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; + struct ctrl_handle *ctrl; + + tall_sgsn_ctx = talloc_named_const(NULL, 0, "nsip_proxy"); + msgb_talloc_ctx_init(tall_sgsn_ctx, 0); + vty_info.tall_ctx = tall_sgsn_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_sgsn_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_sgsn_ctx); + osmo_stats_init(tall_sgsn_ctx); + + bssgp_nsi = gprs_ns_instantiate(&proxy_ns_cb, tall_sgsn_ctx); + if (!bssgp_nsi) { + LOGP(DGPRS, LOGL_ERROR, "Unable to instantiate NS\n"); + exit(1); + } + + gbcfg = talloc_zero(tall_sgsn_ctx, struct gbproxy_config); + if (!gbcfg) { + LOGP(DGPRS, LOGL_FATAL, "Unable to allocate config\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_sgsn_ctx, NULL, + vty_get_bind_addr(), OSMO_VTY_PORT_GBPROXY); + if (rc < 0) + exit(1); + + /* Start control interface after getting config for + * ctrl_vty_get_bind_addr() */ + ctrl = ctrl_interface_setup_dynip(gbcfg, ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_GBPROXY, NULL); + if (!ctrl) { + LOGP(DGPRS, LOGL_FATAL, "Failed to create CTRL interface.\n"); + exit(1); + } + + if (gb_ctrl_cmds_install() != 0) { + LOGP(DGPRS, LOGL_FATAL, "Failed to install CTRL commands.\n"); + 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); +} diff --git a/src/gprs/gb_proxy_patch.c b/src/gprs/gb_proxy_patch.c new file mode 100644 index 000000000..251bb67d6 --- /dev/null +++ b/src/gprs/gb_proxy_patch.c @@ -0,0 +1,465 @@ +/* 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 <http://www.gnu.org/licenses/>. + * + */ + +#include <osmocom/sgsn/gb_proxy.h> + +#include <osmocom/sgsn/gprs_utils.h> +#include <osmocom/sgsn/gprs_gb_parse.h> + +#include <osmocom/sgsn/debug.h> + +#include <osmocom/gprs/protocol/gsm_08_18.h> +#include <osmocom/core/rate_ctr.h> +#include <osmocom/gsm/apn.h> + +extern void *tall_sgsn_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; + 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; + 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_sgsn_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; +} diff --git a/src/gprs/gb_proxy_peer.c b/src/gprs/gb_proxy_peer.c new file mode 100644 index 000000000..48482b6a1 --- /dev/null +++ b/src/gprs/gb_proxy_peer.c @@ -0,0 +1,240 @@ +/* Gb proxy peer handling */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * (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 <http://www.gnu.org/licenses/>. + * + */ + +#include <osmocom/sgsn/gb_proxy.h> + +#include <osmocom/sgsn/debug.h> + +#include <osmocom/gprs/protocol/gsm_08_18.h> +#include <osmocom/core/rate_ctr.h> +#include <osmocom/core/stats.h> +#include <osmocom/core/talloc.h> +#include <osmocom/gsm/tlv.h> + +#include <string.h> + +extern void *tall_sgsn_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; +} + +static void clean_stale_timer_cb(void *data) +{ + time_t now; + struct timespec ts = {0,}; + struct gbproxy_peer *peer = (struct gbproxy_peer *) data; + + osmo_clock_gettime(CLOCK_MONOTONIC, &ts); + now = ts.tv_sec; + gbproxy_remove_stale_link_infos(peer, now); + if (peer->cfg->clean_stale_timer_freq != 0) + osmo_timer_schedule(&peer->clean_stale_timer, + peer->cfg->clean_stale_timer_freq, 0); +} + +struct gbproxy_peer *gbproxy_peer_alloc(struct gbproxy_config *cfg, uint16_t bvci) +{ + struct gbproxy_peer *peer; + + peer = talloc_zero(tall_sgsn_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); + + osmo_timer_setup(&peer->clean_stale_timer, clean_stale_timer_cb, peer); + if (peer->cfg->clean_stale_timer_freq != 0) + osmo_timer_schedule(&peer->clean_stale_timer, + peer->cfg->clean_stale_timer_freq, 0); + + return peer; +} + +void gbproxy_peer_free(struct gbproxy_peer *peer) +{ + llist_del(&peer->list); + osmo_timer_del(&peer->clean_stale_timer); + 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; +} diff --git a/src/gprs/gb_proxy_tlli.c b/src/gprs/gb_proxy_tlli.c new file mode 100644 index 000000000..0c027d5fb --- /dev/null +++ b/src/gprs/gb_proxy_tlli.c @@ -0,0 +1,723 @@ +/* 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 <http://www.gnu.org/licenses/>. + * + */ + +#include <osmocom/gsm/gsm48.h> + +#include <osmocom/sgsn/gb_proxy.h> + +#include <osmocom/sgsn/gprs_utils.h> +#include <osmocom/sgsn/gprs_gb_parse.h> + +#include <osmocom/sgsn/debug.h> + +#include <osmocom/gsm/gsm_utils.h> + +#include <osmocom/core/rate_ctr.h> +#include <osmocom/core/talloc.h> + +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]; +} + +static 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; +} + + diff --git a/src/gprs/gb_proxy_vty.c b/src/gprs/gb_proxy_vty.c new file mode 100644 index 000000000..52c39fdcd --- /dev/null +++ b/src/gprs/gb_proxy_vty.c @@ -0,0 +1,926 @@ +/* + * (C) 2010 by Harald Welte <laforge@gnumonks.org> + * (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 <http://www.gnu.org/licenses/>. + * + */ + +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <time.h> +#include <inttypes.h> + +#include <osmocom/core/talloc.h> +#include <osmocom/core/rate_ctr.h> +#include <osmocom/gsm/gsm48.h> + +#include <osmocom/gprs/gprs_ns.h> +#include <osmocom/gsm/apn.h> + +#include <osmocom/sgsn/debug.h> +#include <osmocom/sgsn/gb_proxy.h> +#include <osmocom/sgsn/gprs_utils.h> +#include <osmocom/sgsn/vty.h> + +#include <osmocom/vty/command.h> +#include <osmocom/vty/vty.h> +#include <osmocom/vty/misc.h> + +static struct gbproxy_config *g_cfg = NULL; + +/* + * vty code for gbproxy 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->clean_stale_timer_freq > 0) + vty_out(vty, " link-list clean-stale-timer %u%s", + g_cfg->clean_stale_timer_freq, 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); + if (g_cfg->stored_msgs_max_len > 0) + vty_out(vty, " link stored-msgs-max-length %"PRIu32"%s", + g_cfg->stored_msgs_max_len, 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_LINK_STR "Set TLLI parameters\n" + +#define GBPROXY_CLEAN_STALE_TIMER_STR "Periodic timer to clean stale links\n" + +DEFUN(cfg_gbproxy_link_list_clean_stale_timer, + cfg_gbproxy_link_list_clean_stale_timer_cmd, + "link-list clean-stale-timer <1-999999>", + GBPROXY_LINK_LIST_STR GBPROXY_CLEAN_STALE_TIMER_STR + "Frequency at which the periodic timer is fired (in seconds)\n") +{ + struct gbproxy_peer *peer; + g_cfg->clean_stale_timer_freq = (unsigned int) atoi(argv[0]); + + /* Re-schedule running timers soon in case prev frequency was really big + and new frequency is desired to be lower. After initial run, periodic + time is used. Use random() to avoid firing timers for all peers at + the same time */ + llist_for_each_entry(peer, &g_cfg->bts_peers, list) + osmo_timer_schedule(&peer->clean_stale_timer, + random() % 5, random() % 1000000); + + return CMD_SUCCESS; +} + +DEFUN(cfg_gbproxy_link_list_no_clean_stale_timer, + cfg_gbproxy_link_list_no_clean_stale_timer_cmd, + "no link-list clean-stale-timer", + NO_STR GBPROXY_LINK_LIST_STR GBPROXY_CLEAN_STALE_TIMER_STR) + +{ + struct gbproxy_peer *peer; + g_cfg->clean_stale_timer_freq = 0; + + llist_for_each_entry(peer, &g_cfg->bts_peers, list) + osmo_timer_del(&peer->clean_stale_timer); + + return CMD_SUCCESS; +} + +#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(cfg_gbproxy_link_stored_msgs_max_len, + cfg_gbproxy_link_stored_msgs_max_len_cmd, + "link stored-msgs-max-length <1-99999>", + GBPROXY_LINK_STR GBPROXY_MAX_LEN_STR + "Maximum number of msgb stored in the logical link waiting to acquire its IMSI\n") +{ + g_cfg->stored_msgs_max_len = (uint32_t) atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_gbproxy_link_no_stored_msgs_max_len, + cfg_gbproxy_link_no_stored_msgs_max_len_cmd, + "no link stored-msgs-max-length", + NO_STR GBPROXY_LINK_STR GBPROXY_MAX_LEN_STR) +{ + g_cfg->stored_msgs_max_len = 0; + + 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,}; + + osmo_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; + + 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 (link_info->stored_msgs_len) + vty_out(vty, ", STORED %"PRIu32"/%"PRIu32, + link_info->stored_msgs_len, + g_cfg->stored_msgs_max_len); + + 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; + + osmo_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_clean_stale_timer_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_link_stored_msgs_max_len_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_clean_stale_timer_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); + install_element(GBPROXY_NODE, &cfg_gbproxy_link_no_stored_msgs_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; +} diff --git a/src/gprs/gprs_gb_parse.c b/src/gprs/gprs_gb_parse.c new file mode 100644 index 000000000..93b90a26d --- /dev/null +++ b/src/gprs/gprs_gb_parse.c @@ -0,0 +1,638 @@ +/* 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 <http://www.gnu.org/licenses/>. + * + */ + +#include <osmocom/gsm/gsm48.h> +#include <osmocom/gsm/protocol/gsm_04_08_gprs.h> + +#include <osmocom/sgsn/gprs_gb_parse.h> + +#include <osmocom/sgsn/gprs_utils.h> + +#include <osmocom/sgsn/debug.h> + +#include <osmocom/gprs/gprs_bssgp.h> + +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 */ + if (osmo_shift_v_fixed(&data, &data_len, 3, NULL) < 3) + return 0; + + /* 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 */ + if (osmo_shift_v_fixed(&data, &data_len, 3, NULL) < 3) + return 0; + + 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 */ + if (osmo_shift_v_fixed(&data, &data_len, 1, NULL) < 1) + return 0; + + 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 */ + if (osmo_shift_v_fixed(&data, &data_len, 2, NULL) < 2) + return 0; + + 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 */ + if (osmo_shift_v_fixed(&data, &data_len, 2, NULL) < 2) + return 0; + + /* 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"; +} diff --git a/src/gprs/gprs_gmm.c b/src/gprs/gprs_gmm.c new file mode 100644 index 000000000..cc6ea4a2f --- /dev/null +++ b/src/gprs/gprs_gmm.c @@ -0,0 +1,3013 @@ +/* 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 <laforge@gnumonks.org> + * (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 <http://www.gnu.org/licenses/>. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <errno.h> +#include <stdbool.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> + +#include "bscconfig.h" + +#include <osmocom/core/msgb.h> +#include <osmocom/gsm/tlv.h> +#include <osmocom/gsm/gsm_utils.h> +#include <osmocom/core/signal.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/rate_ctr.h> +#include <osmocom/core/utils.h> +#include <osmocom/crypt/auth.h> +#include <osmocom/gsm/apn.h> +#include <osmocom/gsm/protocol/gsm_04_08_gprs.h> + +#include <osmocom/gprs/gprs_bssgp.h> + +#ifdef BUILD_IU +#include <osmocom/ranap/ranap_ies_defs.h> +#include <osmocom/ranap/ranap_msg_factory.h> +#include <osmocom/ranap/iu_client.h> +#endif + +#include <osmocom/sgsn/debug.h> +#include <osmocom/sgsn/gprs_llc.h> +#include <osmocom/sgsn/gprs_sgsn.h> +#include <osmocom/sgsn/gprs_gmm.h> +#include <osmocom/sgsn/gprs_utils.h> +#include <osmocom/sgsn/gprs_subscriber.h> +#include <osmocom/sgsn/sgsn.h> +#include <osmocom/sgsn/gprs_gmm_attach.h> +#include <osmocom/sgsn/signal.h> +#include <osmocom/sgsn/gprs_sndcp.h> + +#include <pdp.h> + +#define PTMSI_ALLOC + +extern struct sgsn_instance *sgsn; +extern void *tall_sgsn_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; + osmo_fsm_inst_dispatch(mm->gmm_att_req.fsm, E_IU_SECURITY_CMD_COMPLETE, NULL); + 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 */ +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); +} +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 */ +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 */ +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 osmo_fsm_inst_dispatch(ctx->gmm_att_req.fsm, E_AUTH_RESP_RECV_SUCCESS, NULL); +} + +/* 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 osmo_fsm_inst_dispatch(ctx->gmm_att_req.fsm, E_AUTH_RESP_RECV_RESYNC, NULL); + /* 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; +} + +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)); + } +} + +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; + + if (ctx->gmm_att_req.fsm->state != ST_INIT) + osmo_fsm_inst_dispatch(ctx->gmm_att_req.fsm, E_VLR_ANSWERED, (void *) 0); + else + 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 */ + if (ctx->gmm_att_req.fsm->state != ST_INIT) + osmo_fsm_inst_dispatch(ctx->gmm_att_req.fsm, E_VLR_ANSWERED, (void *) 0); + 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); + if (ctx->gmm_att_req.fsm->state != ST_INIT) + osmo_fsm_inst_dispatch(ctx->gmm_att_req.fsm, E_REJECT, (void *) (long) gmm_cause); + 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_IMSI, 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; + long mi_typel = mi_type; + 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 osmo_fsm_inst_dispatch(ctx->gmm_att_req.fsm, E_IDEN_RESP_RECV, (void *)mi_typel); +} + +/* 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); + } + + osmo_fsm_inst_dispatch(ctx->gmm_att_req.fsm, E_ATTACH_REQ_RECV, msg); + return 0; + +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; + +} + + +/* Checks if two attach request contain the IEs and IE values + * return 0 if equal + * return -1 if error + * return 1 if unequal + * + * Only do a simple memcmp for now. + */ +int gprs_gmm_attach_req_ies(struct msgb *a, struct msgb *b) +{ + struct gsm48_hdr *gh_a = (struct gsm48_hdr *) msgb_gmmh(a); + struct gsm48_hdr *gh_b = (struct gsm48_hdr *) msgb_gmmh(b); + +#define GMM_ATTACH_REQ_LEN 26 + + /* there is the LLC FCS behind */ + if (msgb_l3len(a) < GMM_ATTACH_REQ_LEN || msgb_l3len(b) < GMM_ATTACH_REQ_LEN) + return -1; + + return !!memcmp(gh_a, gh_b, GMM_ATTACH_REQ_LEN); +} + +/* 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 */ +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_IMPL_DETACHED; + 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_IMPL_DETACHED; + 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; + + osmo_fsm_inst_dispatch(mmctx->gmm_att_req.fsm, E_ATTACH_COMPLETE_RECV, 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_rearm(struct sgsn_pdp_ctx *pdp, unsigned int T, unsigned int seconds) +{ + if (osmo_timer_pending(&pdp->timer)) + LOGPDPCTXP(LOGL_ERROR, pdp, "Scheduling PDP timer %u while old " + "timer %u pending\n", T, pdp->T); + osmo_timer_schedule(&pdp->timer, seconds, 0); +} + +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; + + osmo_timer_setup(&pdp->timer, pdpctx_timer_cb, pdp); + pdpctx_timer_rearm(pdp, pdp->T, seconds); +} + +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); +} + +void pdp_ctx_detach_mm_ctx(struct sgsn_pdp_ctx *pdp) +{ + /* Detach from MM context */ + llist_del(&pdp->list); + pdp->mm = NULL; + + /* stop timer 3395 */ + pdpctx_timer_stop(pdp, 3395); +} + +#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, bool teardown) +{ + struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 PDP DET REQ"); + struct gsm48_hdr *gh; + uint8_t transaction_id = tid ^ 0x8; /* flip */ + uint8_t tear_down_ind = (0x9 << 4) | (!!teardown); + + 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); + msgb_v_put(msg, tear_down_ind); + + return gsm48_gmm_sendmsg(msg, 0, mm, true); +} +int gsm48_tx_gsm_deact_pdp_req(struct sgsn_pdp_ctx *pdp, uint8_t sm_cause, bool teardown) +{ + pdpctx_timer_start(pdp, 3395, sgsn->cfg.timers.T3395); + + return _gsm48_tx_gsm_deact_pdp_req(pdp->mm, pdp->ti, sm_cause, teardown); +} + +/* 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_sgsn_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 = bssgp_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); + if (pdp->ggsn) + return sgsn_delete_pdp_ctx(pdp); + /* GTP side already detached, freeing */ + sgsn_pdp_ctx_free(pdp); + return 0; +} + +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 >= 5) { + LOGPDPCTXP(LOGL_NOTICE, pdp, "T3395 expired >= 5 times\n"); + pdp->state = PDP_STATE_INACTIVE; + if (pdp->ggsn) + sgsn_delete_pdp_ctx(pdp); + else + sgsn_pdp_ctx_free(pdp); + break; + } + _gsm48_tx_gsm_deact_pdp_req(pdp->mm, pdp->ti, GSM_CAUSE_NET_FAIL, true); + pdpctx_timer_rearm(pdp, 3395, sgsn->cfg.timers.T3395); + 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 diff --git a/src/gprs/gprs_gmm_attach.c b/src/gprs/gprs_gmm_attach.c new file mode 100644 index 000000000..6fdb3af40 --- /dev/null +++ b/src/gprs/gprs_gmm_attach.c @@ -0,0 +1,436 @@ +#include <osmocom/sgsn/gprs_gmm_attach.h> + +#include <osmocom/gsm/protocol/gsm_04_08_gprs.h> +#include <osmocom/sgsn/debug.h> +#include <osmocom/sgsn/gprs_gmm.h> +#include <osmocom/sgsn/sgsn.h> + +#define X(s) (1 << (s)) + +static int require_identity_imei = 1; +static int require_auth = 1; + +static void st_init(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct sgsn_mm_ctx *ctx = fi->priv; + struct msgb *attach_req = data; + + /* we can run st_init multiple times */ + if (ctx->gmm_att_req.attach_req) + msgb_free(ctx->gmm_att_req.attach_req); + + ctx->gmm_att_req.attach_req = msgb_copy(attach_req, "Attach Request"); + ctx->auth_state = SGSN_AUTH_UNKNOWN; + ctx->gmm_att_req.auth_reattempt = 0; + + /* + * TODO: remove pending_req as soon the sgsn_auth code doesn't depend + * on it. + * pending_req must be set, even this fsm doesn't use it, because + * the sgsn_auth code is using this too + */ + ctx->pending_req = GSM48_MT_GMM_ATTACH_REQ; + + if (require_identity_imei) { + ctx->gmm_att_req.id_type = GSM_MI_TYPE_IMEI; + osmo_fsm_inst_state_chg(fi, ST_IDENTIY, sgsn->cfg.timers.T3370, 3370); + } else if (!strlen(ctx->imsi)) { + ctx->gmm_att_req.id_type = GSM_MI_TYPE_IMSI; + osmo_fsm_inst_state_chg(fi, ST_IDENTIY, sgsn->cfg.timers.T3370, 3370); + } else if (require_auth) + osmo_fsm_inst_state_chg(fi, ST_AUTH, sgsn->cfg.timers.T3360, 3360); + else + osmo_fsm_inst_state_chg(fi, ST_ACCEPT, sgsn->cfg.timers.T3350, 3350); +} + +static void st_identity_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct sgsn_mm_ctx *ctx = fi->priv; + int ret = 0; + + ctx->num_T_exp = 0; + + switch (ctx->gmm_att_req.id_type) { + case GSM_MI_TYPE_IMEI: + case GSM_MI_TYPE_IMSI: + break; + default: + /* TODO logging */ + osmo_fsm_inst_dispatch(fi, E_REJECT, NULL); + return; + } + + ctx->t3370_id_type = ctx->gmm_att_req.id_type; + ret = gsm48_tx_gmm_id_req(ctx, ctx->gmm_att_req.id_type); + if (ret < 0) { + LOGPFSM(fi, "Can not send tx_gmm_id %d.\n", ret); + osmo_fsm_inst_dispatch(fi, E_REJECT, NULL); + } +} + +static void st_identity(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct sgsn_mm_ctx *ctx = fi->priv; + + OSMO_ASSERT(event == E_IDEN_RESP_RECV); + + /* check if we received a identity response */ + long type = (long) data; + switch (type) { + case GSM_MI_TYPE_IMEI: + case GSM_MI_TYPE_IMSI: + break; + default: + LOGMMCTXP(LOGL_ERROR, ctx, "Unknown mi type: 0x%lx, rejecting MS.\n", type); + osmo_fsm_inst_dispatch(fi, E_REJECT, (void *) GMM_CAUSE_NET_FAIL); + return; + } + + if (type != ctx->gmm_att_req.id_type) { + /* ignore wrong package */ + /* TODO logging */ + return; + } + + if (type == GSM_MI_TYPE_IMEI && !strlen(ctx->imsi)) { + ctx->gmm_att_req.id_type = GSM_MI_TYPE_IMSI; + osmo_fsm_inst_state_chg(fi, ST_IDENTIY, sgsn->cfg.timers.T3370, 3370); + } else if (require_auth) + osmo_fsm_inst_state_chg(fi, ST_AUTH, sgsn->cfg.timers.T3360, 3360); + else + osmo_fsm_inst_state_chg(fi, ST_ACCEPT, sgsn->cfg.timers.T3350, 3350); +} + +static void st_auth_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct sgsn_mm_ctx *ctx = fi->priv; + enum sgsn_auth_state auth_state; + + ctx->num_T_exp = 0; + + /* TODO: remove this layer violation. Don't parse any auth_policy here + * The correct way would be to ask the SGSN is this mmctx has to be auth + * regardless of the state. + * Otherwise someone else could steal the TLLI and just use it without further + * auth. + */ + if (sgsn->cfg.auth_policy != SGSN_AUTH_POLICY_REMOTE) { + /* we can "trust" sgsn_auth_state as long it's not remote */ + auth_state = sgsn_auth_state(ctx); + } else { + auth_state = ctx->auth_state; + } + + switch(auth_state) { + case SGSN_AUTH_UMTS_RESYNC: /* ask the vlr for a new vector to match the simcards seq */ + case SGSN_AUTH_UNKNOWN: /* the SGSN doesn know this MS */ + osmo_fsm_inst_state_chg(fi, ST_ASK_VLR, sgsn->cfg.timers.T3350, 3350); + break; + case SGSN_AUTH_REJECTED: + /* TODO: correct GMM cause */ + osmo_fsm_inst_dispatch(fi, E_REJECT, (void *) GMM_CAUSE_GPRS_NOTALLOWED); + break; + case SGSN_AUTH_ACCEPTED: + osmo_fsm_inst_state_chg(fi, ST_ACCEPT, sgsn->cfg.timers.T3350, 3350); + break; + case SGSN_AUTH_AUTHENTICATE: + if (ctx->auth_triplet.key_seq == GSM_KEY_SEQ_INVAL) { + /* invalid key material */ + osmo_fsm_inst_state_chg(fi, ST_ASK_VLR, sgsn->cfg.timers.T3350, 3350); + } + + struct gsm_auth_tuple *at = &ctx->auth_triplet; + if (gsm48_tx_gmm_auth_ciph_req(ctx, &at->vec, at->key_seq, + false) < 0) { + /* network failure */ + osmo_fsm_inst_dispatch(fi, E_REJECT, (void *) GMM_CAUSE_NET_FAIL); + } + ctx->gmm_att_req.auth_reattempt++; + break; + } +} + +static void st_auth(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct sgsn_mm_ctx *ctx = fi->priv; + + switch (event) { + case E_AUTH_RESP_RECV_SUCCESS: + sgsn_auth_request(ctx); +#ifdef BUILD_IU + if (ctx->ran_type == MM_CTX_T_UTRAN_Iu && !ctx->iu.ue_ctx->integrity_active) + osmo_fsm_inst_state_chg(fi, ST_IU_SECURITY_CMD, sgsn->cfg.timers.T3350, 3350); + else +#endif /* BUILD_IU */ + osmo_fsm_inst_state_chg(fi, ST_ACCEPT, sgsn->cfg.timers.T3350, 3350); + break; + case E_AUTH_RESP_RECV_RESYNC: + if (ctx->gmm_att_req.auth_reattempt <= 1) + osmo_fsm_inst_state_chg(fi, ST_ASK_VLR, sgsn->cfg.timers.T3350, 3350); + else + osmo_fsm_inst_dispatch(fi, E_REJECT, (void *) GMM_CAUSE_SYNC_FAIL); + break; + } +} + +static void st_accept_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct sgsn_mm_ctx *ctx = fi->priv; + + ctx->num_T_exp = 0; + + /* TODO: remove pending_req as soon the sgsn_auth code doesn't depend on it */ + ctx->pending_req = 0; + gsm48_tx_gmm_att_ack(ctx); +} + +static void st_accept(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct sgsn_mm_ctx *ctx = fi->priv; + + switch(event) { + case E_ATTACH_COMPLETE_RECV: + /* TODO: #ifdef ! PTMSI_ALLOC is not supported */ + extract_subscr_msisdn(ctx); + extract_subscr_hlr(ctx); + osmo_fsm_inst_state_chg(fi, ST_INIT, 0, 0); + break; + } +} + +static void st_reject(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct sgsn_mm_ctx *ctx = fi->priv; + long reject_cause = (long) data; + + if (reject_cause != GMM_DISCARD_MS_WITHOUT_REJECT) + gsm48_tx_gmm_att_rej(ctx, (uint8_t) reject_cause); + + sgsn_mm_ctx_cleanup_free(ctx); +} + +static void st_ask_vlr_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct sgsn_mm_ctx *ctx = fi->priv; + + /* FIXME: remove this layer violation. + * The VLR should send the message to the HLR and not the rx function + * gsm48_rx_gmm_auth_ciph_fail. Because gmm_auth_ciph_fail already send a + * message to the HLR, we don't send here a request. */ + if (ctx->auth_state == SGSN_AUTH_UMTS_RESYNC) + return; + + /* ask the auth layer for more data */ + sgsn_auth_request(ctx); +} + +static void st_ask_vlr(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + switch(event) { + case E_VLR_ANSWERED: + osmo_fsm_inst_state_chg(fi, ST_AUTH, sgsn->cfg.timers.T3360, 3360); + break; + } +} + +static void st_iu_security_cmd_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ +#ifdef BUILD_IU + struct sgsn_mm_ctx *ctx = fi->priv; + + /* TODO: shouldn't this set always? not only when the integrity_active? */ + if (ctx->iu.ue_ctx->integrity_active) { + osmo_fsm_inst_state_chg(fi, ST_ACCEPT, sgsn->cfg.timers.T3350, 3350); + return; + } + + ranap_iu_tx_sec_mode_cmd(ctx->iu.ue_ctx, &ctx->auth_triplet.vec, 0, ctx->iu.new_key); + ctx->iu.new_key = 0; +#endif +} + +static void st_iu_security_cmd(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + switch(event) { + case E_IU_SECURITY_CMD_COMPLETE: + osmo_fsm_inst_state_chg(fi, ST_ACCEPT, sgsn->cfg.timers.T3350, 3350); + break; + } +} + +static struct osmo_fsm_state gmm_attach_req_fsm_states[] = { + /* default state for non-DTX and DTX when SPEECH is in progress */ + [ST_INIT] = { + .in_event_mask = X(E_ATTACH_REQ_RECV), + .out_state_mask = X(ST_INIT) | X(ST_IDENTIY) | X(ST_AUTH) | X(ST_ACCEPT), + .name = "Init", + .action = st_init, + }, + [ST_ASK_VLR] = { + .in_event_mask = X(E_VLR_ANSWERED), + .out_state_mask = X(ST_INIT) | X(ST_AUTH) | X(ST_ACCEPT) | X(ST_REJECT), + .name = "AskVLR", + .onenter = st_ask_vlr_on_enter, + .action = st_ask_vlr, + }, + [ST_IDENTIY] = { + .in_event_mask = X(E_IDEN_RESP_RECV), + .out_state_mask = X(ST_INIT) | X(ST_AUTH) | X(ST_ACCEPT) | X(ST_IDENTIY) | X(ST_REJECT), + .onenter = st_identity_on_enter, + .name = "CheckIdentity", + .action = st_identity, + }, + [ST_AUTH] = { + .in_event_mask = X(E_AUTH_RESP_RECV_SUCCESS) | X(E_AUTH_RESP_RECV_RESYNC), + .out_state_mask = X(ST_INIT) | X(ST_AUTH) | X(ST_IU_SECURITY_CMD) | X(ST_ACCEPT) | X(ST_ASK_VLR) | X(ST_REJECT), + .name = "Authenticate", + .onenter = st_auth_on_enter, + .action = st_auth, + }, + [ST_IU_SECURITY_CMD] = { + .in_event_mask = X(E_IU_SECURITY_CMD_COMPLETE), + .out_state_mask = X(ST_INIT) | X(ST_AUTH) | X(ST_ACCEPT) | X(ST_REJECT), + .name = "IuSecurityCommand", + .onenter = st_iu_security_cmd_on_enter, + .action = st_iu_security_cmd, + }, + [ST_ACCEPT] = { + .in_event_mask = X(E_ATTACH_COMPLETE_RECV), + .out_state_mask = X(ST_INIT) | X(ST_REJECT), + .name = "WaitAttachComplete", + .onenter = st_accept_on_enter, + .action = st_accept, + }, + [ST_REJECT] = { + .in_event_mask = X(E_REJECT), + .out_state_mask = X(ST_INIT), + .name = "Reject", + .action = st_reject, + }, +}; + +const struct value_string gmm_attach_req_fsm_event_names[] = { + { E_ATTACH_REQ_RECV, "Received an attach request" }, + { E_IDEN_RESP_RECV, "Identity Request received" }, + { E_AUTH_RESP_RECV_SUCCESS, "Authentication Response received" }, + { E_AUTH_RESP_RECV_RESYNC, "Authentication Failure with resync received" }, + { E_ATTACH_ACCEPTED, "Attach accepted" }, + { E_ATTACH_ACCEPT_SENT, "Attach accept sent" }, + { E_ATTACH_COMPLETE_RECV, "Attach complete received." }, + { E_IU_SECURITY_CMD_COMPLETE, "IU Security Command Complete received." }, + { E_REJECT, "Reject the MS"}, + { E_VLR_ANSWERED, "VLR answered"}, + { 0, NULL } +}; + +void gmm_attach_allstate_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) { + struct sgsn_mm_ctx *ctx = fi->priv; + struct msgb *new_attach_req = data; + + switch (event) { + case E_ATTACH_REQ_RECV: + switch (fi->state) { + case ST_INIT: + case ST_REJECT: + st_init(fi, event, data); + break; + + case ST_ACCEPT: + /* TODO: drop all state (e.g. PDP Ctx) and do this procedure */ + osmo_fsm_inst_state_chg(fi, ST_INIT, 0, 0); + st_init(fi, event, data); + break; + + case ST_ASK_VLR: + case ST_AUTH: + case ST_IDENTIY: + case ST_RETRIEVE_AUTH: + /* 04.08 4.7.3.1.6 d) Abnormal Case + * Only do action if Req IEs differs. */ + if (ctx->gmm_att_req.attach_req && + gprs_gmm_attach_req_ies(new_attach_req, ctx->gmm_att_req.attach_req)) { + osmo_fsm_inst_state_chg(fi, ST_INIT, 0, 0); + st_init(fi, event, data); + } + break; + } + break; + case E_REJECT: + if (fi->state != ST_REJECT) + osmo_fsm_inst_state_chg(fi, ST_REJECT, 0, 0); + st_reject(fi, event, data); + break; + } +} + +int gmm_attach_timer_cb(struct osmo_fsm_inst *fi) +{ + struct sgsn_mm_ctx *ctx = fi->priv; + struct gsm_auth_tuple *at = &ctx->auth_triplet; + + ctx->num_T_exp++; + + switch(fi->state) { + case ST_ASK_VLR: + /* TODO: replace T3350 by a better timer or it's own + * re-use T3350 - not defined by standard */ + LOGMMCTXP(LOGL_ERROR, ctx, "HLR did not answer in time. Rejecting.\n"); + osmo_fsm_inst_dispatch(fi, E_REJECT, + (void *) GMM_CAUSE_NET_FAIL); + break; + case ST_IDENTIY: + /* T3370 */ + if (ctx->num_T_exp >= 5) { + osmo_fsm_inst_dispatch(fi, E_REJECT, + (void *) GMM_CAUSE_MS_ID_NOT_DERIVED); + break; + } + gsm48_tx_gmm_id_req(ctx, ctx->gmm_att_req.id_type); + osmo_timer_schedule(&fi->timer, sgsn->cfg.timers.T3370, 0); + break; + case ST_AUTH: + /* T3360 */ + if (ctx->num_T_exp >= 5) { + osmo_fsm_inst_dispatch(fi, E_REJECT, (void *) GMM_DISCARD_MS_WITHOUT_REJECT); + break; + } + gsm48_tx_gmm_auth_ciph_req(ctx, &at->vec, at->key_seq, false); + osmo_timer_schedule(&fi->timer, sgsn->cfg.timers.T3360, 0); + break; + case ST_ACCEPT: + /* T3350 */ + if (ctx->num_T_exp >= 5) { + osmo_fsm_inst_dispatch(fi, E_REJECT, (void *) GMM_DISCARD_MS_WITHOUT_REJECT); + break; + } + gsm48_tx_gmm_att_ack(ctx); + osmo_timer_schedule(&fi->timer, sgsn->cfg.timers.T3350, 0); + break; + } + + return 0; +} + +struct osmo_fsm gmm_attach_req_fsm = { + .name = "GMM_ATTACH_REQ_FSM", + .states = gmm_attach_req_fsm_states, + .num_states = ARRAY_SIZE(gmm_attach_req_fsm_states), + .event_names = gmm_attach_req_fsm_event_names, + .allstate_event_mask = X(E_REJECT) | X(E_ATTACH_REQ_RECV), + .allstate_action = gmm_attach_allstate_action, + .log_subsys = DMM, + .timer_cb = gmm_attach_timer_cb, +}; + +static __attribute__((constructor)) void gprs_gmm_fsm_init(void) +{ + osmo_fsm_register(&gmm_attach_req_fsm); +} + +void gmm_att_req_free(struct sgsn_mm_ctx *mm) { + if (mm->gmm_att_req.fsm) + osmo_fsm_inst_free(mm->gmm_att_req.fsm); + + if (mm->gmm_att_req.attach_req) + msgb_free(mm->gmm_att_req.attach_req); +} diff --git a/src/gprs/gprs_llc.c b/src/gprs/gprs_llc.c new file mode 100644 index 000000000..abbb74278 --- /dev/null +++ b/src/gprs/gprs_llc.c @@ -0,0 +1,1140 @@ +/* GPRS LLC protocol implementation as per 3GPP TS 04.64 */ + +/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU 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 <http://www.gnu.org/licenses/>. + * + */ + +#include <errno.h> +#include <stdint.h> +#include <stdbool.h> + +#include <osmocom/core/msgb.h> +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/timer.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/rate_ctr.h> +#include <osmocom/gprs/gprs_bssgp.h> +#include <osmocom/gsm/gsm_utils.h> + +#include <osmocom/sgsn/debug.h> +#include <osmocom/sgsn/gprs_sgsn.h> +#include <osmocom/sgsn/gprs_gmm.h> +#include <osmocom/sgsn/gprs_llc.h> +#include <osmocom/sgsn/crc24.h> +#include <osmocom/sgsn/sgsn.h> +#include <osmocom/sgsn/gprs_llc_xid.h> +#include <osmocom/sgsn/gprs_sndcp_comp.h> +#include <osmocom/sgsn/gprs_sndcp.h> + +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; + } + /* set l3 layer & remove the fcs */ + msg->l3h = llhp.data; + msgb_l3trim(msg, llhp.data_len); + + /* 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) { + 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); +} diff --git a/src/gprs/gprs_llc_parse.c b/src/gprs/gprs_llc_parse.c new file mode 100644 index 000000000..1d97004e0 --- /dev/null +++ b/src/gprs/gprs_llc_parse.c @@ -0,0 +1,250 @@ +/* GPRS LLC protocol implementation as per 3GPP TS 04.64 */ + +/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU 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 <http://www.gnu.org/licenses/>. + * + */ + +#include <errno.h> +#include <stdint.h> + +#include <osmocom/core/msgb.h> +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/timer.h> +#include <osmocom/core/talloc.h> +#include <osmocom/gprs/gprs_bssgp.h> + +#include <osmocom/sgsn/debug.h> +#include <osmocom/sgsn/gprs_sgsn.h> +#include <osmocom/sgsn/gprs_gmm.h> +#include <osmocom/sgsn/gprs_llc.h> +#include <osmocom/sgsn/crc24.h> + +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; +} diff --git a/src/gprs/gprs_llc_vty.c b/src/gprs/gprs_llc_vty.c new file mode 100644 index 000000000..418be8348 --- /dev/null +++ b/src/gprs/gprs_llc_vty.c @@ -0,0 +1,115 @@ +/* VTY interface for our GPRS LLC implementation */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU 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 <http://www.gnu.org/licenses/>. + * + */ + +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <stdint.h> +#include <time.h> + +#include <arpa/inet.h> + +#include <osmocom/core/msgb.h> +#include <osmocom/gsm/tlv.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/select.h> +#include <osmocom/core/rate_ctr.h> +#include <osmocom/sgsn/debug.h> +#include <osmocom/sgsn/signal.h> +#include <osmocom/sgsn/gprs_llc.h> + +#include <osmocom/vty/vty.h> +#include <osmocom/vty/command.h> + +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}; + osmo_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; +} diff --git a/src/gprs/gprs_llc_xid.c b/src/gprs/gprs_llc_xid.c new file mode 100644 index 000000000..de60e49ee --- /dev/null +++ b/src/gprs/gprs_llc_xid.c @@ -0,0 +1,281 @@ +/* GPRS LLC XID field encoding/decoding as per 3GPP TS 44.064 */ + +/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <errno.h> + +#include <osmocom/core/utils.h> +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/talloc.h> + +#include <osmocom/sgsn/debug.h> +#include <osmocom/sgsn/gprs_llc.h> +#include <osmocom/sgsn/sgsn.h> +#include <osmocom/sgsn/gprs_llc_xid.h> + +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); + } + } +} diff --git a/src/gprs/gprs_sgsn.c b/src/gprs/gprs_sgsn.c new file mode 100644 index 000000000..01f039a66 --- /dev/null +++ b/src/gprs/gprs_sgsn.c @@ -0,0 +1,996 @@ +/* GPRS SGSN functionality */ + +/* (C) 2009 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU 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 <http://www.gnu.org/licenses/>. + * + */ + +#include <stdint.h> + +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/timer.h> +#include <osmocom/core/rate_ctr.h> +#include <osmocom/core/stats.h> +#include <osmocom/core/backtrace.h> +#include <osmocom/gprs/gprs_ns.h> +#include <osmocom/gprs/gprs_bssgp.h> +#include <osmocom/gsm/protocol/gsm_04_08_gprs.h> +#include <osmocom/gsm/apn.h> +#include <osmocom/gsm/gsm_utils.h> +#include <osmocom/gsm/gsup.h> + +#include <osmocom/sgsn/gprs_subscriber.h> +#include <osmocom/sgsn/debug.h> +#include <osmocom/sgsn/gprs_sgsn.h> +#include <osmocom/sgsn/sgsn.h> +#include <osmocom/sgsn/gprs_gmm.h> +#include <osmocom/sgsn/gprs_utils.h> +#include <osmocom/sgsn/signal.h> +#include <osmocom/sgsn/gprs_gmm_attach.h> +#include <osmocom/sgsn/gprs_llc.h> + +#include <pdp.h> + +#include <time.h> + +#include "../../bscconfig.h" + +#if BUILD_IU +#include <osmocom/ranap/iu_client.h> +#endif + +#define GPRS_LLME_CHECK_TICK 30 + +extern struct sgsn_instance *sgsn; +extern void *tall_sgsn_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_sgsn_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_sgsn_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; + } + ctx->gmm_att_req.fsm = osmo_fsm_inst_alloc(&gmm_attach_req_fsm, ctx, ctx, LOGL_DEBUG, "gb_gmm_req"); + 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_sgsn_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; + } + ctx->gmm_att_req.fsm = osmo_fsm_inst_alloc(&gmm_attach_req_fsm, ctx, ctx, LOGL_DEBUG, "gb_gmm_req"); + + /* 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); + } + + if (mm->gmm_att_req.fsm) + gmm_att_req_free(mm); + + 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, + struct sgsn_ggsn_ctx *ggsn, + uint8_t nsapi) +{ + struct sgsn_pdp_ctx *pdp; + + pdp = sgsn_pdp_ctx_by_nsapi(mm, nsapi); + if (pdp) + return NULL; + + pdp = talloc_zero(tall_sgsn_ctx, struct sgsn_pdp_ctx); + if (!pdp) + return NULL; + + pdp->mm = mm; + pdp->ggsn = ggsn; + 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); + sgsn_ggsn_ctx_add_pdp(pdp->ggsn, pdp); + 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 */ + pdp_ctx_detach_mm_ctx(pdp); + if (pdp->ggsn) + 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); + if (pdp->ggsn) + sgsn_ggsn_ctx_remove_pdp(pdp->ggsn, pdp); + 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; + } + + talloc_free(pdp); +} + +void sgsn_ggsn_ctx_check_echo_timer(struct sgsn_ggsn_ctx *ggc) +{ + if (llist_empty(&ggc->pdp_list) || ggc->echo_interval <= 0) { + if (osmo_timer_pending(&ggc->echo_timer)) + osmo_timer_del(&ggc->echo_timer); + } else { + if (!osmo_timer_pending(&ggc->echo_timer)) + osmo_timer_schedule(&ggc->echo_timer, ggc->echo_interval, 0); + } +} + +/* GGSN contexts */ +static void echo_timer_cb(void *data) +{ + struct sgsn_ggsn_ctx *ggc = (struct sgsn_ggsn_ctx *) data; + sgsn_ggsn_echo_req(ggc); + osmo_timer_schedule(&ggc->echo_timer, ggc->echo_interval, 0); +} + +struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_alloc(uint32_t id) +{ + struct sgsn_ggsn_ctx *ggc; + + ggc = talloc_zero(tall_sgsn_ctx, struct sgsn_ggsn_ctx); + if (!ggc) + return NULL; + + ggc->id = id; + ggc->gtp_version = 1; + ggc->remote_restart_ctr = -1; + ggc->echo_interval = -1; + /* if we are called from config file parse, this gsn doesn't exist yet */ + ggc->gsn = sgsn->gsn; + INIT_LLIST_HEAD(&ggc->pdp_list); + osmo_timer_setup(&ggc->echo_timer, echo_timer_cb, ggc); + llist_add(&ggc->list, &sgsn_ggsn_ctxts); + + return ggc; +} + +void sgsn_ggsn_ctx_free(struct sgsn_ggsn_ctx *ggc) +{ + OSMO_ASSERT(llist_empty(&ggc->pdp_list)); + 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_sgsn_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; +} + +void sgsn_ggsn_ctx_drop_pdp(struct sgsn_pdp_ctx *pctx) +{ + /* the MM context can be deleted while the GGSN is not reachable or + * if has been crashed. */ + if (pctx->mm && pctx->mm->gmm_state == GMM_REGISTERED_NORMAL) { + gsm48_tx_gsm_deact_pdp_req(pctx, GSM_CAUSE_NET_FAIL, true); + sgsn_ggsn_ctx_remove_pdp(pctx->ggsn, pctx); + } else { + /* FIXME: GPRS paging in case MS is SUSPENDED */ + LOGPDPCTXP(LOGL_NOTICE, pctx, "Hard-dropping PDP ctx due to GGSN " + "recovery\n"); + /* FIXME: how to tell this to libgtp? */ + sgsn_pdp_ctx_free(pctx); + } +} + +/* High-level function to be called in case a GGSN has disappeared or + * otherwise lost state (recovery procedure). It will detach all related pdp ctx + * from a ggsn and communicate deact to MS. Optionally (!NULL), one pdp ctx can + * be kept alive to allow handling later message which contained the Recovery IE. */ +int sgsn_ggsn_ctx_drop_all_pdp_except(struct sgsn_ggsn_ctx *ggsn, struct sgsn_pdp_ctx *except) +{ + int num = 0; + + struct sgsn_pdp_ctx *pdp, *pdp2; + llist_for_each_entry_safe(pdp, pdp2, &ggsn->pdp_list, ggsn_list) { + if (pdp == except) + continue; + sgsn_ggsn_ctx_drop_pdp(pdp); + num++; + } + + return num; +} + +void sgsn_ggsn_ctx_add_pdp(struct sgsn_ggsn_ctx *ggc, struct sgsn_pdp_ctx *pdp) +{ + sgsn_ggsn_ctx_check_echo_timer(ggc); + + llist_add(&pdp->ggsn_list, &ggc->pdp_list); +} +void sgsn_ggsn_ctx_remove_pdp(struct sgsn_ggsn_ctx *ggc, struct sgsn_pdp_ctx *pdp) +{ + llist_del(&pdp->ggsn_list); + sgsn_ggsn_ctx_check_echo_timer(ggc); + if (pdp->destroy_ggsn) + sgsn_ggsn_ctx_free(pdp->ggsn); + pdp->ggsn = NULL; + /* Drop references to libgtp since the conn is down */ + if (pdp->lib) + pdp_freepdp(pdp->lib); + pdp->lib = NULL; +} + +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 = osmo_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); +} + +struct sgsn_instance *sgsn_instance_alloc(void *talloc_ctx) +{ + struct sgsn_instance *inst; + inst = talloc_zero(talloc_ctx, struct sgsn_instance); + inst->cfg.gtp_statedir = talloc_strdup(inst, "./"); + inst->cfg.auth_policy = SGSN_AUTH_POLICY_CLOSED; + inst->cfg.gsup_server_port = OSMO_GSUP_PORT; + return inst; +} + +void sgsn_inst_init(struct sgsn_instance *sgsn) +{ + osmo_timer_setup(&sgsn->llme_timer, sgsn_llme_check_cb, NULL); + osmo_timer_schedule(&sgsn->llme_timer, GPRS_LLME_CHECK_TICK, 0); +} diff --git a/src/gprs/gprs_sndcp.c b/src/gprs/gprs_sndcp.c new file mode 100644 index 000000000..f0239cb66 --- /dev/null +++ b/src/gprs/gprs_sndcp.c @@ -0,0 +1,1257 @@ +/* GPRS SNDCP protocol implementation as per 3GPP TS 04.65 */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * (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 <http://www.gnu.org/licenses/>. + * + */ + +#include <errno.h> +#include <stdint.h> +#include <stdbool.h> + +#include <osmocom/core/msgb.h> +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/timer.h> +#include <osmocom/core/talloc.h> +#include <osmocom/gprs/gprs_bssgp.h> + +#include <osmocom/sgsn/debug.h> +#include <osmocom/sgsn/gprs_llc.h> +#include <osmocom/sgsn/sgsn.h> +#include <osmocom/sgsn/gprs_sndcp.h> +#include <osmocom/sgsn/gprs_llc_xid.h> +#include <osmocom/sgsn/gprs_sndcp_xid.h> +#include <osmocom/sgsn/gprs_sndcp_pcomp.h> +#include <osmocom/sgsn/gprs_sndcp_dcomp.h> +#include <osmocom/sgsn/gprs_sndcp_comp.h> + +#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; + + 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.pcomp = 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.dcomp = 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.pcomp) { + 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.dcomp) { + 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; +} diff --git a/src/gprs/gprs_sndcp_comp.c b/src/gprs/gprs_sndcp_comp.c new file mode 100644 index 000000000..0b4c67cd4 --- /dev/null +++ b/src/gprs/gprs_sndcp_comp.c @@ -0,0 +1,331 @@ +/* GPRS SNDCP header compression entity management tools */ + +/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <math.h> +#include <errno.h> +#include <stdbool.h> + +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/utils.h> + +#include <osmocom/sgsn/debug.h> +#include <osmocom/sgsn/gprs_sndcp_xid.h> +#include <osmocom/sgsn/gprs_sndcp_comp.h> +#include <osmocom/sgsn/gprs_sndcp_pcomp.h> +#include <osmocom/sgsn/gprs_sndcp_dcomp.h> + +/* 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)); + comp_entity->algo.pcomp = comp_field->algo.pcomp; + } 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)); + comp_entity->algo.pcomp = comp_field->algo.pcomp; + } 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)); + comp_entity->algo.pcomp = comp_field->algo.pcomp; + } 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)); + comp_entity->algo.dcomp = comp_field->algo.dcomp; + } 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)); + comp_entity->algo.dcomp = comp_field->algo.dcomp; + } 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); + } + + /* 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); + + /* Create an algorithm specific compression context */ + switch (comp_entity->compclass) { + case SNDCP_XID_PROTOCOL_COMPRESSION: + if (gprs_sndcp_pcomp_init(ctx, comp_entity, comp_field) != 0) { + talloc_free(comp_entity); + comp_entity = NULL; + } + break; + case SNDCP_XID_DATA_COMPRESSION: + if (gprs_sndcp_dcomp_init(ctx, comp_entity, comp_field) != 0) { + talloc_free(comp_entity); + comp_entity = NULL; + } + break; + default: + /* comp_field is somehow invalid */ + OSMO_ASSERT(false); + } + + /* 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]; +} diff --git a/src/gprs/gprs_sndcp_dcomp.c b/src/gprs/gprs_sndcp_dcomp.c new file mode 100644 index 000000000..00e40a7ef --- /dev/null +++ b/src/gprs/gprs_sndcp_dcomp.c @@ -0,0 +1,358 @@ +/* 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 <http://www.gnu.org/licenses/>. + * + */ + +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <math.h> +#include <errno.h> +#include <stdbool.h> + +#include <osmocom/core/utils.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/talloc.h> +#include <osmocom/gsm/tlv.h> + +#include <osmocom/sgsn/gprs_llc.h> +#include <osmocom/sgsn/sgsn.h> +#include <osmocom/sgsn/gprs_sndcp_xid.h> +#include <osmocom/sgsn/v42bis.h> +#include <osmocom/sgsn/v42bis_private.h> +#include <osmocom/sgsn/debug.h> +#include <osmocom/sgsn/gprs_sndcp_comp.h> +#include <osmocom/sgsn/gprs_sndcp_dcomp.h> + +/* 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.dcomp == 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.dcomp == 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.dcomp == 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.dcomp == 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; +} diff --git a/src/gprs/gprs_sndcp_pcomp.c b/src/gprs/gprs_sndcp_pcomp.c new file mode 100644 index 000000000..5f7f22aed --- /dev/null +++ b/src/gprs/gprs_sndcp_pcomp.c @@ -0,0 +1,282 @@ +/* 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 <http://www.gnu.org/licenses/>. + * + */ + +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <math.h> +#include <errno.h> +#include <stdbool.h> + +#include <osmocom/core/utils.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/talloc.h> +#include <osmocom/gsm/tlv.h> + +#include <osmocom/sgsn/gprs_llc.h> +#include <osmocom/sgsn/sgsn.h> +#include <osmocom/sgsn/gprs_sndcp_xid.h> +#include <osmocom/sgsn/slhc.h> +#include <osmocom/sgsn/debug.h> +#include <osmocom/sgsn/gprs_sndcp_comp.h> +#include <osmocom/sgsn/gprs_sndcp_pcomp.h> + +/* 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.pcomp == 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.pcomp == 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.pcomp == 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.pcomp == 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; +} diff --git a/src/gprs/gprs_sndcp_vty.c b/src/gprs/gprs_sndcp_vty.c new file mode 100644 index 000000000..0994a4cd2 --- /dev/null +++ b/src/gprs/gprs_sndcp_vty.c @@ -0,0 +1,70 @@ +/* VTY interface for our GPRS SNDCP implementation */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU 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 <http://www.gnu.org/licenses/>. + * + */ + +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <stdint.h> + +#include <arpa/inet.h> + +#include <osmocom/core/msgb.h> +#include <osmocom/gsm/tlv.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/select.h> +#include <osmocom/core/rate_ctr.h> +#include <osmocom/sgsn/debug.h> +#include <osmocom/sgsn/signal.h> +#include <osmocom/sgsn/gprs_llc.h> +#include <osmocom/sgsn/gprs_sndcp.h> + +#include <osmocom/vty/vty.h> +#include <osmocom/vty/command.h> + +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; +} diff --git a/src/gprs/gprs_sndcp_xid.c b/src/gprs/gprs_sndcp_xid.c new file mode 100644 index 000000000..a19f64514 --- /dev/null +++ b/src/gprs/gprs_sndcp_xid.c @@ -0,0 +1,1901 @@ +/* GPRS SNDCP XID field encoding/decoding as per 3GPP TS 44.065 */ + +/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <math.h> +#include <errno.h> + +#include <osmocom/core/utils.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/talloc.h> +#include <osmocom/gsm/tlv.h> + +#include <osmocom/sgsn/debug.h> +#include <osmocom/sgsn/gprs_llc.h> +#include <osmocom/sgsn/sgsn.h> +#include <osmocom/sgsn/gprs_sndcp_xid.h> + +/* 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 */ + union gprs_sndcp_comp_algo algo; + enum gprs_sndcp_xid_param_types compclass; +}; + +/* 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; + enum gprs_sndcp_xid_param_types compclass; + + 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 */ + compclass = gprs_sndcp_get_compression_class(comp_field); + switch (compclass) { + case SNDCP_XID_PROTOCOL_COMPRESSION: + OSMO_ASSERT(comp_field->algo.pcomp >= 0 && comp_field->algo.pcomp <= 0x1f); + break; + case SNDCP_XID_DATA_COMPRESSION: + OSMO_ASSERT(comp_field->algo.dcomp >= 0 && comp_field->algo.dcomp <= 0x1f); + break; + default: + OSMO_ASSERT(false); + } + + /* 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) { + if (compclass == SNDCP_XID_PROTOCOL_COMPRESSION) + *dst |= comp_field->algo.pcomp & 0x1F; + else + *dst |= comp_field->algo.dcomp & 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?) */ +enum gprs_sndcp_xid_param_types 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 SNDCP_XID_INVALID_COMPRESSION; +} + +/* 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 protocol compression algorithm identfier by entity ID */ +static enum gprs_sndcp_hdr_comp_algo lookup_algorithm_identifier_pcomp(int entity, + const struct entity_algo_table *lt, + unsigned int lt_len) +{ + int i; + + if (!lt) + return -1; + + for (i = 0; i < lt_len; i++) { + if ((lt[i].entity == entity) + && (lt[i].compclass == SNDCP_XID_PROTOCOL_COMPRESSION)) + return lt[i].algo.pcomp; + } + + return SNDCP_XID_INVALID_COMPRESSION; +} + +/* Lookup a data compression algorithm identfier by entity ID */ +static enum gprs_sndcp_data_comp_algo lookup_algorithm_identifier_dcomp(int entity, + const struct entity_algo_table *lt, + unsigned int lt_len) +{ + int i; + + if (!lt) + return -1; + + for (i = 0; i < lt_len; i++) { + if ((lt[i].entity == entity) + && (lt[i].compclass == SNDCP_XID_DATA_COMPRESSION)) + return lt[i].algo.dcomp; + } + + return SNDCP_XID_INVALID_COMPRESSION; +} + +/* 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, enum gprs_sndcp_xid_param_types compclass) +{ + int src_counter = 0; + int i; + + if (comp_field->p) { + /* Determine the number of expected PCOMP/DCOMP values */ + switch (compclass) { + case SNDCP_XID_PROTOCOL_COMPRESSION: + /* For protocol compression */ + switch (comp_field->algo.pcomp) { + 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; + } + break; + case SNDCP_XID_DATA_COMPRESSION: + /* For data compression */ + switch (comp_field->algo.dcomp) { + 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; + } + break; + 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.pcomp) { + 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.dcomp) { + 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, + enum gprs_sndcp_xid_param_types 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) { + switch (compclass) { + case SNDCP_XID_PROTOCOL_COMPRESSION: + comp_field->algo.pcomp = (*src) & 0x1F; + break; + case SNDCP_XID_DATA_COMPRESSION: + comp_field->algo.dcomp = (*src) & 0x1F; + break; + default: + return -EINVAL; + } + src_counter++; + src++; + } + /* Alternatively take the information from the lookup table */ + else { + switch (compclass) { + case SNDCP_XID_PROTOCOL_COMPRESSION: + comp_field->algo.pcomp = + lookup_algorithm_identifier_pcomp(comp_field->entity, lt, lt_len); + break; + case SNDCP_XID_DATA_COMPRESSION: + comp_field->algo.dcomp = + lookup_algorithm_identifier_dcomp(comp_field->entity, lt, lt_len); + break; + default: + return -EINVAL; + } + } + + /* 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; + enum gprs_sndcp_xid_param_types compclass; + + if (!comp_fields) + return -EINVAL; + if (!lt) + return -EINVAL; + + memset(lt, 0, sizeof(*lt)); + + llist_for_each_entry(comp_field, comp_fields, list) { + compclass = gprs_sndcp_get_compression_class(comp_field); + switch (compclass) { + case SNDCP_XID_PROTOCOL_COMPRESSION: + lt[i].algo.pcomp = comp_field->algo.pcomp; + break; + case SNDCP_XID_DATA_COMPRESSION: + lt[i].algo.dcomp = comp_field->algo.dcomp; + break; + case SNDCP_XID_INVALID_COMPRESSION: + continue; + default: + memset(lt, 0, sizeof(*lt)); + return -EINVAL; + } + lt[i].compclass = compclass; + lt[i].entity = comp_field->entity; + 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) +{ + enum gprs_sndcp_xid_param_types compclass; + + compclass = gprs_sndcp_get_compression_class(comp_field_dst); + if (compclass == SNDCP_XID_INVALID_COMPRESSION) + 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.pcomp) { + 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.dcomp) { + 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; + enum gprs_sndcp_xid_param_types compclass; + + OSMO_ASSERT(comp_fields); + + llist_for_each_entry(comp_field, comp_fields, list) { + compclass = gprs_sndcp_get_compression_class(comp_field); + LOGP(DSNDCP, logl, "SNDCP-XID:\n"); + LOGP(DSNDCP, logl, "struct gprs_sndcp_comp_field {\n"); + LOGP(DSNDCP, logl, " entity=%d;\n", comp_field->entity); + switch (compclass) { + case SNDCP_XID_PROTOCOL_COMPRESSION: + LOGP(DSNDCP, logl, " algo=%d;\n", comp_field->algo.pcomp); + break; + case SNDCP_XID_DATA_COMPRESSION: + LOGP(DSNDCP, logl, " algo=%d;\n", comp_field->algo.dcomp); + break; + default: + LOGP(DSNDCP, logl, " algo invalid!\n"); + break; + } + 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]); + } + + switch (compclass) { + case SNDCP_XID_PROTOCOL_COMPRESSION: + dump_pcomp_params(comp_field, logl); + break; + case SNDCP_XID_DATA_COMPRESSION: + dump_dcomp_params(comp_field, logl); + break; + default: + LOGP(DSNDCP, logl, " compression algorithm invalid!\n"); + break; + } + + LOGP(DSNDCP, logl, "}\n"); + } + +} diff --git a/src/gprs/gprs_subscriber.c b/src/gprs/gprs_subscriber.c new file mode 100644 index 000000000..4ab45c288 --- /dev/null +++ b/src/gprs/gprs_subscriber.c @@ -0,0 +1,943 @@ +/* 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 <http://www.gnu.org/licenses/>. + * + */ + +#include <osmocom/gsm/protocol/gsm_04_08_gprs.h> +#include <osmocom/gsm/gsup.h> +#include <osmocom/gsm/apn.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/logging.h> +#include <osmocom/sgsn/gprs_subscriber.h> +#include <osmocom/gsupclient/gsup_client.h> + +#include <osmocom/sgsn/sgsn.h> +#include <osmocom/sgsn/gprs_sgsn.h> +#include <osmocom/sgsn/gprs_gmm.h> +#include <osmocom/sgsn/gprs_utils.h> + +#include <osmocom/sgsn/debug.h> + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <limits.h> + +#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_sgsn_ctx; + +LLIST_HEAD(_gprs_subscribers); +struct llist_head * const gprs_subscribers = &_gprs_subscribers; + +static int gsup_read_cb(struct osmo_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 = osmo_gsup_client_create( + sgi, + "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 osmo_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_sgsn_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 = osmo_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 osmo_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); + + if (pdp_info->qos_enc) { + memcpy(&pdp_data->qos_subscribed[0], 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); + sdata->error_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; +} diff --git a/src/gprs/gprs_utils.c b/src/gprs/gprs_utils.c new file mode 100644 index 000000000..13641c1e1 --- /dev/null +++ b/src/gprs/gprs_utils.c @@ -0,0 +1,164 @@ +/* GPRS utility functions */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * (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 <http://www.gnu.org/licenses/>. + * + */ +#include <osmocom/sgsn/gprs_utils.h> + +#include <osmocom/core/msgb.h> +#include <osmocom/gprs/gprs_ns.h> + +#include <osmocom/gsm/protocol/gsm_04_08_gprs.h> +#include <osmocom/gsm/protocol/gsm_04_08.h> +#include <osmocom/gsm/gsm48.h> + +#include <string.h> + +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); +} diff --git a/src/gprs/gtphub.c b/src/gprs/gtphub.c new file mode 100644 index 000000000..ca5857b39 --- /dev/null +++ b/src/gprs/gtphub.c @@ -0,0 +1,2960 @@ +/* GTP Hub Implementation */ + +/* (C) 2015 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <string.h> +#include <errno.h> +#include <inttypes.h> +#include <time.h> +#include <limits.h> +#include <unistd.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <gtp.h> +#include <gtpie.h> + +#include <osmocom/sgsn/gtphub.h> +#include <osmocom/sgsn/debug.h> +#include <osmocom/sgsn/gprs_utils.h> + +#include <osmocom/core/utils.h> +#include <osmocom/core/logging.h> +#include <osmocom/core/socket.h> +#include <osmocom/core/rate_ctr.h> +#include <osmocom/core/stats.h> + +#include <osmocom/gsm/apn.h> + + +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 (<loglevel is debug>) + (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(osmo_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; +} diff --git a/src/gprs/gtphub_ares.c b/src/gprs/gtphub_ares.c new file mode 100644 index 000000000..87dc860c4 --- /dev/null +++ b/src/gprs/gtphub_ares.c @@ -0,0 +1,220 @@ +/* GTP Hub Implementation */ + +/* (C) 2015 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <string.h> +#include <unistd.h> + +#include <osmocom/sgsn/gtphub.h> +#include <osmocom/sgsn/debug.h> + +#include <osmocom/core/utils.h> +#include <osmocom/gsm/apn.h> + +/* TODO split GRX ares from sgsn into a separate struct and allow use without + * globals. */ +#include <osmocom/sgsn/sgsn.h> +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; +} diff --git a/src/gprs/gtphub_main.c b/src/gprs/gtphub_main.c new file mode 100644 index 000000000..0369ed77b --- /dev/null +++ b/src/gprs/gtphub_main.c @@ -0,0 +1,403 @@ +/* GTP Hub main program */ + +/* (C) 2015 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <unistd.h> +#include <signal.h> +#include <string.h> +#include <errno.h> +#include <inttypes.h> +#include <sys/stat.h> + +#define _GNU_SOURCE +#include <getopt.h> + +#include <osmocom/core/signal.h> +#include <osmocom/core/application.h> +#include <osmocom/core/logging.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/rate_ctr.h> + +#include <osmocom/vty/logging.h> +#include <osmocom/vty/telnet_interface.h> +#include <osmocom/vty/ports.h> +#include <osmocom/vty/misc.h> + +#include <osmocom/sgsn/debug.h> +#include <osmocom/sgsn/gtphub.h> +#include <osmocom/sgsn/vty.h> + +#include "../../bscconfig.h" + +#if BUILD_IU +#include <osmocom/sigtran/osmo_ss7.h> +#endif + +extern void *osmo_gtphub_ctx; +void *tall_sgsn_ctx; + +const char *gtphub_copyright = + "Copyright (C) 2015 sysmocom s.f.m.c GmbH <info@sysmocom.de>\r\n" + "License AGPLv3+: GNU AGPL version 2 or later <http://gnu.org/licenses/agpl-3.0.html>\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 <cat> 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 <path> The config file to use [%s].\n", + ccfg->config_file); + printf(" -e,--log-level <nr> Set a global log level.\n"); + printf(" -r,--restart-file <path> File for counting restarts [%s].\n", + ccfg->restart_counter_file); + printf(" -V,--version Print the version number.\n"); +} + +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'}, + { "version", 0, 0, 'V' }, + {NULL, 0, 0, 0} + }; + + c = getopt_long(argc, argv, "hd:Dc:sTe:r:V", + 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; + case 'V': + print_version(1); + exit(EXIT_SUCCESS); + 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); +} diff --git a/src/gprs/gtphub_sock.c b/src/gprs/gtphub_sock.c new file mode 100644 index 000000000..1acd5a624 --- /dev/null +++ b/src/gprs/gtphub_sock.c @@ -0,0 +1,60 @@ +/* GTP Hub Implementation */ + +/* (C) 2015 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <osmocom/sgsn/gtphub.h> +#include <osmocom/sgsn/debug.h> + +/* 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; +} + diff --git a/src/gprs/gtphub_vty.c b/src/gprs/gtphub_vty.c new file mode 100644 index 000000000..abc08fd69 --- /dev/null +++ b/src/gprs/gtphub_vty.c @@ -0,0 +1,613 @@ +/* (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 <http://www.gnu.org/licenses/>. + * + */ + +#include <string.h> +#include <inttypes.h> + +#include <ares.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <osmocom/core/talloc.h> +#include <osmocom/vty/command.h> +#include <osmocom/vty/misc.h> + +#include <osmocom/sgsn/vty.h> +#include <osmocom/sgsn/gtphub.h> + +/* TODO split GRX ares from sgsn into a separate struct and allow use without + * globals. */ +#include <osmocom/sgsn/sgsn.h> +extern struct sgsn_instance *sgsn; +extern void *tall_sgsn_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_sgsn_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=<hex>: SGSN <-> GGSN (expiry in minutes), with each:%s" + " <IP-Ctrl>[/<IP-User>] (TEI C=<TEI-Ctrl-hex> U=<TEI-User-hex>)%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; +} diff --git a/src/gprs/sgsn_ares.c b/src/gprs/sgsn_ares.c new file mode 100644 index 000000000..ba168d62e --- /dev/null +++ b/src/gprs/sgsn_ares.c @@ -0,0 +1,175 @@ +/* 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 <http://www.gnu.org/licenses/>. + * + */ + +#include <osmocom/sgsn/sgsn.h> +#include <osmocom/sgsn/debug.h> + +#include <netdb.h> + +extern void *tall_sgsn_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_sgsn_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_sgsn_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); diff --git a/src/gprs/sgsn_auth.c b/src/gprs/sgsn_auth.c new file mode 100644 index 000000000..694bece7a --- /dev/null +++ b/src/gprs/sgsn_auth.c @@ -0,0 +1,313 @@ +/* MS authorization and subscriber data handling */ + +/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU 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 <http://www.gnu.org/licenses/>. + * + */ + +#include <osmocom/gsm/protocol/gsm_04_08_gprs.h> +#include <osmocom/core/utils.h> +#include <osmocom/sgsn/sgsn.h> +#include <osmocom/sgsn/gprs_sgsn.h> +#include <osmocom/sgsn/gprs_gmm.h> +#include <osmocom/sgsn/gprs_subscriber.h> +#include <osmocom/sgsn/debug.h> + +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; +} diff --git a/src/gprs/sgsn_cdr.c b/src/gprs/sgsn_cdr.c new file mode 100644 index 000000000..93ed0af04 --- /dev/null +++ b/src/gprs/sgsn_cdr.c @@ -0,0 +1,301 @@ +/* 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 <http://www.gnu.org/licenses/>. + * + */ + +#include <osmocom/ctrl/control_if.h> + +#include <osmocom/sgsn/sgsn.h> +#include <osmocom/sgsn/signal.h> +#include <osmocom/sgsn/gprs_utils.h> +#include <osmocom/sgsn/debug.h> +#include <osmocom/gsm/apn.h> + +#include <osmocom/sgsn/vty.h> + +#include <gtp.h> +#include <pdp.h> + +#include <arpa/inet.h> + +#include <time.h> + +#include <stdio.h> +#include <inttypes.h> + +/* 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(sgsn_addr, 0, sizeof(sgsn_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)); + + osmo_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: + osmo_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; +} diff --git a/src/gprs/sgsn_ctrl.c b/src/gprs/sgsn_ctrl.c new file mode 100644 index 000000000..dc5ae79ae --- /dev/null +++ b/src/gprs/sgsn_ctrl.c @@ -0,0 +1,68 @@ +/* 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 <http://www.gnu.org/licenses/>. + * + */ + +#include <osmocom/ctrl/control_if.h> +#include <osmocom/ctrl/control_cmd.h> +#include <osmocom/sgsn/gprs_sgsn.h> +#include <osmocom/sgsn/sgsn.h> +#include <osmocom/sgsn/debug.h> + +#include <pdp.h> + +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); +} diff --git a/src/gprs/sgsn_libgtp.c b/src/gprs/sgsn_libgtp.c new file mode 100644 index 000000000..478d4020c --- /dev/null +++ b/src/gprs/sgsn_libgtp.c @@ -0,0 +1,908 @@ +/* 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 <laforge@gnumonks.org> + * (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 <http://www.gnu.org/licenses/>. + * + */ + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <errno.h> +#include <signal.h> +#include <sys/fcntl.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include "bscconfig.h" + +#include <osmocom/core/talloc.h> +#include <osmocom/core/select.h> +#include <osmocom/core/rate_ctr.h> +#include <osmocom/gprs/gprs_bssgp.h> +#include <osmocom/gsm/protocol/gsm_04_08_gprs.h> + +#include <osmocom/sgsn/signal.h> +#include <osmocom/sgsn/debug.h> +#include <osmocom/sgsn/sgsn.h> +#include <osmocom/sgsn/gprs_llc.h> +#include <osmocom/sgsn/gprs_sgsn.h> +#include <osmocom/sgsn/gprs_gmm.h> +#include <osmocom/sgsn/gprs_subscriber.h> +#include <osmocom/sgsn/gprs_sndcp.h> + +#ifdef BUILD_IU +#include <osmocom/ranap/iu_client.h> +#include <osmocom/ranap/ranap_ies_defs.h> +#endif + +#include <gtp.h> +#include <pdp.h> + +/* 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, ggsn, 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; + + //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 */ + if (TLVP_PRESENT(tp, GSM48_IE_GSM_APN)) { + 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); + } else { + pdp->apn_use.l = 0; + } + + /* Protocol Configuration Options from GMM */ + if (TLVP_PRESENT(tp, GSM48_IE_GSM_PROTO_CONF_OPT)) { + 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); + } else { + pdp->pco_req.l = 0; + } + + /* 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_INFO, pctx, "Delete PDP Context\n"); + + /* FIXME: decide if we need teardown or not ! */ + return gtp_delete_context_req2(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 = 0; + + LOGPDPCTXP(LOGL_INFO, pctx, "Received CREATE PDP CTX CONF, cause=%d(%s)\n", + cause, get_value_string(gtp_cause_strs, cause)); + + if (!pctx->mm) { + goto reject; + } + + /* 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 */ + if (pctx->mm) + 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); +} + +void sgsn_ggsn_echo_req(struct sgsn_ggsn_ctx *ggc) +{ + gtp_echo_req(ggc->gsn, ggc->gtp_version, NULL, &ggc->remote_addr); +} + +#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"); + } + + 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_recovery2(struct sockaddr_in *peer, struct pdp_t *pdp, uint8_t recovery) +{ + struct sgsn_ggsn_ctx *ggsn; + struct sgsn_pdp_ctx *pctx = NULL; + + 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) pdp=%p, " + "releasing all%s PDP contexts\n", + ggsn->remote_restart_ctr, recovery, pdp, pdp ? " other" : ""); + ggsn->remote_restart_ctr = recovery; + if (pdp) + pctx = pdp->priv; + sgsn_ggsn_ctx_drop_all_pdp_except(ggsn, pctx); + } + 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) +{ + struct sgsn_pdp_ctx *pctx = pdp->priv; + + LOGPDPX(DGPRS, LOGL_INFO, pdp, "Context %p was deleted\n", pdp); + + /* unlink the now non-existing library handle from the pdp + * context */ + pctx->lib = NULL; + + sgsn_ggsn_ctx_drop_pdp(pctx); + 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_recovery2(gsn, cb_recovery2); + 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; +} diff --git a/src/gprs/sgsn_main.c b/src/gprs/sgsn_main.c new file mode 100644 index 000000000..1c76d6f13 --- /dev/null +++ b/src/gprs/sgsn_main.c @@ -0,0 +1,531 @@ +/* GPRS SGSN Implementation */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * (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 <http://www.gnu.org/licenses/>. + * + */ + +#include <unistd.h> +#include <stdio.h> +#include <time.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <errno.h> +#include <signal.h> +#include <sys/fcntl.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <osmocom/core/application.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/select.h> +#include <osmocom/core/rate_ctr.h> +#include <osmocom/core/logging.h> +#include <osmocom/core/stats.h> + +#include <osmocom/gprs/gprs_ns.h> +#include <osmocom/gprs/gprs_bssgp.h> + +#include <osmocom/vty/telnet_interface.h> +#include <osmocom/vty/logging.h> +#include <osmocom/vty/stats.h> +#include <osmocom/vty/ports.h> +#include <osmocom/vty/misc.h> + +#include <osmocom/ctrl/control_vty.h> + +#include <osmocom/sgsn/signal.h> +#include <osmocom/sgsn/debug.h> +#include <osmocom/sgsn/vty.h> +#include <osmocom/sgsn/sgsn.h> +#include <osmocom/sgsn/gprs_llc.h> +#include <osmocom/sgsn/gprs_gmm.h> + +#include <osmocom/ctrl/control_if.h> +#include <osmocom/ctrl/ports.h> + +#include <gtp.h> + +#include "../../bscconfig.h" + +#if BUILD_IU +#include <osmocom/sigtran/osmo_ss7.h> +#include <osmocom/sigtran/protocol/m3ua.h> +#include <osmocom/ranap/iu_client.h> +#endif + +#define _GNU_SOURCE +#include <getopt.h> + +void *tall_sgsn_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 <http://gnu.org/licenses/agpl-3.0.html>\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" + + +struct sgsn_instance *sgsn; + +/* 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_sgsn_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': + osmo_talloc_replace_string(sgsn, &sgsn->config_file, optarg); + break; + case 'T': + log_set_print_timestamp(osmo_stderr_target, 1); + break; + case 'V': + print_version(1); + exit(0); + break; + case 'e': + log_set_log_level(osmo_stderr_target, atoi(optarg)); + break; + 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_sgsn_ctx = talloc_named_const(NULL, 0, "osmo_sgsn"); + sgsn = sgsn_instance_alloc(tall_sgsn_ctx); + msgb_talloc_ctx_init(tall_sgsn_ctx, 0); + vty_info.tall_ctx = tall_sgsn_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_sgsn_ctx, &gprs_log_info); + osmo_stats_init(tall_sgsn_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->cfg); + ctrl_vty_init(tall_sgsn_ctx); + +#if BUILD_IU + osmo_ss7_init(); + osmo_ss7_vty_init_asp(tall_sgsn_ctx); + osmo_sccp_vty_init(); +#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->config_file) { + /* No -c option was passed */ + if (file_exists(CONFIG_FILE_LEGACY) + && !file_exists(CONFIG_FILE_DEFAULT)) + osmo_talloc_replace_string(sgsn, &sgsn->config_file, CONFIG_FILE_LEGACY); + else + osmo_talloc_replace_string(sgsn, &sgsn->config_file, CONFIG_FILE_DEFAULT); + } + + rate_ctr_init(tall_sgsn_ctx); + + gprs_ns_set_log_ss(DNS); + bssgp_set_log_ss(DBSSGP); + + sgsn_nsi = gprs_ns_instantiate(&sgsn_ns_cb, tall_sgsn_ctx); + if (!sgsn_nsi) { + LOGP(DGPRS, LOGL_ERROR, "Unable to instantiate NS\n"); + exit(1); + } + bssgp_nsi = sgsn->cfg.nsi = sgsn_nsi; + + gprs_llc_init("/usr/local/lib/osmocom/crypt/"); + sgsn_rate_ctr_init(); + sgsn_inst_init(sgsn); + + gprs_ns_vty_init(bssgp_nsi); + bssgp_vty_init(); + gprs_llc_vty_init(); + gprs_sndcp_vty_init(); + sgsn_auth_init(); + sgsn_cdr_init(sgsn); + /* FIXME: register signal handler for SS_L_NS */ + + rc = sgsn_parse_config(sgsn->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_sgsn_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); + 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); + 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_sgsn_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_sgsn_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); +} diff --git a/src/gprs/sgsn_vty.c b/src/gprs/sgsn_vty.c new file mode 100644 index 000000000..601b3c59f --- /dev/null +++ b/src/gprs/sgsn_vty.c @@ -0,0 +1,1447 @@ +/* + * (C) 2010-2016 by Harald Welte <laforge@gnumonks.org> + * (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 <http://www.gnu.org/licenses/>. + * + */ + +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <time.h> +#include <inttypes.h> + +#include <osmocom/core/talloc.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/rate_ctr.h> +#include <osmocom/gsm/protocol/gsm_04_08_gprs.h> +#include <osmocom/gsm/apn.h> + +#include <osmocom/sgsn/debug.h> +#include <osmocom/sgsn/sgsn.h> +#include <osmocom/gprs/gprs_ns.h> +#include <osmocom/sgsn/gprs_gmm.h> +#include <osmocom/sgsn/gprs_sgsn.h> +#include <osmocom/sgsn/vty.h> +#include <osmocom/gsupclient/gsup_client.h> + +#include <osmocom/vty/command.h> +#include <osmocom/vty/vty.h> +#include <osmocom/vty/misc.h> +#include <osmocom/crypt/gprs_cipher.h> +#include <osmocom/abis/ipa.h> + +#include <osmocom/gprs/gprs_bssgp.h> + +#include <pdp.h> +#include <gtp.h> + +#include "../../bscconfig.h" + +#ifdef BUILD_IU +#include <osmocom/ranap/iu_client.h> +#endif + +extern void *tall_sgsn_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 (gctx->echo_interval != -1) + vty_out(vty, " ggsn %u echo-interval %"PRId32"%s", + gctx->id, gctx->echo_interval, VTY_NEWLINE); + else + vty_out(vty, " ggsn %u no echo-interval%s", + gctx->id, 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; +} + +/* Seee 3GPP TS 29.060 section 7.2.1 */ +DEFUN(cfg_ggsn_echo_interval, cfg_ggsn_echo_interval_cmd, + "ggsn <0-255> echo-interval <1-36000>", + GGSN_STR "GGSN Number\n" + "Send an echo request to this static GGSN every interval.\n" + "Interval between echo requests in seconds.\n") +{ + uint32_t id = atoi(argv[0]); + struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(id); + + ggc->echo_interval = atoi(argv[1]); + + if (ggc->echo_interval < 60) + vty_out(vty, "%% 3GPP TS 29.060 section states inteval should " \ + "not be lower than 60 seconds, use this value for " \ + "testing purposes only!%s", VTY_NEWLINE); + + sgsn_ggsn_ctx_check_echo_timer(ggc); + return CMD_SUCCESS; +} + +DEFUN(cfg_ggsn_no_echo_interval, cfg_ggsn_no_echo_interval_cmd, + "ggsn <0-255> no echo-interval", + GGSN_STR "GGSN Number\n" + NO_STR "Send an echo request to this static GGSN every interval.\n") +{ + uint32_t id = atoi(argv[0]); + struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(id); + + ggc->echo_interval = -1; + sgsn_ggsn_ctx_check_echo_timer(ggc); + + 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_sgsn_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] = { '0' }; + const char *op = argv[0]; + const char *imsi = imsi_sanitized; + size_t len = strnlen(argv[1], GSM23003_IMSI_MAX_DIGITS + 1); + int rc; + + /* Sanitize IMSI */ + if (len > GSM23003_IMSI_MAX_DIGITS) { + vty_out(vty, "%% IMSI (%s) too long (max %u digits) -- ignored!%s", + argv[1], GSM23003_IMSI_MAX_DIGITS, VTY_NEWLINE); + return CMD_WARNING; + } + + osmo_strlcpy(imsi_sanitized + GSM23003_IMSI_MAX_DIGITS - len, argv[1], + sizeof(imsi_sanitized) - (GSM23003_IMSI_MAX_DIGITS - len)); + + 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 <osmocom/sgsn/gprs_subscriber.h> + +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); +} + +#define RESET_SGSN_STATE_STR \ + "Remove all known subscribers, MM contexts and flush BSSGP queues." \ + " Useful only when running tests against the SGSN\n" + +DEFUN_HIDDEN(reset_sgsn_state, + reset_sgsn_state_cmd, + "reset sgsn state", + RESET_SGSN_STATE_STR RESET_SGSN_STATE_STR RESET_SGSN_STATE_STR) +{ + struct gprs_subscr *subscr, *tmp_subscr; + struct sgsn_mm_ctx *mm, *tmp_mm; + + llist_for_each_entry_safe(mm, tmp_mm, &sgsn_mm_ctxts, list) + { + gsm0408_gprs_access_cancelled(mm, SGSN_ERROR_CAUSE_NONE); + } + vty_out(vty, "Cancelled MM Ctx. %s", VTY_NEWLINE); + + llist_for_each_entry_safe(subscr, tmp_subscr, gprs_subscribers, entry) { + gprs_subscr_get(subscr); + gprs_subscr_cancel(subscr); + gprs_subscr_put(subscr); + } + vty_out(vty, "Removed all gprs subscribers.%s", VTY_NEWLINE); + + bssgp_flush_all_queues(); + vty_out(vty, "Flushed all BSSGPs queues.%s", VTY_NEWLINE); + + gtp_clear_queues(sgsn->gsn); + vty_out(vty, "Flushed rx & tx queus towards the GGSN.%s", VTY_NEWLINE); + + /* remove all queues to bssgp */ + return CMD_SUCCESS; +} + +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(ENABLE_NODE, &reset_sgsn_state_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_ggsn_echo_interval_cmd); + install_element(SGSN_NODE, &cfg_ggsn_no_echo_interval_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; +} diff --git a/src/gprs/slhc.c b/src/gprs/slhc.c new file mode 100644 index 000000000..20e571d48 --- /dev/null +++ b/src/gprs/slhc.c @@ -0,0 +1,813 @@ +/* + * 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 <string.h> +#include <stdio.h> +#include <errno.h> +#include <stdint.h> +#include <arpa/inet.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/talloc.h> +#include <osmocom/sgsn/slhc.h> +#include <osmocom/sgsn/debug.h> + +#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;i<ihl*2;i++) + { + temp = ((*iph) << 8)&0xFF00; + iph++; + temp |= (*iph)&0xFF; + iph++; + + accumulator+=temp; + if(accumulator>0xFFFF) + { + 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(isize<sizeof(struct iphdr)) + return isize; + + ip = (struct iphdr *) icp; + + /* Bail if this packet isn't TCP, or is an IP fragment */ + if (ip->protocol != 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); + } +} + diff --git a/src/gprs/v42bis.c b/src/gprs/v42bis.c new file mode 100644 index 000000000..0759cdf1c --- /dev/null +++ b/src/gprs/v42bis.c @@ -0,0 +1,767 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * v42bis.c + * + * Written by Steve Underwood <steveu@coppice.org> + * + * 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 <stdio.h> +#include <stdlib.h> +#include <inttypes.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <ctype.h> +#include <assert.h> + +#include <osmocom/sgsn/v42bis.h> +#include <osmocom/sgsn/v42bis_private.h> +#include <osmocom/sgsn/debug.h> +#include <osmocom/core/talloc.h> + + +#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 ------------------------------------------------------------*/ diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 000000000..035cfb010 --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,72 @@ +SUBDIRS = \ + gprs \ + gbproxy \ + gtphub \ + sgsn \ + 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 $@ diff --git a/tests/atlocal.in b/tests/atlocal.in new file mode 100644 index 000000000..c85448765 --- /dev/null +++ b/tests/atlocal.in @@ -0,0 +1,3 @@ +enable_sgsn_test='@found_libgtp_and_libcares@' +enable_oap_test='@found_libgtp_and_libcares@' +enable_gtphub_test='@found_libgtp_and_libcares@' diff --git a/tests/ctrl_test_runner.py b/tests/ctrl_test_runner.py new file mode 100755 index 000000000..57ef82eee --- /dev/null +++ b/tests/ctrl_test_runner.py @@ -0,0 +1,200 @@ +#!/usr/bin/env python2 + +# (C) 2013 by Jacob Erlbeck <jerlbeck@sysmocom.de> +# (C) 2014 by Holger Hans Peter Freyther +# based on vty_test_runner.py: +# (C) 2013 by Katerina Barone-Adesi <kat.obsc@gmail.com> +# (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 <http://www.gnu.org/licenses/>. + +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)) diff --git a/tests/gbproxy/Makefile.am b/tests/gbproxy/Makefile.am new file mode 100644 index 000000000..ef38fb626 --- /dev/null +++ b/tests/gbproxy/Makefile.am @@ -0,0 +1,50 @@ +AM_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) diff --git a/tests/gbproxy/gbproxy_test.c b/tests/gbproxy/gbproxy_test.c new file mode 100644 index 000000000..55776be8f --- /dev/null +++ b/tests/gbproxy/gbproxy_test.c @@ -0,0 +1,5077 @@ +/* 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 <jerlbeck@sysmocom.de> + */ + +#undef _GNU_SOURCE +#define _GNU_SOURCE + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <getopt.h> +#include <dlfcn.h> +#include <time.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include <osmocom/core/msgb.h> +#include <osmocom/core/application.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/logging.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/signal.h> +#include <osmocom/core/rate_ctr.h> +#include <osmocom/gsm/tlv.h> +#include <osmocom/gsm/gsm_utils.h> +#include <osmocom/gsm/protocol/gsm_04_08_gprs.h> +#include <osmocom/gprs/gprs_msgb.h> +#include <osmocom/gprs/gprs_ns.h> +#include <osmocom/gprs/gprs_bssgp.h> + +#include <osmocom/sgsn/gb_proxy.h> +#include <osmocom/sgsn/gprs_utils.h> +#include <osmocom/sgsn/gprs_llc.h> +#include <osmocom/sgsn/gprs_gb_parse.h> +#include <osmocom/sgsn/debug.h> + +#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_sgsn_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 = bssgp_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_sgsn_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_sgsn_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_sgsn_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_sgsn_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_sgsn_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_sgsn_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_sgsn_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_sgsn_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_sgsn_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_sgsn_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_sgsn_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_sgsn_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_sgsn_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_sgsn_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_sgsn_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_sgsn_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_sgsn_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(); +} + +/* See OS#3178 "gbproxy: failed to parse invalid BSSGP-UNITDATA message" */ +static void test_gbproxy_parse_bssgp_unitdata() +{ + const char *hex = "0000239401e155cfea000004088872f4801018009c4000800e000601c0416c4338"; + struct msgb *msg = msgb_alloc(1034, "bssgp_unitdata"); + struct gprs_gb_parse_context parse_ctx; + int rc; + + memset(&parse_ctx, 0, sizeof(parse_ctx)); + + OSMO_ASSERT(msg); + msgb_bssgph(msg) = msg->head; + msgb_put(msg, osmo_hexparse(hex, msg->head, msgb_tailroom(msg))); + + parse_ctx.to_bss = 0; + parse_ctx.peer_nsei = msgb_nsei(msg); + + rc = gprs_gb_parse_bssgp(msg->data, msg->len, &parse_ctx); + if (!rc) + fprintf(stderr, "%s: Test passed; Failed to parse invalid message %s\n", __func__, msgb_hexdump(msg)); + else + fprintf(stderr, "%s: Test failed; invalid message was accepted by parser: %s\n", __func__, msgb_hexdump(msg)); + + OSMO_ASSERT(!rc); + + /* Manually decoded message according to: + ETSI TS 148 018 V10.6.0 (2012 07) 96 + 3GPP TS 48.018 version 10.6.0 Release 10 + Table 10.2.2: UL-UNITDATA PDU content + + 00 - PDU type UL-UNITDATA (ok) + + 11.3.35 Temporary logical link Identity (TLLI) + 00 - TLLI[0] + 23 - TLLI[1] + 94 - TLLI[2] + 01 - TLLI[3] + TLLI == "00239401" + + e1 - QOS[0] (bit rate MSB) + 55 - QOS[1] (bit rate LSB) + bit rate = "57685" (57685*100000 bit/s per PBRG) + cf - QOS[2] PBRG = 11 (bit rate is expressed in 100000 bit/s increments), + C/R 0 (contains LLC ACK/SACK), + T 0 (contains signalling), + A 1 (radio if uses MAC/UNITDATA, + Precedence 111 (reserved value) + + ea - CELL_ID[0] (TLV IEI: wrong, should be 0x08) + 00 - CELL_ID[1] (length 1) + 00 - CELL_ID[2] (length 2) + lenth == 0 + 04 -- CELL_ID[3] + 08 -- CELL_ID[4] + 88 -- CELL_ID[5] + 72 -- CELL_ID[6] + f4 -- CELL_ID[7] + 80 -- CELL_ID[8] + 10 -- CELL_DI[9] + + 18 -- QOSP[0] OoS Profile IEI + not allowed in BSSGP Userdata + 00 -- QOSP[1] + 9c -- QOSP[2] + 40 -- QOSP[3] + 00 -- QOSP[4] + + 80 -- IEI for "E-UTRAN Inter RAT Handover Info" + not allowed in BSSGP Userdata + 0e -- length (14 bytes -- only 8 bytes remain) + 00 06 01 c0 41 6c 43 38 */ + + msgb_free(msg); + + 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_sgsn_ctx = talloc_named_const(NULL, 0, "gbproxy_test"); + void *log_ctx = talloc_named_const(tall_sgsn_ctx, 0, "log"); + + msgb_talloc_ctx_init(tall_sgsn_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_sgsn_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(); + test_gbproxy_parse_bssgp_unitdata(); + 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_sgsn_ctx) == 2); + talloc_free(tall_sgsn_ctx); + + return 0; +} diff --git a/tests/gbproxy/gbproxy_test.ok b/tests/gbproxy/gbproxy_test.ok new file mode 100644 index 000000000..9016ed365 --- /dev/null +++ b/tests/gbproxy/gbproxy_test.ok @@ -0,0 +1,7492 @@ +===== 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 + +MESSAGE to BSS at 0x01020304:1111, msg length 1 +07 + +==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 + +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 + +MESSAGE to BSS at 0x01020304:2222, msg length 1 +07 + +==> got signal NS_UNBLOCK, NS-VC 0x2001/1.2.3.4:2222 + +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 + +MESSAGE to BSS at 0x01020304:3333, msg length 1 +07 + +==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:3333 + +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 + +MESSAGE to BSS at 0x01020304:1111, msg length 1 +07 + +==> got signal NS_UNBLOCK, NS-VC 0x2001/1.2.3.4:1111 + +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 + +MESSAGE to BSS at 0x01020304:1111, msg length 1 +07 + +==> got signal NS_UNBLOCK, NS-VC 0x2001/1.2.3.4:1111 + +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 + +MESSAGE to BSS at 0x01020304:4444, msg length 1 +07 + +==> got signal NS_UNBLOCK, NS-VC 0x2001/1.2.3.4:4444 + +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 + +MESSAGE to BSS at 0x01020304:3333, msg length 1 +07 + +==> got signal NS_UNBLOCK, NS-VC 0x2001/1.2.3.4:3333 + +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 + +MESSAGE to BSS at 0x01020304:1111, msg length 1 +07 + +==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 + +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 + +MESSAGE to BSS at 0x01020304:1111, msg length 1 +07 + +==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 + +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 + +MESSAGE to BSS at 0x01020304:1111, msg length 1 +07 + +==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 + +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 + +MESSAGE to BSS at 0x01020304:1111, msg length 1 +07 + +==> got signal NS_UNBLOCK, NS-VC 0x2001/1.2.3.4:1111 + +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 + +MESSAGE to BSS at 0x01020304:1111, msg length 1 +07 + +==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 + +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 + +MESSAGE to BSS at 0x01020304:1111, msg length 1 +07 + +==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 + +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 + +MESSAGE to BSS at 0x01020304:1111, msg length 1 +07 + +==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 + +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 + +MESSAGE to BSS at 0x01020304:1111, msg length 1 +07 + +==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 + +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 + +MESSAGE to BSS at 0x01020304:1111, msg length 1 +07 + +==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 + +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 + +MESSAGE to BSS at 0x01020304:1111, msg length 1 +07 + +==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 + +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 + +MESSAGE to BSS at 0x01020304:1111, msg length 1 +07 + +==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 + +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 + +MESSAGE to BSS at 0x01020304:1111, msg length 1 +07 + +==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 + +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 + diff --git a/tests/gprs/Makefile.am b/tests/gprs/Makefile.am new file mode 100644 index 000000000..902313f2a --- /dev/null +++ b/tests/gprs/Makefile.am @@ -0,0 +1,10 @@ +AM_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) diff --git a/tests/gprs/gprs_test.c b/tests/gprs/gprs_test.c new file mode 100644 index 000000000..99e3ea519 --- /dev/null +++ b/tests/gprs/gprs_test.c @@ -0,0 +1,141 @@ +#include <stdio.h> +#include <stdlib.h> +#include <inttypes.h> + +#include <osmocom/sgsn/gprs_llc.h> +#include <osmocom/sgsn/gprs_utils.h> + +#include <osmocom/sgsn/debug.h> + +#include <osmocom/core/application.h> +#include <osmocom/gsm/gsup.h> + +#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; +} diff --git a/tests/gprs/gprs_test.ok b/tests/gprs/gprs_test.ok new file mode 100644 index 000000000..da7888c6a --- /dev/null +++ b/tests/gprs/gprs_test.ok @@ -0,0 +1,17 @@ +Testing 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. diff --git a/tests/gtphub/Makefile.am b/tests/gtphub/Makefile.am new file mode 100644 index 000000000..523df61d7 --- /dev/null +++ b/tests/gtphub/Makefile.am @@ -0,0 +1,40 @@ +AM_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) diff --git a/tests/gtphub/gtphub_test.c b/tests/gtphub/gtphub_test.c new file mode 100644 index 000000000..2e48bb106 --- /dev/null +++ b/tests/gtphub/gtphub_test.c @@ -0,0 +1,1789 @@ +/* Test the GTP hub */ + +/* (C) 2015 by sysmocom s.f.m.c. GmbH + * All Rights Reserved + * + * Author: Neels Hofmeyr <nhofmeyr@sysmcom.de> + * + * 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 <http://www.gnu.org/licenses/>. + * + */ + +#include <stdio.h> +#include <string.h> +#include <limits.h> +#include <unistd.h> + +#include <osmocom/core/utils.h> +#include <osmocom/core/application.h> + +#include <osmocom/sgsn/debug.h> + +#include <osmocom/sgsn/gtphub.h> +#include <gtp.h> +#include <gtpie.h> + +#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; +} + diff --git a/tests/gtphub/gtphub_test.ok b/tests/gtphub/gtphub_test.ok new file mode 100644 index 000000000..e60d5f2b2 --- /dev/null +++ b/tests/gtphub/gtphub_test.ok @@ -0,0 +1,42 @@ +test_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 diff --git a/tests/sgsn/Makefile.am b/tests/sgsn/Makefile.am new file mode 100644 index 000000000..956ef8d8f --- /dev/null +++ b/tests/sgsn/Makefile.am @@ -0,0 +1,83 @@ +AM_CPPFLAGS = \ + $(all_includes) \ + -I$(top_srcdir)/include \ + $(NULL) + +AM_CFLAGS = \ + -Wall \ + -ggdb3 \ + $(LIBOSMOCORE_CFLAGS) \ + $(LIBOSMOABIS_CFLAGS) \ + $(LIBOSMOGSM_CFLAGS) \ + $(LIBOSMOGSUPCLIENT_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=osmo_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_attach.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 \ + $(LIBOSMOABIS_LIBS) \ + $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOGSM_LIBS) \ + $(LIBOSMOGB_LIBS) \ + $(LIBOSMOGSUPCLIENT_LIBS) \ + $(LIBCARES_LIBS) \ + $(LIBGTP_LIBS) \ + -lrt \ + -lm \ + $(NULL) + +if BUILD_IU +sgsn_test_LDADD += \ + $(LIBOSMORANAP_LIBS) \ + $(LIBOSMOSIGTRAN_LIBS) \ + $(LIBASN1C_LIBS) \ + $(NULL) +endif diff --git a/tests/sgsn/sgsn_test.c b/tests/sgsn/sgsn_test.c new file mode 100644 index 000000000..111515e03 --- /dev/null +++ b/tests/sgsn/sgsn_test.c @@ -0,0 +1,1674 @@ +/* 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 <http://www.gnu.org/licenses/>. + * + */ + +#include <osmocom/sgsn/gprs_llc.h> +#include <osmocom/sgsn/sgsn.h> +#include <osmocom/sgsn/gprs_gmm.h> +#include <osmocom/sgsn/debug.h> +#include <osmocom/sgsn/gprs_subscriber.h> +#include <osmocom/gsm/gsup.h> +#include <osmocom/gsupclient/gsup_client.h> +#include <osmocom/sgsn/gprs_utils.h> +#include <osmocom/sgsn/gprs_gb_parse.h> + +#include <osmocom/gprs/gprs_bssgp.h> + +#include <osmocom/gsm/gsm_utils.h> + +#include <osmocom/core/application.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/rate_ctr.h> +#include <osmocom/core/utils.h> + +#include <stdio.h> + +void *tall_sgsn_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_osmo_gsup_client_send(struct osmo_gsup_client *gsupc, struct msgb *msg); +int (*osmo_gsup_client_send_cb)(struct osmo_gsup_client *gsupc, struct msgb *msg) = + &__real_osmo_gsup_client_send; + +int __wrap_osmo_gsup_client_send(struct osmo_gsup_client *gsupc, struct msgb *msg) +{ + return (*osmo_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 osmo_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(); +} + +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; +} + +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; +} + +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; +} + +#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)); +}; + +int my_gsup_client_send(struct osmo_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 = osmo_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; +}; + +/* + * 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(); +} + +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"); + + osmo_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]); + + osmo_gsup_client_send_cb = __real_osmo_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_sgsn_ctx = talloc_named_const(osmo_sgsn_ctx, 0, "sgsn"); + 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_reject(); + test_gmm_cancel(); + 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_sgsn_ctx) == 2); + return 0; +} + + +/* stubs */ +struct osmo_prim_hdr; +int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx) +{ + abort(); +} diff --git a/tests/sgsn/sgsn_test.ok b/tests/sgsn/sgsn_test.ok new file mode 100644 index 000000000..e7e7cf6e4 --- /dev/null +++ b/tests/sgsn/sgsn_test.ok @@ -0,0 +1,27 @@ +Testing 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) +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 APN matching +Testing GGSN selection +Done diff --git a/tests/slhc/Makefile.am b/tests/slhc/Makefile.am new file mode 100644 index 000000000..818ae2ef3 --- /dev/null +++ b/tests/slhc/Makefile.am @@ -0,0 +1,14 @@ +AM_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) + + diff --git a/tests/slhc/slhc_test.c b/tests/slhc/slhc_test.c new file mode 100644 index 000000000..5c1331355 --- /dev/null +++ b/tests/slhc/slhc_test.c @@ -0,0 +1,275 @@ +/* Test SLHC/RFC1144 TCP/IP Header compression/decompression */ + +/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <osmocom/sgsn/slhc.h> +#include <osmocom/sgsn/debug.h> + +#include <osmocom/core/talloc.h> +#include <osmocom/core/utils.h> + +#include <osmocom/core/application.h> + +#include <stdio.h> +#include <string.h> +#include <arpa/inet.h> + +/* 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(); +} diff --git a/tests/slhc/slhc_test.ok b/tests/slhc/slhc_test.ok new file mode 100644 index 000000000..71f48a179 --- /dev/null +++ b/tests/slhc/slhc_test.ok @@ -0,0 +1,154 @@ +Allocating 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 diff --git a/tests/sndcp_xid/Makefile.am b/tests/sndcp_xid/Makefile.am new file mode 100644 index 000000000..fbcb36c54 --- /dev/null +++ b/tests/sndcp_xid/Makefile.am @@ -0,0 +1,20 @@ +AM_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 + + diff --git a/tests/sndcp_xid/sndcp_xid_test.c b/tests/sndcp_xid/sndcp_xid_test.c new file mode 100644 index 000000000..56b97e336 --- /dev/null +++ b/tests/sndcp_xid/sndcp_xid_test.c @@ -0,0 +1,287 @@ +/* Test SNDCP-XID Encoding/Decoding */ + +/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <osmocom/sgsn/gprs_sndcp_xid.h> +#include <osmocom/sgsn/debug.h> + +#include <osmocom/core/talloc.h> +#include <osmocom/core/utils.h> + +#include <osmocom/core/application.h> + +#include <stdio.h> +#include <string.h> + +/* 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.pcomp = 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.pcomp = 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.pcomp = 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.dcomp = 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.dcomp = 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(); +} diff --git a/tests/sndcp_xid/sndcp_xid_test.ok b/tests/sndcp_xid/sndcp_xid_test.ok new file mode 100644 index 000000000..f3572827c --- /dev/null +++ b/tests/sndcp_xid/sndcp_xid_test.ok @@ -0,0 +1,11 @@ +Testing 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 diff --git a/tests/testsuite.at b/tests/testsuite.at new file mode 100644 index 000000000..d30115e01 --- /dev/null +++ b/tests/testsuite.at @@ -0,0 +1,56 @@ +AT_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([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 diff --git a/tests/v42bis/Makefile.am b/tests/v42bis/Makefile.am new file mode 100644 index 000000000..8e17b4afd --- /dev/null +++ b/tests/v42bis/Makefile.am @@ -0,0 +1,14 @@ +AM_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) + + diff --git a/tests/v42bis/v42bis_test.c b/tests/v42bis/v42bis_test.c new file mode 100644 index 000000000..e9c506926 --- /dev/null +++ b/tests/v42bis/v42bis_test.c @@ -0,0 +1,438 @@ +/* Test v42bis Compression/Decompression */ + +/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <osmocom/sgsn/v42bis.h> +#include <osmocom/sgsn/v42bis_private.h> +#include <osmocom/sgsn/debug.h> + +#include <osmocom/core/talloc.h> +#include <osmocom/core/utils.h> + +#include <osmocom/core/application.h> + +#include <stdio.h> +#include <string.h> +#include <stdbool.h> + +/* 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(); +} diff --git a/tests/v42bis/v42bis_test.ok b/tests/v42bis/v42bis_test.ok new file mode 100644 index 000000000..070767473 --- /dev/null +++ b/tests/v42bis/v42bis_test.ok @@ -0,0 +1,648 @@ +Testing 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...{'..{........<xd..'..^.8....g6n|.....#... +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= 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= 0001330003060e2448b0e08409070f22449870c488850b19326c3863c6c3871021461c3264e2448a142b4e9972f122468c19c78cd9b89123c78e73e67cfc081264c84183468e2449b2e4a449274fa2449972d4a8952b59b26c396bd6cb973061c61c366ce64c9a346b4e9b76f3264e9c39c78ddbb99327cf9ef3e6fdfc091468d0820587124c98d028c2860d93328c189129c48a159f52cc98512ac68e1dab720c19122bc89225b7924c99d22bca962dc3b28c19932ccc9a35cfd2cc99532dce9e3ddbf20c1a142e50a2440d1e3d8a54a9d2a54d9b3a850a35ead4a954ad5abd9a35ab56ae5cbb7efd0a56acd8b165cb9a458b36eddab56cddba7d1b37aedcb97307d4ad7bf0eedd8579f33edcbb7762dfbe17fffedd1838f0c7c18347162e7cf2f0e1958913bf5cbc7866e3c6371f3fde1939f2cf00 ASCII:..3....$H......"D.p.....2l8c....!F.2d.D..+N.r."F.......#..s.|...d.A.F.$I...I'O.D.r...+Y.l9k...0a..6l.L.4kN.v.&N.9.....'........h.....L..(....2...)....R..Q*....r...+..%..L..+..-.....,..5....S-..=......P.D..=.T...M.:..5...T.Z..5.V.\.~..V...e..E.6...l..}.7...s...{....y.>..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`<!.G..2j.$E.'....a..#'..7.G..|gN..26......:`.(.aF...X.../....3.T.1....A....Z..=1..1.F"..XV..AD}..V..A.........YdD.4.....%jp..?..Q....2..NR....1l..R.~..L.Lq.c..#d...#..Y.F..1.<......Acfl..N....5...E..@.g..6...A...X4Z..y.4h. +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= 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= 450000030ae418a060c0802109a4bd18b68a20160896b40d493825c40808aa8631eb3267d0068b31742424406041830104ee5c5860e51a232548ae8c903162c9952b5364d088410342032266cca09973a7c7083878dedc6133874c9d3b32e0b0d983e6cc8b1164e6cc79c3c60c19a772e2c8c813e74c0c3d5c632c6dfa34ea9db656b16ae5ea15ac583664cda255cb76cf9a3b70deb8d843d86ed7af61c7960d3cb8f0e1375aefa0d933f56cdab573d4221ed142468ba0438b1e8561644d543b68908ec073270d8c1d5a5fc7a691432b1b3b7264efc0c103068ddcbb83cfc87123070c3c66ecc0a8a1d5ca1a3669ee8869c2c48ad6226cd4e0184dd4e81d184fc8c45183878c1a3449d1c409ba07c6661873eac849c3e6cdfa112504df99d343868ccd3176c7e183a60e583b0a6398910347061622f4edcb70b263c60c36556accf029b944d02572ecb4d6a3474f8c3e72ccb89148312896d5756010511fa7b515b27c903864aeb0e347363244161951054d7e32ab6540891adc86f38f3a4614a9af07a78c28ac93d4ef29c3460c1b23a6d4bf9fff2e9323539cc79831c208d9fbe7eb88654356869127478c048f412368e93871d098191b27a9133446757ecd83e67bd1a33090c49978868df9a467d0a47923168d56357d9e060d1a00 ASCII:E.......`..!...... .....I8%.....1.2g...1t$$@`A....\X`..#%H...1b..+Sd..A.B."f...s...8x..a3.L.;2........d..y.....r.....L.=\c,m.4...V.j....X6d..U.v..;p...C.n..a...<...7Z...3.l..s."..BF..C...adMT;h...s'...Z_...C+.;rd...........q#..<f.......6i..i...."l...M....O..Q....4I......f.s..I.....%....C...1v.....X;.c...G.."...p.c..6Uj..).D.%r....GO.>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....<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><html>.<title>Directory listing for /</title>.<body>.<h2>Directory listing for /</h2>.<hr>.<ul>.<li><a href="redphone.png">redphone.png</a>.</ul>.<hr>.</body>.</html>. +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....<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><html>.<title>Directory listing for /</title>.<body>.<h2>Directory listing for /</h2>.<hr>.<ul>.<li><a href="redphone.png">redphone.png</a>.</ul>.<hr>.</body>.</html>. +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"><html>.<title>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 /</title>.<body>.<h2>Directory listing for /</h2>.<hr>.<ul>.<li><a href="redphone.png">redphone.png</a>.</ul>.<hr>.</body>.</html>. +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....<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><html>.<title>Directory listing for /</title>.<body>.<h2>Directory listing for /</h2>.<hr>.<ul>.<li><a href="redphone.png">redphone.png</a>.</ul>.<hr>.</body>.</html>. +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....<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><html>.<title>Directory listing for /</title>.<body>.<h2>Directory listing for /</h2>.<hr>.<ul>.<li><a href="redphone.png">redphone.png</a>.</ul>.<hr>.</body>.</html>. +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....<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><html>.<title>Directory listing for /</title>.<body>.<h2>Directory listing for /</h2>.<hr>.<ul>.<li><a href="redphone.png">redphone.png</a>.</ul>.<hr>.</body>.</html>. +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....<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><html>.<title>Directory listing f.or /</title>.<body>.<h2>Directory listing for /</h2>.<hr>.<ul>.<li><a href="redphone.png">redphone.png</a>.</ul>.<hr>.</body>.</html>. +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...<u..)k...#...E.'..3d.,u..iO.y.|?....8........4o.....O..u.0d.....+[..!....2.... +uncompressed= 4500025b000a00004006ac35c0a800020a0901ab40011f90c293b0a8af5e58be5018400072a60000474554202f72656470686f6e652e706e6720485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a2031302e392e312e3137313a383038300d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a526566657265723a20687474703a2f2f31302e392e312e3137313a383038302f0d0a0d0a ASCII:E..[....@..5........@........^X.P.@.r...GET /redphone.png 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..Referer: http://10.9.1.171:8080/.... + +Testing decompression with sniffed compressed TCP/IP packets: +Packet No.: 13 +v42bis_decompress_flush() rc=0 +v42bis_decompress() rc=0 +v42bis_decompress_flush() rc=0 +compressed= 4500010267001200004006ac21c0a800020a0901ab40011f90c293b0a8af5e58be80184000ee770000050aaf5e6437af5e8c230000474554202f72656470686f6e652e706e6720485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d33006fc8de41b3c72b5cb974e7cc853ca2858c164c9d42950ac3c81aae76d04c1d81e74e1a183bc8e2d64d2307593676e4ecde8183070c1ac2892b9f91e3460e1878ccd8815183ac95356cd2dc11d3848915b245d8a8c1c1fa69d43b309e90098a878c1a3454d1c461ba0706691873eac849c3e6cdfc112514df99d343860cd23188c7e183a68e5a3b126398910347061622fcfdcb70b263c60c36556acc48bab904d32572ecd8a63123060fa54a75d0e861d224532cb4ebc020223f8e6d2b3cf920b1585d62c9936c64a82c32a20a9a826468cb80c255b98deb27758c28d25f4f5119516a27e9df54868d1836464ce9ffbf20612647a65c8f316384119effd5e0a9c3968d5b234f8e1851ae94a9ec3871d098691b87aa1334518fa6cd83063d54a93090c4d978864d50aa67d0a479c3160d59357db432fd9ba66245aa0a193aac7953278d9e3f679894c1946900 ASCII:E...g....@..!........@........^X...@..w.....^d7.^.#..GET /redphone.png HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed, application/vnd.wap.xhtml+xml, application/xhtml+xm3.o..A..+\.t...<....L.B......v.L...N..;...M#.Y6v..........+...F..x...Q...5l........E.....i.;0.......4T..a...i.s..I.....%....C...1......Z;.c...G.."...p.c..6Uj.H....%r...1#...Ju..a.$S,... "?.m+<. .X]b..ld.,2....dh...U...'u.(._OQ.Qj'..T...6FL... a&G.\.1c..........[#O..Q....8q..i....4Q.....=T.0...x.MP.g..y...Y5}.2...bE...:.yS'..?g....i. +uncompressed= 45000267001200004006ac21c0a800020a0901ab40011f90c293b0a8af5e58be80184000ee770000050aaf5e6437af5e8c230000474554202f72656470686f6e652e706e6720485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a2031302e392e312e3137313a383038300d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a526566657265723a20687474703a2f2f31302e392e312e3137313a383038302f0d0a0d0a ASCII:E..g....@..!........@........^X...@..w.....^d7.^.#..GET /redphone.png 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..Referer: http://10.9.1.171:8080/.... + +Testing decompression with sniffed compressed TCP/IP packets: +Packet No.: 14 +v42bis_decompress_flush() rc=0 +v42bis_decompress() rc=0 +v42bis_decompress_flush() rc=0 +compressed= 4500010236003000004006cf03c0a80002550d93d740020050c30e84a9441d06ac80184000c2f400000101080a00052df410fc31bd474554202f20485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d6978656433005cbd82154b968d59b46ad9baddb3e60e9c372ef618c6fb35ecd8b26707173e9cf80dd73b68f6544dbbb6ed1cb68a47b490d16268d1a34961185933d50e1aa523f0dc49036307d7d8b369e4e0cac68e1cda3b70f080416377efe13372dcc801038f193b306a70b5b2864d9a3b629a30b1c2b5081b35384a1b457a07c6133271d4e021a306cd52347186ee81d119c69c3a72d2b079d37e4409c277e6f49021a3738cde71f8a0a923d60ec31866e4c0918185887dfc329cec9831834d951a3380522e3174891c3baff5e8d113a38f1c336e285a1c8aa5751d1844d8c7796dc52c1f24109d33f408928d8c9145465441b39f4c6b1950a60eb7011da48e1145eeebc929238aeb24f77dcab011c3c68829f7f3efbfcbe4c814e831668c3062367ffa3a64d9989561e4c91123c363d0186a3a4e1c3466cac659ea040dd29d61f3a0097f34290c24712a9e61837ee9193469de9045c3554d9fa843870600 ASCII:E...6.0..@.......U...@..P....D.....@...........-...1.GET / HTTP/1.1..Accept: multipart/mixed, application/vnd.wap.multipart.mixed3.\...K..Y.j.......7.....5...g..>....;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<!.G..2j.dE.G....a..#'..7.G..|gN..2T.P....:o.`.....,D."..d...l....4t..K....G...}..q.R...Xt..A....V..A.q;..+..pYdD.4.....%,t..W..Q....2..Nr....1l..rP!C.L.L..c..#s......\.F..1.=......AcFn..N.X].6......@.'..6...A..M\4i...*.p....B.H1...B...*5. +uncompressed= 45000264005800004006ceadc0a80002550d93d740050050c31389acaf7b26538018400075c900000101080a000537d010fc354a474554202f382d4269742f41636f726e2e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a207777772e7a6f636b2e636f6d0d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a526566657265723a20687474703a2f2f7777772e7a6f636b2e636f6d2f0d0a0d0a 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/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.: 18 +v42bis_decompress_flush() rc=0 +v42bis_decompress() rc=0 +v42bis_decompress_flush() rc=0 +compressed= 4500010266007600004006ce8dc0a80002550d93d740060050c31431ada11fa06780184000f08e00000101080a00053b3c10fc35ef474554202f382d4269742f416d73747261642e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d33009733bf597b07cd9e9e78f5f2d5a379440b192da656bd9a1586913563eda0d13a02cf9d343076ac052e9c468eb56cecc819be03070f18349433973e23c78d1c30f098b103a3c65a2b6bd8a4b923a609132b6b77aac141db2ad63b309e9089a3060f193568b6a28933750f0cc830e6d4919386cd9bfd234a50be33a7870c199063308fc3074d9db87636c63023078e0c2c44103294e164c78c196caad49801957489a94be404ddaa478f9e187de4987133b2e4542cbcebc020a23f8e6f2b75f920f9d87d63cb976c64c82c32a20a9a876478cb803256ba8def2f758c28a2500f5319517a27512855868d1836464c51d8f061622647a67c8f31638491ba0defd799cba6ae0c234f8e18911e83c6d4db71e2a0314337ce562768b03a859b07cdfbab5961208943f20c1bfb5bcfa049f3662e9ab56afa849d3a388d478f5b2756bcf813e850a3484d4e9d1a00 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/xhtm3..3.Y{....x....yD..-.V.....5c...:...40v....F..l........4.3.>#...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`<!.G..2j.hE.G....a..#'..7.G..|gN..2R.H....../.0#...,D....d...l.....t..K....G...}..q...T,... r?.n+s. .h."J.ld.,2....dr.."....*u.(bP.Q.Qt'1.T...6FL1.paa&G.p.1c........f..#O..y.....q..1#7.V'h.&u...{.Xa ......Z..I.&...j....7.F.Z.F..3...ACJ... +uncompressed= 45000264007700004006ce8ec0a80002550d93d740040050c3135ddb2189e0108018400060d600000101080a00053b4010fc35e7474554202f382d4269742f41746172692e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a207777772e7a6f636b2e636f6d0d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a526566657265723a20687474703a2f2f7777772e7a6f636b2e636f6d2f0d0a0d0a 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/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.: 20 +v42bis_decompress_flush() rc=0 +v42bis_decompress() rc=0 +v42bis_decompress_flush() rc=0 +compressed= 4500010264007c00004006ce89c0a80002550d93d740070050c314f3aefa37ceb18018400009f900000101080a00053e3410fc369e474554202f382d4269742f4170706c652e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d3300942dbf517b07cd1e9f76f1ea9d93f7f28816325a48a56a152b0c236bc4da419375049e3b6960ec50dbfb378d1c6ad9d891037c070e1e30681c4ffe7c468e1b3960e0316307460db556d6b04973474c132656d4f254832376d5ab77603c2113470d1e326ad068451347ea1e18a961cca923270d9ba15a4b44be33a7870c19a963248fc3074d9db7762cc63023078e0c2c400da691e164c78c196caad498f1147409a94be4d8d9ad478f9e187de49871e311a4542cb9ebc020723fce6e2b73f920d1a8dd224a956c64b42c32a20a9aa16472cb8022f6b90dee2a758c2862504f521951742731e854868d1836464c29a910cd602647a6708f31638491b908e9d789cb66ae0c234f8e18791e8386d4d971e2a03123378e562768ae2e7dc3260f1af656b1c24012e7e31936f3b59e4193e64d5c346ad5f4012bd56f9a8c19b53a84281167ce9d3f8b86942a3500 ASCII:E...d.|..@.......U...@..P.....7....@...........>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`<!.G..2j.hE.G....a..#'...ZKD.3.....c$...M..v,.0#...,@....d...l.....t..K....G...}..q...T,... r?.n+s. ..."J.ld.,2....dr.."....*u.(bPOR.Qt'1.T...6FL)...`&G.p.1c........f..#O..y.....q..1#7.V'h..}.&...V..@....6...A..M\4j...+.o....:.(.g..?...*5. +uncompressed= 45000264007c00004006ce89c0a80002550d93d740070050c314f3aefa37ceb18018400009f900000101080a00053e3410fc369e474554202f382d4269742f4170706c652e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a207777772e7a6f636b2e636f6d0d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a526566657265723a20687474703a2f2f7777772e7a6f636b2e636f6d2f0d0a0d0a ASCII:E..d.|..@.......U...@..P.....7....@...........>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`<!.G..2j.xE......a.......#JX.3.....cT..G(];.c...G.."...p.c..6Uj..j...%r....GO.>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.......<w....vxq.9...#....<`.h.....7r..c....n..IsGL.&V..T..v..w`<!.G..2j.xE......a..#'..7.G..|gN..2&.x....:t.t.aF...X.0t(....3.T.1c...V....\..=1..1...I.X~..A....V..A..|..1...YdD.4......lu..c..Q....2..O.....1l....a..L.L..c..#x...cWh^#O......U.q..1s7.W'h.F...M|.\a .c....^..I.....j...Z8.H.^+^..t...KQZ... +uncompressed= 45000269009e00004006ce62c0a80002550d93d740060050c3143845a11fab60801840003c1c00000101080a000547d810fc38b7474554202f382d4269742f526164696f536861636b2e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a207777772e7a6f636b2e636f6d0d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a526566657265723a20687474703a2f2f7777772e7a6f636b2e636f6d2f0d0a0d0a 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/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.: 29 +v42bis_decompress_flush() rc=0 +v42bis_decompress() rc=0 +v42bis_decompress_flush() rc=0 +compressed= 4500010266009f00004006ce64c0a80002550d93d740050050c313903caf7b2f9580183d5ce4ef00000101080a0005481810fc3934474554202f382d4269742f5068696c6970732e47494620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d3300952fbf597b07cd1e9f8cf5f2d58379440b192da85ac5aa1586913563eda0d93a02cf9d343076acfd1d9c468eb56cecc811be03070f1834922f8f3e23c78d1c30f098b103a3c65a2b41d3dc11d3848995b53cd5e0987d35eb1d184fc8c45183878c1a345cd1c4a1ba0786631873eac8492334ff881292efcce9214386e318cbe3f0415307b19d8b31ccc88123030b1183086538d93163069b2a3566441d5d82ea1239767aebd1a327461f3966dc7c0c4915cbee3a3088e08fd3db0a5d3e483672bf9872251b192e8b8ca882e60d1a32bb6540191bdd86f7953a461439a847a98c28bc931c7c2ac3460c1b23a61c5428f830932353bcc79831c2085d85f5ebc8654357869127478c448f4183eaed3871d098991b87ab133459990ecd83a63d56ad3090c40179860d7dae67d0a4792317cd5a357d8852059c46a346ae0f234ecca9b32750a322a9520d00 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/xhtm3../.Y{.........yD..-.Z.....5c...:...40v....F..l........4./.>#...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.<K.#....TF..I.>.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.<h....$..9g...z.M.7t..U.......l..5a..:c.tsQ.U... +uncompressed= 4500049b00ae00004006cc20c0a80002550d93d740050050c31396ceaf7b396e801840009ad400000101080a0005611010fc3d6b474554202f382d4269742f6d72776f6e672e67696620485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a207777772e7a6f636b2e636f6d0d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a526566657265723a20687474703a2f2f7777772e7a6f636b2e636f6d2f0d0a0d0a474554202f382d4269742f7374756d626c6575706f6e2e706e6720485454502f312e310d0a4163636570743a206d756c7469706172742f6d697865642c206170706c69636174696f6e2f766e642e7761702e6d756c7469706172742e6d697865642c206170706c69636174696f6e2f766e642e7761702e7868746d6c2b786d6c2c206170706c69636174696f6e2f7868746d6c2b786d6c2c20746578742f766e642e7761702e776d6c2c202a2f2a0d0a4163636570742d436861727365743a207574662d382c207574662d31362c2069736f2d383835392d312c2069736f2d31303634362d7563732d322c2053686966745f4a49532c20426967350d0a4163636570742d4c616e67756167653a20656e0d0a782d7761702d70726f66696c653a2022687474703a2f2f7761702e736f6e796572696373736f6e2e636f6d2f554170726f662f4b38303069523230312e786d6c220d0a486f73743a207777772e7a6f636b2e636f6d0d0a557365722d4167656e743a20536f6e794572696373736f6e4b383030692f5232422052656c656173652f4d61722d31332d323030372042726f777365722f4e657446726f6e742f332e332050726f66696c652f4d4944502d322e3020436f6e66696775726174696f6e2f434c44432d312e310d0a436f6e6e656374696f6e3a204b6565702d416c6976650d0a4163636570742d456e636f64696e673a206465666c6174652c20677a69700d0a526566657265723a20687474703a2f2f7777772e7a6f636b2e636f6d2f0d0a0d0a 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/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/....GET /8-Bit/stumbleupon.png 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.: 31 +v42bis_decompress_flush() rc=0 +v42bis_decompress() rc=0 +v42bis_decompress_flush() rc=0 +compressed= 45000101a0f3d84000400679210a0901abc0a800021f90400538e210f07a827bb1501900ed7be90000485454502f312e3020323030204f4b0d0a5365727665723a2053696d706c65485454502f302e3620507974686f6e2f322e372e360d0a446174653a205765642c203301312041756720323031362030393a32383a353220474d540d0a436f6e74656e742d747970653a20746578742f68746d6c3b20636861727365743d5554462d380d0a436f6e74656e742d4c656e6774683a203232320d0a0d0a66003f481c9162e40a97294800002068746d6c20506600588a3c6162644409183200002f5733432f2f4454442048544d4c20332e322046696e616c2f2f454e223e3c68746d66006f8234f87187cd1d006c653e4469726563746f7279206c697374696e672066016f72202f3c2f7469746c990068bcff2823e70c9fff6b6a943f9f7e7dfbf7f1e7d7bf9f5fb1c2ef6beafcc7d3fd3b7ced646aa34f03a404fa3373a4c64113630efdec27534e7509538d7e32ff657044a8f1bb0c82067f72f71e00 ASCII:E......@.@.y!..........@.8...z.{.P...{...HTTP/1.0 200 OK..Server: SimpleHTTP/0.6 Python/2.7.6..Date: Wed, 3.1 Aug 2016 09:28:52 GMT..Content-type: text/html; charset=UTF-8..Content-Length: 222....f.?H..b...)H.. html Pf.X.<abdD..2../W3C//DTD HTML 3.2 Final//EN"><htmf.o.4.q....le>Directory listing f.or /</titl..h..(#....kj.?.~}......._...k.....;|.dj.O....3s..A.c...'SNu.S.~2.epD.......r... +uncompressed= 450001a0f3d84000400679210a0901abc0a800021f90400538e210f07a827bb1501900ed7be90000485454502f312e3020323030204f4b0d0a5365727665723a2053696d706c65485454502f302e3620507974686f6e2f322e372e360d0a446174653a205765642c2033312041756720323031362030393a32383a353220474d540d0a436f6e74656e742d747970653a20746578742f68746d6c3b20636861727365743d5554462d380d0a436f6e74656e742d4c656e6774683a203232320d0a0d0a3c21444f43545950452068746d6c205055424c494320222d2f2f5733432f2f4454442048544d4c20332e322046696e616c2f2f454e223e3c68746d6c3e0a3c7469746c653e4469726563746f7279206c697374696e6720666f72202f3c2f7469746c653e0a3c626f64793e0a3c68323e4469726563746f7279206c697374696e6720666f72202f3c2f68323e0a3c68723e0a3c756c3e0a3c6c693e3c6120687265663d2272656470686f6e652e706e67223e72656470686f6e652e706e673c2f613e0a3c2f756c3e0a3c68723e0a3c2f626f64793e0a3c2f68746d6c3e0a ASCII:E.....@.@.y!..........@.8...z.{.P...{...HTTP/1.0 200 OK..Server: SimpleHTTP/0.6 Python/2.7.6..Date: Wed, 31 Aug 2016 09:28:52 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 /</title>.<body>.<h2>Directory listing for /</h2>.<hr>.<ul>.<li><a href="redphone.png">redphone.png</a>.</ul>.<hr>.</body>.</html>. + +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.<abdD..2../W3C//DTD HTML 3.2 Final//EN"><html>.<title>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 /</title>.<body>.<h2>Directory listing for /</h2>.<hr>.<ul>.<li><a href="redphone.png">redphone.png</a>.</ul>.<hr>.</body>.</html>. + +Done diff --git a/tests/vty_test_runner.py b/tests/vty_test_runner.py new file mode 100755 index 000000000..73dd39965 --- /dev/null +++ b/tests/vty_test_runner.py @@ -0,0 +1,323 @@ +#!/usr/bin/env python2 + +# (C) 2013 by Katerina Barone-Adesi <kat.obsc@gmail.com> +# (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 <http://www.gnu.org/licenses/>. + +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", "gbproxy") + + 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 diff --git a/tests/xid/Makefile.am b/tests/xid/Makefile.am new file mode 100644 index 000000000..92876ec39 --- /dev/null +++ b/tests/xid/Makefile.am @@ -0,0 +1,37 @@ +AM_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) + diff --git a/tests/xid/xid_test.c b/tests/xid/xid_test.c new file mode 100644 index 000000000..89d82e999 --- /dev/null +++ b/tests/xid/xid_test.c @@ -0,0 +1,167 @@ +/* Test LLC-XID Encoding/Decoding */ + +/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <osmocom/sgsn/gprs_llc_xid.h> +#include <osmocom/sgsn/debug.h> + +#include <osmocom/core/talloc.h> +#include <osmocom/core/utils.h> + +#include <osmocom/core/application.h> + +#include <stdio.h> +#include <string.h> + +/* 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(); +} diff --git a/tests/xid/xid_test.ok b/tests/xid/xid_test.ok new file mode 100644 index 000000000..4cf825ca5 --- /dev/null +++ b/tests/xid/xid_test.ok @@ -0,0 +1,12 @@ +Testing 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 |