laforge has submitted this change. ( https://gerrit.osmocom.org/c/osmo-mgw/+/26651 )
Change subject: mgcp_client: add new codec IUFP as VND.3GPP.IUFP
......................................................................
mgcp_client: add new codec IUFP as VND.3GPP.IUFP
3GPP TS 25.414 5.1.3.3.1.6 specifies that IuUP can use RTP as transport.
The payload type is specified from 96-127, which shall be ignored on the
receiving end anyway.
The payload type number we use shall be 96 by default.
Change-Id: Ifd1210a897743396899f34457c96e6fd2109c6b3
Related: SYS#5152
---
M include/osmocom/mgcp_client/mgcp_client.h
M src/libosmo-mgcp-client/mgcp_client.c
2 files changed, 2 insertions(+), 0 deletions(-)
Approvals:
Jenkins Builder: Verified
laforge: Looks good to me, approved
pespin: Looks good to me, but someone else must approve
diff --git a/include/osmocom/mgcp_client/mgcp_client.h b/include/osmocom/mgcp_client/mgcp_client.h
index 0405175..d421273 100644
--- a/include/osmocom/mgcp_client/mgcp_client.h
+++ b/include/osmocom/mgcp_client/mgcp_client.h
@@ -49,6 +49,7 @@
CODEC_GSMHR_8000_1 = 111,
CODEC_AMR_8000_1 = 112,
CODEC_AMRWB_16000_1 = 113,
+ CODEC_IUFP = 96,
};
/* Note: when new codec types are added, the corresponding value strings
* in mgcp_client.c (codec_table) must be updated as well. Enumerations
diff --git a/src/libosmo-mgcp-client/mgcp_client.c b/src/libosmo-mgcp-client/mgcp_client.c
index 8c2c118..3711f24 100644
--- a/src/libosmo-mgcp-client/mgcp_client.c
+++ b/src/libosmo-mgcp-client/mgcp_client.c
@@ -59,6 +59,7 @@
{ CODEC_GSMHR_8000_1, "GSM-HR-08/8000/1" },
{ CODEC_AMR_8000_1, "AMR/8000/1" },
{ CODEC_AMRWB_16000_1, "AMR-WB/16000/1" },
+ { CODEC_IUFP, "VND.3GPP.IUFP/16000" },
{ 0, NULL },
};
--
To view, visit https://gerrit.osmocom.org/c/osmo-mgw/+/26651
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings
Gerrit-Project: osmo-mgw
Gerrit-Branch: master
Gerrit-Change-Id: Ifd1210a897743396899f34457c96e6fd2109c6b3
Gerrit-Change-Number: 26651
Gerrit-PatchSet: 6
Gerrit-Owner: dexter <pmaier(a)sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: daniel <dwillmann(a)sysmocom.de>
Gerrit-Reviewer: laforge <laforge(a)osmocom.org>
Gerrit-Reviewer: lynxis lazus <lynxis(a)fe80.eu>
Gerrit-Reviewer: pespin <pespin(a)sysmocom.de>
Gerrit-MessageType: merged
pespin has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-hnbgw/+/26771 )
Change subject: Initial structure + import code from osmo-iuh.git
......................................................................
Initial structure + import code from osmo-iuh.git
Imported from osmo-iuh.git 9b4de3f401c890fc2c0dfae9e827daaaadd80db0.
Change-Id: I569d221aeb83d352c1621c44c013a0e4c82fc8a8
---
A .gitignore
A .gitreview
A AUTHORS
A COPYING
A Makefile.am
A README.md
A TODO-RELEASE
A configure.ac
A contrib/Makefile.am
A contrib/jenkins.sh
A contrib/osmo-hnbgw.spec.in
A contrib/systemd/Makefile.am
A contrib/systemd/osmo-hnbgw.service
A debian/changelog
A debian/compat
A debian/control
A debian/copyright
A debian/osmo-hnbgw.install
A debian/rules
A debian/source/format
A doc/Makefile.am
A doc/examples/Makefile.am
A doc/examples/osmo-hnbgw/osmo-hnbgw.cfg
A doc/manuals/Makefile.am
A doc/manuals/chapters/overview.adoc
A doc/manuals/chapters/running.adoc
A doc/manuals/osmohnbgw-usermanual-docinfo.xml
A doc/manuals/osmohnbgw-usermanual.adoc
A doc/manuals/osmohnbgw-vty-reference.xml
A doc/manuals/regen_doc.sh
A doc/manuals/vty/hnbgw_vty_additions.xml
A doc/protocols_around_hnbgw.txt
A git-version-gen
A include/Makefile.am
A include/osmocom/Makefile.am
A include/osmocom/hnbgw/Makefile.am
A include/osmocom/hnbgw/context_map.h
A include/osmocom/hnbgw/hnbgw.h
A include/osmocom/hnbgw/hnbgw_cn.h
A include/osmocom/hnbgw/hnbgw_hnbap.h
A include/osmocom/hnbgw/hnbgw_ranap.h
A include/osmocom/hnbgw/hnbgw_rua.h
A include/osmocom/hnbgw/vty.h
A osmoappdesc.py
A src/Makefile.am
A src/osmo-hnbgw/Makefile.am
A src/osmo-hnbgw/context_map.c
A src/osmo-hnbgw/hnbgw.c
A src/osmo-hnbgw/hnbgw_cn.c
A src/osmo-hnbgw/hnbgw_hnbap.c
A src/osmo-hnbgw/hnbgw_ranap.c
A src/osmo-hnbgw/hnbgw_rua.c
A src/osmo-hnbgw/hnbgw_vty.c
A tests/Makefile.am
A tests/atlocal.in
A tests/ctrl_test_runner.py
A tests/osmo-hnbgw.vty
A tests/testsuite.at
58 files changed, 5,949 insertions(+), 0 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmo-hnbgw refs/changes/71/26771/1
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a7cb104
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,73 @@
+hnbgwdebian/*.log
+*.o
+*.lo
+*.a
+.deps
+Makefile
+Makefile.in
+config.h
+config.h.in
+*.pc
+*~
+
+*.*~
+*.sw?
+.libs
+*.pyc
+*.gcda
+*.gcno
+
+**/TAGS
+
+#configure
+aclocal.m4
+autom4te.cache/
+config.log
+config.status
+config.guess
+config.sub
+configure
+compile
+depcomp
+install-sh
+missing
+stamp-h1
+libtool
+ltmain.sh
+m4/*.m4
+
+# git-version-gen magic
+.tarball-version
+.version
+osmo-hnbgw-*.tar.bz2
+osmo-hnbgw-*.tar.gz
+
+tags
+/deps
+
+src/osmo-hnbgw/osmo-hnbgw
+
+#tests
+tests/testsuite.dir
+tests/*/*_test
+
+tests/atconfig
+tests/atlocal
+tests/package.m4
+tests/testsuite
+tests/testsuite.log
+
+writtenconfig/
+
+# manuals
+doc/manuals/*.html
+doc/manuals/*.svg
+doc/manuals/*.pdf
+doc/manuals/*__*.png
+doc/manuals/*.check
+doc/manuals/generated/
+doc/manuals/osmohnbgw-usermanual.xml
+doc/manuals/common
+doc/manuals/build
+
+contrib/osmo-hnbgw.spec
diff --git a/.gitreview b/.gitreview
new file mode 100644
index 0000000..b69f431
--- /dev/null
+++ b/.gitreview
@@ -0,0 +1,3 @@
+[gerrit]
+host=gerrit.osmocom.org
+project=osmo-hnbgw
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..f621811
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1 @@
+Pau Espin Pedrol <pespin(a)sysmocom.de>
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..dba13ed
--- /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 0000000..e3176be
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,36 @@
+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 = \
+ include \
+ src \
+ tests \
+ doc \
+ contrib \
+ $(NULL)
+
+BUILT_SOURCES = $(top_srcdir)/.version
+EXTRA_DIST = \
+ .version \
+ contrib/osmo-hnbgw.spec.in \
+ debian \
+ git-version-gen \
+ osmoappdesc.py \
+ $(NULL)
+
+AM_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
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..bb5afe3
--- /dev/null
+++ b/README.md
@@ -0,0 +1,61 @@
+osmo-hnbgw - Osmocom hNodeB Implementation
+===========================================
+
+This repository contains a C-language implementation of a 3G Home NodeB Gateway (OsmoHNBGW).
+It is part of the [Osmocom](https://osmocom.org/) Open Source Mobile Communications
+project.
+
+You can use it to interface Iuh-speaking Home NodeB (HnodeB), such as
+osmo-hnodeb or ip.access nano3g, to Iu-speaking MSCs and SGSNs.
+
+Homepage
+--------
+
+The official homepage of the project is
+https://osmocom.org/projects/osmohnbgw/wiki
+
+GIT Repository
+--------------
+
+You can clone from the official osmo-hnbgw.git repository using
+
+ git clone git://git.osmocom.org/osmo-hnbgw.git
+
+There is a cgit interface at https://git.osmocom.org/osmo-hnbgw/
+
+Documentation
+-------------
+
+User Manuals and VTY reference manuals are [optionally] built in PDF form
+as part of the build process.
+
+Pre-rendered PDF version of the current "master" can be found at
+[User Manual](https://ftp.osmocom.org/docs/latest/osmohnbgw-usermanual.pdf)
+as well as the [VTY Reference Manual](https://ftp.osmocom.org/docs/latest/osmohnbgw-vty-reference.pdf)
+
+
+Mailing List
+------------
+
+Discussions related to osmo-hnbgw are happening on the
+openbsc(a)lists.osmocom.org mailing list, please see
+https://lists.osmocom.org/mailman/listinfo/openbsc for subscription
+options and the list archive.
+
+Please observe the [Osmocom Mailing List
+Rules](https://osmocom.org/projects/cellular-infrastructure/wiki/Mailing_List_Rules)
+when posting.
+
+Contributing
+------------
+
+Our coding standards are described at
+https://osmocom.org/projects/cellular-infrastructure/wiki/Coding_standards
+
+We us a gerrit based patch submission/review process for managing
+contributions. Please see
+https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit for
+more details
+
+The current patch queue for osmo-hnbgw can be seen at
+https://gerrit.osmocom.org/#/q/project:osmo-hnbgw+status:open
diff --git a/TODO-RELEASE b/TODO-RELEASE
new file mode 100644
index 0000000..d0852fc
--- /dev/null
+++ b/TODO-RELEASE
@@ -0,0 +1,9 @@
+# When cleaning up this file: bump API version in corresponding Makefile.am and rename corresponding debian/lib*.install
+# according to https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info…
+# In short:
+# LIBVERSION=c:r:a
+# If the library source code has changed at all since the last update, then increment revision: c:r + 1:a.
+# If any interfaces have been added, removed, or changed since the last update: c + 1:0:0.
+# If any interfaces have been added since the last public release: c:r:a + 1.
+# If any interfaces have been removed or changed since the last public release: c:r:0.
+#library what description / commit summary line
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..3e02bd9
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,242 @@
+dnl Process this file with autoconf to produce a configure script
+AC_INIT([osmo-hnbgw],
+ m4_esyscmd([./git-version-gen .tarball-version]),
+ [openbsc(a)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)
+
+CFLAGS="$CFLAGS -std=gnu11"
+
+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 patching ${archive_cmds} to affect generation of file "libtool" to fix linking with clang
+AS_CASE(["$LD"],[*clang*],
+ [AS_CASE(["${host_os}"],
+ [*linux*],[archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'])])
+
+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)
+old_LIBS=$LIBS
+AC_SEARCH_LIBS([sctp_recvmsg], [sctp], [
+ AC_DEFINE(HAVE_LIBSCTP, 1, [Define 1 to enable SCTP support])
+ AC_SUBST(HAVE_LIBSCTP, [1])
+ if test -n "$ac_lib"; then
+ AC_SUBST(LIBSCTP_LIBS, [-l$ac_lib])
+ fi
+ ], [
+ AC_MSG_ERROR([sctp_recvmsg not found in searched libs])])
+LIBS=$old_LIBS
+
+PKG_CHECK_MODULES(LIBASN1C, libasn1c >= 0.9.30)
+PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.6.0)
+PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.6.0)
+PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.6.0)
+PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.6.0)
+PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.1.0)
+PKG_CHECK_MODULES(LIBOSMOSIGTRAN, libosmo-sigtran >= 1.5.0)
+PKG_CHECK_MODULES(LIBOSMORUA, libosmo-rua >= 1.1.0)
+PKG_CHECK_MODULES(LIBOSMORANAP, libosmo-ranap >= 1.1.0)
+PKG_CHECK_MODULES(LIBOSMOHNBAP, libosmo-hnbap >= 1.1.0)
+
+
+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)
+
+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([-Wnull-dereference], [CFLAGS="$CFLAGS -Wnull-dereference"])
+AX_CHECK_COMPILE_FLAG([-Werror=sizeof-array-argument], [CFLAGS="$CFLAGS -Werror=sizeof-array-argument"])
+AX_CHECK_COMPILE_FLAG([-Werror=sizeof-pointer-memaccess], [CFLAGS="$CFLAGS -Werror=sizeof-pointer-memaccess"])
+
+# Coverage build taken from WebKit's configure.in
+AC_MSG_CHECKING([whether to enable code coverage support])
+AC_ARG_ENABLE(coverage,
+ AC_HELP_STRING([--enable-coverage],
+ [enable code coverage support [default=no]]),
+ [],[enable_coverage="no"])
+AC_MSG_RESULT([$enable_coverage])
+if test "$enable_coverage" = "yes"; then
+ COVERAGE_CFLAGS="-ftest-coverage -fprofile-arcs"
+ COVERAGE_LDFLAGS="-ftest-coverage -fprofile-arcs"
+ AC_SUBST([COVERAGE_CFLAGS])
+ AC_SUBST([COVERAGE_LDFLAGS])
+fi
+
+AC_ARG_ENABLE(profile,
+ [AS_HELP_STRING([--enable-profile], [Compile with profiling support enabled], )],
+ [profile=$enableval], [profile="no"])
+if test x"$profile" = x"yes"
+then
+ CFLAGS="$CFLAGS -pg"
+ CPPFLAGS="$CPPFLAGS -pg"
+fi
+
+AC_ARG_ENABLE([external_tests],
+ AC_HELP_STRING([--enable-external-tests],
+ [Include the VTY/CTRL tests in make check [default=no]]),
+ [enable_ext_tests="$enableval"],[enable_ext_tests="no"])
+if test "x$enable_ext_tests" = "xyes" ; then
+ AC_CHECK_PROG(PYTHON3_AVAIL,python3,yes)
+ if test "x$PYTHON3_AVAIL" != "xyes" ; then
+ AC_MSG_ERROR([Please install python3 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")
+
+# Generate manuals
+AC_ARG_ENABLE(manuals,
+ [AS_HELP_STRING(
+ [--enable-manuals],
+ [Generate manual PDFs [default=no]],
+ )],
+ [osmo_ac_build_manuals=$enableval], [osmo_ac_build_manuals="no"])
+AM_CONDITIONAL([BUILD_MANUALS], [test x"$osmo_ac_build_manuals" = x"yes"])
+AC_ARG_VAR(OSMO_GSM_MANUALS_DIR, [path to common osmo-gsm-manuals files, overriding pkg-config and "../osmo-gsm-manuals"
+ fallback])
+if test x"$osmo_ac_build_manuals" = x"yes"
+then
+ # Find OSMO_GSM_MANUALS_DIR (env, pkg-conf, fallback)
+ if test -n "$OSMO_GSM_MANUALS_DIR"; then
+ echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (from env)"
+ else
+ OSMO_GSM_MANUALS_DIR="$($PKG_CONFIG osmo-gsm-manuals --variable=osmogsmmanualsdir 2>/dev/null)"
+ if test -n "$OSMO_GSM_MANUALS_DIR"; then
+ echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (from pkg-conf)"
+ else
+ OSMO_GSM_MANUALS_DIR="../osmo-gsm-manuals"
+ echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (fallback)"
+ fi
+ fi
+ if ! test -d "$OSMO_GSM_MANUALS_DIR"; then
+ AC_MSG_ERROR("OSMO_GSM_MANUALS_DIR does not exist! Install osmo-gsm-manuals or set OSMO_GSM_MANUALS_DIR.")
+ fi
+
+ # Find and run check-depends
+ CHECK_DEPENDS="$OSMO_GSM_MANUALS_DIR/check-depends.sh"
+ if ! test -x "$CHECK_DEPENDS"; then
+ CHECK_DEPENDS="osmo-gsm-manuals-check-depends"
+ fi
+ if ! $CHECK_DEPENDS; then
+ AC_MSG_ERROR("missing dependencies for --enable-manuals")
+ fi
+
+ # Put in Makefile with absolute path
+ OSMO_GSM_MANUALS_DIR="$(realpath "$OSMO_GSM_MANUALS_DIR")"
+ AC_SUBST([OSMO_GSM_MANUALS_DIR])
+fi
+
+# 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(config.h)
+
+AC_OUTPUT(
+ include/Makefile
+ include/osmocom/Makefile
+ include/osmocom/hnbgw/Makefile
+ src/Makefile
+ src/osmo-hnbgw/Makefile
+ tests/Makefile
+ tests/atlocal
+ doc/Makefile
+ doc/examples/Makefile
+ doc/manuals/Makefile
+ contrib/Makefile
+ contrib/systemd/Makefile
+ contrib/osmo-hnbgw.spec
+ Makefile)
diff --git a/contrib/Makefile.am b/contrib/Makefile.am
new file mode 100644
index 0000000..3439c97
--- /dev/null
+++ b/contrib/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = systemd
diff --git a/contrib/jenkins.sh b/contrib/jenkins.sh
new file mode 100755
index 0000000..24607db
--- /dev/null
+++ b/contrib/jenkins.sh
@@ -0,0 +1,69 @@
+#!/usr/bin/env bash
+# jenkins build helper script for osmo-hnbgw. This is how we build on jenkins.osmocom.org
+#
+# environment variables:
+# * WITH_MANUALS: build manual PDFs if set to "1"
+# * PUBLISH: upload manuals after building if set to "1" (ignored without WITH_MANUALS = "1")
+#
+
+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
+
+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"
+export PATH="$inst/bin:$PATH"
+
+osmo-build-dep.sh libosmocore "" --disable-doxygen
+osmo-build-dep.sh libosmo-abis
+osmo-build-dep.sh libosmo-netif
+osmo-build-dep.sh libosmo-sccp
+osmo-build-dep.sh libasn1c
+osmo-build-dep.sh osmo-iuh
+
+# Additional configure options and depends
+CONFIG=""
+if [ "$WITH_MANUALS" = "1" ]; then
+ CONFIG="--enable-manuals"
+fi
+
+set +x
+echo
+echo
+echo
+echo " =============================== osmo-hnbgw ==============================="
+echo
+set -x
+
+cd "$base"
+autoreconf --install --force
+./configure --enable-sanitize --enable-external-tests $CONFIG
+$MAKE $PARALLEL_MAKE
+LD_LIBRARY_PATH="$inst/lib" $MAKE check \
+ || cat-testlogs.sh
+LD_LIBRARY_PATH="$inst/lib" \
+ DISTCHECK_CONFIGURE_FLAGS="--enable-vty-tests --enable-external-tests $CONFIG" \
+ $MAKE $PARALLEL_MAKE distcheck \
+ || cat-testlogs.sh
+
+if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
+ make -C "$base/doc/manuals" publish
+fi
+
+$MAKE $PARALLEL_MAKE maintainer-clean
+osmo-clean-workspace.sh
diff --git a/contrib/osmo-hnbgw.spec.in b/contrib/osmo-hnbgw.spec.in
new file mode 100644
index 0000000..5ab0f5a
--- /dev/null
+++ b/contrib/osmo-hnbgw.spec.in
@@ -0,0 +1,97 @@
+#
+# spec file for package osmo-hnbgw
+#
+# Copyright (c) 2017, Martin Hauke <mardnh(a)gmx.de>
+#
+# All modifications and additions to the file contributed by third parties
+# remain the property of their copyright owners, unless otherwise agreed
+# upon. The license for this file, and modifications and additions to the
+# file, is the same license as for the pristine package itself (unless the
+# license for the pristine package is not an Open Source License, in which
+# case the license is the MIT License). An "Open Source License" is a
+# license that conforms to the Open Source Definition (Version 1.9)
+# published by the Open Source Initiative.
+
+## Disable LTO for now since it breaks compilation of the tests
+## https://osmocom.org/issues/4113
+%define _lto_cflags %{nil}
+
+Name: osmo-hnbgw
+Version: @VERSION@
+Release: 0
+Summary: OsmoHNBGW: Osmocom's Base Station Controller for 2G CS mobile networks
+License: AGPL-3.0-or-later AND GPL-2.0-or-later
+Group: Hardware/Mobile
+URL: https://osmocom.org/projects/osmohnbgw
+Source: %{name}-%{version}.tar.xz
+BuildRequires: autoconf-archive
+BuildRequires: automake >= 1.9
+BuildRequires: libtool >= 2
+BuildRequires: lksctp-tools-devel
+BuildRequires: pkgconfig >= 0.20
+%if 0%{?suse_version}
+BuildRequires: systemd-rpm-macros
+%endif
+BuildRequires: pkgconfig(libcrypto) >= 0.9.5
+BuildRequires: pkgconfig(libosmo-netif) >= 1.1.0
+BuildRequires: pkgconfig(libosmo-sigtran) >= 1.5.0
+BuildRequires: pkgconfig(libosmoabis) >= 1.2.0
+BuildRequires: pkgconfig(libosmotrau) >= 1.2.0
+BuildRequires: pkgconfig(libosmocore) >= 1.6.0
+BuildRequires: pkgconfig(libosmoctrl) >= 1.6.0
+BuildRequires: pkgconfig(libosmogb) >= 1.6.0
+BuildRequires: pkgconfig(libosmogsm) >= 1.6.0
+BuildRequires: pkgconfig(libosmovty) >= 1.6.0
+BuildRequires: pkgconfig(libosmo-hnbap) >= 1.1.0
+BuildRequires: pkgconfig(libosmo-ranap) >= 1.1.0
+BuildRequires: pkgconfig(libosmo-rua) >= 1.1.0
+BuildRequires: pkgconfig(talloc)
+BuildRequires: pkgconfig(libasn1c) >= 0.9.30
+%{?systemd_requires}
+
+%description
+OsmoHNBGW: Osmocom's Home NodeB for 3G mobile networks.
+
+%prep
+%setup -q
+
+%build
+echo "%{version}" >.tarball-version
+autoreconf -fi
+%configure \
+ --docdir=%{_docdir}/%{name} \
+ --with-systemdsystemunitdir=%{_unitdir}
+make %{?_smp_mflags}
+
+%install
+%make_install
+
+%if 0%{?suse_version}
+%preun
+%service_del_preun %{name}.service
+
+%postun
+%service_del_postun %{name}.service
+
+%pre
+%service_add_pre %{name}.service
+
+%post
+%service_add_post %{name}.service
+%endif
+
+%check
+make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
+
+%files
+%license COPYING
+%doc AUTHORS README.md
+%{_bindir}/osmo-hnbgw
+%dir %{_docdir}/%{name}/examples
+%dir %{_docdir}/%{name}/examples/osmo-hnbgw
+%{_docdir}/%{name}/examples/osmo-hnbgw/osmo-hnbgw.cfg
+%dir %{_sysconfdir}/osmocom
+%config(noreplace) %{_sysconfdir}/osmocom/osmo-hnbgw.cfg
+%{_unitdir}/%{name}.service
+
+%changelog
diff --git a/contrib/systemd/Makefile.am b/contrib/systemd/Makefile.am
new file mode 100644
index 0000000..212601c
--- /dev/null
+++ b/contrib/systemd/Makefile.am
@@ -0,0 +1,6 @@
+EXTRA_DIST = osmo-hnbgw.service
+
+if HAVE_SYSTEMD
+systemdsystemunit_DATA = \
+ osmo-hnbgw.service
+endif
diff --git a/contrib/systemd/osmo-hnbgw.service b/contrib/systemd/osmo-hnbgw.service
new file mode 100644
index 0000000..7aca29f
--- /dev/null
+++ b/contrib/systemd/osmo-hnbgw.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=Osmocom Home Nodeb Gateway (OsmoHNBGW)
+
+[Service]
+Type=simple
+Restart=always
+ExecStart=/usr/bin/osmo-hnbgw -c /etc/osmocom/osmo-hnbgw.cfg
+RestartSec=2
+
+[Install]
+WantedBy=multi-user.target
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1 @@
+
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..ec63514
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+9
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..1ea3ad2
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,51 @@
+Source: osmo-hnbgw
+Section: net
+Priority: extra
+Maintainer: Osmocom team <openbsc(a)lists.osmocom.org>
+Build-Depends: debhelper (>=9),
+ dh-autoreconf,
+ autotools-dev,
+ autoconf,
+ autoconf-archive,
+ automake,
+ libtool,
+ pkg-config,
+ python3-minimal,
+ libtalloc-dev,
+ libasn1c-dev (>= 0.9.30),
+ libsctp-dev,
+ libosmocore-dev (>= 1.6.0),
+ libosmo-sigtran-dev (>= 1.5.0),
+ libosmo-abis-dev (>= 1.2.0),
+ libosmo-netif-dev (>= 1.1.0),
+ libosmo-hnbap-dev (>= 1.1.0),
+ libosmo-ranap-dev (>= 1.1.0),
+ libosmo-rua-dev (>= 1.1.0),
+ osmo-gsm-manuals-dev (>= 1.2.0)
+Standards-Version: 3.9.8
+Vcs-Git: git://git.osmocom.org/osmo-hnbgw.git
+Vcs-Browser: https://git.osmocom.org/osmo-hnbgw/
+Homepage: https://projects.osmocom.org/projects/osmo-hnbgw
+
+Package: osmo-hnbgw
+Architecture: any
+Multi-Arch: foreign
+Depends: ${misc:Depends}, ${shlibs:Depends}
+Recommends: osmo-mgw
+Description: OsmoHNBGW: Osmocom Home Node B Gateway
+
+Package: osmo-hnbgw-dbg
+Section: debug
+Architecture: any
+Multi-Arch: same
+Depends: osmo-hnbgw (= ${binary:Version}), ${misc:Depends}
+Description: OsmoHNBGW: Osmocom Home Node B Gateway
+
+Package: osmo-hnbgw-doc
+Architecture: all
+Section: doc
+Priority: optional
+Depends: ${misc:Depends}
+Description: ${misc:Package} PDF documentation
+ Various manuals: user manual, VTY reference manual and/or
+ protocol/interface manuals.
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..cfcb0de
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,19 @@
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: osmo-hnbgw
+Source: git://git.osmocom.org/osmo-hnbgw
+
+Files: *
+Copyright: 2021 sysmocom - s.f.m.c. GmbH <info(a)sysmocom.de>
+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/>.
diff --git a/debian/osmo-hnbgw.install b/debian/osmo-hnbgw.install
new file mode 100644
index 0000000..ac1f931
--- /dev/null
+++ b/debian/osmo-hnbgw.install
@@ -0,0 +1,4 @@
+etc/osmocom/osmo-hnbgw.cfg
+lib/systemd/system/osmo-hnbgw.service
+usr/bin/osmo-hnbgw
+usr/share/doc/osmo-hnbgw/examples/osmo-hnbgw/osmo-hnbgw.cfg usr/share/doc/osmo-hnbgw/examples
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..bce8ed6
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,66 @@
+#!/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
+CONFIGURE_FLAGS += --with-systemdsystemunitdir=/lib/systemd/system --enable-manuals
+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-practic…
+override_dh_strip:
+ dh_strip -posmo-hnbgw --dbg-package=osmo-hnbgw-dbg
+
+# Print test results in case of a failure
+override_dh_auto_test:
+ dh_auto_test || (find . -name testsuite.log -exec cat {} \; ; false)
+
+# Don't create .pdf.gz files (barely saves space and they can't be opened directly by most pdf readers)
+override_dh_compress:
+ dh_compress -X.pdf
diff --git a/debian/source/format b/debian/source/format
new file mode 100644
index 0000000..89ae9db
--- /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 0000000..15f36b7
--- /dev/null
+++ b/doc/Makefile.am
@@ -0,0 +1,4 @@
+SUBDIRS = \
+ examples \
+ manuals \
+ $(NULL)
diff --git a/doc/examples/Makefile.am b/doc/examples/Makefile.am
new file mode 100644
index 0000000..d1c6d51
--- /dev/null
+++ b/doc/examples/Makefile.am
@@ -0,0 +1,30 @@
+OSMOCONF_FILES = \
+ osmo-hnbgw/osmo-hnbgw.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-hnbgw/osmo-hnbgw.cfg b/doc/examples/osmo-hnbgw/osmo-hnbgw.cfg
new file mode 100644
index 0000000..9286b2a
--- /dev/null
+++ b/doc/examples/osmo-hnbgw/osmo-hnbgw.cfg
@@ -0,0 +1,25 @@
+!
+! OsmoHNBGW (0) configuration saved from vty
+!!
+!
+log stderr
+ logging filter all 1
+ logging color 1
+ logging print category 1
+ logging timestamp 1
+ logging print extended-timestamp 1
+ logging level all 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
+ logging level lctrl notice
+ logging level lgtp notice
+ logging level lstats notice
+hnbgw
+ iuh
+ local-ip 0.0.0.0
+ hnbap-allow-tmsi 1
diff --git a/doc/manuals/Makefile.am b/doc/manuals/Makefile.am
new file mode 100644
index 0000000..595f157
--- /dev/null
+++ b/doc/manuals/Makefile.am
@@ -0,0 +1,25 @@
+EXTRA_DIST = \
+ osmohnbgw-usermanual.adoc \
+ osmohnbgw-usermanual-docinfo.xml \
+ chapters \
+ osmohnbgw-vty-reference.xml \
+ regen_doc.sh \
+ vty
+
+if BUILD_MANUALS
+ ASCIIDOC = osmohnbgw-usermanual.adoc
+ ASCIIDOC_DEPS = $(srcdir)/chapters/*.adoc
+ include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.asciidoc.inc
+
+ VTY_REFERENCE = osmohnbgw-vty-reference.xml
+
+ BUILT_REFERENCE_XML = $(builddir)/vty/hnbgw_vty_reference.xml
+ $(builddir)/vty/hnbgw_vty_reference.xml: $(top_builddir)/src/osmo-hnbgw
+ mkdir -p $(builddir)/vty
+ $(top_builddir)/src/osmo-hnbgw --vty-ref-xml > $@
+
+ include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc
+
+ OSMO_REPOSITORY = osmo-hnbgw
+ include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.common.inc
+endif
diff --git a/doc/manuals/chapters/overview.adoc b/doc/manuals/chapters/overview.adoc
new file mode 100644
index 0000000..2368b4f
--- /dev/null
+++ b/doc/manuals/chapters/overview.adoc
@@ -0,0 +1,56 @@
+[[overview]]
+== Overview
+
+
+[[intro_overview]]
+=== About OsmoHNBGW
+
+OsmoHNBGW implements the Home NodeB Gateway function in the 3G network architecture. It serves
+as a gateway between the classic 3G core network (CN) domain with its IuCS and IuPS interface
+and the femtocell based RAN.
+
+A typical 3G network consisting of Osmocom components will look as illustrated in the following
+diagram:
+
+[[fig-3g]]
+.Typical 3G network architecture used with OsmoHNBGW
+----
+ +------------+ +--------+ +----------+ +---------+
+ UE <-->| hNodeB |<--Iuh---->| HNB-GW |<--IuCS-->| OsmoMSC |<--GSUP-->| OsmoHLR |
+ UE <-->| femto cell | ...-->| | ...-->| | | |
+ | | | | +----------+ +---------|
+ +------------+<--GTP-U | |
+ \ | | +------+ +------+
+ | | |<--IuPS-->| SGSN |<--GTP-C-->| GGSN |
+ | +--------+ ...-->| | GTP-U-->| |
+ | +------+ / +------+
+ \_______________________________/
+----
+
+The HNB-GW performs a translation interface between the IuCS/IuPS interfaces on the one hand
+side, and the Iuh interface on the or ther hand:
+
+----
+ Iuh IuCS/IuPS
+
+NAS +----+----+ +----+----+
+Non-Access Stratum | CC | MM | | CC | MM |
+- - - - - - - - - - - +----+----+-------+ +----+----+
+ | RANAP | | H | RANAP |
+Access Stratum +---------+ HNBAP | N +---------+ - - SCCP USER SAP
+ | RUA | | B | SUA | \
+ +---------+-------+ - +---------+ |
+ | SCTP | G | SCTP | } SIGTRAN
+ +-----------------+ W +---------+ |
+ | IP | | IP | /
+ +-----------------+ +---------+
+----
+
+On the femtocell (Home NodeB) side, OsmoHNBGW implements the Iuh interface as specified by 3GPP.
+
+=== The Iuh interface
+
+Iuh consists of the following sub-layers:
+
+- HNBAP (Home NodeB Application Part)
+- RUA (RANAP User Adaptation, between RANAP and SCTP
diff --git a/doc/manuals/chapters/running.adoc b/doc/manuals/chapters/running.adoc
new file mode 100644
index 0000000..f40027a
--- /dev/null
+++ b/doc/manuals/chapters/running.adoc
@@ -0,0 +1,119 @@
+== Running OsmoHNBGW
+
+The OsmoHNBGW executable (`osmo-hnbgw`) offers the following command-line
+arguments:
+
+=== SYNOPSIS
+
+*osmo-hnbgw* [-h|-V] [-d 'DBGMASK'] [-D] [-c 'CONFIGFILE'] [-s] [-T] [-e 'LOGLEVEL']
+
+=== OPTIONS
+
+*-h, --help*::
+ Print a short help message about the supported options
+*-V, --version*::
+ Print the compile-time version number of the OsmoHNBGW program
+*-d, --debug 'DBGMASK','DBGLEVELS'*::
+ Set the log subsystems and levels for logging to stderr. This
+ has mostly been superseded by VTY-based logging configuration,
+ see <<logging>> for further information.
+*-D, --daemonize*::
+ Fork the process as a daemon into background.
+*-c, --config-file 'CONFIGFILE'*::
+ Specify the file and path name of the configuration file to be
+ used. If none is specified, use `osmo-msc.cfg` in the current
+ working directory.
+*-s, --disable-color*::
+ Disable colors for logging to stderr. This has mostly been
+ deprecated by VTY based logging configuration, see <<logging>>
+ for more information.
+*-T, --timestamp*::
+ Enable time-stamping of log messages to stderr. This has mostly
+ been deprecated by VTY based logging configuration, see
+ <<logging>> for more information.
+*-e, --log-level 'LOGLEVEL'*::
+ Set the global log level for logging to stderr. This has mostly
+ been deprecated by VTY based logging configuration, see
+ <<logging>> for more information.
+
+
+=== Multiple instances
+
+Running multiple instances of `osmo-hnbgw` on the same computer is possible if
+all interfaces (VTY, CTRL, Iuh) are separated using the appropriate
+configuration options. The IP based interfaces are binding to local host by
+default. In order to separate the processes, the user has to bind those
+services to specific but different IP addresses and/or ports.
+
+The VTY and the Control interface can be bound to IP addresses from the loopback
+address range, for example:
+
+----
+line vty
+ bind 127.0.0.2
+ctrl
+ bind 127.0.0.2
+----
+
+The Iuh interface can be bound to an individual port:
+
+----
+hnbgw
+ iuh
+ local-ip 0.0.0.0
+ local-port 29169
+----
+
+For the following links, OsmoHNBGW acts as a client and does not listen/bind to a
+specific interface, and will hence not encounter conflicts for multiple instances
+running on the same interface:
+
+- The SCCP/M3UA links are established by OsmoHNBGW contacting an STP.
+
+To run multiple OsmoHNBGW instances on the same SCCP routing, each HNBGW has to
+configure a distinct point-code, see <<configure_iucs_iups>>.
+
+
+=== Configuring Primary Links
+
+[[configure_iucs_iups]]
+==== Configure SCCP/M3UA to connect to an MSC's _IuCS_ and an SGSN's _IuPS_ interface
+
+OsmoHNBGW acts as client to contact an STP instance and establish an SCCP/M3UA
+link.
+
+An example configuration of OsmoHNBGW's SCCP link:
+
+----
+cs7 instance 0
+ point-code 0.23.5
+ asp asp-clnt-OsmoHNBGW 2905 0 m3ua
+ remote-ip 127.0.0.1
+ sctp-role client
+ sccp-address msc
+ routing-indicator PC
+ point-code 0.23.1
+ sccp-address sgsn
+ routing-indicator PC
+ point-code 0.23.2
+hnbgw
+ iucs
+ remote-addr msc
+ iups
+ remote-addr sgsn
+----
+
+This configuration is explained in detail in <<cs7_config>>.
+
+==== Configure RUA to accept Iuh connections from hNodeB
+
+OsmoHNBGW acts as server to accept Iuh connections from hNodeB devices.
+
+An example configuration for OsmoHNBGW's RUA server:
+
+----
+hnbgw
+ iuh
+ local-ip 10.9.8.7
+ local-port 29169
+----
diff --git a/doc/manuals/osmohnbgw-usermanual-docinfo.xml b/doc/manuals/osmohnbgw-usermanual-docinfo.xml
new file mode 100644
index 0000000..404d2a1
--- /dev/null
+++ b/doc/manuals/osmohnbgw-usermanual-docinfo.xml
@@ -0,0 +1,51 @@
+<revhistory>
+ <revision>
+ <revnumber>1</revnumber>
+ <date>November 30th, 2019</date>
+ <authorinitials>HW</authorinitials>
+ <revremark>
+ Initial version
+ </revremark>
+ </revision>
+</revhistory>
+
+<authorgroup>
+ <author>
+ <firstname>Harald</firstname>
+ <surname>Welte</surname>
+ <email>hwelte(a)sysmocom.de</email>
+ <authorinitials>HW</authorinitials>
+ <affiliation>
+ <shortaffil>sysmocom</shortaffil>
+ <orgname>sysmocom - s.f.m.c. GmbH</orgname>
+ <jobtitle>Managing Director</jobtitle>
+ </affiliation>
+ </author>
+</authorgroup>
+
+<copyright>
+ <year>2019</year>
+ <holder>sysmocom - s.f.m.c. GmbH</holder>
+</copyright>
+
+<legalnotice>
+ <para>
+ Permission is granted to copy, distribute and/or modify this
+ document under the terms of the GNU Free Documentation License,
+ Version 1.3 or any later version published by the Free Software
+ Foundation; with the Invariant Sections being just 'Foreword',
+ 'Acknowledgements' and 'Preface', with no Front-Cover Texts,
+ and no Back-Cover Texts. A copy of the license is included in
+ the section entitled "GNU Free Documentation License".
+ </para>
+ <para>
+ The Asciidoc source code of this manual can be found at
+ <ulink url="http://git.osmocom.org/osmo-hnbgw/">
+ http://git.osmocom.org/osmo-hnbgw/
+ </ulink>
+ and of the common chapters at
+ <ulink url="http://git.osmocom.org/osmo-gsm-manuals/">
+ http://git.osmocom.org/osmo-gsm-manuals/
+ </ulink>
+ </para>
+</legalnotice>
diff --git a/doc/manuals/osmohnbgw-usermanual.adoc b/doc/manuals/osmohnbgw-usermanual.adoc
new file mode 100644
index 0000000..21faed7
--- /dev/null
+++ b/doc/manuals/osmohnbgw-usermanual.adoc
@@ -0,0 +1,37 @@
+:gfdl-enabled:
+:program-name: OsmoHNBGW
+
+OsmoHNBGW User Manual
+=====================
+Harald Welte <hwelte(a)sysmocom.de>
+
+
+include::./common/chapters/preface.adoc[]
+
+include::{srcdir}/chapters/overview.adoc[]
+
+include::{srcdir}/chapters/running.adoc[]
+
+// include::{srcdir}/chapters/control.adoc[]
+
+// include::./common/chapters/counters-overview.adoc[]
+
+// include::{srcdir}/chapters/counters.adoc[]
+
+include::./common/chapters/vty.adoc[]
+
+include::./common/chapters/logging.adoc[]
+
+include::./common/chapters/cs7-config.adoc[]
+
+// include::{srcdir}/chapters/net.adoc[]
+
+// include::./common/chapters/control_if.adoc[]
+
+include::./common/chapters/port_numbers.adoc[]
+
+include::./common/chapters/bibliography.adoc[]
+
+include::./common/chapters/glossary.adoc[]
+
+include::./common/chapters/gfdl.adoc[]
diff --git a/doc/manuals/osmohnbgw-vty-reference.xml b/doc/manuals/osmohnbgw-vty-reference.xml
new file mode 100644
index 0000000..cd69333
--- /dev/null
+++ b/doc/manuals/osmohnbgw-vty-reference.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ex:ts=2:sw=42sts=2:et
+ -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
+-->
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML 5.0//EN"
+"http://docbook.org/xml/5.0/dtd/docbook.dtd" [
+<!ENTITY chapter-vty SYSTEM "./common/chapters/vty.xml" >
+<!ENTITY sections-vty SYSTEM "generated/docbook_vty.xml" >
+]>
+
+<book>
+ <info>
+ <revhistory>
+ <revision>
+ <revnumber>v1</revnumber>
+ <date>29th July 2019</date>
+ <authorinitials>dw</authorinitials>
+ <revremark>Initial</revremark>
+ </revision>
+ </revhistory>
+
+ <title>OsmoHNBGW VTY Reference</title>
+
+ <copyright>
+ <year>2019</year>
+ </copyright>
+
+ <legalnotice>
+ <para>This work is copyright by <orgname>sysmocom - s.f.m.c. GmbH</orgname>. All rights reserved.
+ </para>
+ </legalnotice>
+ </info>
+
+ <!-- Main chapters-->
+ &chapter-vty;
+</book>
+
diff --git a/doc/manuals/regen_doc.sh b/doc/manuals/regen_doc.sh
new file mode 100755
index 0000000..39dd9ee
--- /dev/null
+++ b/doc/manuals/regen_doc.sh
@@ -0,0 +1,17 @@
+#!/bin/sh -x
+
+if [ -z "$DOCKER_PLAYGROUND" ]; then
+ echo "You need to set DOCKER_PLAYGROUND"
+ exit 1
+fi
+
+SCRIPT=$(realpath "$0")
+MANUAL_DIR=$(dirname "$SCRIPT")
+
+COMMIT=${COMMIT:-$(git log -1 --format=format:%H)}
+
+cd "$DOCKER_PLAYGROUND/scripts" || exit 1
+
+OSMO_SGSN_BRANCH=$COMMIT ./regen_doc.sh osmo-hnbgw 4261 \
+ "$MANUAL_DIR/chapters/counters_generated.adoc" \
+ "$MANUAL_DIR/vty/hnbgw_vty_reference.xml"
diff --git a/doc/manuals/vty/hnbgw_vty_additions.xml b/doc/manuals/vty/hnbgw_vty_additions.xml
new file mode 100644
index 0000000..a4c675e
--- /dev/null
+++ b/doc/manuals/vty/hnbgw_vty_additions.xml
@@ -0,0 +1,2 @@
+<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>
+</vtydoc>
diff --git a/doc/protocols_around_hnbgw.txt b/doc/protocols_around_hnbgw.txt
new file mode 100644
index 0000000..3eef155
--- /dev/null
+++ b/doc/protocols_around_hnbgw.txt
@@ -0,0 +1,60 @@
+Protocols Around the Home Node B Gateway
+========================================
+
+ +--------+
+ ,-->| Osmo |
+ / | MGCPGW |
+ | | |<--MGCP
+ | +--------+ \
+ / |
+ +------------+<--RTP +--------+ `->+----------+
+ UE <-->| hNodeB | | Osmo | | OsmoMSC | +------+
+ UE <-->| femto cell |<--Iuh---->| HNB-GW |<--IuCS-->| | | Osmo |
+ | | | | | (VLR)|<-GSUP->| HLR |
+ | | | | +----------+ GSUP->+------+
+ +------------+<--GTP-U | | /
+ \ | | +------+<---' +------+
+ | | |<--IuPS-->| Osmo |<--GTP-C--->| Open |
+ | +--------+ | SGSN | GTP-U--->| GGSN |
+ | +------+ / +------+
+ \_______________________________/
+
+
+
+ Iuh IuCS/IuPS
+
+NAS +----+----+ +----+----+
+Non-Access Stratum | CC | MM | | CC | MM |
+- - - - - - - - - - - +----+----+-------+ +----+----+
+ | RANAP | | H | RANAP |
+Access Stratum +---------+ HNBAP | N +---------+ - - SCCP USER SAP
+ | RUA | | B | SUA | \
+ +---------+-------+ - +---------+ |
+ | SCTP | G | SCTP | } SIGTRAN
+ +-----------------+ W +---------+ |
+ | IP | | IP | /
+ +-----------------+ +---------+
+
+
+Various SIGTRAN implementations:
+
+ IuCS/IuPS
+ usual
+ | simplest
+ | |
+ v v
+ +------+------+------+-----+
+ | SCCP | SCCP | | |
+ +------+------+ SCCP | |
+ | MTP3 | MTP3 | | |
+ +------+------+------+ SUA |
+ | MTP2 | | | |
+ +------+ M2UA | M3UA | |
+ | M2PA | | | |
+ +------+------+------+-----+
+ | SCTP |
+ +--------------------------+
+ | IP |
+ +--------------------------+
+
+UE (User Endpoint) == MS (Mobile Subscriber) == mobile device
diff --git a/git-version-gen b/git-version-gen
new file mode 100755
index 0000000..42cf3d2
--- /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 0000000..9d963a0
--- /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 0000000..50c69be
--- /dev/null
+++ b/include/osmocom/Makefile.am
@@ -0,0 +1,3 @@
+SUBDIRS = \
+ hnbgw \
+ $(NULL)
diff --git a/include/osmocom/hnbgw/Makefile.am b/include/osmocom/hnbgw/Makefile.am
new file mode 100644
index 0000000..b2a667d
--- /dev/null
+++ b/include/osmocom/hnbgw/Makefile.am
@@ -0,0 +1,4 @@
+noinst_HEADERS = \
+ vty.h \
+ context_map.h hnbgw.h hnbgw_cn.h \
+ hnbgw_hnbap.h hnbgw_rua.h hnbgw_ranap.h
diff --git a/include/osmocom/hnbgw/context_map.h b/include/osmocom/hnbgw/context_map.h
new file mode 100644
index 0000000..6279b91
--- /dev/null
+++ b/include/osmocom/hnbgw/context_map.h
@@ -0,0 +1,51 @@
+#pragma once
+
+#include <stdint.h>
+#include <osmocom/core/linuxlist.h>
+
+enum hnbgw_context_map_state {
+ MAP_S_NULL,
+ MAP_S_ACTIVE, /* currently active map */
+ MAP_S_RESERVED1, /* just disconnected, still resrved */
+ MAP_S_RESERVED2, /* still reserved */
+ MAP_S_NUM_STATES /* Number of states, keep this at the end */
+};
+
+extern const struct value_string hnbgw_context_map_state_names[];
+static inline const char *hnbgw_context_map_state_name(enum hnbgw_context_map_state val)
+{ return get_value_string(hnbgw_context_map_state_names, val); }
+
+struct hnb_context;
+struct hnbgw_cnlink;
+
+struct hnbgw_context_map {
+ /* entry in the per-CN list of mappings */
+ struct llist_head cn_list;
+ /* entry in the per-HNB list of mappings */
+ struct llist_head hnb_list;
+ /* pointer to HNB */
+ struct hnb_context *hnb_ctx;
+ /* pointer to CN */
+ struct hnbgw_cnlink *cn_link;
+ /* RUA contxt ID */
+ uint32_t rua_ctx_id;
+ /* False for CS, true for PS */
+ bool is_ps;
+ /* SCCP User SAP connection ID */
+ uint32_t scu_conn_id;
+
+ enum hnbgw_context_map_state state;
+};
+
+
+struct hnbgw_context_map *
+context_map_alloc_by_hnb(struct hnb_context *hnb, uint32_t rua_ctx_id,
+ bool is_ps,
+ struct hnbgw_cnlink *cn_if_new);
+
+struct hnbgw_context_map *
+context_map_by_cn(struct hnbgw_cnlink *cn, uint32_t scu_conn_id);
+
+void context_map_deactivate(struct hnbgw_context_map *map);
+
+int context_map_init(struct hnb_gw *gw);
diff --git a/include/osmocom/hnbgw/hnbgw.h b/include/osmocom/hnbgw/hnbgw.h
new file mode 100644
index 0000000..fc8298d
--- /dev/null
+++ b/include/osmocom/hnbgw/hnbgw.h
@@ -0,0 +1,174 @@
+#pragma once
+
+#include <osmocom/core/select.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/write_queue.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/sigtran/sccp_sap.h>
+#include <osmocom/sigtran/osmo_ss7.h>
+#include <osmocom/ctrl/control_if.h>
+#define DEBUG
+#include <osmocom/core/logging.h>
+
+
+enum {
+ DMAIN,
+ DHNBAP,
+ DRUA,
+ DRANAP,
+};
+
+#define LOGHNB(x, ss, lvl, fmt, args ...) \
+ LOGP(ss, lvl, "%s " fmt, hnb_context_name(x), ## args)
+
+enum hnb_ctrl_node {
+ CTRL_NODE_HNB = _LAST_CTRL_NODE,
+ _LAST_CTRL_NODE_HNB
+};
+
+#define HNBGW_LOCAL_IP_DEFAULT "0.0.0.0"
+/* TODO: CS and PS now both connect to OsmoSTP, i.e. that's always going to be the same address. Drop the
+ * duplicity. */
+#define HNBGW_IUCS_REMOTE_IP_DEFAULT "127.0.0.1"
+#define HNBGW_IUPS_REMOTE_IP_DEFAULT "127.0.0.1"
+
+/* 25.467 Section 7.1 */
+#define IUH_DEFAULT_SCTP_PORT 29169
+#define RNA_DEFAULT_SCTP_PORT 25471
+
+#define IUH_PPI_RUA 19
+#define IUH_PPI_HNBAP 20
+#define IUH_PPI_SABP 31
+#define IUH_PPI_RNA 42
+#define IUH_PPI_PUA 55
+
+#define IUH_MSGB_SIZE 2048
+
+struct umts_cell_id {
+ uint16_t mcc; /*!< Mobile Country Code */
+ uint16_t mnc; /*!< Mobile Network Code */
+ uint16_t lac; /*!< Locaton Area Code */
+ uint16_t rac; /*!< Routing Area Code */
+ uint16_t sac; /*!< Service Area Code */
+ uint32_t cid; /*!< Cell ID */
+};
+
+struct hnb_gw;
+
+enum hnbgw_cnlink_state {
+ /* we have just been initialized or were disconnected */
+ CNLINK_S_NULL,
+ /* establishment of the SUA/SCCP link is pending */
+ CNLINK_S_EST_PEND,
+ /* establishment of the SUA/SCCP link was confirmed */
+ CNLINK_S_EST_CONF,
+ /* we have esnt the RANAP RESET and wait for the ACK */
+ CNLINK_S_EST_RST_TX_WAIT_ACK,
+ /* we have received the RANAP RESET ACK and are active */
+ CNLINK_S_EST_ACTIVE,
+};
+
+struct hnbgw_cnlink {
+ struct llist_head list;
+ enum hnbgw_cnlink_state state;
+ struct hnb_gw *gw;
+ /* timer for re-transmitting the RANAP Reset */
+ struct osmo_timer_list T_RafC;
+ /* reference to the SCCP User SAP by which we communicate */
+ struct osmo_sccp_instance *sccp;
+ struct osmo_sccp_user *sccp_user;
+ uint32_t next_conn_id;
+
+ /* linked list of hnbgw_context_map */
+ struct llist_head map_list;
+};
+
+struct hnb_context {
+ /*! Entry in HNB-global list of HNB */
+ struct llist_head list;
+ /*! HNB-GW we are part of */
+ struct hnb_gw *gw;
+ /*! SCTP socket + write queue for Iuh to this specific HNB */
+ struct osmo_stream_srv *conn;
+ /*! copied from HNB-Identity-Info IE */
+ char identity_info[256];
+ /*! copied from Cell Identity IE */
+ struct umts_cell_id id;
+
+ /*! SCTP stream ID for HNBAP */
+ uint16_t hnbap_stream;
+ /*! SCTP stream ID for RUA */
+ uint16_t rua_stream;
+
+ /*! True if a HNB-REGISTER-REQ from this HNB has been accepted. Note that
+ * this entire data structure is freed if the HNB sends HNB-DE-REGISTER-REQ. */
+ bool hnb_registered;
+
+ /* linked list of hnbgw_context_map */
+ struct llist_head map_list;
+};
+
+struct ue_context {
+ /*! Entry in the HNB-global list of UE */
+ struct llist_head list;
+ /*! Unique Context ID for this UE */
+ uint32_t context_id;
+ char imsi[16+1];
+ uint32_t tmsi;
+ /*! UE is serviced via this HNB */
+ struct hnb_context *hnb;
+};
+
+struct hnb_gw {
+ struct {
+ const char *iuh_local_ip;
+ /*! SCTP port for Iuh listening */
+ uint16_t iuh_local_port;
+ /*! The UDP port where we receive multiplexed CS user
+ * plane traffic from HNBs */
+ uint16_t iuh_cs_mux_port;
+ const char *iucs_remote_addr_name;
+ const char *iups_remote_addr_name;
+ uint16_t rnc_id;
+ bool hnbap_allow_tmsi;
+ /*! print hnb-id (true) or MCC-MNC-LAC-RAC-SAC (false) in logs */
+ bool log_prefix_hnb_id;
+ } config;
+ /*! SCTP listen socket for incoming connections */
+ struct osmo_stream_srv_link *iuh;
+ /* list of struct hnb_context */
+ struct llist_head hnb_list;
+ /* list of struct ue_context */
+ struct llist_head ue_list;
+ /* next availble UE Context ID */
+ uint32_t next_ue_ctx_id;
+ struct ctrl_handle *ctrl;
+ /* currently active CN links for CS and PS */
+ struct {
+ struct osmo_sccp_instance *client;
+ struct hnbgw_cnlink *cnlink;
+ struct osmo_sccp_addr local_addr;
+ struct osmo_sccp_addr iucs_remote_addr;
+ struct osmo_sccp_addr iups_remote_addr;
+ } sccp;
+};
+
+extern void *talloc_asn1_ctx;
+
+struct hnb_context *hnb_context_by_id(struct hnb_gw *gw, uint32_t cid);
+struct hnb_context *hnb_context_by_identity_info(struct hnb_gw *gw, const char *identity_info);
+const char *hnb_context_name(struct hnb_context *ctx);
+unsigned hnb_contexts(const struct hnb_gw *gw);
+
+struct ue_context *ue_context_by_id(struct hnb_gw *gw, uint32_t id);
+struct ue_context *ue_context_by_imsi(struct hnb_gw *gw, const char *imsi);
+struct ue_context *ue_context_by_tmsi(struct hnb_gw *gw, uint32_t tmsi);
+struct ue_context *ue_context_alloc(struct hnb_context *hnb, const char *imsi,
+ uint32_t tmsi);
+void ue_context_free(struct ue_context *ue);
+
+struct hnb_context *hnb_context_alloc(struct hnb_gw *gw, struct osmo_stream_srv_link *link, int new_fd);
+void hnb_context_release(struct hnb_context *ctx);
+
+void hnbgw_vty_init(struct hnb_gw *gw, void *tall_ctx);
+int hnbgw_vty_go_parent(struct vty *vty);
diff --git a/include/osmocom/hnbgw/hnbgw_cn.h b/include/osmocom/hnbgw/hnbgw_cn.h
new file mode 100644
index 0000000..b481a69
--- /dev/null
+++ b/include/osmocom/hnbgw/hnbgw_cn.h
@@ -0,0 +1,5 @@
+#pragma once
+
+#include <osmocom/hnbgw/hnbgw.h>
+
+int hnbgw_cnlink_init(struct hnb_gw *gw, const char *stp_host, uint16_t stp_port, const char *local_ip);
diff --git a/include/osmocom/hnbgw/hnbgw_hnbap.h b/include/osmocom/hnbgw/hnbgw_hnbap.h
new file mode 100644
index 0000000..d0af955
--- /dev/null
+++ b/include/osmocom/hnbgw/hnbgw_hnbap.h
@@ -0,0 +1,6 @@
+#pragma once
+
+#include <osmocom/hnbgw/hnbgw.h>
+
+int hnbgw_hnbap_rx(struct hnb_context *hnb, struct msgb *msg);
+int hnbgw_hnbap_init(void);
diff --git a/include/osmocom/hnbgw/hnbgw_ranap.h b/include/osmocom/hnbgw/hnbgw_ranap.h
new file mode 100644
index 0000000..a5d7380
--- /dev/null
+++ b/include/osmocom/hnbgw/hnbgw_ranap.h
@@ -0,0 +1,6 @@
+#pragma once
+
+#include <osmocom/hnbgw/hnbgw.h>
+
+int hnbgw_ranap_rx(struct msgb *msg, uint8_t *data, size_t len);
+int hnbgw_ranap_init(void);
diff --git a/include/osmocom/hnbgw/hnbgw_rua.h b/include/osmocom/hnbgw/hnbgw_rua.h
new file mode 100644
index 0000000..4b65115
--- /dev/null
+++ b/include/osmocom/hnbgw/hnbgw_rua.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include <osmocom/hnbgw/hnbgw.h>
+#include <osmocom/rua/RUA_Cause.h>
+
+int hnbgw_rua_rx(struct hnb_context *hnb, struct msgb *msg);
+int hnbgw_rua_init(void);
+
+int rua_tx_udt(struct hnb_context *hnb, const uint8_t *data, unsigned int len);
+int rua_tx_dt(struct hnb_context *hnb, int is_ps, uint32_t context_id,
+ const uint8_t *data, unsigned int len);
+int rua_tx_disc(struct hnb_context *hnb, int is_ps, uint32_t context_id,
+ const RUA_Cause_t *cause, const uint8_t *data, unsigned int len);
diff --git a/include/osmocom/hnbgw/vty.h b/include/osmocom/hnbgw/vty.h
new file mode 100644
index 0000000..3d05da5
--- /dev/null
+++ b/include/osmocom/hnbgw/vty.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include <osmocom/vty/vty.h>
+
+enum osmo_iuh_vty_node {
+ HNBGW_NODE = _LAST_OSMOVTY_NODE + 1,
+ IUH_NODE,
+ IUCS_NODE,
+ IUPS_NODE,
+};
+
diff --git a/osmoappdesc.py b/osmoappdesc.py
new file mode 100644
index 0000000..151abe9
--- /dev/null
+++ b/osmoappdesc.py
@@ -0,0 +1,27 @@
+#!/usr/bin/env python3
+
+# (C) 2021 by sysmocom - s.m.f.c. GmbH <info(a)sysmocom.de>
+# 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 = {
+ "osmo-hnbgw": ["doc/examples/osmo-hnbgw/osmo-hnbgw.cfg"]
+}
+
+apps = [(4273, "src/osmo-hnbgw/osmo-hnbgw", "OsmoHNBGW", "osmo-hnbgw")
+ ]
+
+vty_command = ["./src/osmo-hnbgw/osmo-hnbgw", "-c",
+ "doc/examples/osmo-hnbgw/osmo-hnbgw.cfg"]
+
+vty_app = apps[0]
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..d83bbf0
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,3 @@
+SUBDIRS = \
+ osmo-hnbgw \
+ $(NULL)
diff --git a/src/osmo-hnbgw/Makefile.am b/src/osmo-hnbgw/Makefile.am
new file mode 100644
index 0000000..bda8633
--- /dev/null
+++ b/src/osmo-hnbgw/Makefile.am
@@ -0,0 +1,55 @@
+AM_CPPFLAGS = \
+ $(all_includes) \
+ -I$(top_srcdir)/include \
+ -I$(top_builddir) \
+ $(NULL)
+
+AM_CFLAGS = \
+ -Wall \
+ $(LIBASN1C_CFLAGS) \
+ $(LIBOSMOCORE_CFLAGS) \
+ $(LIBOSMOGSM_CFLAGS) \
+ $(LIBOSMOVTY_CFLAGS) \
+ $(LIBOSMOCTRL_CFLAGS) \
+ $(LIBOSMONETIF_CFLAGS) \
+ $(COVERAGE_CFLAGS) \
+ $(LIBOSMOABIS_CFLAGS) \
+ $(LIBOSMOTRAU_CFLAGS) \
+ $(LIBOSMOSIGTRAN_CFLAGS) \
+ $(LIBOSMORUA_CFLAGS) \
+ $(LIBOSMORANAP_CFLAGS) \
+ $(LIBOSMOHNBAP_CFLAGS) \
+ $(NULL)
+
+AM_LDFLAGS = \
+ $(COVERAGE_LDFLAGS) \
+ $(NULL)
+
+bin_PROGRAMS = \
+ osmo-hnbgw \
+ $(NULL)
+
+osmo_hnbgw_SOURCES = \
+ hnbgw.c \
+ hnbgw_hnbap.c \
+ hnbgw_rua.c \
+ hnbgw_ranap.c \
+ hnbgw_vty.c \
+ context_map.c \
+ hnbgw_cn.c \
+ $(NULL)
+
+osmo_hnbgw_LDADD = \
+ $(LIBASN1C_LIBS) \
+ $(LIBOSMOCORE_LIBS) \
+ $(LIBOSMOGSM_LIBS) \
+ $(LIBOSMOVTY_LIBS) \
+ $(LIBOSMOCTRL_LIBS) \
+ $(LIBOSMONETIF_LIBS) \
+ $(COVERAGE_LDFLAGS) \
+ $(LIBOSMOSIGTRAN_LIBS) \
+ $(LIBOSMORUA_LIBS) \
+ $(LIBOSMORANAP_LIBS) \
+ $(LIBOSMOHNBAP_LIBS) \
+ $(LIBSCTP_LIBS) \
+ $(NULL)
diff --git a/src/osmo-hnbgw/context_map.c b/src/osmo-hnbgw/context_map.c
new file mode 100644
index 0000000..09aa965
--- /dev/null
+++ b/src/osmo-hnbgw/context_map.c
@@ -0,0 +1,181 @@
+/* Mapper between RUA ContextID (24 bit, per HNB) and the SUA/SCCP
+ * Connection ID (32bit, per signalling link) */
+
+/* (C) 2015 by Harald Welte <laforge(a)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/>.
+ *
+ */
+
+/* an expired mapping is destroyed after 1..2 * EXPIRY_TIMER_SECS */
+#define EXPIRY_TIMER_SECS 23
+
+#include <osmocom/core/timer.h>
+
+#include <osmocom/hnbgw/hnbgw.h>
+#include <osmocom/hnbgw/context_map.h>
+
+const struct value_string hnbgw_context_map_state_names[] = {
+ {MAP_S_NULL , "not-initialized"},
+ {MAP_S_ACTIVE , "active"},
+ {MAP_S_RESERVED1, "inactive-reserved"},
+ {MAP_S_RESERVED2, "inactive-discard"},
+ {0, NULL}
+};
+
+/* is a given SCCP USER SAP Connection ID in use for a given CN link? */
+static int cn_id_in_use(struct hnbgw_cnlink *cn, uint32_t id)
+{
+ struct hnbgw_context_map *map;
+
+ llist_for_each_entry(map, &cn->map_list, cn_list) {
+ if (map->scu_conn_id == id)
+ return 1;
+ }
+ return 0;
+}
+
+/* try to allocate a new SCCP User SAP Connection ID */
+static int alloc_cn_conn_id(struct hnbgw_cnlink *cn, uint32_t *id_out)
+{
+ uint32_t i;
+ uint32_t id;
+
+ for (i = 0; i < 0xffffffff; i++) {
+ id = cn->next_conn_id++;
+ if (!cn_id_in_use(cn, id)) {
+ *id_out = id;
+ return 1;
+ }
+ }
+ return -1;
+}
+
+/* Map from a HNB + ContextID to the SCCP-side Connection ID */
+struct hnbgw_context_map *
+context_map_alloc_by_hnb(struct hnb_context *hnb, uint32_t rua_ctx_id,
+ bool is_ps,
+ struct hnbgw_cnlink *cn_if_new)
+{
+ struct hnbgw_context_map *map;
+ uint32_t new_scu_conn_id;
+
+ llist_for_each_entry(map, &hnb->map_list, hnb_list) {
+ if (map->state != MAP_S_ACTIVE)
+ continue;
+ if (map->cn_link != cn_if_new) {
+ continue;
+ }
+ if (map->rua_ctx_id == rua_ctx_id
+ && map->is_ps == is_ps) {
+ return map;
+ }
+ }
+
+ if (alloc_cn_conn_id(cn_if_new, &new_scu_conn_id) < 0) {
+ LOGHNB(hnb, DMAIN, LOGL_ERROR, "Unable to allocate CN connection ID\n");
+ return NULL;
+ }
+
+ LOGHNB(hnb, DMAIN, LOGL_INFO, "Creating new Mapping RUA CTX %p/%u <-> SCU Conn ID %p/%u\n",
+ hnb, rua_ctx_id, cn_if_new, new_scu_conn_id);
+
+ /* alloate a new map entry */
+ map = talloc_zero(hnb, struct hnbgw_context_map);
+ map->state = MAP_S_NULL;
+ map->cn_link = cn_if_new;
+ map->hnb_ctx = hnb;
+ map->rua_ctx_id = rua_ctx_id;
+ map->is_ps = is_ps;
+ map->scu_conn_id = new_scu_conn_id;
+
+ /* put it into both lists */
+ llist_add_tail(&map->hnb_list, &hnb->map_list);
+ llist_add_tail(&map->cn_list, &cn_if_new->map_list);
+ map->state = MAP_S_ACTIVE;
+
+ return map;
+}
+
+/* Map from a CN + Connection ID to HNB + Context ID */
+struct hnbgw_context_map *
+context_map_by_cn(struct hnbgw_cnlink *cn, uint32_t scu_conn_id)
+{
+ struct hnbgw_context_map *map;
+
+ llist_for_each_entry(map, &cn->map_list, cn_list) {
+ if (map->state != MAP_S_ACTIVE)
+ continue;
+ if (map->scu_conn_id == scu_conn_id) {
+ return map;
+ }
+ }
+ /* we don't allocate new mappings in the CN->HNB
+ * direction, as the RUA=SCCP=SUA connections are always
+ * established from HNB towards CN. */
+ LOGP(DMAIN, LOGL_NOTICE, "Unable to resolve map for CN " "connection ID %p/%u\n", cn, scu_conn_id);
+ return NULL;
+}
+
+void context_map_deactivate(struct hnbgw_context_map *map)
+{
+ /* set the state to reserved. We still show up in the list and
+ * avoid re-allocation of the context-id until we are cleaned up
+ * by the context_map garbage collector timer */
+
+ if (map->state != MAP_S_RESERVED2)
+ map->state = MAP_S_RESERVED1;
+}
+
+static struct osmo_timer_list context_map_tmr;
+
+static void context_map_tmr_cb(void *data)
+{
+ struct hnb_gw *gw = data;
+ struct hnbgw_cnlink *cn = gw->sccp.cnlink;
+ struct hnbgw_context_map *map, *next_map;
+
+ DEBUGP(DMAIN, "Running context mapper garbage collection\n");
+ llist_for_each_entry_safe(map, next_map, &cn->map_list, cn_list) {
+ switch (map->state) {
+ case MAP_S_RESERVED1:
+ /* first time we see this reserved
+ * entry: mark it for stage 2 */
+ map->state = MAP_S_RESERVED2;
+ break;
+ case MAP_S_RESERVED2:
+ /* second time we see this reserved
+ * entry: remove it */
+ map->state = MAP_S_NULL;
+ llist_del(&map->cn_list);
+ llist_del(&map->hnb_list);
+ talloc_free(map);
+ break;
+ default:
+ break;
+ }
+ }
+ /* re-schedule this timer */
+ osmo_timer_schedule(&context_map_tmr, EXPIRY_TIMER_SECS, 0);
+}
+
+int context_map_init(struct hnb_gw *gw)
+{
+ context_map_tmr.cb = context_map_tmr_cb;
+ context_map_tmr.data = gw;
+ osmo_timer_schedule(&context_map_tmr, EXPIRY_TIMER_SECS, 0);
+
+ return 0;
+}
diff --git a/src/osmo-hnbgw/hnbgw.c b/src/osmo-hnbgw/hnbgw.c
new file mode 100644
index 0000000..da15bfc
--- /dev/null
+++ b/src/osmo-hnbgw/hnbgw.c
@@ -0,0 +1,700 @@
+/* main application for hnb-gw part of osmo-iuh */
+
+/* (C) 2015 by Harald Welte <laforge(a)gnumonks.org>
+ * (C) 2016 by sysmocom s.f.m.c. GmbH <info(a)sysmocom.de>
+ * 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 "config.h"
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdbool.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/sctp.h>
+#include <arpa/inet.h>
+
+#include <osmocom/core/application.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/write_queue.h>
+#include <osmocom/ctrl/control_if.h>
+#include <osmocom/ctrl/control_cmd.h>
+#include <osmocom/ctrl/control_vty.h>
+#include <osmocom/ctrl/ports.h>
+#include <osmocom/vty/telnet_interface.h>
+#include <osmocom/vty/logging.h>
+#include <osmocom/vty/command.h>
+#include <osmocom/vty/ports.h>
+
+#include <osmocom/netif/stream.h>
+
+#include <osmocom/ranap/ranap_common.h>
+
+#include <osmocom/sigtran/protocol/m3ua.h>
+#include <osmocom/sigtran/sccp_sap.h>
+
+#include <osmocom/hnbgw/hnbgw.h>
+#include <osmocom/hnbgw/hnbgw_hnbap.h>
+#include <osmocom/hnbgw/hnbgw_rua.h>
+#include <osmocom/hnbgw/hnbgw_cn.h>
+#include <osmocom/hnbgw/context_map.h>
+
+static const char * const osmo_hnbgw_copyright =
+ "OsmoHNBGW - Osmocom Home Node B Gateway implementation\r\n"
+ "Copyright (C) 2016 by sysmocom s.f.m.c. GmbH <info(a)sysmocom.de>\r\n"
+ "Contributions by Daniel Willmann, Harald Welte, Neels Hofmeyr\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";
+
+static void *tall_hnb_ctx;
+
+static struct hnb_gw *g_hnb_gw;
+
+static struct hnb_gw *hnb_gw_create(void *ctx)
+{
+ struct hnb_gw *gw = talloc_zero(ctx, struct hnb_gw);
+
+ /* strdup so we can easily talloc_free in the VTY code */
+ gw->config.iuh_local_ip = talloc_strdup(gw, HNBGW_LOCAL_IP_DEFAULT);
+ gw->config.iuh_local_port = IUH_DEFAULT_SCTP_PORT;
+ gw->config.log_prefix_hnb_id = true;
+
+ gw->next_ue_ctx_id = 23;
+ INIT_LLIST_HEAD(&gw->hnb_list);
+ INIT_LLIST_HEAD(&gw->ue_list);
+
+ context_map_init(gw);
+
+ return gw;
+}
+
+struct hnb_context *hnb_context_by_id(struct hnb_gw *gw, uint32_t cid)
+{
+ struct hnb_context *hnb;
+
+ llist_for_each_entry(hnb, &gw->hnb_list, list) {
+ if (hnb->id.cid == cid)
+ return hnb;
+ }
+
+ return NULL;
+}
+
+struct hnb_context *hnb_context_by_identity_info(struct hnb_gw *gw, const char *identity_info)
+{
+ struct hnb_context *hnb;
+
+ llist_for_each_entry(hnb, &gw->hnb_list, list) {
+ if (strcmp(identity_info, hnb->identity_info) == 0)
+ return hnb;
+ }
+
+ return NULL;
+}
+
+
+unsigned hnb_contexts(const struct hnb_gw *gw)
+{
+ unsigned num_ctx = 0;
+ struct hnb_context *hnb;
+
+ llist_for_each_entry(hnb, &gw->hnb_list, list) {
+ num_ctx++;
+ }
+
+ return num_ctx;
+}
+
+struct ue_context *ue_context_by_id(struct hnb_gw *gw, uint32_t id)
+{
+ struct ue_context *ue;
+
+ llist_for_each_entry(ue, &gw->ue_list, list) {
+ if (ue->context_id == id)
+ return ue;
+ }
+ return NULL;
+
+}
+
+struct ue_context *ue_context_by_imsi(struct hnb_gw *gw, const char *imsi)
+{
+ struct ue_context *ue;
+
+ llist_for_each_entry(ue, &gw->ue_list, list) {
+ if (!strcmp(ue->imsi, imsi))
+ return ue;
+ }
+ return NULL;
+}
+
+struct ue_context *ue_context_by_tmsi(struct hnb_gw *gw, uint32_t tmsi)
+{
+ struct ue_context *ue;
+
+ llist_for_each_entry(ue, &gw->ue_list, list) {
+ if (ue->tmsi == tmsi)
+ return ue;
+ }
+ return NULL;
+}
+
+void ue_context_free_by_hnb(struct hnb_gw *gw, const struct hnb_context *hnb)
+{
+ struct ue_context *ue, *tmp;
+
+ llist_for_each_entry_safe(ue, tmp, &gw->ue_list, list) {
+ if (ue->hnb == hnb)
+ ue_context_free(ue);
+ }
+}
+
+static uint32_t get_next_ue_ctx_id(struct hnb_gw *gw)
+{
+ uint32_t id;
+
+ do {
+ id = gw->next_ue_ctx_id++;
+ } while (ue_context_by_id(gw, id));
+
+ return id;
+}
+
+struct ue_context *ue_context_alloc(struct hnb_context *hnb, const char *imsi,
+ uint32_t tmsi)
+{
+ struct ue_context *ue;
+
+ ue = talloc_zero(tall_hnb_ctx, struct ue_context);
+ if (!ue)
+ return NULL;
+
+ ue->hnb = hnb;
+ if (imsi)
+ OSMO_STRLCPY_ARRAY(ue->imsi, imsi);
+ else
+ ue->imsi[0] = '\0';
+ ue->tmsi = tmsi;
+ ue->context_id = get_next_ue_ctx_id(hnb->gw);
+ llist_add_tail(&ue->list, &hnb->gw->ue_list);
+
+ LOGP(DHNBAP, LOGL_INFO, "created UE context: id 0x%x, imsi %s, tmsi 0x%x\n",
+ ue->context_id, imsi? imsi : "-", tmsi);
+
+ return ue;
+}
+
+void ue_context_free(struct ue_context *ue)
+{
+ llist_del(&ue->list);
+ talloc_free(ue);
+}
+
+static int hnb_read_cb(struct osmo_stream_srv *conn)
+{
+ struct hnb_context *hnb = osmo_stream_srv_get_data(conn);
+ struct msgb *msg = msgb_alloc(IUH_MSGB_SIZE, "Iuh rx");
+ int rc;
+
+ if (!msg)
+ return -ENOMEM;
+
+ /* we store a reference to the HomeNodeB in the msg->dest for the
+ * benefit of varoius downstream processing functions */
+ msg->dst = hnb;
+
+ rc = osmo_stream_srv_recv(conn, msg);
+ if (rc == -EAGAIN) {
+ /* Notification received */
+ msgb_free(msg);
+ return 0;
+ } else if (rc < 0) {
+ LOGHNB(hnb, DMAIN, LOGL_ERROR, "Error during sctp_recvmsg()\n");
+ /* FIXME: clean up after disappeared HNB */
+ hnb_context_release(hnb);
+ goto out;
+ } else if (rc == 0) {
+ hnb_context_release(hnb);
+ rc = -1;
+
+ goto out;
+ } else {
+ msgb_put(msg, rc);
+ }
+
+ switch (msgb_sctp_ppid(msg)) {
+ case IUH_PPI_HNBAP:
+ hnb->hnbap_stream = msgb_sctp_stream(msg);
+ rc = hnbgw_hnbap_rx(hnb, msg);
+ break;
+ case IUH_PPI_RUA:
+ hnb->rua_stream = msgb_sctp_stream(msg);
+ rc = hnbgw_rua_rx(hnb, msg);
+ break;
+ case IUH_PPI_SABP:
+ case IUH_PPI_RNA:
+ case IUH_PPI_PUA:
+ LOGHNB(hnb, DMAIN, LOGL_ERROR, "Unimplemented SCTP PPID=%lu received\n", msgb_sctp_ppid(msg));
+ rc = 0;
+ break;
+ default:
+ LOGHNB(hnb, DMAIN, LOGL_ERROR, "Unknown SCTP PPID=%lu received\n", msgb_sctp_ppid(msg));
+ rc = 0;
+ break;
+ }
+
+out:
+ msgb_free(msg);
+ return rc;
+}
+
+struct hnb_context *hnb_context_alloc(struct hnb_gw *gw, struct osmo_stream_srv_link *link, int new_fd)
+{
+ struct hnb_context *ctx;
+
+ ctx = talloc_zero(tall_hnb_ctx, struct hnb_context);
+ if (!ctx)
+ return NULL;
+ INIT_LLIST_HEAD(&ctx->map_list);
+
+ ctx->gw = gw;
+ ctx->conn = osmo_stream_srv_create(tall_hnb_ctx, link, new_fd, hnb_read_cb, NULL, ctx);
+ if (!ctx->conn) {
+ LOGP(DMAIN, LOGL_INFO, "error while creating connection\n");
+ talloc_free(ctx);
+ return NULL;
+ }
+
+ llist_add_tail(&ctx->list, &gw->hnb_list);
+ return ctx;
+}
+
+static const char *umts_cell_id_name(const struct umts_cell_id *ucid)
+{
+ static __thread char buf[40];
+
+ snprintf(buf, sizeof(buf), "%u-%u-L%u-R%u-S%u", ucid->mcc, ucid->mnc, ucid->lac, ucid->rac, ucid->sac);
+ return buf;
+}
+
+const char *hnb_context_name(struct hnb_context *ctx)
+{
+ if (!ctx)
+ return "NULL";
+
+ if (ctx->gw->config.log_prefix_hnb_id)
+ return ctx->identity_info;
+ else
+ return umts_cell_id_name(&ctx->id);
+}
+
+void hnb_context_release(struct hnb_context *ctx)
+{
+ struct hnbgw_context_map *map, *map2;
+
+ /* remove from the list of HNB contexts */
+ llist_del(&ctx->list);
+
+ /* deactivate all context maps */
+ llist_for_each_entry_safe(map, map2, &ctx->map_list, hnb_list) {
+ /* remove it from list, as HNB context will soon be
+ * gone. Let's hope the second osmo_llist_del in the
+ * map garbage collector works fine? */
+ llist_del(&map->hnb_list);
+ llist_del(&map->cn_list);
+ context_map_deactivate(map);
+ }
+ ue_context_free_by_hnb(ctx->gw, ctx);
+
+ osmo_stream_srv_destroy(ctx->conn);
+
+ talloc_free(ctx);
+}
+
+/*! call-back when the listen FD has something to read */
+static int accept_cb(struct osmo_stream_srv_link *srv, int fd)
+{
+ struct hnb_gw *gw = osmo_stream_srv_link_get_data(srv);
+ struct hnb_context *ctx;
+
+ ctx = hnb_context_alloc(gw, srv, fd);
+ if (!ctx)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static const struct log_info_cat log_cat[] = {
+ [DMAIN] = {
+ .name = "DMAIN", .loglevel = LOGL_NOTICE, .enabled = 1,
+ .color = "",
+ .description = "Main program",
+ },
+ [DHNBAP] = {
+ .name = "DHNBAP", .loglevel = LOGL_NOTICE, .enabled = 1,
+ .color = "",
+ .description = "Home Node B Application Part",
+ },
+ [DRUA] = {
+ .name = "DRUA", .loglevel = LOGL_NOTICE, .enabled = 1,
+ .color = "",
+ .description = "RANAP User Adaptation",
+ },
+ [DRANAP] = {
+ .name = "DRANAP", .loglevel = LOGL_NOTICE, .enabled = 1,
+ .color = "",
+ .description = "RAN Application Part",
+ },
+};
+
+static const struct log_info hnbgw_log_info = {
+ .cat = log_cat,
+ .num_cat = ARRAY_SIZE(log_cat),
+};
+
+static struct vty_app_info vty_info = {
+ .name = "OsmoHNBGW",
+ .version = PACKAGE_VERSION,
+ .go_parent_cb = hnbgw_vty_go_parent,
+};
+
+static struct {
+ int daemonize;
+ const char *config_file;
+ bool log_disable_color;
+ bool log_enable_timestamp;
+ int log_level;
+ const char *log_category_mask;
+} hnbgw_cmdline_config = {
+ 0,
+ "osmo-hnbgw.cfg",
+ false,
+ false,
+ 0,
+ NULL,
+};
+
+static void print_usage()
+{
+ printf("Usage: osmo-hnbgw\n");
+}
+
+static void print_help()
+{
+ printf(" -h --help This text.\n");
+ printf(" -d option --debug=DHNBAP:DRUA:DRANAP:DMAIN Enable debugging.\n");
+ printf(" -D --daemonize Fork the process into a background daemon.\n");
+ printf(" -c --config-file filename The config file to use.\n");
+ printf(" -s --disable-color\n");
+ printf(" -T --timestamp Prefix every log line with a timestamp.\n");
+ printf(" -V --version Print the version of OsmoHNBGW.\n");
+ printf(" -e --log-level number Set a global loglevel.\n");
+
+ printf("\nVTY reference generation:\n");
+ printf(" --vty-ref-mode MODE VTY reference generation mode (e.g. 'expert').\n");
+ printf(" --vty-ref-xml Generate the VTY reference XML output and exit.\n");
+}
+
+static void handle_long_options(const char *prog_name, const int long_option)
+{
+ static int vty_ref_mode = VTY_REF_GEN_MODE_DEFAULT;
+
+ switch (long_option) {
+ case 1:
+ vty_ref_mode = get_string_value(vty_ref_gen_mode_names, optarg);
+ if (vty_ref_mode < 0) {
+ fprintf(stderr, "%s: Unknown VTY reference generation "
+ "mode '%s'\n", prog_name, optarg);
+ exit(2);
+ }
+ break;
+ case 2:
+ fprintf(stderr, "Generating the VTY reference in mode '%s' (%s)\n",
+ get_value_string(vty_ref_gen_mode_names, vty_ref_mode),
+ get_value_string(vty_ref_gen_mode_desc, vty_ref_mode));
+ vty_dump_xml_ref_mode(stdout, (enum vty_ref_gen_mode) vty_ref_mode);
+ exit(0);
+ default:
+ fprintf(stderr, "%s: error parsing cmdline options\n", prog_name);
+ exit(2);
+ }
+}
+
+static void handle_options(int argc, char **argv)
+{
+ while (1) {
+ int option_index = 0, c;
+ static int long_option = 0;
+ 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'},
+ {"vty-ref-mode", 1, &long_option, 1},
+ {"vty-ref-xml", 0, &long_option, 2},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long(argc, argv, "hd:Dc:sTVe:",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 0:
+ handle_long_options(argv[0], long_option);
+ break;
+ case 'h':
+ print_usage();
+ print_help();
+ exit(0);
+ case 's':
+ hnbgw_cmdline_config.log_disable_color = true;
+ break;
+ case 'd':
+ hnbgw_cmdline_config.log_category_mask = optarg;
+ break;
+ case 'D':
+ hnbgw_cmdline_config.daemonize = 1;
+ break;
+ case 'c':
+ hnbgw_cmdline_config.config_file = optarg;
+ break;
+ case 'T':
+ hnbgw_cmdline_config.log_enable_timestamp = true;
+ break;
+ case 'e':
+ hnbgw_cmdline_config.log_level = atoi(optarg);
+ break;
+ case 'V':
+ print_version(1);
+ exit(0);
+ break;
+ default:
+ /* catch unknown options *as well as* missing arguments. */
+ fprintf(stderr, "Error in command line options. Exiting.\n");
+ exit(-1);
+ break;
+ }
+ }
+
+ if (argc > optind) {
+ fprintf(stderr, "Unsupported positional arguments on command line\n");
+ exit(2);
+ }
+}
+
+CTRL_CMD_DEFINE_RO(hnb_info, "info");
+static int get_hnb_info(struct ctrl_cmd *cmd, void *data)
+{
+ struct hnb_context *hnb = data;
+
+ cmd->reply = talloc_strdup(cmd, hnb->identity_info);
+
+ return CTRL_CMD_REPLY;
+}
+
+CTRL_CMD_DEFINE_RO(hnbs, "num-hnb");
+static int get_hnbs(struct ctrl_cmd *cmd, void *data)
+{
+ cmd->reply = talloc_asprintf(cmd, "%u", hnb_contexts(data));
+
+ return CTRL_CMD_REPLY;
+}
+
+int hnb_ctrl_cmds_install()
+{
+ int rc = 0;
+
+ rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_hnbs);
+ rc |= ctrl_cmd_install(CTRL_NODE_HNB, &cmd_hnb_info);
+
+ return rc;
+}
+
+static int hnb_ctrl_node_lookup(void *data, vector vline, int *node_type, void **node_data, int *i)
+{
+ const char *token = vector_slot(vline, *i);
+ struct hnb_context *hnb;
+ long num;
+
+ switch (*node_type) {
+ case CTRL_NODE_ROOT:
+ if (strcmp(token, "hnb") != 0)
+ return 0;
+
+ (*i)++;
+
+ if (!ctrl_parse_get_num(vline, *i, &num))
+ return -ERANGE;
+
+ hnb = hnb_context_by_id(data, num);
+ if (!hnb)
+ return -ENODEV;
+
+ *node_data = hnb;
+ *node_type = CTRL_NODE_HNB;
+ break;
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+int main(int argc, char **argv)
+{
+ struct osmo_stream_srv_link *srv;
+ int rc;
+
+ tall_hnb_ctx = talloc_named_const(NULL, 0, "hnb_context");
+ talloc_asn1_ctx = talloc_named_const(NULL, 1, "asn1_context");
+ msgb_talloc_ctx_init(tall_hnb_ctx, 0);
+
+ g_hnb_gw = hnb_gw_create(tall_hnb_ctx);
+ g_hnb_gw->config.rnc_id = 23;
+
+ rc = osmo_init_logging2(tall_hnb_ctx, &hnbgw_log_info);
+ if (rc < 0)
+ exit(1);
+
+ rc = osmo_ss7_init();
+ if (rc < 0) {
+ LOGP(DMAIN, LOGL_FATAL, "osmo_ss7_init() failed with rc=%d\n", rc);
+ exit(1);
+ }
+
+ vty_info.copyright = osmo_hnbgw_copyright;
+ vty_init(&vty_info);
+
+ osmo_ss7_vty_init_asp(tall_hnb_ctx);
+ osmo_sccp_vty_init();
+ hnbgw_vty_init(g_hnb_gw, tall_hnb_ctx);
+ ctrl_vty_init(tall_hnb_ctx);
+ logging_vty_add_cmds();
+
+ /* Handle options after vty_init(), for --version */
+ handle_options(argc, argv);
+
+ rc = vty_read_config_file(hnbgw_cmdline_config.config_file, NULL);
+ if (rc < 0) {
+ LOGP(DMAIN, LOGL_FATAL, "Failed to parse the config file: '%s'\n",
+ hnbgw_cmdline_config.config_file);
+ return 1;
+ }
+
+ /*
+ * cmdline options take precedence over config file, but if no options
+ * were passed we must not override the config file.
+ */
+ if (hnbgw_cmdline_config.log_disable_color)
+ log_set_use_color(osmo_stderr_target, 0);
+ if (hnbgw_cmdline_config.log_category_mask)
+ log_parse_category_mask(osmo_stderr_target,
+ hnbgw_cmdline_config.log_category_mask);
+ if (hnbgw_cmdline_config.log_enable_timestamp)
+ log_set_print_timestamp(osmo_stderr_target, 1);
+ if (hnbgw_cmdline_config.log_level)
+ log_set_log_level(osmo_stderr_target,
+ hnbgw_cmdline_config.log_level);
+
+ rc = telnet_init_dynif(tall_hnb_ctx, g_hnb_gw, vty_get_bind_addr(), OSMO_VTY_PORT_HNBGW);
+ if (rc < 0) {
+ perror("Error binding VTY port");
+ exit(1);
+ }
+
+ g_hnb_gw->ctrl = ctrl_interface_setup_dynip2(g_hnb_gw, ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_HNBGW,
+ hnb_ctrl_node_lookup, _LAST_CTRL_NODE_HNB);
+ if (!g_hnb_gw->ctrl) {
+ LOGP(DMAIN, LOGL_ERROR, "Failed to create CTRL interface on %s:%u\n",
+ ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_HNBGW);
+ exit(1);
+ } else {
+ rc = hnb_ctrl_cmds_install();
+ if (rc) {
+ LOGP(DMAIN, LOGL_ERROR, "Failed to install CTRL interface commands\n");
+ return 2;
+ }
+ }
+
+ ranap_set_log_area(DRANAP);
+
+ rc = hnbgw_cnlink_init(g_hnb_gw, "localhost", M3UA_PORT, "localhost");
+ if (rc < 0) {
+ LOGP(DMAIN, LOGL_ERROR, "Failed to initialize SCCP link to CN\n");
+ exit(1);
+ }
+
+ LOGP(DHNBAP, LOGL_NOTICE, "Using RNC-Id %u\n", g_hnb_gw->config.rnc_id);
+
+ OSMO_ASSERT(g_hnb_gw->config.iuh_local_ip);
+ LOGP(DMAIN, LOGL_NOTICE, "Listening for Iuh at %s %d\n",
+ g_hnb_gw->config.iuh_local_ip,
+ g_hnb_gw->config.iuh_local_port);
+ srv = osmo_stream_srv_link_create(tall_hnb_ctx);
+ if (!srv) {
+ perror("cannot create server");
+ exit(1);
+ }
+ osmo_stream_srv_link_set_data(srv, g_hnb_gw);
+ osmo_stream_srv_link_set_proto(srv, IPPROTO_SCTP);
+ osmo_stream_srv_link_set_nodelay(srv, true);
+ osmo_stream_srv_link_set_addr(srv, g_hnb_gw->config.iuh_local_ip);
+ osmo_stream_srv_link_set_port(srv, g_hnb_gw->config.iuh_local_port);
+ osmo_stream_srv_link_set_accept_cb(srv, accept_cb);
+
+ if (osmo_stream_srv_link_open(srv) < 0) {
+ perror("Cannot open server");
+ exit(1);
+ }
+ g_hnb_gw->iuh = srv;
+
+ if (hnbgw_cmdline_config.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/osmo-hnbgw/hnbgw_cn.c b/src/osmo-hnbgw/hnbgw_cn.c
new file mode 100644
index 0000000..7fbb691
--- /dev/null
+++ b/src/osmo-hnbgw/hnbgw_cn.c
@@ -0,0 +1,559 @@
+/* IuCS/IuPS Core Network interface of HNB-GW */
+
+/* (C) 2015 by Harald Welte <laforge(a)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 <arpa/inet.h>
+#include <errno.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/timer.h>
+
+#include <osmocom/sigtran/protocol/m3ua.h>
+#include <osmocom/sigtran/sccp_sap.h>
+#include <osmocom/sigtran/sccp_helpers.h>
+
+#include <osmocom/hnbgw/hnbgw.h>
+#include <osmocom/hnbgw/hnbgw_rua.h>
+#include <osmocom/ranap/ranap_ies_defs.h>
+#include <osmocom/ranap/ranap_msg_factory.h>
+#include <osmocom/hnbgw/context_map.h>
+
+/***********************************************************************
+ * Outbound RANAP RESET to CN
+ ***********************************************************************/
+
+void hnbgw_cnlink_change_state(struct hnbgw_cnlink *cnlink, enum hnbgw_cnlink_state state);
+
+static int transmit_rst(struct hnb_gw *gw, RANAP_CN_DomainIndicator_t domain,
+ struct osmo_sccp_addr *remote_addr)
+{
+ struct msgb *msg;
+ RANAP_Cause_t cause = {
+ .present = RANAP_Cause_PR_transmissionNetwork,
+ .choice. transmissionNetwork = RANAP_CauseTransmissionNetwork_signalling_transport_resource_failure,
+ };
+
+ LOGP(DRANAP, LOGL_NOTICE, "Tx RESET to %s %s\n",
+ domain == RANAP_CN_DomainIndicator_cs_domain ? "IuCS" : "IuPS",
+ osmo_sccp_inst_addr_name(gw->sccp.cnlink->sccp, remote_addr));
+
+ msg = ranap_new_msg_reset(domain, &cause);
+
+ return osmo_sccp_tx_unitdata_msg(gw->sccp.cnlink->sccp_user,
+ &gw->sccp.local_addr,
+ remote_addr,
+ msg);
+}
+
+static int transmit_reset_ack(struct hnb_gw *gw, RANAP_CN_DomainIndicator_t domain,
+ const struct osmo_sccp_addr *remote_addr)
+{
+ struct msgb *msg;
+
+ LOGP(DRANAP, LOGL_NOTICE, "Tx RESET ACK to %s %s\n",
+ domain == RANAP_CN_DomainIndicator_cs_domain ? "IuCS" : "IuPS",
+ osmo_sccp_inst_addr_name(gw->sccp.cnlink->sccp, remote_addr));
+
+ msg = ranap_new_msg_reset_ack(domain, NULL);
+
+ return osmo_sccp_tx_unitdata_msg(gw->sccp.cnlink->sccp_user,
+ &gw->sccp.local_addr,
+ remote_addr,
+ msg);
+}
+
+/* Timer callback once T_RafC expires */
+static void cnlink_trafc_cb(void *data)
+{
+ struct hnb_gw *gw = data;
+
+ transmit_rst(gw, RANAP_CN_DomainIndicator_cs_domain, &gw->sccp.iucs_remote_addr);
+ transmit_rst(gw, RANAP_CN_DomainIndicator_ps_domain, &gw->sccp.iups_remote_addr);
+ hnbgw_cnlink_change_state(gw->sccp.cnlink, CNLINK_S_EST_RST_TX_WAIT_ACK);
+ /* The spec states that we should abandon after a configurable
+ * number of times. We decide to simply continue trying */
+}
+
+/* change the state of a CN Link */
+void hnbgw_cnlink_change_state(struct hnbgw_cnlink *cnlink, enum hnbgw_cnlink_state state)
+{
+ switch (state) {
+ case CNLINK_S_NULL:
+ case CNLINK_S_EST_PEND:
+ break;
+ case CNLINK_S_EST_CONF:
+ cnlink_trafc_cb(cnlink->gw);
+ break;
+ case CNLINK_S_EST_RST_TX_WAIT_ACK:
+ osmo_timer_schedule(&cnlink->T_RafC, 5, 0);
+ break;
+ case CNLINK_S_EST_ACTIVE:
+ osmo_timer_del(&cnlink->T_RafC);
+ break;
+ }
+}
+
+/***********************************************************************
+ * Incoming primitives from SCCP User SAP
+ ***********************************************************************/
+
+static int cn_ranap_rx_reset_cmd(struct hnbgw_cnlink *cnlink,
+ const struct osmo_scu_unitdata_param *unitdata,
+ RANAP_InitiatingMessage_t *imsg)
+{
+ RANAP_CN_DomainIndicator_t domain;
+ RANAP_ResetIEs_t ies;
+ int rc;
+
+ rc = ranap_decode_reseties(&ies, &imsg->value);
+ domain = ies.cN_DomainIndicator;
+ ranap_free_reseties(&ies);
+
+ LOGP(DRANAP, LOGL_NOTICE, "Rx RESET from %s %s, returning ACK\n",
+ domain == RANAP_CN_DomainIndicator_cs_domain ? "IuCS" : "IuPS",
+ osmo_sccp_inst_addr_name(cnlink->sccp, &unitdata->calling_addr));
+
+ /* FIXME: actually reset connections, if any */
+
+ if (transmit_reset_ack(cnlink->gw, domain, &unitdata->calling_addr))
+ LOGP(DRANAP, LOGL_ERROR, "Error: cannot send RESET ACK to %s %s\n",
+ domain == RANAP_CN_DomainIndicator_cs_domain ? "IuCS" : "IuPS",
+ osmo_sccp_inst_addr_name(cnlink->sccp, &unitdata->calling_addr));
+
+ return rc;
+}
+
+static int cn_ranap_rx_reset_ack(struct hnbgw_cnlink *cnlink,
+ RANAP_SuccessfulOutcome_t *omsg)
+{
+ RANAP_ResetAcknowledgeIEs_t ies;
+ int rc;
+
+ rc = ranap_decode_resetacknowledgeies(&ies, &omsg->value);
+
+ hnbgw_cnlink_change_state(cnlink, CNLINK_S_EST_ACTIVE);
+
+ ranap_free_resetacknowledgeies(&ies);
+ return rc;
+}
+
+static int cn_ranap_rx_paging_cmd(struct hnbgw_cnlink *cnlink,
+ RANAP_InitiatingMessage_t *imsg,
+ const uint8_t *data, unsigned int len)
+{
+ struct hnb_gw *gw = cnlink->gw;
+ struct hnb_context *hnb;
+ RANAP_PagingIEs_t ies;
+ int rc;
+
+ rc = ranap_decode_pagingies(&ies, &imsg->value);
+ if (rc < 0)
+ return rc;
+
+ /* FIXME: determine which HNBs to send this Paging command,
+ * rather than broadcasting to all HNBs */
+ llist_for_each_entry(hnb, &gw->hnb_list, list) {
+ rc = rua_tx_udt(hnb, data, len);
+ }
+
+ ranap_free_pagingies(&ies);
+ return 0;
+}
+
+static int cn_ranap_rx_initiating_msg(struct hnbgw_cnlink *cnlink,
+ const struct osmo_scu_unitdata_param *unitdata,
+ RANAP_InitiatingMessage_t *imsg,
+ const uint8_t *data, unsigned int len)
+{
+ switch (imsg->procedureCode) {
+ case RANAP_ProcedureCode_id_Reset:
+ return cn_ranap_rx_reset_cmd(cnlink, unitdata, imsg);
+ case RANAP_ProcedureCode_id_Paging:
+ return cn_ranap_rx_paging_cmd(cnlink, imsg, data, len);
+ case RANAP_ProcedureCode_id_OverloadControl: /* Overload ind */
+ break;
+ case RANAP_ProcedureCode_id_ErrorIndication: /* Error ind */
+ break;
+ case RANAP_ProcedureCode_id_ResetResource: /* request */
+ case RANAP_ProcedureCode_id_InformationTransfer:
+ case RANAP_ProcedureCode_id_DirectInformationTransfer:
+ case RANAP_ProcedureCode_id_UplinkInformationExchange:
+ LOGP(DRANAP, LOGL_NOTICE, "Received unsupported RANAP "
+ "Procedure %ld from CN, ignoring\n", imsg->procedureCode);
+ break;
+ default:
+ LOGP(DRANAP, LOGL_NOTICE, "Received suspicious RANAP "
+ "Procedure %ld from CN, ignoring\n", imsg->procedureCode);
+ break;
+ }
+ return 0;
+}
+
+static int cn_ranap_rx_successful_msg(struct hnbgw_cnlink *cnlink,
+ RANAP_SuccessfulOutcome_t *omsg)
+{
+ switch (omsg->procedureCode) {
+ case RANAP_ProcedureCode_id_Reset: /* Reset acknowledge */
+ return cn_ranap_rx_reset_ack(cnlink, omsg);
+ case RANAP_ProcedureCode_id_ResetResource: /* response */
+ case RANAP_ProcedureCode_id_InformationTransfer:
+ case RANAP_ProcedureCode_id_DirectInformationTransfer:
+ case RANAP_ProcedureCode_id_UplinkInformationExchange:
+ LOGP(DRANAP, LOGL_NOTICE, "Received unsupported RANAP "
+ "Procedure %ld from CN, ignoring\n", omsg->procedureCode);
+ break;
+ default:
+ LOGP(DRANAP, LOGL_NOTICE, "Received suspicious RANAP "
+ "Procedure %ld from CN, ignoring\n", omsg->procedureCode);
+ break;
+ }
+ return 0;
+}
+
+
+static int _cn_ranap_rx(struct hnbgw_cnlink *cnlink,
+ const struct osmo_scu_unitdata_param *unitdata,
+ RANAP_RANAP_PDU_t *pdu, const uint8_t *data, unsigned int len)
+{
+ int rc;
+
+ switch (pdu->present) {
+ case RANAP_RANAP_PDU_PR_initiatingMessage:
+ rc = cn_ranap_rx_initiating_msg(cnlink, unitdata, &pdu->choice.initiatingMessage,
+ data, len);
+ break;
+ case RANAP_RANAP_PDU_PR_successfulOutcome:
+ rc = cn_ranap_rx_successful_msg(cnlink, &pdu->choice.successfulOutcome);
+ break;
+ case RANAP_RANAP_PDU_PR_unsuccessfulOutcome:
+ LOGP(DRANAP, LOGL_NOTICE, "Received unsupported RANAP "
+ "unsuccessful outcome procedure %ld from CN, ignoring\n",
+ pdu->choice.unsuccessfulOutcome.procedureCode);
+ rc = -ENOTSUP;
+ break;
+ default:
+ LOGP(DRANAP, LOGL_NOTICE, "Received suspicious RANAP "
+ "presence %u from CN, ignoring\n", pdu->present);
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+static int handle_cn_ranap(struct hnbgw_cnlink *cnlink, const struct osmo_scu_unitdata_param *unitdata,
+ const uint8_t *data, unsigned int len)
+{
+ RANAP_RANAP_PDU_t _pdu, *pdu = &_pdu;
+ asn_dec_rval_t dec_ret;
+ int rc;
+
+ memset(pdu, 0, sizeof(*pdu));
+ dec_ret = aper_decode(NULL,&asn_DEF_RANAP_RANAP_PDU, (void **) &pdu,
+ data, len, 0, 0);
+ if (dec_ret.code != RC_OK) {
+ LOGP(DRANAP, LOGL_ERROR, "Error in RANAP ASN.1 decode\n");
+ return -1;
+ }
+
+ rc = _cn_ranap_rx(cnlink, unitdata, pdu, data, len);
+
+ return rc;
+}
+
+static bool pc_and_ssn_match(const struct osmo_sccp_addr *a, const struct osmo_sccp_addr *b)
+{
+ return (a == b)
+ || ((a->pc == b->pc)
+ && (a->ssn == b->ssn));
+}
+
+static int classify_cn_remote_addr(const struct hnb_gw *gw,
+ const struct osmo_sccp_addr *cn_remote_addr,
+ bool *is_ps)
+{
+ if (pc_and_ssn_match(cn_remote_addr, &gw->sccp.iucs_remote_addr)) {
+ if (is_ps)
+ *is_ps = false;
+ return 0;
+ }
+ if (pc_and_ssn_match(cn_remote_addr, &gw->sccp.iups_remote_addr)) {
+ if (is_ps)
+ *is_ps = true;
+ return 0;
+ }
+ LOGP(DMAIN, LOGL_ERROR, "Unexpected remote address, matches neither CS nor PS address: %s\n",
+ osmo_sccp_addr_dump(cn_remote_addr));
+ return -1;
+}
+
+static int handle_cn_unitdata(struct hnbgw_cnlink *cnlink,
+ const struct osmo_scu_unitdata_param *param,
+ struct osmo_prim_hdr *oph)
+{
+ if (param->called_addr.ssn != OSMO_SCCP_SSN_RANAP) {
+ LOGP(DMAIN, LOGL_NOTICE, "N-UNITDATA.ind for unknown SSN %u\n",
+ param->called_addr.ssn);
+ return -1;
+ }
+
+ if (classify_cn_remote_addr(cnlink->gw, ¶m->calling_addr, NULL) < 0)
+ return -1;
+
+ return handle_cn_ranap(cnlink, param, msgb_l2(oph->msg), msgb_l2len(oph->msg));
+}
+
+static int handle_cn_conn_conf(struct hnbgw_cnlink *cnlink,
+ const struct osmo_scu_connect_param *param,
+ struct osmo_prim_hdr *oph)
+{
+ /* we don't actually need to do anything, as RUA towards the HNB
+ * doesn't seem to know any confirmations to its CONNECT
+ * operation */
+
+ LOGP(DMAIN, LOGL_DEBUG, "handle_cn_conn_conf() conn_id=%d\n",
+ param->conn_id);
+ LOGP(DMAIN, LOGL_DEBUG, "handle_cn_conn_conf() called_addr=%s\n",
+ inet_ntoa(param->called_addr.ip.v4));
+ LOGP(DMAIN, LOGL_DEBUG, "handle_cn_conn_conf() calling_addr=%s\n",
+ inet_ntoa(param->calling_addr.ip.v4));
+ LOGP(DMAIN, LOGL_DEBUG, "handle_cn_conn_conf() responding_addr=%s\n",
+ inet_ntoa(param->responding_addr.ip.v4));
+
+ return 0;
+}
+
+static int handle_cn_data_ind(struct hnbgw_cnlink *cnlink,
+ const struct osmo_scu_data_param *param,
+ struct osmo_prim_hdr *oph)
+{
+ struct hnbgw_context_map *map;
+
+ /* connection-oriented data is always passed transparently
+ * towards the specific HNB, via a RUA connection identified by
+ * conn_id */
+
+ map = context_map_by_cn(cnlink, param->conn_id);
+ if (!map) {
+ /* FIXME: Return an error / released primitive */
+ return 0;
+ }
+
+ return rua_tx_dt(map->hnb_ctx, map->is_ps, map->rua_ctx_id,
+ msgb_l2(oph->msg), msgb_l2len(oph->msg));
+}
+
+static int handle_cn_disc_ind(struct hnbgw_cnlink *cnlink,
+ const struct osmo_scu_disconn_param *param,
+ struct osmo_prim_hdr *oph)
+{
+ struct hnbgw_context_map *map;
+
+ LOGP(DMAIN, LOGL_DEBUG, "handle_cn_disc_ind() conn_id=%d originator=%d\n",
+ param->conn_id, param->originator);
+ LOGP(DMAIN, LOGL_DEBUG, "handle_cn_disc_ind() responding_addr=%s\n",
+ inet_ntoa(param->responding_addr.ip.v4));
+
+ RUA_Cause_t rua_cause = {
+ .present = RUA_Cause_PR_NOTHING,
+ /* FIXME: Convert incoming SCCP cause to RUA cause */
+ };
+
+ /* we need to notify the HNB associated with this connection via
+ * a RUA DISCONNECT */
+
+ map = context_map_by_cn(cnlink, param->conn_id);
+ if (!map) {
+ /* FIXME: Return an error / released primitive */
+ return 0;
+ }
+
+ return rua_tx_disc(map->hnb_ctx, map->is_ps, map->rua_ctx_id,
+ &rua_cause, msgb_l2(oph->msg), msgb_l2len(oph->msg));
+}
+
+/* Entry point for primitives coming up from SCCP User SAP */
+static int sccp_sap_up(struct osmo_prim_hdr *oph, void *ctx)
+{
+ struct osmo_sccp_user *scu = ctx;
+ struct hnbgw_cnlink *cnlink;
+ struct osmo_scu_prim *prim = (struct osmo_scu_prim *) oph;
+ int rc = 0;
+
+ LOGP(DMAIN, LOGL_DEBUG, "sccp_sap_up(%s)\n", osmo_scu_prim_name(oph));
+
+ if (!scu) {
+ LOGP(DMAIN, LOGL_ERROR,
+ "sccp_sap_up(): NULL osmo_sccp_user, cannot send prim (sap %u prim %u op %d)\n",
+ oph->sap, oph->primitive, oph->operation);
+ return -1;
+ }
+
+ cnlink = osmo_sccp_user_get_priv(scu);
+ if (!cnlink) {
+ LOGP(DMAIN, LOGL_ERROR,
+ "sccp_sap_up(): NULL hnbgw_cnlink, cannot send prim (sap %u prim %u op %d)\n",
+ oph->sap, oph->primitive, oph->operation);
+ return -1;
+ }
+
+ switch (OSMO_PRIM_HDR(oph)) {
+ case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION):
+ rc = handle_cn_unitdata(cnlink, &prim->u.unitdata, oph);
+ break;
+ case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_CONFIRM):
+ rc = handle_cn_conn_conf(cnlink, &prim->u.connect, oph);
+ break;
+ case OSMO_PRIM(OSMO_SCU_PRIM_N_DATA, PRIM_OP_INDICATION):
+ rc = handle_cn_data_ind(cnlink, &prim->u.data, oph);
+ break;
+ case OSMO_PRIM(OSMO_SCU_PRIM_N_DISCONNECT, PRIM_OP_INDICATION):
+ rc = handle_cn_disc_ind(cnlink, &prim->u.disconnect, oph);
+ break;
+ default:
+ LOGP(DMAIN, LOGL_ERROR,
+ "Received unknown prim %u from SCCP USER SAP\n",
+ OSMO_PRIM_HDR(oph));
+ break;
+ }
+
+ msgb_free(oph->msg);
+
+ return rc;
+}
+
+static bool addr_has_pc_and_ssn(const struct osmo_sccp_addr *addr)
+{
+ if (!(addr->presence & OSMO_SCCP_ADDR_T_SSN))
+ return false;
+ if (!(addr->presence & OSMO_SCCP_ADDR_T_PC))
+ return false;
+ return true;
+}
+
+static int resolve_addr_name(struct osmo_sccp_addr *dest, struct osmo_ss7_instance **ss7,
+ const char *addr_name, const char *label,
+ uint32_t default_pc)
+{
+ struct osmo_ss7_instance *ss7_tmp;
+
+ if (!addr_name) {
+ osmo_sccp_make_addr_pc_ssn(dest, default_pc, OSMO_SCCP_SSN_RANAP);
+ LOGP(DMAIN, LOGL_INFO, "%s remote addr not configured, using default: %s\n", label,
+ osmo_sccp_addr_name(*ss7, dest));
+ return 0;
+ }
+
+ ss7_tmp = osmo_sccp_addr_by_name(dest, addr_name);
+ if (!ss7_tmp) {
+ LOGP(DMAIN, LOGL_ERROR, "%s remote addr: no such SCCP address book entry: '%s'\n",
+ label, addr_name);
+ return -1;
+ }
+
+ if (*ss7 && (*ss7 != ss7_tmp)) {
+ LOGP(DMAIN, LOGL_ERROR, "IuCS and IuPS cannot be served from separate CS7 instances,"
+ " cs7 instance %d != %d\n", (*ss7)->cfg.id, ss7_tmp->cfg.id);
+ return -1;
+ }
+
+ *ss7 = ss7_tmp;
+
+ osmo_sccp_addr_set_ssn(dest, OSMO_SCCP_SSN_RANAP);
+
+ if (!addr_has_pc_and_ssn(dest)) {
+ LOGP(DMAIN, LOGL_ERROR, "Invalid/incomplete %s remote-addr: %s\n",
+ label, osmo_sccp_addr_name(*ss7, dest));
+ return -1;
+ }
+
+ LOGP(DRANAP, LOGL_NOTICE, "Remote %s SCCP addr: %s\n",
+ label, osmo_sccp_addr_name(*ss7, dest));
+ return 0;
+}
+
+int hnbgw_cnlink_init(struct hnb_gw *gw, const char *stp_host, uint16_t stp_port, const char *local_ip)
+{
+ struct hnbgw_cnlink *cnlink;
+ struct osmo_ss7_instance *ss7;
+ uint32_t local_pc;
+
+ OSMO_ASSERT(!gw->sccp.client);
+ OSMO_ASSERT(!gw->sccp.cnlink);
+
+ ss7 = NULL;
+ if (resolve_addr_name(&gw->sccp.iucs_remote_addr, &ss7,
+ gw->config.iucs_remote_addr_name, "IuCS", (23 << 3) + 1))
+ return -1;
+ if (resolve_addr_name(&gw->sccp.iups_remote_addr, &ss7,
+ gw->config.iups_remote_addr_name, "IuPS", (23 << 3) + 4))
+ return -1;
+
+ if (!ss7) {
+ LOGP(DRANAP, LOGL_NOTICE, "No cs7 instance configured for IuCS nor IuPS,"
+ " creating default instance\n");
+ ss7 = osmo_ss7_instance_find_or_create(gw, 0);
+ ss7->cfg.primary_pc = (23 << 3) + 5;
+ }
+
+ if (!osmo_ss7_pc_is_valid(ss7->cfg.primary_pc)) {
+ LOGP(DMAIN, LOGL_ERROR, "IuCS/IuPS uplink cannot be setup: CS7 instance %d has no point-code set\n",
+ ss7->cfg.id);
+ return -1;
+ }
+ local_pc = ss7->cfg.primary_pc;
+
+ osmo_sccp_make_addr_pc_ssn(&gw->sccp.local_addr, local_pc, OSMO_SCCP_SSN_RANAP);
+ LOGP(DRANAP, LOGL_NOTICE, "Local SCCP addr: %s\n", osmo_sccp_addr_name(ss7, &gw->sccp.local_addr));
+
+ gw->sccp.client = osmo_sccp_simple_client_on_ss7_id(gw, ss7->cfg.id, "OsmoHNBGW",
+ local_pc, OSMO_SS7_ASP_PROT_M3UA,
+ 0, local_ip, stp_port, stp_host);
+ if (!gw->sccp.client) {
+ LOGP(DMAIN, LOGL_ERROR, "Failed to init SCCP Client\n");
+ return -1;
+ }
+
+ cnlink = talloc_zero(gw, struct hnbgw_cnlink);
+ cnlink->gw = gw;
+ INIT_LLIST_HEAD(&cnlink->map_list);
+ cnlink->T_RafC.cb = cnlink_trafc_cb;
+ cnlink->T_RafC.data = gw;
+ cnlink->next_conn_id = 1000;
+
+ cnlink->sccp_user = osmo_sccp_user_bind_pc(gw->sccp.client, "OsmoHNBGW", sccp_sap_up,
+ OSMO_SCCP_SSN_RANAP, gw->sccp.local_addr.pc);
+ if (!cnlink->sccp_user) {
+ LOGP(DMAIN, LOGL_ERROR, "Failed to init SCCP User\n");
+ return -1;
+ }
+
+ LOGP(DRANAP, LOGL_NOTICE, "Remote SCCP addr: IuCS: %s\n",
+ osmo_sccp_addr_name(ss7, &gw->sccp.iucs_remote_addr));
+ LOGP(DRANAP, LOGL_NOTICE, "Remote SCCP addr: IuPS: %s\n",
+ osmo_sccp_addr_name(ss7, &gw->sccp.iups_remote_addr));
+
+ /* In sccp_sap_up() we expect the cnlink in the user's priv. */
+ osmo_sccp_user_set_priv(cnlink->sccp_user, cnlink);
+
+ gw->sccp.cnlink = cnlink;
+
+ return 0;
+}
diff --git a/src/osmo-hnbgw/hnbgw_hnbap.c b/src/osmo-hnbgw/hnbgw_hnbap.c
new file mode 100644
index 0000000..5b92e4e
--- /dev/null
+++ b/src/osmo-hnbgw/hnbgw_hnbap.c
@@ -0,0 +1,632 @@
+/* hnb-gw specific code for HNBAP */
+
+/* (C) 2015 by Harald Welte <laforge(a)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/core/msgb.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/gsm/gsm48.h>
+#include <osmocom/netif/stream.h>
+
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+#include "asn1helpers.h"
+#include <osmocom/hnbap/hnbap_common.h>
+#include <osmocom/ranap/iu_helpers.h>
+
+#include <osmocom/hnbgw/hnbgw.h>
+#include <osmocom/hnbap/hnbap_ies_defs.h>
+
+#define IU_MSG_NUM_IES 32
+#define IU_MSG_NUM_EXT_IES 32
+
+static int hnbgw_hnbap_tx(struct hnb_context *ctx, struct msgb *msg)
+{
+ if (!msg)
+ return -EINVAL;
+
+ msgb_sctp_ppid(msg) = IUH_PPI_HNBAP;
+ osmo_stream_srv_send(ctx->conn, msg);
+
+ return 0;
+}
+
+static int hnbgw_tx_hnb_register_rej(struct hnb_context *ctx)
+{
+ HNBAP_HNBRegisterReject_t reject_out;
+ HNBAP_HNBRegisterRejectIEs_t reject;
+ struct msgb *msg;
+ int rc;
+
+ reject.presenceMask = 0,
+ reject.cause.present = HNBAP_Cause_PR_radioNetwork;
+ reject.cause.choice.radioNetwork = HNBAP_CauseRadioNetwork_unspecified;
+
+ /* encode the Information Elements */
+ memset(&reject_out, 0, sizeof(reject_out));
+ rc = hnbap_encode_hnbregisterrejecties(&reject_out, &reject);
+ if (rc < 0) {
+ LOGHNB(ctx, DHNBAP, LOGL_ERROR, "Failure to encode HNB-REGISTER-REJECT to %s: rc=%d\n",
+ ctx->identity_info, rc);
+ return rc;
+ }
+
+ /* generate a successfull outcome PDU */
+ msg = hnbap_generate_unsuccessful_outcome(HNBAP_ProcedureCode_id_HNBRegister,
+ HNBAP_Criticality_reject,
+ &asn_DEF_HNBAP_HNBRegisterReject,
+ &reject_out);
+
+ ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_HNBAP_HNBRegisterReject, &reject_out);
+
+ rc = hnbgw_hnbap_tx(ctx, msg);
+ if (rc == 0) {
+ /* Tell libosmo-netif to destroy this connection when it is done
+ * sending our HNB-REGISTER-REJECT response. */
+ osmo_stream_srv_set_flush_and_destroy(ctx->conn);
+ } else {
+ /* The message was not queued. Destroy the connection right away. */
+ hnb_context_release(ctx);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int hnbgw_tx_hnb_register_acc(struct hnb_context *ctx)
+{
+ HNBAP_HNBRegisterAccept_t accept_out;
+ struct msgb *msg;
+ int rc;
+
+ /* Single required response IE: RNC-ID */
+ HNBAP_HNBRegisterAcceptIEs_t accept = {
+ .rnc_id = ctx->gw->config.rnc_id
+ };
+
+ /* encode the Information Elements */
+ memset(&accept_out, 0, sizeof(accept_out));
+ rc = hnbap_encode_hnbregisteraccepties(&accept_out, &accept);
+ if (rc < 0) {
+ LOGHNB(ctx, DHNBAP, LOGL_ERROR, "Failure to encode HNB-REGISTER-ACCEPT to %s: rc=%d\n",
+ ctx->identity_info, rc);
+ return rc;
+ }
+
+ /* generate a successfull outcome PDU */
+ msg = hnbap_generate_successful_outcome(HNBAP_ProcedureCode_id_HNBRegister,
+ HNBAP_Criticality_reject,
+ &asn_DEF_HNBAP_HNBRegisterAccept,
+ &accept_out);
+
+ ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_HNBAP_HNBRegisterAccept, &accept_out);
+
+ LOGHNB(ctx, DHNBAP, LOGL_NOTICE, "Accepting HNB-REGISTER-REQ from %s\n", ctx->identity_info);
+
+ return hnbgw_hnbap_tx(ctx, msg);
+}
+
+
+static int hnbgw_tx_ue_register_acc(struct ue_context *ue)
+{
+ HNBAP_UERegisterAccept_t accept_out;
+ HNBAP_UERegisterAcceptIEs_t accept;
+ struct msgb *msg;
+ uint8_t encoded_imsi[10];
+ uint32_t ctx_id;
+ size_t encoded_imsi_len;
+ int rc;
+
+ encoded_imsi_len = ranap_imsi_encode(encoded_imsi,
+ sizeof(encoded_imsi), ue->imsi);
+
+ memset(&accept, 0, sizeof(accept));
+ accept.uE_Identity.present = HNBAP_UE_Identity_PR_iMSI;
+ OCTET_STRING_fromBuf(&accept.uE_Identity.choice.iMSI,
+ (const char *)encoded_imsi, encoded_imsi_len);
+ asn1_u24_to_bitstring(&accept.context_ID, &ctx_id, ue->context_id);
+
+ memset(&accept_out, 0, sizeof(accept_out));
+ rc = hnbap_encode_ueregisteraccepties(&accept_out, &accept);
+ if (rc < 0) {
+ return rc;
+ }
+
+ msg = hnbap_generate_successful_outcome(HNBAP_ProcedureCode_id_UERegister,
+ HNBAP_Criticality_reject,
+ &asn_DEF_HNBAP_UERegisterAccept,
+ &accept_out);
+
+ ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_OCTET_STRING, &accept.uE_Identity.choice.iMSI);
+ ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_HNBAP_UERegisterAccept, &accept_out);
+
+ return hnbgw_hnbap_tx(ue->hnb, msg);
+}
+
+static int hnbgw_tx_ue_register_rej_tmsi(struct hnb_context *hnb, HNBAP_UE_Identity_t *ue_id)
+{
+ HNBAP_UERegisterReject_t reject_out;
+ HNBAP_UERegisterRejectIEs_t reject;
+ struct msgb *msg;
+ int rc;
+
+ memset(&reject, 0, sizeof(reject));
+ reject.uE_Identity.present = ue_id->present;
+
+ /* Copy the identity over to the reject message */
+ switch (ue_id->present) {
+ case HNBAP_UE_Identity_PR_tMSILAI:
+ LOGHNB(hnb, DHNBAP, LOGL_DEBUG, "REJ UE_Id tMSI %d %s\n", ue_id->choice.tMSILAI.tMSI.size,
+ osmo_hexdump(ue_id->choice.tMSILAI.tMSI.buf, ue_id->choice.tMSILAI.tMSI.size));
+
+ LOGHNB(hnb, DHNBAP, LOGL_DEBUG, "REJ UE_Id pLMNID %d %s\n", ue_id->choice.tMSILAI.lAI.pLMNID.size,
+ osmo_hexdump(ue_id->choice.tMSILAI.lAI.pLMNID.buf, ue_id->choice.tMSILAI.lAI.pLMNID.size));
+
+ LOGHNB(hnb, DHNBAP, LOGL_DEBUG, "REJ UE_Id lAC %d %s\n", ue_id->choice.tMSILAI.lAI.lAC.size,
+ osmo_hexdump(ue_id->choice.tMSILAI.lAI.lAC.buf, ue_id->choice.tMSILAI.lAI.lAC.size));
+
+ BIT_STRING_fromBuf(&reject.uE_Identity.choice.tMSILAI.tMSI,
+ ue_id->choice.tMSILAI.tMSI.buf,
+ ue_id->choice.tMSILAI.tMSI.size * 8
+ - ue_id->choice.tMSILAI.tMSI.bits_unused);
+ OCTET_STRING_fromBuf(&reject.uE_Identity.choice.tMSILAI.lAI.pLMNID,
+ (const char *)ue_id->choice.tMSILAI.lAI.pLMNID.buf,
+ ue_id->choice.tMSILAI.lAI.pLMNID.size);
+ OCTET_STRING_fromBuf(&reject.uE_Identity.choice.tMSILAI.lAI.lAC,
+ (const char *)ue_id->choice.tMSILAI.lAI.lAC.buf,
+ ue_id->choice.tMSILAI.lAI.lAC.size);
+ break;
+
+ case HNBAP_UE_Identity_PR_pTMSIRAI:
+ LOGHNB(hnb, DHNBAP, LOGL_DEBUG, "REJ UE_Id pTMSI %d %s\n", ue_id->choice.pTMSIRAI.pTMSI.size,
+ osmo_hexdump(ue_id->choice.pTMSIRAI.pTMSI.buf, ue_id->choice.pTMSIRAI.pTMSI.size));
+
+ LOGHNB(hnb, DHNBAP, LOGL_DEBUG, "REJ UE_Id pLMNID %d %s\n", ue_id->choice.pTMSIRAI.rAI.lAI.pLMNID.size,
+ osmo_hexdump(ue_id->choice.pTMSIRAI.rAI.lAI.pLMNID.buf, ue_id->choice.pTMSIRAI.rAI.lAI.pLMNID.size));
+
+ LOGHNB(hnb, DHNBAP, LOGL_DEBUG, "REJ UE_Id lAC %d %s\n", ue_id->choice.pTMSIRAI.rAI.lAI.lAC.size,
+ osmo_hexdump(ue_id->choice.pTMSIRAI.rAI.lAI.lAC.buf, ue_id->choice.pTMSIRAI.rAI.lAI.lAC.size));
+
+ LOGHNB(hnb, DHNBAP, LOGL_DEBUG, "REJ UE_Id rAC %d %s\n", ue_id->choice.pTMSIRAI.rAI.rAC.size,
+ osmo_hexdump(ue_id->choice.pTMSIRAI.rAI.rAC.buf, ue_id->choice.pTMSIRAI.rAI.rAC.size));
+
+ BIT_STRING_fromBuf(&reject.uE_Identity.choice.pTMSIRAI.pTMSI,
+ ue_id->choice.pTMSIRAI.pTMSI.buf,
+ ue_id->choice.pTMSIRAI.pTMSI.size * 8
+ - ue_id->choice.pTMSIRAI.pTMSI.bits_unused);
+ OCTET_STRING_fromBuf(&reject.uE_Identity.choice.pTMSIRAI.rAI.lAI.pLMNID,
+ (const char *)ue_id->choice.pTMSIRAI.rAI.lAI.pLMNID.buf,
+ ue_id->choice.pTMSIRAI.rAI.lAI.pLMNID.size);
+ OCTET_STRING_fromBuf(&reject.uE_Identity.choice.pTMSIRAI.rAI.lAI.lAC,
+ (const char *)ue_id->choice.pTMSIRAI.rAI.lAI.lAC.buf,
+ ue_id->choice.pTMSIRAI.rAI.lAI.lAC.size);
+ OCTET_STRING_fromBuf(&reject.uE_Identity.choice.pTMSIRAI.rAI.rAC,
+ (const char *)ue_id->choice.pTMSIRAI.rAI.rAC.buf,
+ ue_id->choice.pTMSIRAI.rAI.rAC.size);
+ break;
+
+ default:
+ LOGHNB(hnb, DHNBAP, LOGL_ERROR, "Cannot compose UE Register Reject:"
+ " unsupported UE ID (present=%d)\n", ue_id->present);
+ return -1;
+ }
+
+ LOGHNB(hnb, DHNBAP, LOGL_ERROR, "Rejecting UE Register Request: TMSI identity registration is switched off\n");
+
+ reject.cause.present = HNBAP_Cause_PR_radioNetwork;
+ reject.cause.choice.radioNetwork = HNBAP_CauseRadioNetwork_invalid_UE_identity;
+
+ memset(&reject_out, 0, sizeof(reject_out));
+ rc = hnbap_encode_ueregisterrejecties(&reject_out, &reject);
+ if (rc < 0)
+ return rc;
+
+ msg = hnbap_generate_unsuccessful_outcome(HNBAP_ProcedureCode_id_UERegister,
+ HNBAP_Criticality_reject,
+ &asn_DEF_HNBAP_UERegisterReject,
+ &reject_out);
+
+ /* Free copied identity IEs */
+ switch (ue_id->present) {
+ case HNBAP_UE_Identity_PR_tMSILAI:
+ ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_BIT_STRING,
+ &reject.uE_Identity.choice.tMSILAI.tMSI);
+ ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_OCTET_STRING,
+ &reject.uE_Identity.choice.tMSILAI.lAI.pLMNID);
+ ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_OCTET_STRING,
+ &reject.uE_Identity.choice.tMSILAI.lAI.lAC);
+ break;
+
+ case HNBAP_UE_Identity_PR_pTMSIRAI:
+ ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_BIT_STRING,
+ &reject.uE_Identity.choice.pTMSIRAI.pTMSI);
+ ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_OCTET_STRING,
+ &reject.uE_Identity.choice.pTMSIRAI.rAI.lAI.pLMNID);
+ ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_OCTET_STRING,
+ &reject.uE_Identity.choice.pTMSIRAI.rAI.lAI.lAC);
+ ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_OCTET_STRING,
+ &reject.uE_Identity.choice.pTMSIRAI.rAI.rAC);
+ break;
+
+ default:
+ /* should never happen after above switch() */
+ break;
+ }
+
+ ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_HNBAP_UERegisterReject, &reject_out);
+
+ return hnbgw_hnbap_tx(hnb, msg);
+}
+
+static int hnbgw_tx_ue_register_acc_tmsi(struct hnb_context *hnb, HNBAP_UE_Identity_t *ue_id)
+{
+ HNBAP_UERegisterAccept_t accept_out;
+ HNBAP_UERegisterAcceptIEs_t accept;
+ struct msgb *msg;
+ uint32_t ctx_id;
+ uint32_t tmsi = 0;
+ struct ue_context *ue;
+ int rc;
+
+ memset(&accept, 0, sizeof(accept));
+ accept.uE_Identity.present = ue_id->present;
+
+ switch (ue_id->present) {
+ case HNBAP_UE_Identity_PR_tMSILAI:
+ BIT_STRING_fromBuf(&accept.uE_Identity.choice.tMSILAI.tMSI,
+ ue_id->choice.tMSILAI.tMSI.buf,
+ ue_id->choice.tMSILAI.tMSI.size * 8
+ - ue_id->choice.tMSILAI.tMSI.bits_unused);
+ tmsi = *(uint32_t*)accept.uE_Identity.choice.tMSILAI.tMSI.buf;
+ OCTET_STRING_fromBuf(&accept.uE_Identity.choice.tMSILAI.lAI.pLMNID,
+ (const char *)ue_id->choice.tMSILAI.lAI.pLMNID.buf,
+ ue_id->choice.tMSILAI.lAI.pLMNID.size);
+ OCTET_STRING_fromBuf(&accept.uE_Identity.choice.tMSILAI.lAI.lAC,
+ (const char *)ue_id->choice.tMSILAI.lAI.lAC.buf,
+ ue_id->choice.tMSILAI.lAI.lAC.size);
+ break;
+
+ case HNBAP_UE_Identity_PR_pTMSIRAI:
+ BIT_STRING_fromBuf(&accept.uE_Identity.choice.pTMSIRAI.pTMSI,
+ ue_id->choice.pTMSIRAI.pTMSI.buf,
+ ue_id->choice.pTMSIRAI.pTMSI.size * 8
+ - ue_id->choice.pTMSIRAI.pTMSI.bits_unused);
+ tmsi = *(uint32_t*)accept.uE_Identity.choice.pTMSIRAI.pTMSI.buf;
+ OCTET_STRING_fromBuf(&accept.uE_Identity.choice.pTMSIRAI.rAI.lAI.pLMNID,
+ (const char *)ue_id->choice.pTMSIRAI.rAI.lAI.pLMNID.buf,
+ ue_id->choice.pTMSIRAI.rAI.lAI.pLMNID.size);
+ OCTET_STRING_fromBuf(&accept.uE_Identity.choice.pTMSIRAI.rAI.lAI.lAC,
+ (const char *)ue_id->choice.pTMSIRAI.rAI.lAI.lAC.buf,
+ ue_id->choice.pTMSIRAI.rAI.lAI.lAC.size);
+ OCTET_STRING_fromBuf(&accept.uE_Identity.choice.pTMSIRAI.rAI.rAC,
+ (const char *)ue_id->choice.pTMSIRAI.rAI.rAC.buf,
+ ue_id->choice.pTMSIRAI.rAI.rAC.size);
+ break;
+
+ default:
+ LOGHNB(hnb, DHNBAP, LOGL_ERROR, "Unsupportedccept UE ID (present=%d)\n", ue_id->present);
+ return -1;
+ }
+
+ tmsi = ntohl(tmsi);
+ LOGHNB(hnb, DHNBAP, LOGL_DEBUG, "HNBAP register with TMSI %x\n", tmsi);
+
+ ue = ue_context_by_tmsi(hnb->gw, tmsi);
+ if (!ue)
+ ue = ue_context_alloc(hnb, NULL, tmsi);
+
+ asn1_u24_to_bitstring(&accept.context_ID, &ctx_id, ue->context_id);
+
+ memset(&accept_out, 0, sizeof(accept_out));
+ rc = hnbap_encode_ueregisteraccepties(&accept_out, &accept);
+ if (rc < 0)
+ return rc;
+
+ msg = hnbap_generate_successful_outcome(HNBAP_ProcedureCode_id_UERegister,
+ HNBAP_Criticality_reject,
+ &asn_DEF_HNBAP_UERegisterAccept,
+ &accept_out);
+
+ switch (ue_id->present) {
+ case HNBAP_UE_Identity_PR_tMSILAI:
+ ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_BIT_STRING,
+ &accept.uE_Identity.choice.tMSILAI.tMSI);
+ ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_OCTET_STRING,
+ &accept.uE_Identity.choice.tMSILAI.lAI.pLMNID);
+ ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_OCTET_STRING,
+ &accept.uE_Identity.choice.tMSILAI.lAI.lAC);
+ break;
+
+ case HNBAP_UE_Identity_PR_pTMSIRAI:
+ ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_BIT_STRING,
+ &accept.uE_Identity.choice.pTMSIRAI.pTMSI);
+ ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_OCTET_STRING,
+ &accept.uE_Identity.choice.pTMSIRAI.rAI.lAI.pLMNID);
+ ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_OCTET_STRING,
+ &accept.uE_Identity.choice.pTMSIRAI.rAI.lAI.lAC);
+ ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_OCTET_STRING,
+ &accept.uE_Identity.choice.pTMSIRAI.rAI.rAC);
+ break;
+
+ default:
+ /* should never happen after above switch() */
+ break;
+ }
+
+ ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_HNBAP_UERegisterAccept, &accept_out);
+
+ return hnbgw_hnbap_tx(hnb, msg);
+}
+
+static int hnbgw_rx_hnb_deregister(struct hnb_context *ctx, ANY_t *in)
+{
+ HNBAP_HNBDe_RegisterIEs_t ies;
+ int rc;
+
+ rc = hnbap_decode_hnbde_registeries(&ies, in);
+ if (rc < 0)
+ return rc;
+
+ LOGHNB(ctx, DHNBAP, LOGL_DEBUG, "HNB-DE-REGISTER cause=%s\n", hnbap_cause_str(&ies.cause));
+
+ hnbap_free_hnbde_registeries(&ies);
+ hnb_context_release(ctx);
+
+ return 0;
+}
+
+static int hnbgw_rx_hnb_register_req(struct hnb_context *ctx, ANY_t *in)
+{
+ struct hnb_context *hnb;
+ HNBAP_HNBRegisterRequestIEs_t ies;
+ int rc;
+
+ rc = hnbap_decode_hnbregisterrequesties(&ies, in);
+ if (rc < 0) {
+ LOGHNB(ctx, DHNBAP, LOGL_ERROR, "Failure to decode HNB-REGISTER-REQ from %s: rc=%d\n",
+ ctx->identity_info, rc);
+ return rc;
+ }
+
+ /* copy all identity parameters from the message to ctx */
+ asn1_strncpy(ctx->identity_info, &ies.hnB_Identity.hNB_Identity_Info,
+ sizeof(ctx->identity_info));
+ ctx->id.lac = asn1str_to_u16(&ies.lac);
+ ctx->id.sac = asn1str_to_u16(&ies.sac);
+ ctx->id.rac = asn1str_to_u8(&ies.rac);
+ ctx->id.cid = asn1bitstr_to_u28(&ies.cellIdentity);
+ gsm48_mcc_mnc_from_bcd(ies.plmNidentity.buf, &ctx->id.mcc, &ctx->id.mnc);
+
+ llist_for_each_entry(hnb, &ctx->gw->hnb_list, list) {
+ if (hnb->hnb_registered && ctx != hnb && memcmp(&ctx->id, &hnb->id, sizeof(ctx->id)) == 0) {
+ struct osmo_fd *ofd = osmo_stream_srv_get_ofd(ctx->conn);
+ char *name = osmo_sock_get_name(ctx, ofd->fd);
+ LOGHNB(ctx, DHNBAP, LOGL_ERROR, "rejecting HNB-REGISTER-REQ with duplicate cell identity "
+ "MCC=%u,MNC=%u,LAC=%u,RAC=%u,SAC=%u,CID=%u from %s\n",
+ ctx->id.mcc, ctx->id.mnc, ctx->id.lac, ctx->id.rac, ctx->id.sac, ctx->id.cid, name);
+ talloc_free(name);
+ return hnbgw_tx_hnb_register_rej(ctx);
+ }
+ }
+
+ ctx->hnb_registered = true;
+
+ LOGHNB(ctx, DHNBAP, LOGL_DEBUG, "HNB-REGISTER-REQ from %s\n", ctx->identity_info);
+
+ /* Send HNBRegisterAccept */
+ rc = hnbgw_tx_hnb_register_acc(ctx);
+ hnbap_free_hnbregisterrequesties(&ies);
+ return rc;
+}
+
+static int hnbgw_rx_ue_register_req(struct hnb_context *ctx, ANY_t *in)
+{
+ HNBAP_UERegisterRequestIEs_t ies;
+ struct ue_context *ue;
+ char imsi[16];
+ int rc;
+
+ rc = hnbap_decode_ueregisterrequesties(&ies, in);
+ if (rc < 0)
+ return rc;
+
+ switch (ies.uE_Identity.present) {
+ case HNBAP_UE_Identity_PR_iMSI:
+ ranap_bcd_decode(imsi, sizeof(imsi), ies.uE_Identity.choice.iMSI.buf,
+ ies.uE_Identity.choice.iMSI.size);
+ break;
+ case HNBAP_UE_Identity_PR_iMSIDS41:
+ ranap_bcd_decode(imsi, sizeof(imsi), ies.uE_Identity.choice.iMSIDS41.buf,
+ ies.uE_Identity.choice.iMSIDS41.size);
+ break;
+ case HNBAP_UE_Identity_PR_iMSIESN:
+ ranap_bcd_decode(imsi, sizeof(imsi), ies.uE_Identity.choice.iMSIESN.iMSIDS41.buf,
+ ies.uE_Identity.choice.iMSIESN.iMSIDS41.size);
+ break;
+ case HNBAP_UE_Identity_PR_tMSILAI:
+ case HNBAP_UE_Identity_PR_pTMSIRAI:
+ if (ctx->gw->config.hnbap_allow_tmsi)
+ rc = hnbgw_tx_ue_register_acc_tmsi(ctx, &ies.uE_Identity);
+ else
+ rc = hnbgw_tx_ue_register_rej_tmsi(ctx, &ies.uE_Identity);
+ /* all has been handled by TMSI, skip the IMSI code below */
+ hnbap_free_ueregisterrequesties(&ies);
+ return rc;
+ default:
+ LOGHNB(ctx, DHNBAP, LOGL_NOTICE, "UE-REGISTER-REQ with unsupported UE Id type %d\n",
+ ies.uE_Identity.present);
+ hnbap_free_ueregisterrequesties(&ies);
+ return rc;
+ }
+
+ LOGHNB(ctx, DHNBAP, LOGL_DEBUG, "UE-REGISTER-REQ ID_type=%d imsi=%s cause=%ld\n",
+ ies.uE_Identity.present, imsi, ies.registration_Cause);
+
+ ue = ue_context_by_imsi(ctx->gw, imsi);
+ if (!ue)
+ ue = ue_context_alloc(ctx, imsi, 0);
+
+ hnbap_free_ueregisterrequesties(&ies);
+ /* Send UERegisterAccept */
+ return hnbgw_tx_ue_register_acc(ue);
+}
+
+static int hnbgw_rx_ue_deregister(struct hnb_context *ctx, ANY_t *in)
+{
+ HNBAP_UEDe_RegisterIEs_t ies;
+ struct ue_context *ue;
+ int rc;
+ uint32_t ctxid;
+
+ rc = hnbap_decode_uede_registeries(&ies, in);
+ if (rc < 0)
+ return rc;
+
+ ctxid = asn1bitstr_to_u24(&ies.context_ID);
+
+ LOGHNB(ctx, DHNBAP, LOGL_DEBUG, "UE-DE-REGISTER context=%u cause=%s\n", ctxid, hnbap_cause_str(&ies.cause));
+
+ ue = ue_context_by_id(ctx->gw, ctxid);
+ if (ue)
+ ue_context_free(ue);
+
+ hnbap_free_uede_registeries(&ies);
+ return 0;
+}
+
+static int hnbgw_rx_err_ind(struct hnb_context *hnb, ANY_t *in)
+{
+ HNBAP_ErrorIndicationIEs_t ies;
+ int rc;
+
+ rc = hnbap_decode_errorindicationies(&ies, in);
+ if (rc < 0)
+ return rc;
+
+ LOGHNB(hnb, DHNBAP, LOGL_NOTICE, "HNBAP ERROR.ind, cause: %s\n", hnbap_cause_str(&ies.cause));
+
+ hnbap_free_errorindicationies(&ies);
+ return 0;
+}
+
+static int hnbgw_rx_initiating_msg(struct hnb_context *hnb, HNBAP_InitiatingMessage_t *imsg)
+{
+ int rc = 0;
+
+ switch (imsg->procedureCode) {
+ case HNBAP_ProcedureCode_id_HNBRegister: /* 8.2 */
+ rc = hnbgw_rx_hnb_register_req(hnb, &imsg->value);
+ break;
+ case HNBAP_ProcedureCode_id_HNBDe_Register: /* 8.3 */
+ rc = hnbgw_rx_hnb_deregister(hnb, &imsg->value);
+ break;
+ case HNBAP_ProcedureCode_id_UERegister: /* 8.4 */
+ rc = hnbgw_rx_ue_register_req(hnb, &imsg->value);
+ break;
+ case HNBAP_ProcedureCode_id_UEDe_Register: /* 8.5 */
+ rc = hnbgw_rx_ue_deregister(hnb, &imsg->value);
+ break;
+ case HNBAP_ProcedureCode_id_ErrorIndication: /* 8.6 */
+ rc = hnbgw_rx_err_ind(hnb, &imsg->value);
+ break;
+ case HNBAP_ProcedureCode_id_TNLUpdate: /* 8.9 */
+ case HNBAP_ProcedureCode_id_HNBConfigTransfer: /* 8.10 */
+ case HNBAP_ProcedureCode_id_RelocationComplete: /* 8.11 */
+ case HNBAP_ProcedureCode_id_U_RNTIQuery: /* 8.12 */
+ case HNBAP_ProcedureCode_id_privateMessage:
+ LOGHNB(hnb, DHNBAP, LOGL_NOTICE, "Unimplemented HNBAP Procedure %ld\n", imsg->procedureCode);
+ break;
+ default:
+ LOGHNB(hnb, DHNBAP, LOGL_NOTICE, "Unknown HNBAP Procedure %ld\n", imsg->procedureCode);
+ break;
+ }
+
+ return rc;
+}
+
+static int hnbgw_rx_successful_outcome_msg(struct hnb_context *hnb, HNBAP_SuccessfulOutcome_t *msg)
+{
+ /* We don't care much about HNBAP */
+ return 0;
+}
+
+static int hnbgw_rx_unsuccessful_outcome_msg(struct hnb_context *hnb, HNBAP_UnsuccessfulOutcome_t *msg)
+{
+ /* We don't care much about HNBAP */
+ LOGHNB(hnb, DHNBAP, LOGL_ERROR, "Received Unsuccessful Outcome, procedureCode %ld, criticality %ld,"
+ " cell mcc %u mnc %u lac %u rac %u sac %u cid %u\n", msg->procedureCode, msg->criticality,
+ hnb->id.mcc, hnb->id.mnc, hnb->id.lac, hnb->id.rac, hnb->id.sac, hnb->id.cid);
+ return 0;
+}
+
+
+static int _hnbgw_hnbap_rx(struct hnb_context *hnb, HNBAP_HNBAP_PDU_t *pdu)
+{
+ int rc = 0;
+
+ /* it's a bit odd that we can't dispatch on procedure code, but
+ * that's not possible */
+ switch (pdu->present) {
+ case HNBAP_HNBAP_PDU_PR_initiatingMessage:
+ rc = hnbgw_rx_initiating_msg(hnb, &pdu->choice.initiatingMessage);
+ break;
+ case HNBAP_HNBAP_PDU_PR_successfulOutcome:
+ rc = hnbgw_rx_successful_outcome_msg(hnb, &pdu->choice.successfulOutcome);
+ break;
+ case HNBAP_HNBAP_PDU_PR_unsuccessfulOutcome:
+ rc = hnbgw_rx_unsuccessful_outcome_msg(hnb, &pdu->choice.unsuccessfulOutcome);
+ break;
+ default:
+ LOGHNB(hnb, DHNBAP, LOGL_NOTICE, "Unknown HNBAP Presence %u\n", pdu->present);
+ rc = -1;
+ }
+
+ return rc;
+}
+
+int hnbgw_hnbap_rx(struct hnb_context *hnb, struct msgb *msg)
+{
+ HNBAP_HNBAP_PDU_t _pdu, *pdu = &_pdu;
+ asn_dec_rval_t dec_ret;
+ int rc;
+
+ /* decode and handle to _hnbgw_hnbap_rx() */
+
+ memset(pdu, 0, sizeof(*pdu));
+ dec_ret = aper_decode(NULL, &asn_DEF_HNBAP_HNBAP_PDU, (void **) &pdu,
+ msg->data, msgb_length(msg), 0, 0);
+ if (dec_ret.code != RC_OK) {
+ LOGHNB(hnb, DHNBAP, LOGL_ERROR, "Error in ASN.1 decode\n");
+ return -1;
+ }
+
+ rc = _hnbgw_hnbap_rx(hnb, pdu);
+
+ ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_HNBAP_HNBAP_PDU, pdu);
+
+ return rc;
+}
+
+
+int hnbgw_hnbap_init(void)
+{
+ return 0;
+}
diff --git a/src/osmo-hnbgw/hnbgw_ranap.c b/src/osmo-hnbgw/hnbgw_ranap.c
new file mode 100644
index 0000000..73b5018
--- /dev/null
+++ b/src/osmo-hnbgw/hnbgw_ranap.c
@@ -0,0 +1,210 @@
+/* hnb-gw specific code for RANAP */
+
+/* (C) 2015 by Harald Welte <laforge(a)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/core/msgb.h>
+#include <osmocom/core/utils.h>
+
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+#include "asn1helpers.h"
+
+#include <osmocom/hnbgw/hnbgw.h>
+#include <osmocom/hnbgw/hnbgw_rua.h>
+#include <osmocom/ranap/ranap_common.h>
+#include <osmocom/ranap/ranap_ies_defs.h>
+#include <osmocom/ranap/ranap_msg_factory.h>
+
+static int ranap_tx_reset_ack(struct hnb_context *hnb,
+ RANAP_CN_DomainIndicator_t domain)
+{
+ struct msgb *msg;
+ int rc;
+
+ msg = ranap_new_msg_reset_ack(domain, NULL);
+ if (!msg)
+ return -1;
+
+ rc = rua_tx_udt(hnb, msg->data, msgb_length(msg));
+
+ msgb_free(msg);
+
+ return rc;
+}
+
+static int ranap_rx_init_reset(struct hnb_context *hnb, ANY_t *in)
+{
+ RANAP_ResetIEs_t ies;
+ int rc, is_ps = 0;
+
+ rc = ranap_decode_reseties(&ies, in);
+ if (rc < 0)
+ return rc;
+
+ if (ies.cN_DomainIndicator == RANAP_CN_DomainIndicator_ps_domain)
+ is_ps=1;
+
+ LOGHNB(hnb, DRANAP, LOGL_INFO, "Rx RESET.req(%s,%s)\n", is_ps ? "ps" : "cs",
+ ranap_cause_str(&ies.cause));
+
+ /* FIXME: Actually we have to wait for some guard time? */
+ /* FIXME: Reset all resources related to this HNB/RNC */
+ ranap_tx_reset_ack(hnb, ies.cN_DomainIndicator);
+
+ return 0;
+}
+
+static int ranap_rx_error_ind(struct hnb_context *hnb, ANY_t *in)
+{
+ RANAP_ErrorIndicationIEs_t ies;
+ int rc;
+
+ rc = ranap_decode_errorindicationies(&ies, in);
+ if (rc < 0)
+ return rc;
+
+ if (ies.presenceMask & ERRORINDICATIONIES_RANAP_CAUSE_PRESENT) {
+ LOGHNB(hnb, DRANAP, LOGL_ERROR, "Rx ERROR.ind(%s)\n", ranap_cause_str(&ies.cause));
+ } else
+ LOGHNB(hnb, DRANAP, LOGL_ERROR, "Rx ERROR.ind\n");
+
+ return 0;
+}
+
+static int ranap_rx_initiating_msg(struct hnb_context *hnb, RANAP_InitiatingMessage_t *imsg)
+{
+ int rc = 0;
+
+ /* according tot the spec, we can primarily receive Overload,
+ * Reset, Reset ACK, Error Indication, reset Resource, Reset
+ * Resurce Acknowledge as connecitonless RANAP. There are some
+ * more messages regarding Information Transfer, Direct
+ * Information Transfer and Uplink Information Trnansfer that we
+ * can ignore. In either case, it is RANAP that we need to
+ * decode... */
+ switch (imsg->procedureCode) {
+ case RANAP_ProcedureCode_id_Reset:
+ /* Reset request */
+ rc = ranap_rx_init_reset(hnb, &imsg->value);
+ break;
+ case RANAP_ProcedureCode_id_OverloadControl: /* Overload ind */
+ break;
+ case RANAP_ProcedureCode_id_ErrorIndication: /* Error ind */
+ rc = ranap_rx_error_ind(hnb, &imsg->value);
+ break;
+ case RANAP_ProcedureCode_id_ResetResource: /* request */
+ case RANAP_ProcedureCode_id_InformationTransfer:
+ case RANAP_ProcedureCode_id_DirectInformationTransfer:
+ case RANAP_ProcedureCode_id_UplinkInformationExchange:
+ LOGHNB(hnb, DRANAP, LOGL_NOTICE, "Received unsupported RANAP "
+ "Procedure %lu from HNB, ignoring\n", imsg->procedureCode);
+ break;
+ default:
+ LOGHNB(hnb, DRANAP, LOGL_NOTICE, "Received suspicious RANAP "
+ "Procedure %lu from HNB, ignoring\n", imsg->procedureCode);
+ break;
+ }
+
+ return rc;
+}
+
+static int ranap_rx_successful_msg(struct hnb_context *hnb, RANAP_SuccessfulOutcome_t *imsg)
+{
+ /* according tot the spec, we can primarily receive Overload,
+ * Reset, Reset ACK, Error Indication, reset Resource, Reset
+ * Resurce Acknowledge as connecitonless RANAP. There are some
+ * more messages regarding Information Transfer, Direct
+ * Information Transfer and Uplink Information Trnansfer that we
+ * can ignore. In either case, it is RANAP that we need to
+ * decode... */
+ switch (imsg->procedureCode) {
+ case RANAP_ProcedureCode_id_Reset: /* Reset acknowledge */
+ break;
+ case RANAP_ProcedureCode_id_ResetResource: /* response */
+ case RANAP_ProcedureCode_id_InformationTransfer:
+ case RANAP_ProcedureCode_id_DirectInformationTransfer:
+ case RANAP_ProcedureCode_id_UplinkInformationExchange:
+ LOGHNB(hnb, DRANAP, LOGL_NOTICE, "Received unsupported RANAP "
+ "Procedure %lu from HNB, ignoring\n", imsg->procedureCode);
+ break;
+ default:
+ LOGHNB(hnb, DRANAP, LOGL_NOTICE, "Received suspicious RANAP "
+ "Procedure %lu from HNB, ignoring\n", imsg->procedureCode);
+ break;
+ }
+
+ return 0;
+}
+
+
+
+static int _hnbgw_ranap_rx(struct hnb_context *hnb, RANAP_RANAP_PDU_t *pdu)
+{
+ int rc = 0;
+
+ switch (pdu->present) {
+ case RANAP_RANAP_PDU_PR_initiatingMessage:
+ rc = ranap_rx_initiating_msg(hnb, &pdu->choice.initiatingMessage);
+ break;
+ case RANAP_RANAP_PDU_PR_successfulOutcome:
+ rc = ranap_rx_successful_msg(hnb, &pdu->choice.successfulOutcome);
+ break;
+ case RANAP_RANAP_PDU_PR_unsuccessfulOutcome:
+ LOGHNB(hnb, DRANAP, LOGL_NOTICE, "Received unsupported RANAP "
+ "unsuccessful outcome procedure %lu from HNB, ignoring\n",
+ pdu->choice.unsuccessfulOutcome.procedureCode);
+ break;
+ default:
+ LOGHNB(hnb, DRANAP, LOGL_NOTICE, "Received suspicious RANAP "
+ "presence %u from HNB, ignoring\n", pdu->present);
+ break;
+ }
+
+ return rc;
+}
+
+
+int hnbgw_ranap_rx(struct msgb *msg, uint8_t *data, size_t len)
+{
+ RANAP_RANAP_PDU_t _pdu, *pdu = &_pdu;
+ struct hnb_context *hnb = msg->dst;
+ asn_dec_rval_t dec_ret;
+ int rc;
+
+ memset(pdu, 0, sizeof(*pdu));
+ dec_ret = aper_decode(NULL,&asn_DEF_RANAP_RANAP_PDU, (void **) &pdu,
+ data, len, 0, 0);
+ if (dec_ret.code != RC_OK) {
+ LOGHNB(hnb, DRANAP, LOGL_ERROR, "Error in RANAP ASN.1 decode\n");
+ return -1;
+ }
+
+ rc = _hnbgw_ranap_rx(hnb, pdu);
+
+ return rc;
+}
+
+int hnbgw_ranap_init(void)
+{
+ return 0;
+}
diff --git a/src/osmo-hnbgw/hnbgw_rua.c b/src/osmo-hnbgw/hnbgw_rua.c
new file mode 100644
index 0000000..24ca167
--- /dev/null
+++ b/src/osmo-hnbgw/hnbgw_rua.c
@@ -0,0 +1,566 @@
+/* hnb-gw specific code for RUA (Ranap User Adaption) */
+
+/* (C) 2015 by Harald Welte <laforge(a)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/core/msgb.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/netif/stream.h>
+
+#include <osmocom/sigtran/sccp_sap.h>
+#include <osmocom/sigtran/sccp_helpers.h>
+
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+#include "asn1helpers.h"
+
+#include <osmocom/hnbgw/hnbgw.h>
+#include <osmocom/hnbgw/hnbgw_ranap.h>
+#include <osmocom/rua/rua_common.h>
+#include <osmocom/rua/rua_ies_defs.h>
+#include <osmocom/hnbgw/context_map.h>
+#include <osmocom/hnbap/HNBAP_CN-DomainIndicator.h>
+
+static const char *cn_domain_indicator_to_str(RUA_CN_DomainIndicator_t cN_DomainIndicator)
+{
+ switch (cN_DomainIndicator) {
+ case RUA_CN_DomainIndicator_cs_domain:
+ return "IuCS";
+ case RUA_CN_DomainIndicator_ps_domain:
+ return "IuPS";
+ default:
+ return "(unknown-domain)";
+ }
+}
+
+static int hnbgw_rua_tx(struct hnb_context *ctx, struct msgb *msg)
+{
+ if (!msg)
+ return -EINVAL;
+
+ msgb_sctp_ppid(msg) = IUH_PPI_RUA;
+ osmo_stream_srv_send(ctx->conn, msg);
+
+ return 0;
+}
+
+int rua_tx_udt(struct hnb_context *hnb, const uint8_t *data, unsigned int len)
+{
+ RUA_ConnectionlessTransfer_t out;
+ RUA_ConnectionlessTransferIEs_t ies;
+ struct msgb *msg;
+ int rc;
+
+ memset(&ies, 0, sizeof(ies));
+ ies.ranaP_Message.buf = (uint8_t *) data;
+ ies.ranaP_Message.size = len;
+
+ /* FIXME: msgb_free(msg)? ownership not yet clear */
+
+ memset(&out, 0, sizeof(out));
+ rc = rua_encode_connectionlesstransferies(&out, &ies);
+ if (rc < 0)
+ return rc;
+
+ msg = rua_generate_initiating_message(RUA_ProcedureCode_id_ConnectionlessTransfer,
+ RUA_Criticality_reject,
+ &asn_DEF_RUA_ConnectionlessTransfer,
+ &out);
+ ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RUA_ConnectionlessTransfer, &out);
+
+ LOGHNB(hnb, DRUA, LOGL_DEBUG, "transmitting RUA payload of %u bytes\n", msgb_length(msg));
+
+ return hnbgw_rua_tx(hnb, msg);
+}
+
+int rua_tx_dt(struct hnb_context *hnb, int is_ps, uint32_t context_id,
+ const uint8_t *data, unsigned int len)
+{
+ RUA_DirectTransfer_t out;
+ RUA_DirectTransferIEs_t ies;
+ uint32_t ctxidbuf;
+ struct msgb *msg;
+ int rc;
+
+ memset(&ies, 0, sizeof(ies));
+ if (is_ps)
+ ies.cN_DomainIndicator = RUA_CN_DomainIndicator_ps_domain;
+ else
+ ies.cN_DomainIndicator = RUA_CN_DomainIndicator_cs_domain;
+ asn1_u24_to_bitstring(&ies.context_ID, &ctxidbuf, context_id);
+ ies.ranaP_Message.buf = (uint8_t *) data;
+ ies.ranaP_Message.size = len;
+
+ /* FIXME: msgb_free(msg)? ownership not yet clear */
+
+ memset(&out, 0, sizeof(out));
+ rc = rua_encode_directtransferies(&out, &ies);
+ if (rc < 0)
+ return rc;
+
+ msg = rua_generate_initiating_message(RUA_ProcedureCode_id_DirectTransfer,
+ RUA_Criticality_reject,
+ &asn_DEF_RUA_DirectTransfer,
+ &out);
+ ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RUA_DirectTransfer, &out);
+
+ LOGHNB(hnb, DRUA, LOGL_DEBUG, "transmitting RUA (cn=%s) payload of %u bytes\n",
+ is_ps ? "ps" : "cs", msgb_length(msg));
+
+ return hnbgw_rua_tx(hnb, msg);
+}
+
+int rua_tx_disc(struct hnb_context *hnb, int is_ps, uint32_t context_id,
+ const RUA_Cause_t *cause, const uint8_t *data, unsigned int len)
+{
+ RUA_Disconnect_t out;
+ RUA_DisconnectIEs_t ies;
+ struct msgb *msg;
+ uint32_t ctxidbuf;
+ int rc;
+
+ memset(&ies, 0, sizeof(ies));
+ if (is_ps)
+ ies.cN_DomainIndicator = RUA_CN_DomainIndicator_ps_domain;
+ else
+ ies.cN_DomainIndicator = RUA_CN_DomainIndicator_cs_domain;
+ asn1_u24_to_bitstring(&ies.context_ID, &ctxidbuf, context_id);
+ memcpy(&ies.cause, cause, sizeof(ies.cause));
+ if (data && len) {
+ ies.presenceMask |= DISCONNECTIES_RUA_RANAP_MESSAGE_PRESENT;
+ ies.ranaP_Message.buf = (uint8_t *) data;
+ ies.ranaP_Message.size = len;
+ }
+
+ /* FIXME: msgb_free(msg)? ownership not yet clear */
+
+ memset(&out, 0, sizeof(out));
+ rc = rua_encode_disconnecties(&out, &ies);
+ if (rc < 0)
+ return rc;
+
+ msg = rua_generate_initiating_message(RUA_ProcedureCode_id_Disconnect,
+ RUA_Criticality_reject,
+ &asn_DEF_RUA_Disconnect,
+ &out);
+ ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RUA_Disconnect, &out);
+
+ LOGHNB(hnb, DRUA, LOGL_DEBUG, "transmitting RUA (cn=%s) payload of %u bytes\n",
+ is_ps ? "ps" : "cs", msgb_length(msg));
+
+
+ return hnbgw_rua_tx(hnb, msg);
+}
+
+
+
+/* forward a RUA message to the SCCP User API to SCCP */
+static int rua_to_scu(struct hnb_context *hnb,
+ RUA_CN_DomainIndicator_t cN_DomainIndicator,
+ enum osmo_scu_prim_type type,
+ uint32_t context_id, uint32_t cause,
+ const uint8_t *data, unsigned int len)
+{
+ struct msgb *msg;
+ struct osmo_scu_prim *prim;
+ struct hnbgw_context_map *map = NULL;
+ struct hnbgw_cnlink *cn = hnb->gw->sccp.cnlink;
+ struct osmo_sccp_addr *remote_addr;
+ bool is_ps;
+ bool release_context_map = false;
+ int rc;
+
+ switch (cN_DomainIndicator) {
+ case RUA_CN_DomainIndicator_cs_domain:
+ remote_addr = &hnb->gw->sccp.iucs_remote_addr;
+ is_ps = false;
+ break;
+ case RUA_CN_DomainIndicator_ps_domain:
+ remote_addr = &hnb->gw->sccp.iups_remote_addr;
+ is_ps = true;
+ break;
+ default:
+ LOGHNB(hnb, DRUA, LOGL_ERROR, "Unsupported Domain %ld\n", cN_DomainIndicator);
+ return -1;
+ }
+
+ if (!cn) {
+ LOGHNB(hnb, DRUA, LOGL_NOTICE, "CN=NULL, discarding message\n");
+ return 0;
+ }
+
+ msg = msgb_alloc(1500, "rua_to_sccp");
+
+ prim = (struct osmo_scu_prim *) msgb_put(msg, sizeof(*prim));
+ osmo_prim_init(&prim->oph, SCCP_SAP_USER, type, PRIM_OP_REQUEST, msg);
+
+ switch (type) {
+ case OSMO_SCU_PRIM_N_UNITDATA:
+ LOGHNB(hnb, DRUA, LOGL_DEBUG, "rua_to_scu() %s to %s, rua_ctx_id %u (unitdata, no scu_conn_id)\n",
+ cn_domain_indicator_to_str(cN_DomainIndicator), osmo_sccp_addr_dump(remote_addr), context_id);
+ break;
+ default:
+ map = context_map_alloc_by_hnb(hnb, context_id, is_ps, cn);
+ OSMO_ASSERT(map);
+ LOGHNB(hnb, DRUA, LOGL_DEBUG, "rua_to_scu() %s to %s, rua_ctx_id %u scu_conn_id %u\n",
+ cn_domain_indicator_to_str(cN_DomainIndicator), osmo_sccp_addr_dump(remote_addr),
+ map->rua_ctx_id, map->scu_conn_id);
+ }
+
+ /* add primitive header */
+ switch (type) {
+ case OSMO_SCU_PRIM_N_CONNECT:
+ prim->u.connect.called_addr = *remote_addr;
+ prim->u.connect.calling_addr = cn->gw->sccp.local_addr;
+ prim->u.connect.sccp_class = 2;
+ prim->u.connect.conn_id = map->scu_conn_id;
+ /* Two separate logs because of osmo_sccp_addr_dump(). */
+ LOGHNB(hnb, DRUA, LOGL_DEBUG, "RUA to SCCP N_CONNECT: called_addr:%s\n",
+ osmo_sccp_addr_dump(&prim->u.connect.called_addr));
+ LOGHNB(hnb, DRUA, LOGL_DEBUG, "RUA to SCCP N_CONNECT: calling_addr:%s\n",
+ osmo_sccp_addr_dump(&prim->u.connect.calling_addr));
+ break;
+ case OSMO_SCU_PRIM_N_DATA:
+ prim->u.data.conn_id = map->scu_conn_id;
+ break;
+ case OSMO_SCU_PRIM_N_DISCONNECT:
+ prim->u.disconnect.conn_id = map->scu_conn_id;
+ prim->u.disconnect.cause = cause;
+ release_context_map = true;
+ break;
+ case OSMO_SCU_PRIM_N_UNITDATA:
+ prim->u.unitdata.called_addr = *remote_addr;
+ prim->u.unitdata.calling_addr = cn->gw->sccp.local_addr;
+ /* Two separate logs because of osmo_sccp_addr_dump(). */
+ LOGHNB(hnb, DRUA, LOGL_DEBUG, "RUA to SCCP N_UNITDATA: called_addr:%s\n",
+ osmo_sccp_addr_dump(&prim->u.unitdata.called_addr));
+ LOGHNB(hnb, DRUA, LOGL_DEBUG, "RUA to SCCP N_UNITDATA: calling_addr:%s\n",
+ osmo_sccp_addr_dump(&prim->u.unitdata.calling_addr));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* add optional data section, if needed */
+ if (data && len) {
+ msg->l2h = msgb_put(msg, len);
+ memcpy(msg->l2h, data, len);
+ }
+
+ rc = osmo_sccp_user_sap_down(cn->sccp_user, &prim->oph);
+
+ if (map && release_context_map)
+ context_map_deactivate(map);
+
+ return rc;
+}
+
+static uint32_t rua_to_scu_cause(RUA_Cause_t *in)
+{
+ /* FIXME: Implement this! */
+#if 0
+ switch (in->present) {
+ case RUA_Cause_PR_NOTHING:
+ break;
+ case RUA_Cause_PR_radioNetwork:
+ switch (in->choice.radioNetwork) {
+ case RUA_CauseRadioNetwork_normal:
+ case RUA_CauseRadioNetwork_connect_failed:
+ case RUA_CauseRadioNetwork_network_release:
+ case RUA_CauseRadioNetwork_unspecified:
+ }
+ break;
+ case RUA_Cause_PR_transport:
+ switch (in->choice.transport) {
+ case RUA_CauseTransport_transport_resource_unavailable:
+ break;
+ case RUA_CauseTransport_unspecified:
+ break;
+ }
+ break;
+ case RUA_Cause_PR_protocol:
+ switch (in->choice.protocol) {
+ case RUA_CauseProtocol_transfer_syntax_error:
+ break;
+ case RUA_CauseProtocol_abstract_syntax_error_reject:
+ break;
+ case RUA_CauseProtocol_abstract_syntax_error_ignore_and_notify:
+ break;
+ case RUA_CauseProtocol_message_not_compatible_with_receiver_state:
+ break;
+ case RUA_CauseProtocol_semantic_error:
+ break;
+ case RUA_CauseProtocol_unspecified:
+ break;
+ case RUA_CauseProtocol_abstract_syntax_error_falsely_constructed_message:
+ break;
+ }
+ break;
+ case RUA_Cause_PR_misc:
+ switch (in->choice.misc) {
+ case RUA_CauseMisc_processing_overload:
+ break;
+ case RUA_CauseMisc_hardware_failure:
+ break;
+ case RUA_CauseMisc_o_and_m_intervention:
+ break;
+ case RUA_CauseMisc_unspecified:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+#else
+ return 0;
+#endif
+
+}
+
+static int rua_rx_init_connect(struct msgb *msg, ANY_t *in)
+{
+ RUA_ConnectIEs_t ies;
+ struct hnb_context *hnb = msg->dst;
+ uint32_t context_id;
+ int rc;
+
+ rc = rua_decode_connecties(&ies, in);
+ if (rc < 0)
+ return rc;
+
+ context_id = asn1bitstr_to_u24(&ies.context_ID);
+
+ LOGHNB(hnb, DRUA, LOGL_DEBUG, "RUA %s Connect.req(ctx=0x%x, %s)\n",
+ cn_domain_indicator_to_str(ies.cN_DomainIndicator), context_id,
+ ies.establishment_Cause == RUA_Establishment_Cause_emergency_call ? "emergency" : "normal");
+
+ rc = rua_to_scu(hnb, ies.cN_DomainIndicator, OSMO_SCU_PRIM_N_CONNECT,
+ context_id, 0, ies.ranaP_Message.buf,
+ ies.ranaP_Message.size);
+
+ rua_free_connecties(&ies);
+
+ return rc;
+}
+
+static int rua_rx_init_disconnect(struct msgb *msg, ANY_t *in)
+{
+ RUA_DisconnectIEs_t ies;
+ struct hnb_context *hnb = msg->dst;
+ uint32_t context_id;
+ uint32_t scu_cause;
+ uint8_t *ranap_data = NULL;
+ unsigned int ranap_len = 0;
+ int rc;
+
+ rc = rua_decode_disconnecties(&ies, in);
+ if (rc < 0)
+ return rc;
+
+ context_id = asn1bitstr_to_u24(&ies.context_ID);
+ scu_cause = rua_to_scu_cause(&ies.cause);
+
+ LOGHNB(hnb, DRUA, LOGL_DEBUG, "RUA Disconnect.req(ctx=0x%x,cause=%s)\n", context_id,
+ rua_cause_str(&ies.cause));
+
+ if (ies.presenceMask & DISCONNECTIES_RUA_RANAP_MESSAGE_PRESENT) {
+ ranap_data = ies.ranaP_Message.buf;
+ ranap_len = ies.ranaP_Message.size;
+ }
+
+ rc = rua_to_scu(hnb, ies.cN_DomainIndicator,
+ OSMO_SCU_PRIM_N_DISCONNECT,
+ context_id, scu_cause, ranap_data, ranap_len);
+
+ rua_free_disconnecties(&ies);
+
+ return rc;
+}
+
+static int rua_rx_init_dt(struct msgb *msg, ANY_t *in)
+{
+ RUA_DirectTransferIEs_t ies;
+ struct hnb_context *hnb = msg->dst;
+ uint32_t context_id;
+ int rc;
+
+ rc = rua_decode_directtransferies(&ies, in);
+ if (rc < 0)
+ return rc;
+
+ context_id = asn1bitstr_to_u24(&ies.context_ID);
+
+ LOGHNB(hnb, DRUA, LOGL_DEBUG, "RUA Data.req(ctx=0x%x)\n", context_id);
+
+ rc = rua_to_scu(hnb,
+ ies.cN_DomainIndicator,
+ OSMO_SCU_PRIM_N_DATA,
+ context_id, 0, ies.ranaP_Message.buf,
+ ies.ranaP_Message.size);
+
+ rua_free_directtransferies(&ies);
+
+ return rc;
+}
+
+static int rua_rx_init_udt(struct msgb *msg, ANY_t *in)
+{
+ RUA_ConnectionlessTransferIEs_t ies;
+ struct hnb_context *hnb = msg->dst;
+ int rc;
+
+ rc = rua_decode_connectionlesstransferies(&ies, in);
+ if (rc < 0)
+ return rc;
+
+ LOGHNB(hnb, DRUA, LOGL_DEBUG, "RUA UData.req()\n");
+
+ /* according tot the spec, we can primarily receive Overload,
+ * Reset, Reset ACK, Error Indication, reset Resource, Reset
+ * Resurce Acknowledge as connecitonless RANAP. There are some
+ * more messages regarding Information Transfer, Direct
+ * Information Transfer and Uplink Information Trnansfer that we
+ * can ignore. In either case, it is RANAP that we need to
+ * decode... */
+ rc = hnbgw_ranap_rx(msg, ies.ranaP_Message.buf, ies.ranaP_Message.size);
+ rua_free_connectionlesstransferies(&ies);
+
+ return rc;
+}
+
+
+static int rua_rx_init_err_ind(struct msgb *msg, ANY_t *in)
+{
+ RUA_ErrorIndicationIEs_t ies;
+ struct hnb_context *hnb = msg->dst;
+ int rc;
+
+ rc = rua_decode_errorindicationies(&ies, in);
+ if (rc < 0)
+ return rc;
+
+ LOGHNB(hnb, DRUA, LOGL_ERROR, "RUA UData.ErrorInd(%s)\n", rua_cause_str(&ies.cause));
+
+ rua_free_errorindicationies(&ies);
+ return rc;
+}
+
+static int rua_rx_initiating_msg(struct msgb *msg, RUA_InitiatingMessage_t *imsg)
+{
+ struct hnb_context *hnb = msg->dst;
+ int rc;
+
+ switch (imsg->procedureCode) {
+ case RUA_ProcedureCode_id_Connect:
+ rc = rua_rx_init_connect(msg, &imsg->value);
+ break;
+ case RUA_ProcedureCode_id_DirectTransfer:
+ rc = rua_rx_init_dt(msg, &imsg->value);
+ break;
+ case RUA_ProcedureCode_id_Disconnect:
+ rc = rua_rx_init_disconnect(msg, &imsg->value);
+ break;
+ case RUA_ProcedureCode_id_ConnectionlessTransfer:
+ rc = rua_rx_init_udt(msg, &imsg->value);
+ break;
+ case RUA_ProcedureCode_id_ErrorIndication:
+ rc = rua_rx_init_err_ind(msg, &imsg->value);
+ break;
+ case RUA_ProcedureCode_id_privateMessage:
+ LOGHNB(hnb, DRUA, LOGL_NOTICE, "Unhandled: RUA Initiating Msg: Private Msg\n");
+ rc = 0;
+ break;
+ default:
+ LOGHNB(hnb, DRUA, LOGL_NOTICE, "Unknown RUA Procedure %lu\n", imsg->procedureCode);
+ rc = -1;
+ }
+
+ return rc;
+}
+
+static int rua_rx_successful_outcome_msg(struct msgb *msg, RUA_SuccessfulOutcome_t *in)
+{
+ struct hnb_context *hnb = msg->dst;
+ /* FIXME */
+ LOGHNB(hnb, DRUA, LOGL_NOTICE, "Unexpected RUA Successful Outcome\n");
+ return -1;
+}
+
+static int rua_rx_unsuccessful_outcome_msg(struct msgb *msg, RUA_UnsuccessfulOutcome_t *in)
+{
+ struct hnb_context *hnb = msg->dst;
+ /* FIXME */
+ LOGHNB(hnb, DRUA, LOGL_NOTICE, "Unexpected RUA Unsucessful Outcome\n");
+ return -1;
+}
+
+
+static int _hnbgw_rua_rx(struct msgb *msg, RUA_RUA_PDU_t *pdu)
+{
+ struct hnb_context *hnb = msg->dst;
+ int rc;
+
+ /* it's a bit odd that we can't dispatch on procedure code, but
+ * that's not possible */
+ switch (pdu->present) {
+ case RUA_RUA_PDU_PR_initiatingMessage:
+ rc = rua_rx_initiating_msg(msg, &pdu->choice.initiatingMessage);
+ break;
+ case RUA_RUA_PDU_PR_successfulOutcome:
+ rc = rua_rx_successful_outcome_msg(msg, &pdu->choice.successfulOutcome);
+ break;
+ case RUA_RUA_PDU_PR_unsuccessfulOutcome:
+ rc = rua_rx_unsuccessful_outcome_msg(msg, &pdu->choice.unsuccessfulOutcome);
+ break;
+ default:
+ LOGHNB(hnb, DRUA, LOGL_NOTICE, "Unknown RUA presence %u\n", pdu->present);
+ rc = -1;
+ }
+
+ return rc;
+}
+
+int hnbgw_rua_rx(struct hnb_context *hnb, struct msgb *msg)
+{
+ RUA_RUA_PDU_t _pdu, *pdu = &_pdu;
+ asn_dec_rval_t dec_ret;
+ int rc;
+
+ /* decode and handle to _hnbgw_hnbap_rx() */
+
+ memset(pdu, 0, sizeof(*pdu));
+ dec_ret = aper_decode(NULL, &asn_DEF_RUA_RUA_PDU, (void **) &pdu,
+ msg->data, msgb_length(msg), 0, 0);
+ if (dec_ret.code != RC_OK) {
+ LOGHNB(hnb, DRUA, LOGL_ERROR, "Error in ASN.1 decode\n");
+ return -1;
+ }
+
+ rc = _hnbgw_rua_rx(msg, pdu);
+
+ return rc;
+}
+
+
+int hnbgw_rua_init(void)
+{
+ return 0;
+}
diff --git a/src/osmo-hnbgw/hnbgw_vty.c b/src/osmo-hnbgw/hnbgw_vty.c
new file mode 100644
index 0000000..4ad1ddb
--- /dev/null
+++ b/src/osmo-hnbgw/hnbgw_vty.c
@@ -0,0 +1,418 @@
+/* HNB-GW interface to quagga VTY */
+
+/* (C) 2016 by sysmocom s.f.m.c. GmbH <info(a)sysmocom.de>
+ * 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 <string.h>
+
+#include <osmocom/core/socket.h>
+#include <osmocom/vty/command.h>
+
+#include <osmocom/hnbgw/vty.h>
+
+#include <osmocom/hnbgw/hnbgw.h>
+#include <osmocom/hnbgw/context_map.h>
+#include <osmocom/sigtran/protocol/sua.h>
+#include <osmocom/sigtran/sccp_helpers.h>
+#include <osmocom/netif/stream.h>
+
+static void *tall_hnb_ctx = NULL;
+static struct hnb_gw *g_hnb_gw = NULL;
+
+static struct cmd_node hnbgw_node = {
+ HNBGW_NODE,
+ "%s(config-hnbgw)# ",
+ 1,
+};
+
+DEFUN(cfg_hnbgw, cfg_hnbgw_cmd,
+ "hnbgw", "Configure HNBGW options")
+{
+ vty->node = HNBGW_NODE;
+ return CMD_SUCCESS;
+}
+
+static struct cmd_node iuh_node = {
+ IUH_NODE,
+ "%s(config-hnbgw-iuh)# ",
+ 1,
+};
+
+DEFUN(cfg_hnbgw_iuh, cfg_hnbgw_iuh_cmd,
+ "iuh", "Configure Iuh options")
+{
+ vty->node = IUH_NODE;
+ return CMD_SUCCESS;
+}
+
+static struct cmd_node iucs_node = {
+ IUCS_NODE,
+ "%s(config-hnbgw-iucs)# ",
+ 1,
+};
+
+DEFUN(cfg_hnbgw_iucs, cfg_hnbgw_iucs_cmd,
+ "iucs", "Configure IuCS options")
+{
+ vty->node = IUCS_NODE;
+ return CMD_SUCCESS;
+}
+
+static struct cmd_node iups_node = {
+ IUPS_NODE,
+ "%s(config-hnbgw-iups)# ",
+ 1,
+};
+
+DEFUN(cfg_hnbgw_iups, cfg_hnbgw_iups_cmd,
+ "iups", "Configure IuPS options")
+{
+ vty->node = IUPS_NODE;
+ return CMD_SUCCESS;
+}
+
+int hnbgw_vty_go_parent(struct vty *vty)
+{
+ switch (vty->node) {
+ case IUH_NODE:
+ case IUCS_NODE:
+ case IUPS_NODE:
+ vty->node = HNBGW_NODE;
+ vty->index = NULL;
+ break;
+ case HNBGW_NODE:
+ vty->node = CONFIG_NODE;
+ vty->index = NULL;
+ break;
+ case CONFIG_NODE:
+ vty->node = ENABLE_NODE;
+ vty->index = NULL;
+ break;
+ default:
+ osmo_ss7_vty_go_parent(vty);
+ break;
+ }
+
+ return vty->node;
+}
+
+DEFUN(show_cnlink, show_cnlink_cmd, "show cnlink",
+ SHOW_STR "Display information on core network link\n")
+{
+ struct osmo_ss7_route *rt;
+ struct osmo_ss7_instance *ss7 = osmo_sccp_get_ss7(g_hnb_gw->sccp.client);
+#define GUARD(STR) \
+ STR ? STR : "", \
+ STR ? ":" : ""
+
+ vty_out(vty, "IuCS: %s <->",
+ osmo_sccp_user_name(g_hnb_gw->sccp.cnlink->sccp_user));
+ vty_out(vty, " %s%s%s%s",
+ GUARD(g_hnb_gw->config.iucs_remote_addr_name),
+ osmo_sccp_inst_addr_name(g_hnb_gw->sccp.client, &g_hnb_gw->sccp.iucs_remote_addr),
+ VTY_NEWLINE);
+
+ rt = osmo_ss7_route_lookup(ss7, g_hnb_gw->sccp.iucs_remote_addr.pc);
+ vty_out(vty, " SS7 route: %s%s", osmo_ss7_route_name(rt, true), VTY_NEWLINE);
+
+ vty_out(vty, "IuPS: %s <->",
+ osmo_sccp_user_name(g_hnb_gw->sccp.cnlink->sccp_user));
+ vty_out(vty, " %s%s%s%s",
+ GUARD(g_hnb_gw->config.iups_remote_addr_name),
+ osmo_sccp_inst_addr_name(g_hnb_gw->sccp.client, &g_hnb_gw->sccp.iups_remote_addr),
+ VTY_NEWLINE);
+
+ rt = osmo_ss7_route_lookup(ss7, g_hnb_gw->sccp.iups_remote_addr.pc);
+ vty_out(vty, " SS7 route: %s%s", osmo_ss7_route_name(rt, true), VTY_NEWLINE);
+
+#undef GUARD
+ return CMD_SUCCESS;
+}
+
+static void vty_out_ofd_addr(struct vty *vty, struct osmo_fd *ofd)
+{
+ char *name;
+ if (!ofd || ofd->fd < 0
+ || !(name = osmo_sock_get_name(vty, ofd->fd))) {
+ vty_out(vty, "(no addr)");
+ return;
+ }
+ vty_out(vty, "%s", name);
+ talloc_free(name);
+}
+
+static void vty_dump_hnb_info__map_states(struct vty *vty, const char *name, unsigned int count,
+ unsigned int state_count[])
+{
+ unsigned int i;
+ if (!count)
+ return;
+ vty_out(vty, " %s: %u contexts:", name, count);
+ for (i = 0; i <= MAP_S_NUM_STATES; i++) {
+ if (!state_count[i])
+ continue;
+ vty_out(vty, " %s:%u", hnbgw_context_map_state_name(i), state_count[i]);
+ }
+ vty_out(vty, VTY_NEWLINE);
+}
+
+static void vty_dump_hnb_info(struct vty *vty, struct hnb_context *hnb)
+{
+ struct hnbgw_context_map *map;
+ unsigned int map_count[2] = {};
+ unsigned int state_count[2][MAP_S_NUM_STATES + 1] = {};
+
+ vty_out(vty, "HNB ");
+ vty_out_ofd_addr(vty, hnb->conn? osmo_stream_srv_get_ofd(hnb->conn) : NULL);
+ vty_out(vty, " \"%s\"%s", hnb->identity_info, VTY_NEWLINE);
+ vty_out(vty, " MCC %u MNC %u LAC %u RAC %u SAC %u CID %u SCTP-stream:HNBAP=%u,RUA=%u%s",
+ hnb->id.mcc, hnb->id.mnc, hnb->id.lac, hnb->id.rac, hnb->id.sac, hnb->id.cid,
+ hnb->hnbap_stream, hnb->rua_stream, VTY_NEWLINE);
+
+ llist_for_each_entry(map, &hnb->map_list, hnb_list) {
+ map_count[map->is_ps? 1 : 0]++;
+ state_count[map->is_ps? 1 : 0]
+ [(map->state >= 0 && map->state < MAP_S_NUM_STATES)?
+ map->state : MAP_S_NUM_STATES]++;
+ }
+ vty_dump_hnb_info__map_states(vty, "IuCS", map_count[0], state_count[0]);
+ vty_dump_hnb_info__map_states(vty, "IuPS", map_count[1], state_count[1]);
+}
+
+static void vty_dump_ue_info(struct vty *vty, struct ue_context *ue)
+{
+ vty_out(vty, "UE IMSI \"%s\" context ID %u%s", ue->imsi, ue->context_id, VTY_NEWLINE);
+}
+
+DEFUN(show_hnb, show_hnb_cmd, "show hnb all", SHOW_STR "Display information about all HNB")
+{
+ struct hnb_context *hnb;
+ unsigned int count = 0;
+
+ if (llist_empty(&g_hnb_gw->hnb_list)) {
+ vty_out(vty, "No HNB connected%s", VTY_NEWLINE);
+ return CMD_SUCCESS;
+ }
+
+ llist_for_each_entry(hnb, &g_hnb_gw->hnb_list, list) {
+ vty_dump_hnb_info(vty, hnb);
+ count++;
+ }
+
+ vty_out(vty, "%u HNB connected%s", count, VTY_NEWLINE);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_one_hnb, show_one_hnb_cmd, "show hnb NAME ", SHOW_STR "Display information about a HNB")
+{
+ struct hnb_context *hnb;
+ const char *identity_info = argv[0];
+
+ if (llist_empty(&g_hnb_gw->hnb_list)) {
+ vty_out(vty, "No HNB connected%s", VTY_NEWLINE);
+ return CMD_SUCCESS;
+ }
+
+ hnb = hnb_context_by_identity_info(g_hnb_gw, identity_info);
+ if (hnb == NULL) {
+ vty_out(vty, "No HNB found with identity '%s'%s", identity_info, VTY_NEWLINE);
+ return CMD_SUCCESS;
+ }
+
+ vty_dump_hnb_info(vty, hnb);
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_ue, show_ue_cmd, "show ue all", SHOW_STR "Display information about a UE")
+{
+ struct ue_context *ue;
+
+ llist_for_each_entry(ue, &g_hnb_gw->ue_list, list) {
+ vty_dump_ue_info(vty, ue);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_talloc, show_talloc_cmd, "show talloc", SHOW_STR "Display talloc info")
+{
+ talloc_report_full(tall_hnb_ctx, stderr);
+ talloc_report_full(talloc_asn1_ctx, stderr);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_hnbgw_rnc_id, cfg_hnbgw_rnc_id_cmd,
+ "rnc-id <0-65535>",
+ "Configure the HNBGW's RNC Id, the common RNC Id used for all connected hNodeB. It is sent to"
+ " each hNodeB upon HNBAP HNB-Register-Accept, and the hNodeB will subsequently send this as"
+ " RANAP InitialUE Messages' GlobalRNC-ID IE. Takes effect as soon as the hNodeB re-registers.\n"
+ "RNC Id value\n")
+{
+ g_hnb_gw->config.rnc_id = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_hnbgw_iuh_local_ip, cfg_hnbgw_iuh_local_ip_cmd, "local-ip A.B.C.D",
+ "Accept Iuh connections on local interface\n"
+ "Local interface IP address (default: " HNBGW_LOCAL_IP_DEFAULT ")")
+{
+ talloc_free((void*)g_hnb_gw->config.iuh_local_ip);
+ g_hnb_gw->config.iuh_local_ip = talloc_strdup(tall_hnb_ctx, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_hnbgw_iuh_local_port, cfg_hnbgw_iuh_local_port_cmd, "local-port <1-65535>",
+ "Accept Iuh connections on local port\n"
+ "Local interface port (default: 29169)")
+{
+ g_hnb_gw->config.iuh_local_port = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_hnbgw_iuh_hnbap_allow_tmsi, cfg_hnbgw_iuh_hnbap_allow_tmsi_cmd,
+ "hnbap-allow-tmsi (0|1)",
+ "Allow HNBAP UE Register messages with TMSI or PTMSI identity\n"
+ "Only accept IMSI identity, reject TMSI or PTMSI\n"
+ "Accept IMSI, TMSI or PTMSI as UE identity\n")
+{
+ g_hnb_gw->config.hnbap_allow_tmsi = (*argv[0] == '1');
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_hnbgw_log_prefix, cfg_hnbgw_log_prefix_cmd,
+ "log-prefix (hnb-id|umts-cell-id)",
+ "Configure the log message prefix\n"
+ "Use the hNB-ID as log message prefix\n"
+ "Use the UMTS Cell ID as log message prefix\n")
+{
+ if (!strcmp(argv[0], "hnb-id"))
+ g_hnb_gw->config.log_prefix_hnb_id = true;
+ else
+ g_hnb_gw->config.log_prefix_hnb_id = false;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_hnbgw_iucs_remote_addr,
+ cfg_hnbgw_iucs_remote_addr_cmd,
+ "remote-addr NAME",
+ "SCCP address to send IuCS to (MSC)\n"
+ "SCCP address book entry name (see 'cs7-instance')\n")
+{
+ g_hnb_gw->config.iucs_remote_addr_name = talloc_strdup(g_hnb_gw, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_hnbgw_iups_remote_addr,
+ cfg_hnbgw_iups_remote_addr_cmd,
+ "remote-addr NAME",
+ "SCCP address to send IuPS to (SGSN)\n"
+ "SCCP address book entry name (see 'cs7-instance')\n")
+{
+ g_hnb_gw->config.iups_remote_addr_name = talloc_strdup(g_hnb_gw, argv[0]);
+ return CMD_SUCCESS;
+}
+
+static int config_write_hnbgw(struct vty *vty)
+{
+ vty_out(vty, "hnbgw%s", VTY_NEWLINE);
+ vty_out(vty, " log-prefix %s%s", g_hnb_gw->config.log_prefix_hnb_id ? "hnb-id" : "umts-cell-id",
+ VTY_NEWLINE);
+ return CMD_SUCCESS;
+}
+
+static int config_write_hnbgw_iuh(struct vty *vty)
+{
+ const char *addr;
+ uint16_t port;
+
+ vty_out(vty, " iuh%s", VTY_NEWLINE);
+
+ addr = g_hnb_gw->config.iuh_local_ip;
+ if (addr && (strcmp(addr, HNBGW_LOCAL_IP_DEFAULT) != 0))
+ vty_out(vty, " local-ip %s%s", addr, VTY_NEWLINE);
+
+ port = g_hnb_gw->config.iuh_local_port;
+ if (port && port != IUH_DEFAULT_SCTP_PORT)
+ vty_out(vty, " local-port %u%s", port, VTY_NEWLINE);
+
+ if (g_hnb_gw->config.hnbap_allow_tmsi)
+ vty_out(vty, " hnbap-allow-tmsi 1%s", VTY_NEWLINE);
+
+ return CMD_SUCCESS;
+}
+
+static int config_write_hnbgw_iucs(struct vty *vty)
+{
+ if (!g_hnb_gw->config.iucs_remote_addr_name)
+ return CMD_SUCCESS;
+
+ vty_out(vty, " iucs%s", VTY_NEWLINE);
+ vty_out(vty, " remote-addr %s%s", g_hnb_gw->config.iucs_remote_addr_name,
+ VTY_NEWLINE);
+
+ return CMD_SUCCESS;
+}
+
+static int config_write_hnbgw_iups(struct vty *vty)
+{
+ if (!g_hnb_gw->config.iups_remote_addr_name)
+ return CMD_SUCCESS;
+
+ vty_out(vty, " iups%s", VTY_NEWLINE);
+ vty_out(vty, " remote-addr %s%s", g_hnb_gw->config.iups_remote_addr_name,
+ VTY_NEWLINE);
+
+ return CMD_SUCCESS;
+}
+
+void hnbgw_vty_init(struct hnb_gw *gw, void *tall_ctx)
+{
+ g_hnb_gw = gw;
+ tall_hnb_ctx = tall_ctx;
+
+ install_element(CONFIG_NODE, &cfg_hnbgw_cmd);
+ install_node(&hnbgw_node, config_write_hnbgw);
+
+ install_element(HNBGW_NODE, &cfg_hnbgw_rnc_id_cmd);
+ install_element(HNBGW_NODE, &cfg_hnbgw_log_prefix_cmd);
+
+ install_element(HNBGW_NODE, &cfg_hnbgw_iuh_cmd);
+ install_node(&iuh_node, config_write_hnbgw_iuh);
+
+ install_element(IUH_NODE, &cfg_hnbgw_iuh_local_ip_cmd);
+ install_element(IUH_NODE, &cfg_hnbgw_iuh_local_port_cmd);
+ install_element(IUH_NODE, &cfg_hnbgw_iuh_hnbap_allow_tmsi_cmd);
+
+ install_element(HNBGW_NODE, &cfg_hnbgw_iucs_cmd);
+ install_node(&iucs_node, config_write_hnbgw_iucs);
+
+ install_element(IUCS_NODE, &cfg_hnbgw_iucs_remote_addr_cmd);
+
+ install_element(HNBGW_NODE, &cfg_hnbgw_iups_cmd);
+ install_node(&iups_node, config_write_hnbgw_iups);
+
+ install_element(IUPS_NODE, &cfg_hnbgw_iups_remote_addr_cmd);
+
+ install_element_ve(&show_cnlink_cmd);
+ install_element_ve(&show_hnb_cmd);
+ install_element_ve(&show_one_hnb_cmd);
+ install_element_ve(&show_ue_cmd);
+ install_element_ve(&show_talloc_cmd);
+}
diff --git a/tests/Makefile.am b/tests/Makefile.am
new file mode 100644
index 0000000..37b70bc
--- /dev/null
+++ b/tests/Makefile.am
@@ -0,0 +1,77 @@
+SUBDIRS = \
+ $(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) \
+ ctrl_test_runner.py \
+ osmo-hnbgw.vty \
+ $(NULL)
+
+TESTSUITE = $(srcdir)/testsuite
+
+DISTCLEANFILES = \
+ atconfig \
+ $(NULL)
+
+if ENABLE_EXT_TESTS
+python-tests: $(BUILT_SOURCES)
+ echo ""
+# TODO: Enable once we have a VTY/CTRL interface:
+# $(MAKE) vty-test
+# osmotestvty.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
+# osmotestconfig.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
+# $(srcdir)/ctrl_test_runner.py -w $(abs_top_builddir) -v
+else
+python-tests: $(BUILT_SOURCES)
+ echo "Not running python-based tests (determined at configure-time)"
+endif
+
+# Run a specific test with: 'make vty-test VTY_TEST=osmo-hnbgw.vty'
+VTY_TEST ?= *.vty
+
+# To update the VTY script from current application behavior,
+# pass -u to vty_script_runner.py by doing:
+# make vty-test U=-u
+vty-test:
+ osmo_verify_transcript_vty.py -v \
+ -n OsmoHNBGW -p 4273 \
+ -r "$(top_builddir)/src/osmo-hnbgw/osmo-hnbgw -c $(top_srcdir)/doc/examples/osmo-hnbgw/osmo-hnbgw.cfg" \
+ $(U) $(srcdir)/$(VTY_TEST)
+
+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 0000000..e69de29
--- /dev/null
+++ b/tests/atlocal.in
diff --git a/tests/ctrl_test_runner.py b/tests/ctrl_test_runner.py
new file mode 100755
index 0000000..701bd89
--- /dev/null
+++ b/tests/ctrl_test_runner.py
@@ -0,0 +1,207 @@
+#!/usr/bin/env python3
+
+# (C) 2013 by Jacob Erlbeck <jerlbeck(a)sysmocom.de>
+# (C) 2014 by Holger Hans Peter Freyther
+# based on vty_test_runner.py:
+# (C) 2013 by Katerina Barone-Adesi <kat.obsc(a)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("Current directory: %s" % os.getcwd(), file=sys.stderr)
+ print("Consider setting -b", file=sys.stderr)
+
+ 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).decode()
+ 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 TestCtrlHNB(TestCtrlBase):
+
+ def tearDown(self):
+ TestCtrlBase.tearDown(self)
+ os.unlink("tmp_dummy_sock")
+
+ def ctrl_command(self):
+ return ["./src/osmo-hnbgw/osmo-hnbgw", "-r", "tmp_dummy_sock", "-c",
+ "doc/examples/osmo-hnbgw/osmo-hnbgw.cfg"]
+
+ def ctrl_app(self):
+ return (4249, "./src/osmo-hnbgw/osmo-hnbgw", "OsmoHNBGW", "hnb")
+
+ def testCtrlErrs(self):
+ r = self.do_get('invalid')
+ self.assertEqual(r['mtype'], 'ERROR')
+ self.assertEqual(r['error'], 'Command not found')
+
+ r = self.do_get('hnbgw.999')
+ self.assertEqual(r['mtype'], 'ERROR')
+ self.assertEqual(r['error'], 'Error while resolving object')
+
+def add_hnbgw_test(suite, workdir, klass):
+ if not os.path.isfile(os.path.join(workdir, "src/osmo-hnbgw/osmo-hnbgw")):
+ print("Skipping the HNBGW test")
+ return
+ test = unittest.TestLoader().loadTestsFromTestCase(klass)
+ 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_hnbgw_test(suite, workdir, TestCtrlHNB)
+ res = unittest.TextTestRunner(verbosity=verbose_level).run(suite)
+ sys.exit(len(res.errors) + len(res.failures))
diff --git a/tests/osmo-hnbgw.vty b/tests/osmo-hnbgw.vty
new file mode 100644
index 0000000..b8d562c
--- /dev/null
+++ b/tests/osmo-hnbgw.vty
@@ -0,0 +1,2 @@
+OsmoHNBGW> enable
+
diff --git a/tests/testsuite.at b/tests/testsuite.at
new file mode 100644
index 0000000..65d1ca0
--- /dev/null
+++ b/tests/testsuite.at
@@ -0,0 +1,8 @@
+AT_INIT
+AT_BANNER([Regression tests.])
+
+#AT_SETUP([foobar])
+#AT_KEYWORDS([foobar])
+#cat $abs_srcdir/foobar/foobar_test.ok > expout
+#AT_CHECK([$abs_top_builddir/tests/foobar/foobar_test], [], [expout], [ignore])
+#AT_CLEANUP
--
To view, visit https://gerrit.osmocom.org/c/osmo-hnbgw/+/26771
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings
Gerrit-Project: osmo-hnbgw
Gerrit-Branch: master
Gerrit-Change-Id: I569d221aeb83d352c1621c44c013a0e4c82fc8a8
Gerrit-Change-Number: 26771
Gerrit-PatchSet: 1
Gerrit-Owner: pespin <pespin(a)sysmocom.de>
Gerrit-MessageType: newchange
dexter has uploaded this change for review. ( https://gerrit.osmocom.org/c/pysim/+/26767 )
Change subject: filesystem: use correct AID for applications found by probing
......................................................................
filesystem: use correct AID for applications found by probing
When printing applications found by probing for a specific AID, then the
wrong variable is used to print the AID.
Change-Id: I3d5ec28e46fe00c0d793a1d9ef0a0e0900649a4d
---
M pySim/filesystem.py
1 file changed, 1 insertion(+), 1 deletion(-)
git pull ssh://gerrit.osmocom.org:29418/pysim refs/changes/67/26767/1
diff --git a/pySim/filesystem.py b/pySim/filesystem.py
index c706131..dfe1677 100644
--- a/pySim/filesystem.py
+++ b/pySim/filesystem.py
@@ -1115,7 +1115,7 @@
for f in set(apps_profile) - set(apps_taken):
data, sw = self.card.select_adf_by_aid(f.aid)
if sw == "9000":
- print(" %s: %s" % (f.name, a))
+ print(" %s: %s" % (f.name, f.aid))
apps_taken.append(f)
return apps_taken
--
To view, visit https://gerrit.osmocom.org/c/pysim/+/26767
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings
Gerrit-Project: pysim
Gerrit-Branch: master
Gerrit-Change-Id: I3d5ec28e46fe00c0d793a1d9ef0a0e0900649a4d
Gerrit-Change-Number: 26767
Gerrit-PatchSet: 1
Gerrit-Owner: dexter <pmaier(a)sysmocom.de>
Gerrit-MessageType: newchange