mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-upf.git
synced 2025-11-02 04:53:24 +00:00
Compare commits
145 Commits
neels/wip
...
neels/nftq
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd7d5aa880 | ||
|
|
73c55eec1d | ||
|
|
095616c928 | ||
|
|
1393ae7c91 | ||
|
|
3d03b34370 | ||
|
|
a555ab7a7d | ||
|
|
2e3aaf32b4 | ||
|
|
510f865a79 | ||
|
|
e6b8353a7c | ||
|
|
3efa019656 | ||
|
|
4738fc2014 | ||
|
|
afe7a51c8f | ||
|
|
7713d784c9 | ||
|
|
0d96ea1730 | ||
|
|
a21bcec358 | ||
|
|
9395752540 | ||
|
|
0b83ceb20d | ||
|
|
77806ea88b | ||
|
|
60cbef5885 | ||
|
|
8ec1871914 | ||
|
|
aa6eabf766 | ||
|
|
203ce0b34f | ||
|
|
4f4163032f | ||
|
|
166bba4532 | ||
|
|
e6c65defac | ||
|
|
edb94b3f8a | ||
|
|
8b85851d22 | ||
|
|
1d422d6283 | ||
|
|
6a2763cfdf | ||
|
|
5db469aa7e | ||
|
|
21dae5cd35 | ||
|
|
5bd84491b8 | ||
|
|
40a30fce4a | ||
|
|
27a90869c7 | ||
|
|
4e4315c2ba | ||
|
|
8e5fa9ef7b | ||
|
|
1670321cdc | ||
|
|
a3e85aefcb | ||
|
|
6007cb92d0 | ||
|
|
36cca044c4 | ||
|
|
8e17c9933c | ||
|
|
1961cf90b5 | ||
|
|
0a87f42f10 | ||
|
|
75c07af406 | ||
|
|
a2f2650786 | ||
|
|
4633c23471 | ||
|
|
d8742f79ca | ||
|
|
6c01708438 | ||
|
|
4e1c680e59 | ||
|
|
fbe70076eb | ||
|
|
091603c4a4 | ||
|
|
fae0ed6d24 | ||
|
|
6cb4231383 | ||
|
|
4e2b367d89 | ||
|
|
374fd1eab4 | ||
|
|
3c0fc60c3c | ||
|
|
e7f812cf18 | ||
|
|
c3bf187588 | ||
|
|
24030881be | ||
|
|
eaf2d153a8 | ||
|
|
341e2ff692 | ||
|
|
d2f02df613 | ||
|
|
cd345bd6cd | ||
|
|
52f9da22ff | ||
|
|
b9d4ac8379 | ||
|
|
4832e932e6 | ||
|
|
2a2884fbbe | ||
|
|
c4eb92d211 | ||
|
|
95ab35035a | ||
|
|
8525c49c5d | ||
|
|
0e66d699ed | ||
|
|
eb8361f4c5 | ||
|
|
9c6a8e32a0 | ||
|
|
08af1f15f8 | ||
|
|
bd737c14fa | ||
|
|
2a9d91792e | ||
|
|
95e56eaecb | ||
|
|
341e130841 | ||
|
|
feeaf35e44 | ||
|
|
629647a535 | ||
|
|
1a341ee418 | ||
|
|
d059391125 | ||
|
|
3572241df5 | ||
|
|
e68eca0e8f | ||
|
|
8e842b890c | ||
|
|
d7f683a66c | ||
|
|
65788ed64e | ||
|
|
88b3b63987 | ||
|
|
391259bd8c | ||
|
|
6d17c43c42 | ||
|
|
54ebc4772b | ||
|
|
0575e9bad9 | ||
|
|
28180a6246 | ||
|
|
0fca3412d8 | ||
|
|
b183aa84af | ||
|
|
527f1b3b94 | ||
|
|
cd3f25cc20 | ||
|
|
701bb8addc | ||
|
|
95eb2c6a89 | ||
|
|
80aefa42c6 | ||
|
|
a3b5488b69 | ||
|
|
6730f104d8 | ||
|
|
2d2fcd81bc | ||
|
|
ffc461ab38 | ||
|
|
361ecd8cd0 | ||
|
|
7c3eeb0760 | ||
|
|
c88dc7866f | ||
|
|
f95bd5b895 | ||
|
|
114277cff7 | ||
|
|
d186d59aa2 | ||
|
|
803c1968e6 | ||
|
|
436c165f1c | ||
|
|
4ee2a4be5e | ||
|
|
b78cc91e4a | ||
|
|
22006ba039 | ||
|
|
9fd081534c | ||
|
|
581f5c63dc | ||
|
|
0db788aecd | ||
|
|
183ff5c0a9 | ||
|
|
4c823221c6 | ||
|
|
2442ccd673 | ||
|
|
8c29080dde | ||
|
|
9f8dd6f043 | ||
|
|
e35c12454a | ||
|
|
1b3e2711cd | ||
|
|
506b763094 | ||
|
|
665e8874f4 | ||
|
|
5b87376ba5 | ||
|
|
77cf6ccf21 | ||
|
|
0388579be9 | ||
|
|
7493d6db71 | ||
|
|
06482c6554 | ||
|
|
bee02fc34f | ||
|
|
d87748957a | ||
|
|
2eeec08d1e | ||
|
|
6c7261640f | ||
|
|
f93859548f | ||
|
|
3574c79194 | ||
|
|
bff9378f25 | ||
|
|
38ce6deeea | ||
|
|
d5b98172f9 | ||
|
|
d212f0ac7b | ||
|
|
3274cc3398 | ||
|
|
bd9747a591 | ||
|
|
897581e06d |
661
COPYING
Normal file
661
COPYING
Normal file
@@ -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/>.
|
||||
@@ -19,7 +19,6 @@ SUBDIRS = \
|
||||
BUILT_SOURCES = $(top_srcdir)/.version
|
||||
EXTRA_DIST = \
|
||||
.version \
|
||||
contrib/osmo-upf.spec.in \
|
||||
debian \
|
||||
git-version-gen \
|
||||
osmoappdesc.py \
|
||||
|
||||
@@ -5,16 +5,16 @@ Homepage
|
||||
--------
|
||||
|
||||
The official homepage of the project is
|
||||
https://osmocom.org/projects/osmoupf/wiki
|
||||
https://osmocom.org/projects/osmo-upf/wiki
|
||||
|
||||
GIT Repository
|
||||
--------------
|
||||
|
||||
You can clone from the official osmo-upf.git repository using
|
||||
|
||||
git clone git://git.osmocom.org/osmo-upf.git
|
||||
git clone https://gitea.osmocom.org/cellular-infrastructure/osmo-upf
|
||||
|
||||
There is a cgit interface at https://git.osmocom.org/osmo-upf/
|
||||
There is a web interface at https://gitea.osmocom.org/cellular-infrastructure/osmo-upf.
|
||||
|
||||
To submit patches, see "Contributing" below.
|
||||
|
||||
|
||||
@@ -7,3 +7,4 @@
|
||||
# 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
|
||||
osmo-pfcp-tool liburing new dependency to support GTP flooding
|
||||
|
||||
36
configure.ac
36
configure.ac
@@ -36,12 +36,26 @@ fi
|
||||
PKG_PROG_PKG_CONFIG([0.20])
|
||||
|
||||
dnl checks for libraries
|
||||
AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DL="$LIBS";LIBS=""])
|
||||
AC_SUBST(LIBRARY_DL)
|
||||
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(LIBOSMOPFCP, libosmo-pfcp >= 0.1.0)
|
||||
PKG_CHECK_MODULES(LIBGTPNL, libgtpnl >= 1.2.0)
|
||||
PKG_CHECK_MODULES(LIBNFTABLES, libnftables >= 1.0.2)
|
||||
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.5.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.5.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.5.0)
|
||||
AC_ARG_ENABLE([uring], [AS_HELP_STRING([--disable-uring], [Build without io_uring support])],
|
||||
[
|
||||
ENABLE_URING=$enableval
|
||||
],
|
||||
[
|
||||
ENABLE_URING="yes"
|
||||
])
|
||||
AS_IF([test "x$ENABLE_URING" = "xyes"], [
|
||||
PKG_CHECK_MODULES(LIBURING, [liburing >= 0.7])
|
||||
AC_DEFINE([HAVE_URING],[1],[Build with io_uring support for GTP flood commands])
|
||||
])
|
||||
AM_CONDITIONAL(ENABLE_URING, test "x$ENABLE_URING" = "xyes")
|
||||
AC_SUBST(ENABLE_URING)
|
||||
|
||||
dnl checks for header files
|
||||
AC_HEADER_STDC
|
||||
@@ -123,7 +137,7 @@ if test "x$enable_ext_tests" = "xyes" ; then
|
||||
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.])
|
||||
AC_MSG_ERROR([Please install https://gitea.osmocom.org/cellular-infrastructure/osmo-python-tests to run the VTY/CTRL tests.])
|
||||
fi
|
||||
fi
|
||||
AC_MSG_CHECKING([whether to enable VTY/CTRL tests])
|
||||
@@ -197,18 +211,14 @@ AM_CONFIG_HEADER(config.h)
|
||||
AC_OUTPUT(
|
||||
include/Makefile
|
||||
include/osmocom/Makefile
|
||||
include/osmocom/gtlv/Makefile
|
||||
include/osmocom/pfcp/Makefile
|
||||
include/osmocom/upf/Makefile
|
||||
include/osmocom/pfcptool/Makefile
|
||||
src/Makefile
|
||||
src/libosmo-gtlv/Makefile
|
||||
src/libosmo-pfcp/Makefile
|
||||
src/osmo-upf/Makefile
|
||||
src/osmo-pfcp-tool/Makefile
|
||||
tests/Makefile
|
||||
tests/atlocal
|
||||
tests/libosmo-gtlv/Makefile
|
||||
tests/libosmo-gtlv/test_gtlv_gen/Makefile
|
||||
tests/libosmo-gtlv/test_tliv/Makefile
|
||||
tests/unique_ids/Makefile
|
||||
doc/Makefile
|
||||
doc/examples/Makefile
|
||||
doc/manuals/Makefile
|
||||
|
||||
@@ -30,6 +30,36 @@ export LD_LIBRARY_PATH="$inst/lib"
|
||||
export PATH="$inst/bin:$PATH"
|
||||
|
||||
osmo-build-dep.sh libosmocore "" --disable-doxygen
|
||||
osmo-build-dep.sh libosmo-pfcp
|
||||
osmo-build-dep.sh libgtpnl
|
||||
|
||||
# build libnftnl and libnftables from git.netfilter.org
|
||||
build_from_netfilter() {
|
||||
### TODO: enable osmo-build-dep.sh to build from git.netfilter.org URL?
|
||||
project="$1"
|
||||
set +x
|
||||
echo
|
||||
echo
|
||||
echo
|
||||
echo " =============================== $project ==============================="
|
||||
echo
|
||||
set -x
|
||||
if [ -d "./$project" ]; then
|
||||
rm -rf "./$project"
|
||||
fi
|
||||
git clone "git://git.netfilter.org/$project" "$project"
|
||||
cd "$project"
|
||||
autoreconf --install --force
|
||||
./configure \
|
||||
--prefix="$inst/stow/$project" \
|
||||
--without-cli \
|
||||
--disable-man-doc \
|
||||
--enable-python=no
|
||||
$MAKE $PARALLEL_MAKE install
|
||||
STOW_DIR="$inst/stow" stow --restow $project
|
||||
}
|
||||
build_from_netfilter libnftnl
|
||||
build_from_netfilter nftables
|
||||
|
||||
# Additional configure options and depends
|
||||
CONFIG=""
|
||||
|
||||
3
contrib/osmo-pfcp-tool-scripts/assoc_setup.vty
Normal file
3
contrib/osmo-pfcp-tool-scripts/assoc_setup.vty
Normal file
@@ -0,0 +1,3 @@
|
||||
pfcp-peer 127.0.0.1
|
||||
tx assoc-setup-req
|
||||
sleep 1
|
||||
8
contrib/osmo-pfcp-tool-scripts/assoc_setup_retrans.vty
Normal file
8
contrib/osmo-pfcp-tool-scripts/assoc_setup_retrans.vty
Normal file
@@ -0,0 +1,8 @@
|
||||
pfcp-peer 127.0.0.1
|
||||
tx assoc-setup-req
|
||||
sleep 3
|
||||
retrans req
|
||||
sleep 5
|
||||
retrans req
|
||||
sleep 1
|
||||
retrans req
|
||||
51
contrib/osmo-pfcp-tool-scripts/gtp_flood.vty
Normal file
51
contrib/osmo-pfcp-tool-scripts/gtp_flood.vty
Normal file
@@ -0,0 +1,51 @@
|
||||
# Establish N PFCP sessions for tunend, and emit massive GTP traffic to the UPF
|
||||
# to each established tunnel.
|
||||
#
|
||||
# osmo-pfcp-tool UPF "internet host"
|
||||
# |GTP-ep -------GTP-----> GTP-ep|UE-IP-addr -------IP------> arbitrary-IP|
|
||||
# |10.0.1.1 10.0.2.1|192.168.10.23 123.234.42.23|
|
||||
# |10.0.1.2
|
||||
# ^ ^ ^
|
||||
# ^ | | |
|
||||
# | | configure by configure by
|
||||
# configure by from UPF 'ue ip' 'target ip',
|
||||
# 'gtp ip' ("F-TEID=choose") 'target port'
|
||||
|
||||
# Configure one or more local GTP endpoints to emit GTP packets from.
|
||||
# Established sessions will use these round-robin.
|
||||
# These need to be local IP addresses for 'gtp flood' to work.
|
||||
gtp local 127.0.1.1 10001
|
||||
gtp local 127.0.1.2 10002
|
||||
|
||||
# use UE IP addresses from this range, +1 for each new UE:
|
||||
# 192.168.0.1, 192.168.0.2, ...
|
||||
ue ip range 192.168.23.1 192.168.23.254
|
||||
|
||||
# now associate with UPF and start N sessions.
|
||||
pfcp-peer 127.0.0.11
|
||||
tx assoc-setup-req
|
||||
sleep 1
|
||||
date
|
||||
|
||||
n 30 session create tunend
|
||||
wait responses
|
||||
# All sessions established
|
||||
date
|
||||
|
||||
# For each established PFCP session, emit GTP packets
|
||||
gtp flood
|
||||
workers 2
|
||||
flows-per-session 1
|
||||
packets-per-flow 1000
|
||||
|
||||
# configure the generated GTP payload: send UDP packets from the UE address
|
||||
# and these source UDP ports to these target addresses and target UDP ports.
|
||||
# They are used round-robin.
|
||||
# Source IP is the UE IP address.
|
||||
payload source port udp range 10000 10010
|
||||
payload target ip range 123.234.42.1 123.234.42.254
|
||||
payload target port udp range 10000 10010
|
||||
|
||||
# All GTP is flowing.
|
||||
# osmo-pfcp-tool will keep this up for as long as there still are active GTP flows,
|
||||
# or until receiving a signal interrupt (ctrl-C).
|
||||
10
contrib/osmo-pfcp-tool-scripts/heartbeat.vty
Normal file
10
contrib/osmo-pfcp-tool-scripts/heartbeat.vty
Normal file
@@ -0,0 +1,10 @@
|
||||
pfcp-peer 127.0.0.1
|
||||
tx heartbeat
|
||||
sleep 2
|
||||
tx heartbeat
|
||||
sleep 2
|
||||
tx heartbeat
|
||||
sleep 2
|
||||
tx heartbeat
|
||||
sleep 2
|
||||
tx heartbeat
|
||||
11
contrib/osmo-pfcp-tool-scripts/osmo-pfcp-tool.cfg
Normal file
11
contrib/osmo-pfcp-tool-scripts/osmo-pfcp-tool.cfg
Normal file
@@ -0,0 +1,11 @@
|
||||
log stderr
|
||||
logging color 1
|
||||
logging print category-hex 0
|
||||
logging print category 1
|
||||
logging timestamp 0
|
||||
logging print file basename last
|
||||
logging print level 1
|
||||
logging level set-all info
|
||||
|
||||
local-addr 127.0.0.2
|
||||
listen
|
||||
25
contrib/osmo-pfcp-tool-scripts/osmo-upf-11.cfg
Normal file
25
contrib/osmo-pfcp-tool-scripts/osmo-upf-11.cfg
Normal file
@@ -0,0 +1,25 @@
|
||||
log stderr
|
||||
logging filter all 1
|
||||
logging color 1
|
||||
logging print category-hex 0
|
||||
logging print category 1
|
||||
logging timestamp 0
|
||||
logging print file basename last
|
||||
logging print level 1
|
||||
logging level set-all notice
|
||||
logging level set-all info
|
||||
logging level session debug
|
||||
logging level nft debug
|
||||
logging level gtp debug
|
||||
#logging level set-all debug
|
||||
|
||||
line vty
|
||||
bind 127.0.0.11
|
||||
ctrl
|
||||
bind 127.0.0.11
|
||||
|
||||
timer pfcp x24 5000
|
||||
pfcp
|
||||
local-addr 127.0.0.11
|
||||
tunend
|
||||
dev create apn11 127.0.0.11
|
||||
25
contrib/osmo-pfcp-tool-scripts/osmo-upf-12.cfg
Normal file
25
contrib/osmo-pfcp-tool-scripts/osmo-upf-12.cfg
Normal file
@@ -0,0 +1,25 @@
|
||||
log stderr
|
||||
logging filter all 1
|
||||
logging color 1
|
||||
logging print category-hex 0
|
||||
logging print category 1
|
||||
logging timestamp 0
|
||||
logging print file basename last
|
||||
logging print level 1
|
||||
logging level set-all notice
|
||||
logging level set-all info
|
||||
logging level session debug
|
||||
logging level nft debug
|
||||
logging level gtp debug
|
||||
#logging level set-all debug
|
||||
|
||||
line vty
|
||||
bind 127.0.0.12
|
||||
ctrl
|
||||
bind 127.0.0.12
|
||||
|
||||
timer pfcp x24 5000
|
||||
pfcp
|
||||
local-addr 127.0.0.12
|
||||
tunmap
|
||||
table-name osmo-upf-12
|
||||
@@ -0,0 +1,6 @@
|
||||
timer pfcp x23 0
|
||||
pfcp-peer 127.0.0.1
|
||||
session tunend
|
||||
ue ip 127.127.127.127
|
||||
gtp access remote f-teid 127.0.0.127 127
|
||||
tx session-est-req
|
||||
16
contrib/osmo-pfcp-tool-scripts/session_mod.vty
Normal file
16
contrib/osmo-pfcp-tool-scripts/session_mod.vty
Normal file
@@ -0,0 +1,16 @@
|
||||
timer pfcp x23 0
|
||||
pfcp-peer 127.0.0.1
|
||||
tx assoc-setup-req
|
||||
sleep 1
|
||||
session
|
||||
ue ip 127.127.127.127
|
||||
gtp access remote f-teid 127.0.0.127 127
|
||||
tx session-est-req drop
|
||||
sleep 3
|
||||
tx session-mod-req far forw
|
||||
sleep 5
|
||||
tx session-mod-req far drop
|
||||
sleep 3
|
||||
tx session-mod-req far forw
|
||||
sleep 3
|
||||
tx session-del-req
|
||||
30
contrib/osmo-pfcp-tool-scripts/tunend_plus_tunmap.vty
Normal file
30
contrib/osmo-pfcp-tool-scripts/tunend_plus_tunmap.vty
Normal file
@@ -0,0 +1,30 @@
|
||||
# ACCESS UPF tunmap UPF tunend (CORE)
|
||||
# session 23 = tunmap session 42 = encaps/decaps
|
||||
# GTP 127.0.0.13 127.0.0.12 127.0.0.11
|
||||
# TEID l:23 r:123 <---> r:23 l:123 | l:142 r:42 <---> r:142 l:42 | 192.168.100.42
|
||||
#
|
||||
# Run two UPF, one listening on / sending from 127.0.0.11, the other on 127.0.0.12.
|
||||
# (Each has to match on the sender address of incoming GTP packets.)
|
||||
|
||||
timer pfcp x23 0
|
||||
|
||||
pfcp-peer 127.0.0.11
|
||||
tx assoc-setup-req
|
||||
sleep 1
|
||||
session tunend 42
|
||||
ue ip 192.168.100.42
|
||||
gtp access local f-teid 127.0.0.11 42
|
||||
gtp access remote f-teid 127.0.0.12 142
|
||||
tx session-est-req
|
||||
sleep 1
|
||||
|
||||
pfcp-peer 127.0.0.12
|
||||
tx assoc-setup-req
|
||||
sleep 1
|
||||
session tunmap 23
|
||||
gtp core remote f-teid 127.0.0.11 42
|
||||
gtp core local f-teid 127.0.0.12 142
|
||||
gtp access local f-teid 127.0.0.12 123
|
||||
gtp access remote f-teid 127.0.0.13 23
|
||||
tx session-est-req
|
||||
sleep 1
|
||||
11
contrib/osmo-pfcp-tool-scripts/tunend_session_est.vty
Normal file
11
contrib/osmo-pfcp-tool-scripts/tunend_session_est.vty
Normal file
@@ -0,0 +1,11 @@
|
||||
timer pfcp x23 0
|
||||
pfcp-peer 127.0.0.1
|
||||
tx assoc-setup-req
|
||||
sleep 1
|
||||
session tunend
|
||||
ue ip 127.127.127.127
|
||||
gtp access local f-teid choose
|
||||
gtp access remote f-teid 127.0.0.12 142
|
||||
tx session-est-req forw
|
||||
sleep 5
|
||||
tx session-del-req
|
||||
12
contrib/osmo-pfcp-tool-scripts/tunmap_session_est.vty
Normal file
12
contrib/osmo-pfcp-tool-scripts/tunmap_session_est.vty
Normal file
@@ -0,0 +1,12 @@
|
||||
timer pfcp x23 0
|
||||
pfcp-peer 127.0.0.1
|
||||
tx assoc-setup-req
|
||||
sleep 1
|
||||
session tunmap
|
||||
gtp core remote f-teid 127.0.0.11 42
|
||||
gtp core local f-teid choose
|
||||
gtp access local f-teid choose
|
||||
gtp access remote f-teid 127.0.0.13 23
|
||||
tx session-est-req
|
||||
sleep 5
|
||||
tx session-del-req
|
||||
@@ -1,86 +0,0 @@
|
||||
#
|
||||
# spec file for package osmo-upf
|
||||
#
|
||||
# Copyright (c) 2017, Martin Hauke <mardnh@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-upf
|
||||
Version: @VERSION@
|
||||
Release: 0
|
||||
Summary: OsmoUPF: Osmocom User Plane Function
|
||||
License: AGPL-3.0-or-later AND GPL-2.0-or-later
|
||||
Group: Hardware/Mobile
|
||||
URL: https://osmocom.org/projects/osmoupf
|
||||
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(libosmocore) >= 1.6.0
|
||||
BuildRequires: pkgconfig(libosmoctrl) >= 1.6.0
|
||||
BuildRequires: pkgconfig(libosmovty) >= 1.6.0
|
||||
BuildRequires: pkgconfig(talloc)
|
||||
%{?systemd_requires}
|
||||
|
||||
%description
|
||||
OsmoUPF: Osmocom User Plane Function
|
||||
|
||||
%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-upf
|
||||
%dir %{_docdir}/%{name}/examples
|
||||
%dir %{_docdir}/%{name}/examples/osmo-upf
|
||||
%{_docdir}/%{name}/examples/osmo-upf/osmo-upf.cfg
|
||||
%dir %{_sysconfdir}/osmocom
|
||||
%config(noreplace) %{_sysconfdir}/osmocom/osmo-upf.cfg
|
||||
%{_unitdir}/%{name}.service
|
||||
|
||||
%changelog
|
||||
4
contrib/set_cap_net_admin.sh
Executable file
4
contrib/set_cap_net_admin.sh
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
set -x
|
||||
bin="${1:-$(which osmo-upf)}"
|
||||
setcap cap_net_admin+pe "$bin"
|
||||
@@ -1,11 +1,18 @@
|
||||
[Unit]
|
||||
Description=Osmocom User Plane Function (UPF)
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
StateDirectory=osmocom
|
||||
WorkingDirectory=%S/osmocom
|
||||
Restart=always
|
||||
User=osmocom
|
||||
Group=osmocom
|
||||
ExecStart=/usr/bin/osmo-upf -c /etc/osmocom/osmo-upf.cfg
|
||||
RestartSec=2
|
||||
AmbientCapabilities=CAP_NET_ADMIN
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
23
debian/changelog
vendored
23
debian/changelog
vendored
@@ -0,0 +1,23 @@
|
||||
osmo-upf (0.1.1) unstable; urgency=medium
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* configure.ac: do not require unused dlopen
|
||||
* Fix missing dash in Redmine project URL
|
||||
|
||||
[ Harald Welte ]
|
||||
* add missing dependencies to libosmo-{gtlv,pfcp} to rpm + dpkg
|
||||
|
||||
[ Neels Hofmeyr ]
|
||||
* add missing COPYING file
|
||||
* debian,RPM,configure: fix packaging (deps etc)
|
||||
|
||||
[ Neels Janosch Hofmeyr ]
|
||||
* configure: set libosmocore >= 1.6.0 like in packaging
|
||||
|
||||
-- Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de> Wed, 24 Aug 2022 16:50:56 +0200
|
||||
|
||||
osmo-upf (0.1.0) testing; urgency=low
|
||||
|
||||
Create new Debian package.
|
||||
|
||||
-- Neels Hofmeyr <nhofmeyr@sysmocom.de> Tue, 16 Aug 2022 23:17:05 +0200
|
||||
|
||||
2
debian/compat
vendored
2
debian/compat
vendored
@@ -1 +1 @@
|
||||
9
|
||||
10
|
||||
|
||||
13
debian/control
vendored
13
debian/control
vendored
@@ -2,7 +2,8 @@ Source: osmo-upf
|
||||
Section: net
|
||||
Priority: extra
|
||||
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
|
||||
Build-Depends: debhelper (>=9),
|
||||
# liburing-dev: don't try to install it on debian 10 and ubuntu 20.04
|
||||
Build-Depends: debhelper (>= 10),
|
||||
dh-autoreconf,
|
||||
autotools-dev,
|
||||
autoconf,
|
||||
@@ -12,11 +13,15 @@ Build-Depends: debhelper (>=9),
|
||||
pkg-config,
|
||||
python3-minimal,
|
||||
libtalloc-dev,
|
||||
libgtpnl-dev (>= 1.2.0),
|
||||
libnftables-dev (>= 1.0.2),
|
||||
libosmocore-dev (>= 1.6.0),
|
||||
osmo-gsm-manuals-dev (>= 1.2.0)
|
||||
libosmo-pfcp-dev (>= 0.1.0),
|
||||
osmo-gsm-manuals-dev (>= 1.2.0),
|
||||
liburing-dev | base-files (<< 11) | ubuntu-keyring (<< 2021),
|
||||
Standards-Version: 3.9.8
|
||||
Vcs-Git: git://git.osmocom.org/osmo-upf.git
|
||||
Vcs-Browser: https://git.osmocom.org/osmo-upf/
|
||||
Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-upf
|
||||
Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-upf
|
||||
Homepage: https://projects.osmocom.org/projects/osmo-upf
|
||||
|
||||
Package: osmo-upf
|
||||
|
||||
2
debian/copyright
vendored
2
debian/copyright
vendored
@@ -1,6 +1,6 @@
|
||||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: osmo-upf
|
||||
Source: git://git.osmocom.org/osmo-upf
|
||||
Source: https://gitea.osmocom.org/cellular-infrastructure/osmo-upf
|
||||
|
||||
Files: *
|
||||
Copyright: 2021-2022 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
|
||||
1
debian/osmo-upf.install
vendored
1
debian/osmo-upf.install
vendored
@@ -1,4 +1,5 @@
|
||||
etc/osmocom/osmo-upf.cfg
|
||||
lib/systemd/system/osmo-upf.service
|
||||
usr/bin/osmo-pfcp-tool
|
||||
usr/bin/osmo-upf
|
||||
usr/share/doc/osmo-upf/examples/osmo-upf/osmo-upf.cfg usr/share/doc/osmo-upf/examples
|
||||
|
||||
38
debian/postinst
vendored
Executable file
38
debian/postinst
vendored
Executable file
@@ -0,0 +1,38 @@
|
||||
#!/bin/sh -e
|
||||
case "$1" in
|
||||
configure)
|
||||
# Create the osmocom group and user (if it doesn't exist yet)
|
||||
if ! getent group osmocom >/dev/null; then
|
||||
groupadd --system osmocom
|
||||
fi
|
||||
if ! getent passwd osmocom >/dev/null; then
|
||||
useradd \
|
||||
--system \
|
||||
--gid osmocom \
|
||||
--home-dir /var/lib/osmocom \
|
||||
--shell /sbin/nologin \
|
||||
--comment "Open Source Mobile Communications" \
|
||||
osmocom
|
||||
fi
|
||||
|
||||
# Fix permissions of previous (root-owned) install (OS#4107)
|
||||
if dpkg --compare-versions "$2" le "0.2.0"; then
|
||||
if [ -e /etc/osmocom/osmo-upf.cfg ]; then
|
||||
chown -v osmocom:osmocom /etc/osmocom/osmo-upf.cfg
|
||||
chmod -v 0660 /etc/osmocom/osmo-upf.cfg
|
||||
fi
|
||||
|
||||
if [ -d /etc/osmocom ]; then
|
||||
chown -v root:osmocom /etc/osmocom
|
||||
chmod -v 2775 /etc/osmocom
|
||||
fi
|
||||
|
||||
mkdir -p /var/lib/osmocom
|
||||
chown -R -v osmocom:osmocom /var/lib/osmocom
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# dh_installdeb(1) will replace this with shell code automatically
|
||||
# generated by other debhelper scripts.
|
||||
#DEBHELPER#
|
||||
15
doc/examples/osmo-upf/osmo-upf-create-dev.cfg
Normal file
15
doc/examples/osmo-upf/osmo-upf-create-dev.cfg
Normal file
@@ -0,0 +1,15 @@
|
||||
log stderr
|
||||
logging filter all 1
|
||||
logging color 1
|
||||
logging print category-hex 0
|
||||
logging print category 1
|
||||
logging timestamp 0
|
||||
logging print file basename last
|
||||
logging print level 1
|
||||
logging level set-all notice
|
||||
|
||||
timer pfcp x24 5000
|
||||
pfcp
|
||||
local-addr 127.0.0.1
|
||||
tunend
|
||||
dev create apn23
|
||||
17
doc/examples/osmo-upf/osmo-upf-mockup.cfg
Normal file
17
doc/examples/osmo-upf/osmo-upf-mockup.cfg
Normal file
@@ -0,0 +1,17 @@
|
||||
log stderr
|
||||
logging filter all 1
|
||||
logging color 1
|
||||
logging print category-hex 0
|
||||
logging print category 1
|
||||
logging timestamp 0
|
||||
logging print file basename last
|
||||
logging print level 1
|
||||
logging level set-all notice
|
||||
|
||||
timer pfcp x24 5000
|
||||
pfcp
|
||||
local-addr 127.0.0.1
|
||||
tunend
|
||||
mockup
|
||||
tunmap
|
||||
mockup
|
||||
@@ -1,9 +1,13 @@
|
||||
log stderr
|
||||
logging filter all 1
|
||||
logging color 1
|
||||
logging print level 1
|
||||
logging print category 1
|
||||
logging print category-hex 0
|
||||
logging print category 1
|
||||
logging timestamp 0
|
||||
logging print file basename last
|
||||
logging print extended-timestamp 1
|
||||
logging print level 1
|
||||
logging level set-all notice
|
||||
|
||||
timer pfcp x24 5000
|
||||
pfcp
|
||||
local-addr 127.0.0.1
|
||||
|
||||
57
doc/manuals/chapters/netinst.adoc
Normal file
57
doc/manuals/chapters/netinst.adoc
Normal file
@@ -0,0 +1,57 @@
|
||||
[[netinst]]
|
||||
== Local GTP Addresses / Network Instance
|
||||
|
||||
PFCP features optional Network Instance IEs, in which the CPF may tell the UPF which local network interface to use for
|
||||
a PDR and/or a FAR.
|
||||
|
||||
NOTE:: osmo-upf only evaluates the Network Instances configured in PDRs. Since osmo-upf always pairs a PDR+FAR with
|
||||
another PDR+FAR in reverse direction, each side's PDR is sufficient.
|
||||
|
||||
Network Instance IEs affect both the tunend and the tunmap use cases, as well as which local IP address is returned
|
||||
in the PFCP response
|
||||
|
||||
1. Look up Network Instance name in the osmo-upf.cfg `netinst` section, to obtain a local IP address.
|
||||
2. Depending on use case:
|
||||
- tunend: create the tunnel on a GTP device matching the local IP address, see <<gtp_module>>.
|
||||
- tunmap: use the local IP address in the netfilter ruleset, see <<nftables>>.
|
||||
3. Usually, return the chosen local IP address in the F-TEID IE of the Created PDR IE in the PFCP response.
|
||||
|
||||
Network Instance configuration consists of {name, IP address} pairs.
|
||||
|
||||
NOTE:: As soon as a `netinst` configuration is nonempty, receiving an undefined Network Instance name results in a PFCP
|
||||
Reject response, and a log message on cateogry `session`, level `NOTICE`. To make the PFCP return success, add the
|
||||
failing name to the `netinst` config.
|
||||
|
||||
=== netinst for tunend
|
||||
|
||||
The following configuration sets up two GTP devices for tunend, expecting Network Instance names `access1` or `access2`:
|
||||
|
||||
----
|
||||
tunend
|
||||
dev create apn1 10.0.0.1
|
||||
dev create apn2 10.0.0.2
|
||||
netinst
|
||||
add access1 10.0.0.1
|
||||
add access2 10.0.0.2
|
||||
----
|
||||
|
||||
For example, if a Create PDR IE indicates Network Instance = `access1`, a GTP tunnel is set up in GTP kernel device
|
||||
`apn1`. For `access2`, use `apn2`.
|
||||
|
||||
=== netinst for tunmap
|
||||
|
||||
For the tunmap use case, it is sufficient to configure `netinst` entries, without any addition to the `tunmap` section.
|
||||
The following example configures various interfaces for tunmap, to match Network Instance names received in PFCP:
|
||||
|
||||
----
|
||||
tunmap
|
||||
table-name osmo-upf
|
||||
netinst
|
||||
add access1 10.0.0.1
|
||||
add access2 10.0.0.2
|
||||
add core1 9.0.0.1
|
||||
add core2 9.0.0.2
|
||||
----
|
||||
|
||||
For example, a Create PDR indicating a Network Instance of `core1` will result in an nftables rule that receives packets
|
||||
on local address `9.0.0.1`.
|
||||
@@ -22,3 +22,189 @@ The aim is to provide:
|
||||
- 1000 modifications of tunnel state per second (add/remove/modify),
|
||||
- 4-8 Gbps throughput,
|
||||
- 100-125k concurrent GTP tunnels.
|
||||
|
||||
A typical network scenario using OsmoUPF is illustrated in the following
|
||||
diagram:
|
||||
|
||||
.Typical network architecture used with OsmoUPF
|
||||
[graphviz]
|
||||
----
|
||||
digraph G {
|
||||
rankdir = LR;
|
||||
|
||||
UE [label="UE\n(3G phone)"]
|
||||
|
||||
subgraph cluster_hnbgw_mgw_upf {
|
||||
style=dotted
|
||||
HNBGW -> UPF [label="PFCP",constraint=false]
|
||||
UPF [label=OsmoUPF,style=bold]
|
||||
}
|
||||
|
||||
subgraph cluster_hnbgw_mgw_upf2 {
|
||||
style=dotted
|
||||
SGSN -> UPF2 [label="PFCP",constraint=false]
|
||||
UPF2 [label=OsmoUPF,style=bold]
|
||||
}
|
||||
|
||||
subgraph cluster_hnbgw_mgw_upf3 {
|
||||
style=dotted
|
||||
GGSN -> UPF3 [label="PFCP",constraint=false]
|
||||
UPF3 [label=OsmoUPF,style=bold]
|
||||
}
|
||||
|
||||
hNodeB [shape="box",label="hNodeB\n(3G femto cell)"]
|
||||
|
||||
UE -> hNodeB [label="Uu"]
|
||||
hNodeB -> HNBGW [label="Iuh",style=dashed]
|
||||
STP [label="STP\n(SCCP/M3UA)"]
|
||||
HNBGW -> STP -> SGSN [label="IuPS",style=dashed]
|
||||
SGSN -> GGSN [label="GTP-C",style="dashed"]
|
||||
hNodeB -> UPF -> UPF2 -> UPF3 [label="GTP-U"]
|
||||
UPF3 -> internet [label="apn"]
|
||||
}
|
||||
----
|
||||
|
||||
NOTE: at the time of writing this section, the only Osmocom component providing
|
||||
a PFCP CPF interface is OsmoHNBGW. PFCP support has not yet made its way into
|
||||
OsmoSGSN nor OsmoGGSN.
|
||||
|
||||
=== the PFCP interface
|
||||
|
||||
PFCP is specified by 3GPP TS 29.244.
|
||||
|
||||
OsmoUPF implements a PFCP User Plane Function interface, listening for PFCP
|
||||
requests from PFCP Control Plane Function clients, to carry out proxy-relaying
|
||||
and encapsulation/decapsulation of GTP tunnels.
|
||||
|
||||
OsmoUPF does not support the complete PFCP feature set. It detects exactly two
|
||||
use cases that will provide service of actual GTP tunnels:
|
||||
|
||||
.tunend use case
|
||||
----
|
||||
Access osmo-upf Core
|
||||
PGW | PDN/internet
|
||||
| PDR1: > FAR1: |
|
||||
| IP/GTP | IP |
|
||||
| ------> F-TEID | -----> |
|
||||
| | |
|
||||
| FAR2: < PDR2: |
|
||||
| IP/GTP | IP |
|
||||
| F-TEID <------ | UE IP addr <----- |
|
||||
----
|
||||
|
||||
* `tunend`: GTP tunnel encapsulation/decapsulation:
|
||||
- One Packet Detection Rule (PDR) accepts a GTP tunnel from the Access side
|
||||
with an Outer Header Removal.
|
||||
- This PDR uses a Forwarding Action Rule (FAR) for plain IP towards Core.
|
||||
- Another PDR accepts plain IP on a specific IP address from Core.
|
||||
- The second PDR uses a FAR towards Access with Outer Header Creation for GTP.
|
||||
|
||||
.tunmap use case
|
||||
----
|
||||
Access osmo-upf Core
|
||||
PGW | PGW
|
||||
| PDR1: > FAR1: |
|
||||
| IP/GTP | IP/GTP |
|
||||
| ------> F-TEID | -----> F-TEID |
|
||||
| | |
|
||||
| FAR2: < PDR2: |
|
||||
| IP/GTP | IP/GTP |
|
||||
| F-TEID <------ | F-TEID <----- |
|
||||
----
|
||||
|
||||
* `tunmap`: GTP tunnel forwarding:
|
||||
- One Packet Detection Rule (PDR) accepts a GTP tunnel from the Access side
|
||||
with an Outer Header Removal.
|
||||
- This PDR uses a Forwarding Action Rule (FAR) towards Core with an Outer
|
||||
Header Creation for GTP.
|
||||
- A second PDR+FAR pair like above, with Access and Core swapped.
|
||||
|
||||
Access and Core must be indicated by the Source Interface IE (PDR) and
|
||||
Destination Interface IE (FAR) in PFCP.
|
||||
|
||||
Any set of rules only partially or not at all matching the above PDR and FAR
|
||||
rules will not result in any actions on the GTP user plane, but will still
|
||||
return a successful outcome in the PFCP messages.
|
||||
|
||||
For example, a rule set using a Source Interface other than "Access" or "Core" results
|
||||
in a PFCP no-op, returning PFCP responses with successful outcome, but not
|
||||
providing any GTP-U service.
|
||||
|
||||
This is a direct result of:
|
||||
|
||||
- allowing PFCP rule sets to be setup incrementally by several subsequent PFCP
|
||||
messages, and of
|
||||
- OsmoUPF using Linux kernel features for the GTP user plane, where there is
|
||||
either a full bidirectional GTP tunnel in place or none at all.
|
||||
|
||||
For example, for `tunmap`, a typical CPF will establish a PFCP session in two
|
||||
steps: first request a local F-TEID from the UPF before passing on a data
|
||||
service request from Access to Core. When the Core side has responded with its
|
||||
GTP details, the PFCP session at the UPF is updated (Session Modifification),
|
||||
to form a complete PFCP rule set.
|
||||
|
||||
.Typical sequence of establishing a GTP-U tunnel relay
|
||||
["mscgen"]
|
||||
----
|
||||
msc {
|
||||
hscale="1";
|
||||
sgsn[label="SGSN"],sgwc[label="SGW-C"],sgwu[label="SGW-U"],pgwc[label="PGW-C"];
|
||||
|
||||
sgsn << pgwc [label="Access"];
|
||||
sgsn >> pgwc [label="Core"];
|
||||
|
||||
sgsn => sgwc [label="GTP Create Session Request\n\n\n"];
|
||||
|
||||
|||;
|
||||
|
||||
sgwc => sgwu [label="PFCP Session Establishment Request\n\n2x Create PDR\nF-TEID = CHOOSE"];
|
||||
|
||||
|||;
|
||||
|
||||
sgwc <= sgwu [label="PFCP Session Establishment Response\n\n2x Created PDR\nwith chosen local F-TEID"];
|
||||
|
||||
|||;
|
||||
|
||||
sgwc => pgwc [label="GTP Create Session Request\nwith chosen local F-TEID towards Core"];
|
||||
sgwc <= pgwc [label="GTP Create Session Response\nwith remote F-TEID at Core"];
|
||||
|
||||
|||;
|
||||
|
||||
sgwc => sgwu [label="PFCP Session Modification Request\n\nUpdate FAR\nwith remote F-TEID at Core"];
|
||||
|
||||
|||;
|
||||
|
||||
sgwc <= sgwu [label="PFCP Session Modification Response\n\n\n"];
|
||||
|
||||
|||;
|
||||
|
||||
sgsn <= sgwc [label="GTP Create Session Response\n\n\n"];
|
||||
}
|
||||
----
|
||||
|
||||
The OsmoUPF logging as well as the VTY interface yield information on whether a
|
||||
ruleset results in an actual bidirectional GTP tunnel being set up.
|
||||
|
||||
|
||||
=== the GTP interface
|
||||
|
||||
OsmoUPF requires the following Linux kernel features to provide the GTP user
|
||||
plane functionality:
|
||||
|
||||
- the Linux kernel GTP module for encapsulation/decapsulation between GTP and
|
||||
plain IP.
|
||||
- the Linux netfilter nftables feature for relaying GTP, i.e. forwarding between
|
||||
two GTP tunnels.
|
||||
|
||||
Tunnel relaying with netfilter requires at least Linux kernel 5.17.
|
||||
|
||||
To be able to interact with these Linux kernel features, the osmo-upf binary
|
||||
needs cap_net_admin privileges, as in:
|
||||
|
||||
----
|
||||
sudo setcap cap_net_admin+pe /usr/bin/osmo-upf
|
||||
----
|
||||
|
||||
Without above Linux kernel features, or when no cap_net_admin is available,
|
||||
OsmoUPF is only useful for testing PFCP clients: the GTP features may be run in
|
||||
mockup mode, so that OsmoUPF serves as a "dry run" PFCP server.
|
||||
|
||||
269
doc/manuals/chapters/running.adoc
Normal file
269
doc/manuals/chapters/running.adoc
Normal file
@@ -0,0 +1,269 @@
|
||||
== Running OsmoUPF
|
||||
|
||||
The OsmoUPF executable (`osmo-upf`) offers the following command-line
|
||||
arguments:
|
||||
|
||||
=== SYNOPSIS
|
||||
|
||||
*osmo-upf* [-h|-V] [-D] [-c 'CONFIGFILE']
|
||||
|
||||
=== 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, --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-upf.cfg` in the current
|
||||
working directory.
|
||||
|
||||
=== Multiple instances
|
||||
|
||||
Running multiple instances of `osmo-upf` on the same computer is possible if
|
||||
all interfaces (VTY, CTRL, PFCP) 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 different ports, or different specific IP addresses.
|
||||
|
||||
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 PFCP port is specified to be fixed as port 8805. Hence, each osmo-upf
|
||||
process needs to run on a distinct local interface:
|
||||
|
||||
----
|
||||
pfcp
|
||||
local-addr 10.9.0.2
|
||||
----
|
||||
|
||||
For GTP encapsulation/decapsulation and GTP tunnel relaying, osmo-upf depends on
|
||||
the IP addresses configured at the Linux kernel GTP module, and the IP addresses
|
||||
negotiated within PFCP by the control plane function.
|
||||
|
||||
If multiple `osmo-upf` processes are running on the same Linux kernel, each
|
||||
`osmo-upf` needs to be configured with a distinct netfilter table name, so that
|
||||
naming of individual tunnel rulesets does not collide:
|
||||
|
||||
----
|
||||
tunmap
|
||||
table-name osmo-upf-2
|
||||
----
|
||||
|
||||
=== Configure PFCP Server
|
||||
|
||||
The following example configures OsmoUPF to listen for PFCP association requests
|
||||
from Control Plane Function entities on local interface 10.9.8.7, port 8805:
|
||||
|
||||
----
|
||||
pfcp
|
||||
local-addr 10.9.8.7
|
||||
----
|
||||
|
||||
3GPP TS 29.244 4.2.2 specifies that PFCP Request messages shall be sent to UDP
|
||||
port 8805, i.e. the PFCP port is fixed as 8805 and currently not configurable in
|
||||
osmo-upf.
|
||||
|
||||
Setting a 'local-addr' is required: the PFCP protocol features a Node ID, which
|
||||
uniquely identifies PFCP peers across different interfaces. According to the
|
||||
PFCP specification, the Node ID can be a fully-qualified domain name (FQDN) or
|
||||
an IP address. Currently, osmo-upf has no support for using an FQDN as Node
|
||||
ID, and so far uses the 'local-addr' as local Node ID -- hence the 'local-addr'
|
||||
must not be "0.0.0.0", which is an unfortunate consequence. This is likely to
|
||||
improve in the future, see https://osmocom.org/issues/5682 .
|
||||
|
||||
=== Linux Kernel Features
|
||||
|
||||
OsmoUPF uses two distinct Linux kernel features:
|
||||
|
||||
* The GTP module is used for `tunend`: GTP encapsulation/decapsulation from/to
|
||||
"the internet".
|
||||
|
||||
* The netfilter framework and nftables are used for `tunmap`: GTP tunnel proxying,
|
||||
also known as tunnel forwarding or tunnel mapping.
|
||||
|
||||
.Linux kernel feature usage
|
||||
[graphviz]
|
||||
----
|
||||
include::upf_gtp_roles.dot[]
|
||||
----
|
||||
|
||||
GTP kernel module configuration in the `tunend` section can be omitted for sites
|
||||
that serve only as GTP forwarding proxy, without encapsulation/decapsulation of
|
||||
GTP payloads -- except to provide GTP Echo service, see <<gtp_echo>>.
|
||||
|
||||
Netfilter configuration in the `tunmap` section can be omitted for sites only
|
||||
serving as GTP tunnel endpoint.
|
||||
|
||||
[[gtp_module]]
|
||||
=== Configure Linux Kernel GTP Module for `tunend`
|
||||
|
||||
The Linux kernel GTP module is used for the `tunend` use case, i.e. GTP
|
||||
encapsulation/decapsulation from/to "the internet".
|
||||
|
||||
To use the GTP kernel module, OsmoUPF requires a GTP device, which is a
|
||||
dedicated network device provided by the Linux kernel, serving as GTP tunnel
|
||||
endpoint. It is typically named like "apn0".
|
||||
|
||||
`osmo-upf` can either create a GTP device on startup, or use a pre-existing GTP
|
||||
device. To en/decapsulate GTP, the APN device needs to be assigned an IP address
|
||||
range that matches the UE IP addresses that are configured in GTP-C / PFCP.
|
||||
|
||||
The following configuration placed in `osmo-upf.cfg` creates a GTP device called
|
||||
`apn23` on startup of osmo-upf, which is destroyed on program exit. It listens
|
||||
for GTP on local IP address `1.2.3.4`:
|
||||
|
||||
----
|
||||
tunend
|
||||
dev create apn23 1.2.3.4
|
||||
----
|
||||
|
||||
TODO:: `osmo-upf` is not yet able to configure this network device's IP address
|
||||
range, MTU etc.
|
||||
|
||||
The following configuration placed in `osmo-upf.cfg` uses a pre-existing device
|
||||
called `apn42`:
|
||||
|
||||
----
|
||||
tunend
|
||||
dev use apn42 2.3.4.5
|
||||
----
|
||||
|
||||
GTP kernel devices can be managed manually using the `gtp-link` program
|
||||
available from the 'libgtpnl' project:
|
||||
|
||||
----
|
||||
# gtp-link add apn42
|
||||
(keep this process running)
|
||||
# ip addr add dev apn42 192.168.42.1/24
|
||||
|
||||
$ osmo-upf -c osmo-upf.cfg
|
||||
----
|
||||
|
||||
It is possible to configure multiple GTP devices in `osmo-upf.cfg`. Depending on
|
||||
the Network Instance name, osmo-upf creates tunnel endpoints on the GTP device
|
||||
with a matching IP address:
|
||||
|
||||
- The Network Instance IE in the PDR on the Access side determines the local IP
|
||||
address to use, see <<netinst>>.
|
||||
- This local IP address in turn determines the GTP device to use.
|
||||
|
||||
It is possible for a GTP device to listen on ANY -- just omit the IP address in
|
||||
the `dev` config. In this case, all Network Instance names will be served by
|
||||
this GTP device. When using ANY, there should be exactly one GTP dev configured.
|
||||
|
||||
[[nftables]]
|
||||
=== Configure Linux netfilter for `tunmap`
|
||||
|
||||
The Linux kernel netfilter module is used for GTP tunnel proxying, also known as
|
||||
tunnel forwarding or tunnel mapping.
|
||||
|
||||
When using the netfilter module, you may set up `osmo-upf.cfg` for:
|
||||
- GTP Echo (required)
|
||||
- nft table name (optional)
|
||||
|
||||
[[gtp_echo]]
|
||||
==== GTP Echo
|
||||
|
||||
You need to ensure that OsmoUPF responds to GTP Echo requests.
|
||||
- A GTP device configured for `tunend` implicitly includes a GTP Echo service.
|
||||
- For `tunmap`, no GTP Echo mechanism is implemented.
|
||||
|
||||
So, when your use case is `tunmap` only, you should still add a GTP device as
|
||||
for `tunend`, only to provide the GTP Echo service.
|
||||
|
||||
Here are some options to do so:
|
||||
|
||||
If you have no GTP devices configured in `osmo-upf.cfg` yet, you can add a
|
||||
single GTP device without a specific IP address, in order to respond to GTP-U
|
||||
Echo requests on all interfaces to anyone that is asking:
|
||||
|
||||
----
|
||||
tunend
|
||||
dev create gtp-echo
|
||||
----
|
||||
|
||||
Note that `gtp-echo` is just an arbitrary GTP device name, choose any string
|
||||
that makes a valid network device name and is still available, as in the `dev`
|
||||
argument in the `ip addr show dev` command on Linux.
|
||||
|
||||
This will bind osmo-upf on 0.0.0.0:2152 to respond to GTP Echo requests.
|
||||
|
||||
If you would like to limit GTP Echo responses to specific network interfaces,
|
||||
you need to add a separate GTP device per local IP address:
|
||||
|
||||
----
|
||||
tunend
|
||||
dev create gtp-echo1 192.168.0.23
|
||||
dev create gtp-echo2 10.9.8.17
|
||||
----
|
||||
|
||||
This will bind osmo-upf only on 192.168.0.23:2152 and 10.9.8.17:2152 to respond
|
||||
to GTP Echo requests.
|
||||
|
||||
For creating and manipulating a GTP device in more versatile ways, see
|
||||
<<gtp_module>>.
|
||||
|
||||
==== nft Table Name
|
||||
|
||||
For `tunmap`, `osmo-upf` creates a new nft table, under which it submits
|
||||
rule sets for GTP tunnel proxying. This table name defaults to `osmo-upf`. A
|
||||
custom table name can be configured in `osmo-upf.cfg` like this:
|
||||
|
||||
----
|
||||
tunmap
|
||||
table-name my-table-name
|
||||
----
|
||||
|
||||
When running more than one osmo-upf process on a system, pick distinct table
|
||||
names to avoid name collisions in the nftables rulesets.
|
||||
|
||||
=== IP Forwarding
|
||||
|
||||
In order to allow forwarding GTP payloads, the Linux operating system must
|
||||
be configured to allow IP forwarding.
|
||||
|
||||
Note that there are many distribution-specific ways to configure this, and there
|
||||
might be higher-level firewall rule management software available like `ufw`.
|
||||
You should configure firewall rules matching your distribution and setup.
|
||||
|
||||
To allow IP forwarding from and to all interfaces globally in a reboot-safe way,
|
||||
you may put a line like this in /etc/sysctl.conf:
|
||||
|
||||
----
|
||||
net.ipv4.ip_forward=1
|
||||
----
|
||||
|
||||
To do the same in an ad-hoc way that is not reboot safe but takes effect
|
||||
immediately:
|
||||
|
||||
----
|
||||
sudo sh -c "echo 1 > /proc/sys/net/ipv4/ip_forward"
|
||||
----
|
||||
|
||||
It is also possible to instruct the firewall to allow IP forwarding for specific
|
||||
network devices only. For example, on a Debian based system, place an nft
|
||||
ruleset like this in `/etc/nftables.conf`:
|
||||
|
||||
----
|
||||
define gtp_netdevs = { eth0, eth23 };
|
||||
|
||||
table inet filter {
|
||||
chain forward {
|
||||
type filter hook forward priority filter; policy drop;
|
||||
iifname $gtp_netdevs oifname $gtp_netdevs udp dport 2152 accept
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
This ruleset allows IP forwarding, but limited to the GTP-U port 2152,
|
||||
and to two specific network devices eth0 and eth23.
|
||||
31
doc/manuals/chapters/upf_gtp_roles.dot
Normal file
31
doc/manuals/chapters/upf_gtp_roles.dot
Normal file
@@ -0,0 +1,31 @@
|
||||
digraph G {
|
||||
rankdir=LR
|
||||
sgsn [label="SGSN"]
|
||||
|
||||
subgraph cluster_sgw {
|
||||
style=invisible
|
||||
sgwc [label="SGW-C"]
|
||||
sgwu [label="OsmoUPF as SGW-U\ntunnel proxy\n*netfilter* kernel module",style=bold,shape=box]
|
||||
sgwc -> sgwu [label="PFCP",constraint=false]
|
||||
}
|
||||
|
||||
subgraph cluster_pgw {
|
||||
style=invisible
|
||||
pgwc [label="PGW-C"]
|
||||
pgwu [label="OsmoUPF as PGW-U\ntunnel proxy\n*netfilter* kernel module",style=bold,shape=box]
|
||||
pgwc -> pgwu [label="PFCP",constraint=false]
|
||||
}
|
||||
|
||||
subgraph cluster_tdf {
|
||||
style=invisible
|
||||
tdfc [label="TDF-C"]
|
||||
tdfu [label="OsmoUPF as TDF-U\ntunnel en-/decaps\n*GTP* kernel module",style=bold,shape=box]
|
||||
tdfc -> tdfu [label="PFCP",constraint=false]
|
||||
}
|
||||
|
||||
pdn [label="PDN\n'the internet'"]
|
||||
|
||||
sgsn -> sgwc -> pgwc -> tdfc [label="GTP-C"]
|
||||
sgsn -> sgwu -> pgwu -> tdfu [label="GTP-U",dir=both]
|
||||
tdfu -> pdn [label="IP",dir=both]
|
||||
}
|
||||
@@ -9,6 +9,10 @@ include::./common/chapters/preface.adoc[]
|
||||
|
||||
include::{srcdir}/chapters/overview.adoc[]
|
||||
|
||||
include::{srcdir}/chapters/running.adoc[]
|
||||
|
||||
include::{srcdir}/chapters/netinst.adoc[]
|
||||
|
||||
include::./common/chapters/vty.adoc[]
|
||||
|
||||
include::./common/chapters/logging.adoc[]
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
SUBDIRS = \
|
||||
gtlv \
|
||||
pfcp \
|
||||
upf \
|
||||
pfcptool \
|
||||
$(NULL)
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
tlv_HEADERS = \
|
||||
gtlv.h \
|
||||
gtlv_dec_enc.h \
|
||||
gtlv_gen.h \
|
||||
$(NULL)
|
||||
|
||||
tlvdir = $(includedir)/osmocom/gtlv
|
||||
@@ -1,157 +0,0 @@
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
struct msgb;
|
||||
struct osmo_gtlv_load;
|
||||
struct osmo_gtlv_put;
|
||||
struct value_string;
|
||||
|
||||
struct osmo_gtlv_tag_inst {
|
||||
unsigned int tag;
|
||||
bool instance_present;
|
||||
unsigned int instance;
|
||||
};
|
||||
|
||||
int osmo_gtlv_tag_inst_cmp(const struct osmo_gtlv_tag_inst *a, const struct osmo_gtlv_tag_inst *b);
|
||||
|
||||
int osmo_gtlv_tag_inst_to_str_buf(char *buf, size_t buflen, const struct osmo_gtlv_tag_inst *ti,
|
||||
const struct value_string *tag_names);
|
||||
char *osmo_gtlv_tag_inst_to_str_c(void *ctx, const struct osmo_gtlv_tag_inst *ti,
|
||||
const struct value_string *tag_names);
|
||||
|
||||
/*! TL configuration for osmo_gtlv_load*() and osmo_gtlv_put*(). Depending on these implementations provided by the caller,
|
||||
* osmo_gtlv can load any sizes of tag and length fields (that don't surpass the value range of unsigned int and size_t,
|
||||
* respectively), as well as TV (fixed-length) or TvLV (variable-sized length).
|
||||
*
|
||||
* See osmo_t8l8v_cfg and osmo_t16l16v_cfg, ready implementations for plain 8bit and 16bit TLV protocols.
|
||||
*
|
||||
* libosmo-pfcp serves as example for using this entire TLV API, uncluding de/encoding to structs and generating parts
|
||||
* of the TLV parsing code based on message definitions. It uses osmo_t16l16v_cfg.
|
||||
*/
|
||||
struct osmo_gtlv_cfg {
|
||||
/*! The length in bytes of the shortest possible TL header (e.g. 4 for T16L16V, or 1 for 8bit tags where TV IEs
|
||||
* without a length exist). A src_data_len passed to store_tl() below is guaranteed to be >= this value. If at
|
||||
* any point there is remaining message data smaller than this value, a parsing error is returned.
|
||||
*/
|
||||
size_t tl_min_size;
|
||||
|
||||
/*! Read one TL from the start of src_data.
|
||||
* \param gtlv Return the T (tag) value read from src_data in gtlv->tag.
|
||||
* Return the L (length) value read from src_data in gtlv->len.
|
||||
* Return the I (instance) value read from src_data in gtlv->len; ignore if there is no I.
|
||||
* Return the position just after the TL in gtlv->*val. If there is V data, point at the start of the
|
||||
* V data in src_data. If there is no V data, point at the byte just after the TL part in src_data.
|
||||
* \param src_data Part of raw message being decoded.
|
||||
* \param src_data_len Remaining message data length at src_data.
|
||||
* \return 0 on success, negative on error.
|
||||
*/
|
||||
int (*load_tl)(struct osmo_gtlv_load *gtlv, const uint8_t *src_data, size_t src_data_len);
|
||||
|
||||
/*! Write a TL to dst_data, and return the size of the TL written.
|
||||
* This is also invoked by osmo_gtlv_put_update_tl() to overwrite a previous TL header. If the TL part's size
|
||||
* can be different than the first time (e.g. due to a large L value in a TvLV protocol), an implementation can
|
||||
* use the 'gtlv' arg to figure out how to memmove the message data:
|
||||
* When invoked by osmo_gtlv_put_tl(), dst_data == gtlv->dst->tail and dst_data_avail == msgb_tailroom().
|
||||
* When invoked by osmo_gtlv_put_update_tl(), dst_data < gtlv->dst->tail, dst_data points at the start of the
|
||||
* TL section written earlier by osmo_gtlv_put_tl() and dst_data_avail == the size of the TL written earlier.
|
||||
*
|
||||
* \param dst_data Write TL data to the start of this buffer.
|
||||
* \param dst_data_avail Remaining available space in dst_data.
|
||||
* \param tag The T value to store in dst_data.
|
||||
* \param instance The I value to store in dst_data (if this tag is a TLIV); ignore when not a TLIV.
|
||||
* \param len The L value to store in dst_data.
|
||||
* \param gtlv Backpointer to the osmo_gtlv_put struct, including gtlv->dst, the underlying msgb.
|
||||
* \return the size of the TL part in bytes on success, -EINVAL if tag is invalid, -EMSGSIZE if len is too large
|
||||
* or dst_data_avail is too small for the TL.
|
||||
*/
|
||||
int (*store_tl)(uint8_t *dst_data, size_t dst_data_avail, const struct osmo_gtlv_tag_inst *ti, size_t len,
|
||||
struct osmo_gtlv_put *gtlv);
|
||||
};
|
||||
|
||||
/*! Configuration that allows parsing an 8bit tag and 8bit length TLV. */
|
||||
extern const struct osmo_gtlv_cfg osmo_t8l8v_cfg;
|
||||
|
||||
/*! Configuration that allows parsing a 16bit tag and 16bit length TLV (see for example PFCP). */
|
||||
extern const struct osmo_gtlv_cfg osmo_t16l16v_cfg;
|
||||
|
||||
/*! State for loading a TLV structure from raw data. */
|
||||
struct osmo_gtlv_load {
|
||||
/*! Caller-defined context pointer available for use by load_tl() and store_tl() implementations. */
|
||||
void *priv;
|
||||
|
||||
/*! Definition of tag and length sizes (by function pointers). */
|
||||
const struct osmo_gtlv_cfg *cfg;
|
||||
|
||||
/*! Overall message buffer being parsed. */
|
||||
struct {
|
||||
const uint8_t *data;
|
||||
size_t len;
|
||||
} src;
|
||||
|
||||
/*! Return value from last invocation of osmo_gtlv_load_next*(): tag value of parsed IE. */
|
||||
struct osmo_gtlv_tag_inst ti;
|
||||
/*! Return value from last invocation of osmo_gtlv_load_next*(): Start of the IE's payload data (after tag and
|
||||
* length). If the end of the src buffer is reached, val == NULL. If a TLV contained no value part, len == 0,
|
||||
* but this still points just after the TL. */
|
||||
const uint8_t *val;
|
||||
/*! Return value from last invocation of osmo_gtlv_load_next*(): Length of the IE's payload data (without tag and
|
||||
* length) */
|
||||
size_t len;
|
||||
};
|
||||
|
||||
/* Start or restart the gtlv from the first IE in the overall TLV data. */
|
||||
static inline void osmo_gtlv_load_start(struct osmo_gtlv_load *gtlv)
|
||||
{
|
||||
gtlv->val = NULL;
|
||||
}
|
||||
|
||||
int osmo_gtlv_load_next(struct osmo_gtlv_load *gtlv);
|
||||
int osmo_gtlv_load_peek_tag(const struct osmo_gtlv_load *gtlv, struct osmo_gtlv_tag_inst *ti);
|
||||
int osmo_gtlv_load_next_by_tag(struct osmo_gtlv_load *gtlv, unsigned int tag);
|
||||
int osmo_gtlv_load_next_by_tag_inst(struct osmo_gtlv_load *gtlv, const struct osmo_gtlv_tag_inst *ti);
|
||||
|
||||
/* State for storing a TLV structure into a msgb. */
|
||||
struct osmo_gtlv_put {
|
||||
/*! Caller-defined context pointer available for use by load_tl() and store_tl() implementations. */
|
||||
void *priv;
|
||||
|
||||
/* Definition of tag and length sizes (by function pointers). */
|
||||
const struct osmo_gtlv_cfg *cfg;
|
||||
|
||||
/* msgb to append new TL to */
|
||||
struct msgb *dst;
|
||||
/* What was the last TL written and where are its TL and V */
|
||||
struct osmo_gtlv_tag_inst last_ti;
|
||||
uint8_t *last_tl;
|
||||
uint8_t *last_val;
|
||||
};
|
||||
|
||||
int osmo_gtlv_put_tl(struct osmo_gtlv_put *gtlv, unsigned int tag, size_t len);
|
||||
int osmo_gtlv_put_tli(struct osmo_gtlv_put *gtlv, const struct osmo_gtlv_tag_inst *ti, size_t len);
|
||||
int osmo_gtlv_put_update_tl(struct osmo_gtlv_put *gtlv);
|
||||
@@ -1,201 +0,0 @@
|
||||
/* Decode and encode the value parts of a TLV structure */
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <osmocom/gtlv/gtlv.h>
|
||||
|
||||
struct value_string;
|
||||
|
||||
/* User defined function to decode a single TLV value part. See struct osmo_gtlv_coding.
|
||||
* \param decoded_struct Pointer to the root struct, as context information, e.g. for logging.
|
||||
* \param decode_to Pointer to the struct member, write the decoded value here.
|
||||
* \param gtlv TLV loader, pointing at a gtlv->val of gtlv->len bytes.
|
||||
* \return 0 on success, nonzero on error, e.g. -EINVAL if the gtlv->val is invalid.
|
||||
*/
|
||||
typedef int (*osmo_gtlv_dec_func)(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *gtlv);
|
||||
|
||||
/* User defined function to encode a single TLV value part. See struct osmo_gtlv_coding.
|
||||
* \param gtlv TLV writer, pointing at a gtlv->dst to msgb_put() data in.
|
||||
* \param decoded_struct Pointer to the root struct, as context information, e.g. for logging.
|
||||
* \param encode_from Pointer to the struct member, obtain the value to encode from here.
|
||||
* \return 0 on success, nonzero on error, e.g. -EINVAL if encode_from has an un-encodable value.
|
||||
*/
|
||||
typedef int (*osmo_gtlv_enc_func)(struct osmo_gtlv_put *gtlv, const void *decoded_struct, const void *encode_from);
|
||||
|
||||
/* Optional user defined function to convert a decoded IE struct (the Value part stored as C struct) to string. See
|
||||
* struct osmo_gtlv_coding.
|
||||
* \param buf Return string in this buffer.
|
||||
* \param buflen Size of buf.
|
||||
* \param str_of Pointer to the struct member described by an osmo_gtlv_coding, obtain the value to encode from here.
|
||||
* \return number of characters that would be written if the buffer is large enough, like snprintf().
|
||||
*/
|
||||
typedef int (*osmo_gtlv_enc_to_str_func)(char *buf, size_t buflen, const void *str_of);
|
||||
|
||||
/* Whether TLV structures nested inside the value data of an outer IE should be parsed in the same order. */
|
||||
enum osmo_gtlv_coding_nested_ies_ordered {
|
||||
/*! When stepping into nested IEs, keep the same ordering requirement as the outer IE. */
|
||||
OSMO_GTLV_NESTED_IES_ORDERING_SAME = 0,
|
||||
/*! Require IEs in a PDU to appear exactly in the order defined by osmo_gtlv_coding arrays. Causes a parsing
|
||||
* failure if the TLVs appear in a different order. Does much less iterating looking for matching tags when
|
||||
* decoding (faster). */
|
||||
OSMO_GTLV_NESTED_IES_ORDERED,
|
||||
/*! Do not require IEs to be in the defined order in decoded PDUs. When encoding a TLV, IEs will always be
|
||||
* encoded in the order they are defined. This has an effect on decoding only. */
|
||||
OSMO_GTLV_NESTED_IES_UNORDERED,
|
||||
};
|
||||
|
||||
#define OSMO_ARRAY_PITCH(arr) ((char *)(&(arr)[1]) - (char *)(arr))
|
||||
#define OSMO_MEMB_ARRAY_PITCH(obj_type, arr_memb) OSMO_ARRAY_PITCH(((obj_type *)0)->arr_memb)
|
||||
|
||||
/*! Definition of how to decode/encode a IE to/from a struct.
|
||||
* Kept in lists describing TLV structures, and nestable.
|
||||
*
|
||||
* Instance lists of this can be composed manually, or auto-generated using gtlv_gen.c. Auto-generating has the benefit
|
||||
* that the decoded structs to match the IEs are also generated at the same time and thus always match the message
|
||||
* definitions. For an example, see tests/libosmo-gtlv/test_gtlv_gen/. */
|
||||
struct osmo_gtlv_coding {
|
||||
/*! the IEI discriminator, and optional instance number */
|
||||
struct osmo_gtlv_tag_inst ti;
|
||||
|
||||
/*! Decoding function callback. Invoked for each defined and present IE encountered in the message.
|
||||
* Return 0 on success, negative on failure. */
|
||||
osmo_gtlv_dec_func dec_func;
|
||||
/*! Encoding function callback. Invoked for each defined and present IE encountered in the message.
|
||||
* Return 0 on success, negative on failure. */
|
||||
osmo_gtlv_enc_func enc_func;
|
||||
|
||||
/*! Means to output the decoded value to a human readable string, optional. */
|
||||
osmo_gtlv_enc_to_str_func enc_to_str_func;
|
||||
|
||||
/*! offsetof(decoded_struct_type, member_var): how far into the base struct you find a specific field for decoded
|
||||
* value. For example, memb_ofs = offsetof(struct foo_msg, ies.bar_response.cause).
|
||||
* When decoding, the decoded value is written here, when encoding it is read from here. */
|
||||
unsigned int memb_ofs;
|
||||
/*! For repeated IEs (.has_count = true), the array pitch / the offset to add to get to the next array index. */
|
||||
unsigned int memb_array_pitch;
|
||||
|
||||
/*! True for optional/conditional IEs. */
|
||||
bool has_presence_flag;
|
||||
/* For optional/conditional IEs (has_presence_flag = true), the offset of the bool foo_present flag,
|
||||
* For example, if there are
|
||||
*
|
||||
* struct foo_msg {
|
||||
* struct baz baz;
|
||||
* bool baz_present;
|
||||
* };
|
||||
*
|
||||
* then set
|
||||
* memb_ofs = offsetof(struct foo_msg, baz);
|
||||
* has_presence_flag = true;
|
||||
* presence_flag_ofs = offsetof(struct foo_msg, baz_present);
|
||||
*/
|
||||
unsigned int presence_flag_ofs;
|
||||
|
||||
/*! True for repeated IEs, for array members:
|
||||
*
|
||||
* struct foo_msg {
|
||||
* struct moo moo[10];
|
||||
* unsigned int moo_count;
|
||||
* };
|
||||
*
|
||||
* memb_ofs = offsetof(struct foo_msg, moo);
|
||||
* has_count = true;
|
||||
* count_ofs = offsetof(struct foo_msg, moo_count);
|
||||
* count_max = 10;
|
||||
*/
|
||||
bool has_count;
|
||||
/*! For repeated IEs, the offset of the unsigned int foo_count indicator of how many array indexes are
|
||||
* in use. See has_count. */
|
||||
unsigned int count_ofs;
|
||||
/*! Maximum array size for member_var[]. See has_count. */
|
||||
unsigned int count_max;
|
||||
/*! If nonzero, it is an error when less than this amount of the repeated IE have been decoded. */
|
||||
unsigned int count_mandatory;
|
||||
|
||||
/*! For nested TLVs: if this IE's value part is itself a separate TLV structure, point this at the list of IE
|
||||
* coding definitions for the inner IEs.
|
||||
* In this example, the nested IEs decode/encode to different sub structs depending on the tag value.
|
||||
*
|
||||
* struct bar {
|
||||
* int aaa;
|
||||
* int bbb;
|
||||
* };
|
||||
*
|
||||
* struct foo_msg {
|
||||
* struct bar bar;
|
||||
* struct bar other_bar;
|
||||
* };
|
||||
*
|
||||
* struct osmo_gtlv_coding bar_nested_ies[] = {
|
||||
* { FOO_IEI_AAA, .memb_ofs = offsetof(struct bar, aaa), },
|
||||
* { FOO_IEI_BBB, .memb_ofs = offsetof(struct bar, bbb), },
|
||||
* {}
|
||||
* };
|
||||
*
|
||||
* struct osmo_gtlv_coding foo_msg_ies[] = {
|
||||
* { FOO_IEI_GOO, .memb_ofs = offsetof(struct foo_msg, bar), .nested_ies = bar_nested_ies, },
|
||||
* { FOO_IEI_OTHER_GOO, .memb_ofs = offsetof(struct foo_msg, other_bar), .nested_ies = bar_nested_ies, },
|
||||
* {}
|
||||
* };
|
||||
*/
|
||||
const struct osmo_gtlv_coding *nested_ies;
|
||||
|
||||
/*! If the nested TLV has a different tag/length size than the outer TLV structure, provide a different config
|
||||
* here. If they are the same, just keep this NULL. */
|
||||
const struct osmo_gtlv_cfg *nested_ies_cfg;
|
||||
|
||||
/*! When stepping into nested IEs, what is the ordering requirement for the nested TLV structure? */
|
||||
enum osmo_gtlv_coding_nested_ies_ordered nested_ies_ordered;
|
||||
};
|
||||
|
||||
|
||||
/*! User defined hook for error logging during TLV and value decoding.
|
||||
* \param decoded_struct Pointer to the base struct describing this message, for context.
|
||||
* \param file Source file of where the error occurred.
|
||||
* \param line Source file line of where the error occurred.
|
||||
* \param fmt Error message string format.
|
||||
* \param ... Error message string args.
|
||||
*/
|
||||
typedef void (*osmo_gtlv_err_cb)(void *data, void *decoded_struct, const char *file, int line, const char *fmt, ...);
|
||||
|
||||
int osmo_gtlvs_decode(void *decoded_struct, unsigned int obj_ofs, struct osmo_gtlv_load *gtlv, bool tlv_ordered,
|
||||
const struct osmo_gtlv_coding *ie_coding,
|
||||
osmo_gtlv_err_cb err_cb, void *err_cb_data, const struct value_string *iei_strs);
|
||||
|
||||
int osmo_gtlvs_encode(struct osmo_gtlv_put *gtlv, const void *decoded_struct, unsigned int obj_ofs,
|
||||
const struct osmo_gtlv_coding *ie_coding,
|
||||
osmo_gtlv_err_cb err_cb, void *err_cb_data, const struct value_string *iei_strs);
|
||||
|
||||
int osmo_gtlvs_encode_to_str_buf(char *buf, size_t buflen, const void *decoded_struct, unsigned int obj_ofs,
|
||||
const struct osmo_gtlv_coding *ie_coding, const struct value_string *iei_strs);
|
||||
char *osmo_gtlvs_encode_to_str_c(void *ctx, const void *decoded_struct, unsigned int obj_ofs,
|
||||
const struct osmo_gtlv_coding *ie_coding, const struct value_string *iei_strs);
|
||||
|
||||
static inline bool osmo_gtlv_coding_end(const struct osmo_gtlv_coding *iec)
|
||||
{
|
||||
return iec->dec_func == NULL && iec->enc_func == NULL && iec->nested_ies == NULL;
|
||||
}
|
||||
@@ -1,176 +0,0 @@
|
||||
/* Write h and c source files for TLV protocol definitions, based on very sparse TLV definitions.
|
||||
* For a usage example see tests/libosmo-gtlv/test_gtlv_gen/. */
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
struct osmo_gtlv_gen_ie;
|
||||
|
||||
/* O means optional, M means mandatory.
|
||||
* If all of the IE struct, tag name and functions can be derived from the name, just pass osmo_gtlv_gen_ie_auto as
|
||||
* TLV_GEN_IE. */
|
||||
#define OSMO_GTLV_GEN_O(TLV_GEN_IE, MEMB_NAME) { MEMB_NAME, .optional = true, .ie = &(TLV_GEN_IE) }
|
||||
#define OSMO_GTLV_GEN_M(TLV_GEN_IE, MEMB_NAME) { MEMB_NAME, .ie = &(TLV_GEN_IE) }
|
||||
#define OSMO_GTLV_GEN_O_MULTI(MAX, TLV_GEN_IE, MEMB_NAME) { MEMB_NAME, .multi = MAX, .ie = &(TLV_GEN_IE) }
|
||||
#define OSMO_GTLV_GEN_M_MULTI(MAX, MAND_COUNT, TLV_GEN_IE, MEMB_NAME) \
|
||||
{ MEMB_NAME, .multi = MAX, .multi_mandatory = MAND_COUNT, .ie = &(TLV_GEN_IE) }
|
||||
#define OSMO_GTLV_GEN_O_INST(INSTANCE, TLV_GEN_IE, MEMB_NAME) { MEMB_NAME, .optional = true, .instance = INSTANCE, .ie = &TLV_GEN_IE }
|
||||
#define OSMO_GTLV_GEN_M_INST(INSTANCE, TLV_GEN_IE, MEMB_NAME) { MEMB_NAME, .instance = INSTANCE, .ie = &(TLV_GEN_IE) }
|
||||
|
||||
#define OSMO_GTLV_GEN_NO_INSTANCE INT_MAX
|
||||
|
||||
/*! osmo_gtlv_gen_ie with all members == NULL, so that all are derived from the member name. */
|
||||
extern const struct osmo_gtlv_gen_ie osmo_gtlv_gen_ie_auto;
|
||||
|
||||
/*! Modifier for Mandatory/Optional/Multiple around an osmo_gtlv_gen_ie. */
|
||||
struct osmo_gtlv_gen_ie_o {
|
||||
/*! The C name of the member in a decoded struct, to be of the type defined by .ie.
|
||||
* All parts of .ie, if NULL, are derived from this name.
|
||||
*
|
||||
* For example, simply this
|
||||
*
|
||||
* struct osmo_gtlv_gen_ie_o foo[] = {
|
||||
* OSMO_GTLV_GEN_O("bar", NULL),
|
||||
* };
|
||||
*
|
||||
* Generates
|
||||
*
|
||||
* struct myproto_msg_foo {
|
||||
* struct myproto_ie_bar bar;
|
||||
* }
|
||||
*
|
||||
* and an osmo_gtlv_coding entry of
|
||||
*
|
||||
* { MYPROTO_IEI_BAR,
|
||||
* .memb_ofs = offsetof(struct myproto_msg_foo, bar),
|
||||
* .dec_func = myproto_dec_bar,
|
||||
* .enc_func = myproto_enc_bar,
|
||||
* .enc_to_str_func = myproto_enc_to_str_bar,
|
||||
* }
|
||||
*
|
||||
* See also osmo_gtlv_gen_cfg.add_enc_to_str.
|
||||
*/
|
||||
const char *name;
|
||||
|
||||
/*! Whether to add a bool foo_present, and to skip encoding/decoding if false.
|
||||
* Only useful for non-multi IEs (compare OSMO_GTLV_GEN_O_MULTI() vs OSMO_GTLV_GEN_M_MULTI()). */
|
||||
bool optional;
|
||||
|
||||
/*! If non-NULL, the member is an array: foo[123] with an unsigned int foo_count.
|
||||
* Set to the maximum number of array elements; for foo[123] set .multi = 123. */
|
||||
unsigned int multi;
|
||||
/*! Number of mandatory occurences of the IE, only has an effect if .multi > 0. */
|
||||
unsigned int multi_mandatory;
|
||||
|
||||
/* If any, the instance nr to match, in C that yields an unsigned int.
|
||||
* e.g. "1" or "MYPROTO_FOO_INST_ONE". */
|
||||
const char *instance;
|
||||
|
||||
/*! IE decoding / encoding instructions. If NULL, the entire IE definition is derived from .name.
|
||||
* 'MYPROTO_IEI_NAME', 'myproto_dec_name()', 'myproto_enc_name()', 'myproto_enc_to_str_name()'.
|
||||
* Your myproto_ies_custom.h needs to define an enum value MYPROTO_IEI_NAME and*/
|
||||
const struct osmo_gtlv_gen_ie *ie;
|
||||
};
|
||||
|
||||
/*! Define decoding and encoding of a single IE, i.e. one full TLV. */
|
||||
struct osmo_gtlv_gen_ie {
|
||||
/*! like "uint32_t" or "struct foo".
|
||||
* If NULL, use "struct myproto_ie_<name>" instead, where <name> comes from the osmo_gtlv_gen_ie_o.
|
||||
* When there are nested IEs, the struct definition is auto-generated, deriving the struct members from the
|
||||
* nested_ies list.
|
||||
* When there are no nested IEs, the type needs to be defined manually by a myproto_ies_custom.h. */
|
||||
const char *decoded_type;
|
||||
|
||||
/*! C name of this tag value, e.g. "foo" to use tag "MYPROTO_IEI_FOO".
|
||||
* If NULL, take "MYPROTO_IEI_"+upper(memb_name) instead, where memb_name comes from the osmo_gtlv_gen_ie_o.
|
||||
* decoded_type and/or dec_enc may be derived from this, if they are NULL. */
|
||||
const char *tag_name;
|
||||
|
||||
/*! Name suffix of the dec/enc functions. "foo" -> myproto_dec_foo(), myproto_enc_foo(),
|
||||
* myproto_enc_to_str_foo().
|
||||
* These functions need to be implemented manually in a myproto_ies_custom.c.
|
||||
* When osmo_gtlv_gen_cfg.add_enc_to_str is false, the myproto_enc_to_str_foo() is not required. */
|
||||
const char *dec_enc;
|
||||
|
||||
/*! List of inner IEs terminated by {}. If non-NULL, this is a "Grouped IE" with an inner TLV structure inside
|
||||
* this IE's V part. */
|
||||
const struct osmo_gtlv_gen_ie_o *nested_ies;
|
||||
|
||||
/*! To place a spec comment in the generated code. */
|
||||
const char *spec_ref;
|
||||
};
|
||||
|
||||
/*! General TLV decoding and encoding definitions applying to all IEs (and nested IEs). */
|
||||
struct osmo_gtlv_gen_cfg {
|
||||
/*! Name of the protocol for use in C type or function names, like "myproto". */
|
||||
const char *proto_name;
|
||||
|
||||
/*! When placing comments to spec references, prefix with this. For example, "3GPP TS 12.345 ". */
|
||||
const char *spec_ref_prefix;
|
||||
|
||||
/*! The type to pass a message discriminator as, like 'enum myproto_message_types' */
|
||||
const char *message_type_enum;
|
||||
/*! To reference a message type discriminator like MYPROTO_MSGT_FOO, this would be "MYPROTO_MSGT_". */
|
||||
const char *message_type_prefix;
|
||||
|
||||
/*! Type to use to represent tag IEI in decoded form.
|
||||
* For example "enum foo_msg_iei". */
|
||||
const char *tag_enum;
|
||||
/*! The tag IEI enum value is uppercase(tag_prefix + (iedef->tag_name or iedef->name)).
|
||||
* For example, with tag_prefix = "OSMO_FOO_IEI_", we would generate code like
|
||||
* enum osmo_foo_iei tag = OSMO_FOO_IEI_BAR; */
|
||||
const char *tag_prefix;
|
||||
|
||||
/*! When an osmo_gtlv_gen_ie provides no decoded_type string, it is derived from .name and this prefix is
|
||||
* added. For example, with decoded_type_prefix = "struct foo_ie_", the decoded_type defaults to
|
||||
* struct foo_ie_bar for an IE definition with name = "bar". */
|
||||
const char *decoded_type_prefix;
|
||||
|
||||
/*! To include user defined headers, set to something like "#include <osmocom/foo/foo_tlv_devs.h". This is put at
|
||||
* the head of the generated .h file. */
|
||||
const char *h_header;
|
||||
|
||||
/*! To include user defined headers, set to something like "#include <osmocom/foo/foo_msg.h". This is put at
|
||||
* the head of the generated .c file. */
|
||||
const char *c_header;
|
||||
|
||||
/*! Array of message IE definitions, indexed by message type. */
|
||||
const struct osmo_gtlv_gen_msg *msg_defs;
|
||||
|
||||
/*! Whether to add to_str functions. When true, every automatically derived IE (that has no nested IEs) needs to
|
||||
* have a myproto_enc_to_str_foo() defined by a myproto_ies_custom.c. When false, osmo_gtlvs_encode_to_str_buf()
|
||||
* will print '?' instead of the IE contents. */
|
||||
bool add_enc_to_str;
|
||||
};
|
||||
|
||||
/*! For generating the outer union that composes a protocol's PDU variants, an entry of the list of message names and
|
||||
* IEs in each message. */
|
||||
struct osmo_gtlv_gen_msg {
|
||||
const char *name;
|
||||
const struct osmo_gtlv_gen_ie_o *ies;
|
||||
};
|
||||
|
||||
int osmo_gtlv_gen_main(const struct osmo_gtlv_gen_cfg *cfg, int argc, const char **argv);
|
||||
@@ -1,22 +0,0 @@
|
||||
pfcp_HEADERS = \
|
||||
pfcp_ies_custom.h \
|
||||
pfcp_proto.h \
|
||||
pfcp_strs.h \
|
||||
$(NULL)
|
||||
|
||||
pfcpdir = $(includedir)/osmocom/pfcp
|
||||
|
||||
BUILT_SOURCES = \
|
||||
pfcp_ies_auto.h \
|
||||
$(NULL)
|
||||
|
||||
CLEANFILES = \
|
||||
pfcp_ies_auto.h \
|
||||
$(NULL)
|
||||
|
||||
pfcp_ies_auto.h: $(top_srcdir)/src/libosmo-pfcp/gen__pfcp_ies_auto.c \
|
||||
$(top_srcdir)/src/libosmo-gtlv/gtlv_gen.c \
|
||||
$(top_srcdir)/include/osmocom/gtlv/gtlv_gen.h
|
||||
$(MAKE) -C $(top_builddir)/src/libosmo-gtlv
|
||||
$(MAKE) -C $(top_builddir)/src/libosmo-pfcp gen__pfcp_ies_auto
|
||||
$(top_builddir)/src/libosmo-pfcp/gen__pfcp_ies_auto h > $(builddir)/pfcp_ies_auto.h
|
||||
@@ -1,163 +0,0 @@
|
||||
/* Definitions for decoded PFCP IEs, to be used by the auto-generated pfcp_ies_auto.c. */
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/socket.h>
|
||||
|
||||
#include <osmocom/pfcp/pfcp_proto.h>
|
||||
|
||||
/* Common pattern used in various PFCP IEs. */
|
||||
struct osmo_pfcp_ip_addrs {
|
||||
bool v4_present;
|
||||
struct osmo_sockaddr v4;
|
||||
bool v6_present;
|
||||
struct osmo_sockaddr v6;
|
||||
};
|
||||
|
||||
int osmo_pfcp_ip_addrs_set(struct osmo_pfcp_ip_addrs *dst, const struct osmo_sockaddr *addr);
|
||||
|
||||
/* 3GPP TS 29.244 8.2.38, IETF RFC 1035 3.1 */
|
||||
struct osmo_pfcp_ie_node_id {
|
||||
enum osmo_pfcp_node_id_type type;
|
||||
union {
|
||||
struct osmo_sockaddr ip;
|
||||
/* Fully qualified domain name in "dot" notation ("host.example.com") */
|
||||
char fqdn[254];
|
||||
};
|
||||
};
|
||||
|
||||
bool osmo_pfcp_bits_get(const uint8_t *bits, unsigned int bitpos);
|
||||
void osmo_pfcp_bits_set(uint8_t *bits, unsigned int bitpos, bool val);
|
||||
int osmo_pfcp_bits_to_str_buf(char *buf, size_t buflen, const uint8_t *bits, const struct value_string *bit_strs);
|
||||
char *osmo_pfcp_bits_to_str_c(void *ctx, const uint8_t *bits, const struct value_string *bit_str);
|
||||
|
||||
/* 3GPP TS 29.244 8.2.25
|
||||
* Usage:
|
||||
* struct osmo_pfcp_ie_up_function_features x;
|
||||
* osmo_pfcp_bits_set(x.bits, OSMO_PFCP_UP_FEAT_BUNDL, true);
|
||||
* if (osmo_pfcp_bits_get(x.bits, OSMO_PFCP_UP_FEAT_BUNDL))
|
||||
* foo();
|
||||
* printf("%s\n", osmo_pfcp_bits_to_str_c(x.bits, osmo_pfcp_up_feature_strs));
|
||||
*/
|
||||
struct osmo_pfcp_ie_up_function_features {
|
||||
uint8_t bits[6];
|
||||
};
|
||||
|
||||
/* 3GPP TS 29.244 8.2.58
|
||||
* struct osmo_pfcp_ie_cp_function_features x;
|
||||
* osmo_pfcp_bits_set(x.bits, OSMO_PFCP_CP_FEAT_BUNDL, true);
|
||||
* if (osmo_pfcp_bits_get(x.bits, OSMO_PFCP_CP_FEAT_BUNDL))
|
||||
* foo();
|
||||
* printf("%s\n", osmo_pfcp_bits_to_str_c(x.bits, osmo_pfcp_cp_feature_strs));
|
||||
*/
|
||||
struct osmo_pfcp_ie_cp_function_features {
|
||||
uint8_t bits[3];
|
||||
};
|
||||
|
||||
/* 3GPP TS 29.244 8.2.37 */
|
||||
struct osmo_pfcp_ie_f_seid {
|
||||
uint64_t seid;
|
||||
struct osmo_pfcp_ip_addrs ip_addr;
|
||||
};
|
||||
|
||||
void osmo_pfcp_ie_f_seid_set(struct osmo_pfcp_ie_f_seid *f_seid, uint64_t seid,
|
||||
const struct osmo_sockaddr *remote_addr);
|
||||
|
||||
/* 3GPP TS 29.244 8.3.2 */
|
||||
struct osmo_pfcp_ie_f_teid {
|
||||
bool choose_flag;
|
||||
union {
|
||||
struct {
|
||||
uint32_t teid;
|
||||
struct osmo_pfcp_ip_addrs ip_addr;
|
||||
} fixed;
|
||||
struct {
|
||||
bool ipv4_addr;
|
||||
bool ipv6_addr;
|
||||
bool choose_id_present;
|
||||
uint8_t choose_id;
|
||||
} choose;
|
||||
};
|
||||
};
|
||||
|
||||
/* 3GPP TS 29.244 8.2.62 */
|
||||
struct osmo_pfcp_ie_ue_ip_address {
|
||||
bool chv6;
|
||||
bool chv4;
|
||||
bool ip_is_destination;
|
||||
struct osmo_pfcp_ip_addrs ip_addr;
|
||||
bool ipv6_prefix_delegation_bits_present;
|
||||
uint8_t ipv6_prefix_delegation_bits;
|
||||
bool ipv6_prefix_length_present;
|
||||
uint8_t ipv6_prefix_length;
|
||||
};
|
||||
|
||||
/* 3GPP TS 29.244 8.2.26.
|
||||
* Usage:
|
||||
* struct osmo_pfcp_ie_apply_action x;
|
||||
* osmo_pfcp_bits_set(x.bits, OSMO_PFCP_APPLY_ACTION_FORW, true);
|
||||
* if (osmo_pfcp_bits_get(x.bits, OSMO_PFCP_APPLY_ACTION_FORW))
|
||||
* foo();
|
||||
* printf("%s\n", osmo_pfcp_bits_to_str_c(x.bits, osmo_pfcp_apply_action_strs));
|
||||
*/
|
||||
struct osmo_pfcp_ie_apply_action {
|
||||
uint8_t bits[2];
|
||||
};
|
||||
|
||||
struct osmo_pfcp_ie_network_inst {
|
||||
/* A domain name may have up to 253 characters; plus nul. */
|
||||
char str[253+1];
|
||||
};
|
||||
|
||||
struct osmo_pfcp_ie_activate_predefined_rules {
|
||||
char str[256];
|
||||
};
|
||||
|
||||
/* 3GPP TS 29.244 8.2.56 */
|
||||
struct osmo_pfcp_ie_outer_header_creation {
|
||||
/* desc_bits Usage:
|
||||
* osmo_pfcp_bits_set(x.desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_GTP_U_UDP_IPV4, true);
|
||||
* if (osmo_pfcp_bits_get(x.desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_GTP_U_UDP_IPV4))
|
||||
* foo();
|
||||
* printf("%s\n", osmo_pfcp_bits_to_str_c(x.desc_bits, osmo_pfcp_outer_header_creation_strs));
|
||||
*/
|
||||
uint8_t desc_bits[2];
|
||||
bool teid_present;
|
||||
uint32_t teid;
|
||||
struct osmo_pfcp_ip_addrs ip_addr;
|
||||
bool port_number_present;
|
||||
uint16_t port_number;
|
||||
bool c_tag_present;
|
||||
uint32_t c_tag;
|
||||
bool s_tag_present;
|
||||
uint32_t s_tag;
|
||||
};
|
||||
|
||||
/* 3GPP TS 29.244 8.2.64. */
|
||||
struct osmo_pfcp_ie_outer_header_removal {
|
||||
enum osmo_pfcp_outer_header_removal_desc desc;
|
||||
bool gtp_u_extension_header_del_present;
|
||||
uint8_t gtp_u_extension_header_del_bits[1];
|
||||
};
|
||||
@@ -1,528 +0,0 @@
|
||||
/* 3GPP TS 29.244: Packet Forwarding Control Protocol */
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define OSMO_PFCP_PORT 8805
|
||||
|
||||
/* Section 7.3 / Table 7.3-1 */
|
||||
enum osmo_pfcp_message_type {
|
||||
OSMO_PFCP_MSGT_NONE = 0,
|
||||
|
||||
/* Node related messages */
|
||||
OSMO_PFCP_MSGT_HEARTBEAT_REQ = 1,
|
||||
OSMO_PFCP_MSGT_HEARTBEAT_RESP = 2,
|
||||
OSMO_PFCP_MSGT_PFD_MGMT_REQ = 3,
|
||||
OSMO_PFCP_MSGT_PFD_MGMT_RESP = 4,
|
||||
OSMO_PFCP_MSGT_ASSOC_SETUP_REQ = 5,
|
||||
OSMO_PFCP_MSGT_ASSOC_SETUP_RESP = 6,
|
||||
OSMO_PFCP_MSGT_ASSOC_UPDATE_REQ = 7,
|
||||
OSMO_PFCP_MSGT_ASSOC_UPDATE_RESP = 8,
|
||||
OSMO_PFCP_MSGT_ASSOC_RELEASE_REQ = 9,
|
||||
OSMO_PFCP_MSGT_ASSOC_RELEASE_RESP = 10,
|
||||
OSMO_PFCP_MSGT_VERSION_NOT_SUPP_RESP = 11,
|
||||
OSMO_PFCP_MSGT_NODE_REPORT_REQ = 12,
|
||||
OSMO_PFCP_MSGT_NODE_REPORT_RESP = 13,
|
||||
OSMO_PFCP_MSGT_SESSION_SET_DEL_REQ = 14,
|
||||
OSMO_PFCP_MSGT_SESSION_SET_DEL_RESP = 15,
|
||||
|
||||
/* Session related messages */
|
||||
OSMO_PFCP_MSGT_SESSION_EST_REQ = 50,
|
||||
OSMO_PFCP_MSGT_SESSION_EST_RESP = 51,
|
||||
OSMO_PFCP_MSGT_SESSION_MOD_REQ = 52,
|
||||
OSMO_PFCP_MSGT_SESSION_MOD_RESP = 53,
|
||||
OSMO_PFCP_MSGT_SESSION_DEL_REQ = 54,
|
||||
OSMO_PFCP_MSGT_SESSION_DEL_RESP = 55,
|
||||
OSMO_PFCP_MSGT_SESSION_REP_REQ = 56,
|
||||
OSMO_PFCP_MSGT_SESSION_REP_RESP = 57,
|
||||
};
|
||||
|
||||
/* Section 8.1.2 / Table 8.1.2-1 */
|
||||
enum osmo_pfcp_iei {
|
||||
OSMO_PFCP_INVALID_IEI = 0,
|
||||
OSMO_PFCP_IEI_CREATE_PDR = 1,
|
||||
OSMO_PFCP_IEI_PDI = 2,
|
||||
OSMO_PFCP_IEI_CREATE_FAR = 3,
|
||||
OSMO_PFCP_IEI_FORW_PARAMS = 4,
|
||||
OSMO_PFCP_IEI_DUPL_PARAMS = 5,
|
||||
OSMO_PFCP_IEI_CREATE_URR = 6,
|
||||
OSMO_PFCP_IEI_CREATE_QER = 7,
|
||||
OSMO_PFCP_IEI_CREATED_PDR = 8,
|
||||
OSMO_PFCP_IEI_UPD_PDR = 9,
|
||||
OSMO_PFCP_IEI_UPD_FAR = 10,
|
||||
OSMO_PFCP_IEI_UPD_FORW_PARAMS = 11,
|
||||
OSMO_PFCP_IEI_UPD_BAR_SESS_REP_RESP = 12,
|
||||
OSMO_PFCP_IEI_UPD_URR = 13,
|
||||
OSMO_PFCP_IEI_UPD_QER = 14,
|
||||
OSMO_PFCP_IEI_REMOVE_PDR = 15,
|
||||
OSMO_PFCP_IEI_REMOVE_FAR = 16,
|
||||
OSMO_PFCP_IEI_REMOVE_URR = 17,
|
||||
OSMO_PFCP_IEI_REMOVE_QER = 18,
|
||||
OSMO_PFCP_IEI_CAUSE = 19,
|
||||
OSMO_PFCP_IEI_SOURCE_IFACE = 20,
|
||||
OSMO_PFCP_IEI_F_TEID = 21,
|
||||
OSMO_PFCP_IEI_NETWORK_INST = 22,
|
||||
OSMO_PFCP_IEI_SDF_FILTER = 23,
|
||||
OSMO_PFCP_IEI_APPLICATION_ID = 24,
|
||||
OSMO_PFCP_IEI_GATE_STATUS = 25,
|
||||
OSMO_PFCP_IEI_MBR = 26,
|
||||
OSMO_PFCP_IEI_GBR = 27,
|
||||
OSMO_PFCP_IEI_QER_CORRELATION_ID = 28,
|
||||
OSMO_PFCP_IEI_PRECEDENCE = 29,
|
||||
OSMO_PFCP_IEI_TRANSPORT_LEVEL_MARKING = 30,
|
||||
OSMO_PFCP_IEI_VOLUME_THRESH = 31,
|
||||
OSMO_PFCP_IEI_TIME_THRESH = 32,
|
||||
OSMO_PFCP_IEI_MONITORING_TIME = 33,
|
||||
OSMO_PFCP_IEI_SUBSEQUENT_VOLUME_THRESH = 34,
|
||||
OSMO_PFCP_IEI_SUBSEQUENT_TIME_THRESH = 35,
|
||||
OSMO_PFCP_IEI_INACT_DETECTION_TIME = 36,
|
||||
OSMO_PFCP_IEI_REPORTING_TRIGGERS = 37,
|
||||
OSMO_PFCP_IEI_REDIRECT_INFO = 38,
|
||||
OSMO_PFCP_IEI_REP_TYPE = 39,
|
||||
OSMO_PFCP_IEI_OFFENDING_IE = 40,
|
||||
OSMO_PFCP_IEI_FORW_POLICY = 41,
|
||||
OSMO_PFCP_IEI_DESTINATION_IFACE = 42,
|
||||
OSMO_PFCP_IEI_UP_FUNCTION_FEATURES = 43,
|
||||
OSMO_PFCP_IEI_APPLY_ACTION = 44,
|
||||
OSMO_PFCP_IEI_DL_DATA_SERVICE_INFO = 45,
|
||||
OSMO_PFCP_IEI_DL_DATA_NOTIFICATION_DELAY = 46,
|
||||
OSMO_PFCP_IEI_DL_BUFF_DURATION = 47,
|
||||
OSMO_PFCP_IEI_DL_BUFF_SUGGESTED_PACKET_COUNT = 48,
|
||||
OSMO_PFCP_IEI_PFCPSMREQ_FLAGS = 49,
|
||||
OSMO_PFCP_IEI_PFCPSRRSP_FLAGS = 50,
|
||||
OSMO_PFCP_IEI_LOAD_CTRL_INFO = 51,
|
||||
OSMO_PFCP_IEI_SEQUENCE_NUMBER = 52,
|
||||
OSMO_PFCP_IEI_METRIC = 53,
|
||||
OSMO_PFCP_IEI_OVERLOAD_CTRL_INFO = 54,
|
||||
OSMO_PFCP_IEI_TIMER = 55,
|
||||
OSMO_PFCP_IEI_PDR_ID = 56,
|
||||
OSMO_PFCP_IEI_F_SEID = 57,
|
||||
OSMO_PFCP_IEI_APPLICATION_IDS_PFDS = 58,
|
||||
OSMO_PFCP_IEI_PFD_CONTEXT = 59,
|
||||
OSMO_PFCP_IEI_NODE_ID = 60,
|
||||
OSMO_PFCP_IEI_PFD_CONTENTS = 61,
|
||||
OSMO_PFCP_IEI_MEAS_METHOD = 62,
|
||||
OSMO_PFCP_IEI_USAGE_REP_TRIGGER = 63,
|
||||
OSMO_PFCP_IEI_MEAS_PERIOD = 64,
|
||||
OSMO_PFCP_IEI_FQ_CSID = 65,
|
||||
OSMO_PFCP_IEI_VOLUME_MEAS = 66,
|
||||
OSMO_PFCP_IEI_DURATION_MEAS = 67,
|
||||
OSMO_PFCP_IEI_APPLICATION_DETECTION_INFO = 68,
|
||||
OSMO_PFCP_IEI_TIME_OF_FIRST_PACKET = 69,
|
||||
OSMO_PFCP_IEI_TIME_OF_LAST_PACKET = 70,
|
||||
OSMO_PFCP_IEI_QUOTA_HOLDING_TIME = 71,
|
||||
OSMO_PFCP_IEI_DROPPED_DL_TRAFFIC_THRESH = 72,
|
||||
OSMO_PFCP_IEI_VOLUME_QUOTA = 73,
|
||||
OSMO_PFCP_IEI_TIME_QUOTA = 74,
|
||||
OSMO_PFCP_IEI_START_TIME = 75,
|
||||
OSMO_PFCP_IEI_END_TIME = 76,
|
||||
OSMO_PFCP_IEI_QUERY_URR = 77,
|
||||
OSMO_PFCP_IEI_USAGE_REP_SESS_MOD_RESP = 78,
|
||||
OSMO_PFCP_IEI_USAGE_REP_SESS_DEL_RESP = 79,
|
||||
OSMO_PFCP_IEI_USAGE_REP_SESS_REP_REQ = 80,
|
||||
OSMO_PFCP_IEI_URR_ID = 81,
|
||||
OSMO_PFCP_IEI_LINKED_URR_ID = 82,
|
||||
OSMO_PFCP_IEI_DL_DATA_REP = 83,
|
||||
OSMO_PFCP_IEI_OUTER_HEADER_CREATION = 84,
|
||||
OSMO_PFCP_IEI_CREATE_BAR = 85,
|
||||
OSMO_PFCP_IEI_UPD_BAR_SESS_MOD_REQ = 86,
|
||||
OSMO_PFCP_IEI_REMOVE_BAR = 87,
|
||||
OSMO_PFCP_IEI_BAR_ID = 88,
|
||||
OSMO_PFCP_IEI_CP_FUNCTION_FEATURES = 89,
|
||||
OSMO_PFCP_IEI_USAGE_INFO = 90,
|
||||
OSMO_PFCP_IEI_APPLICATION_INST_ID = 91,
|
||||
OSMO_PFCP_IEI_FLOW_INFO = 92,
|
||||
OSMO_PFCP_IEI_UE_IP_ADDRESS = 93,
|
||||
OSMO_PFCP_IEI_PACKET_RATE = 94,
|
||||
OSMO_PFCP_IEI_OUTER_HEADER_REMOVAL = 95,
|
||||
OSMO_PFCP_IEI_RECOVERY_TIME_STAMP = 96,
|
||||
OSMO_PFCP_IEI_DL_FLOW_LEVEL_MARKING = 97,
|
||||
OSMO_PFCP_IEI_HEADER_ENRICHMENT = 98,
|
||||
OSMO_PFCP_IEI_ERROR_IND_REP = 99,
|
||||
OSMO_PFCP_IEI_MEAS_INFO = 100,
|
||||
OSMO_PFCP_IEI_NODE_REP_TYPE = 101,
|
||||
OSMO_PFCP_IEI_USER_PLANE_PATH_FAILURE_REP = 102,
|
||||
OSMO_PFCP_IEI_REMOTE_GTP_U_PEER = 103,
|
||||
OSMO_PFCP_IEI_UR_SEQN = 104,
|
||||
OSMO_PFCP_IEI_UPD_DUPL_PARAMS = 105,
|
||||
OSMO_PFCP_IEI_ACTIVATE_PREDEFINED_RULES = 106,
|
||||
OSMO_PFCP_IEI_DEACTIVATE_PREDEFINED_RULES = 107,
|
||||
OSMO_PFCP_IEI_FAR_ID = 108,
|
||||
OSMO_PFCP_IEI_QER_ID = 109,
|
||||
OSMO_PFCP_IEI_OCI_FLAGS = 110,
|
||||
OSMO_PFCP_IEI_PFCP_ASSOC_RELEASE_REQ = 111,
|
||||
OSMO_PFCP_IEI_GRACEFUL_RELEASE_PERIOD = 112,
|
||||
OSMO_PFCP_IEI_PDN_TYPE = 113,
|
||||
OSMO_PFCP_IEI_FAILED_RULE_ID = 114,
|
||||
OSMO_PFCP_IEI_TIME_QUOTA_MECHANISM = 115,
|
||||
OSMO_PFCP_IEI_RESERVED = 116,
|
||||
OSMO_PFCP_IEI_USER_PLANE_INACT_TIMER = 117,
|
||||
OSMO_PFCP_IEI_AGGREGATED_URRS = 118,
|
||||
OSMO_PFCP_IEI_MULTIPLIER = 119,
|
||||
OSMO_PFCP_IEI_AGGREGATED_URR_ID = 120,
|
||||
OSMO_PFCP_IEI_SUBSEQUENT_VOLUME_QUOTA = 121,
|
||||
OSMO_PFCP_IEI_SUBSEQUENT_TIME_QUOTA = 122,
|
||||
OSMO_PFCP_IEI_RQI = 123,
|
||||
OSMO_PFCP_IEI_QFI = 124,
|
||||
OSMO_PFCP_IEI_QUERY_URR_REFERENCE = 125,
|
||||
OSMO_PFCP_IEI_ADDITIONAL_USAGE_REPS_INFO = 126,
|
||||
OSMO_PFCP_IEI_CREATE_TRAFFIC_ENDPOINT = 127,
|
||||
OSMO_PFCP_IEI_CREATED_TRAFFIC_ENDPOINT = 128,
|
||||
OSMO_PFCP_IEI_UPD_TRAFFIC_ENDPOINT = 129,
|
||||
OSMO_PFCP_IEI_REMOVE_TRAFFIC_ENDPOINT = 130,
|
||||
OSMO_PFCP_IEI_TRAFFIC_ENDPOINT_ID = 131,
|
||||
OSMO_PFCP_IEI_ETHERNET_PACKET_FILTER = 132,
|
||||
OSMO_PFCP_IEI_MAC_ADDRESS = 133,
|
||||
OSMO_PFCP_IEI_C_TAG = 134,
|
||||
OSMO_PFCP_IEI_S_TAG = 135,
|
||||
OSMO_PFCP_IEI_ETHERTYPE = 136,
|
||||
OSMO_PFCP_IEI_PROXYING = 137,
|
||||
OSMO_PFCP_IEI_ETHERNET_FILTER_ID = 138,
|
||||
OSMO_PFCP_IEI_ETHERNET_FILTER_PROPERTIES = 139,
|
||||
OSMO_PFCP_IEI_SUGGESTED_BUFF_PACKETS_COUNT = 140,
|
||||
OSMO_PFCP_IEI_USER_ID = 141,
|
||||
OSMO_PFCP_IEI_ETHERNET_PDU_SESS_INFO = 142,
|
||||
OSMO_PFCP_IEI_ETHERNET_TRAFFIC_INFO = 143,
|
||||
OSMO_PFCP_IEI_MAC_ADDRS_DETECTED = 144,
|
||||
OSMO_PFCP_IEI_MAC_ADDRS_REMOVED = 145,
|
||||
OSMO_PFCP_IEI_ETHERNET_INACT_TIMER = 146,
|
||||
OSMO_PFCP_IEI_ADDITIONAL_MONITORING_TIME = 147,
|
||||
OSMO_PFCP_IEI_EVENT_QUOTA = 148,
|
||||
OSMO_PFCP_IEI_EVENT_THRESH = 149,
|
||||
OSMO_PFCP_IEI_SUBSEQUENT_EVENT_QUOTA = 150,
|
||||
OSMO_PFCP_IEI_SUBSEQUENT_EVENT_THRESH = 151,
|
||||
OSMO_PFCP_IEI_TRACE_INFO = 152,
|
||||
OSMO_PFCP_IEI_FRAMED_ROUTE = 153,
|
||||
OSMO_PFCP_IEI_FRAMED_ROUTING = 154,
|
||||
OSMO_PFCP_IEI_FRAMED_IPV6_ROUTE = 155,
|
||||
OSMO_PFCP_IEI_TIME_STAMP = 156,
|
||||
OSMO_PFCP_IEI_AVERAGING_WINDOW = 157,
|
||||
OSMO_PFCP_IEI_PAGING_POLICY_INDICATOR = 158,
|
||||
OSMO_PFCP_IEI_APN_DNN = 159,
|
||||
OSMO_PFCP_IEI_3GPP_IFACE_TYPE = 160,
|
||||
OSMO_PFCP_IEI_PFCPSRREQ_FLAGS = 161,
|
||||
OSMO_PFCP_IEI_PFCPAUREQ_FLAGS = 162,
|
||||
OSMO_PFCP_IEI_ACTIVATION_TIME = 163,
|
||||
OSMO_PFCP_IEI_DEACTIVATION_TIME = 164,
|
||||
OSMO_PFCP_IEI_CREATE_MAR = 165,
|
||||
OSMO_PFCP_IEI_3GPP_ACCESS_FORW_ACTION_INFO = 166,
|
||||
OSMO_PFCP_IEI_NON_3GPP_ACCESS_FORW_ACTION_INFO = 167,
|
||||
OSMO_PFCP_IEI_REMOVE_MAR = 168,
|
||||
OSMO_PFCP_IEI_UPD_MAR = 169,
|
||||
OSMO_PFCP_IEI_MAR_ID = 170,
|
||||
OSMO_PFCP_IEI_STEERING_FUNCTIONALITY = 171,
|
||||
OSMO_PFCP_IEI_STEERING_MODE = 172,
|
||||
OSMO_PFCP_IEI_WEIGHT = 173,
|
||||
OSMO_PFCP_IEI_PRIORITY = 174,
|
||||
OSMO_PFCP_IEI_UPD_3GPP_ACCESS_FORW_ACTION_INFO = 175,
|
||||
OSMO_PFCP_IEI_UPD_NON_3GPP_ACCESS_FORW_ACTION_INFO = 176,
|
||||
OSMO_PFCP_IEI_UE_IP_ADDRESS_POOL_IDENTITY = 177,
|
||||
OSMO_PFCP_IEI_ALTERNATIVE_SMF_IP_ADDRESS = 178,
|
||||
OSMO_PFCP_IEI_PACKET_REPLICATION_AND_DETECTION_CARRY_ON_INFO = 179,
|
||||
OSMO_PFCP_IEI_SMF_SET_ID = 180,
|
||||
OSMO_PFCP_IEI_QUOTA_VALIDITY_TIME = 181,
|
||||
OSMO_PFCP_IEI_NUMBER_OF_REPS = 182,
|
||||
OSMO_PFCP_IEI_PFCP_SESS_RETENTION_INFO_IN_ASSOC_SETUP_REQ = 183,
|
||||
OSMO_PFCP_IEI_PFCPASRSP_FLAGS = 184,
|
||||
OSMO_PFCP_IEI_CP_ENTITY_IP_ADDRESS = 185,
|
||||
OSMO_PFCP_IEI_PFCPSEREQ_FLAGS = 186,
|
||||
OSMO_PFCP_IEI_USER_PLANE_PATH_RECOVERY_REP = 187,
|
||||
OSMO_PFCP_IEI_IP_MULTICAST_ADDR_INFO_IN_SESS_EST_REQ = 188,
|
||||
OSMO_PFCP_IEI_JOIN_IP_MULTICAST_INFO_IE_IN_USAGE_REP = 189,
|
||||
OSMO_PFCP_IEI_LEAVE_IP_MULTICAST_INFO_IE_IN_USAGE_REP = 190,
|
||||
OSMO_PFCP_IEI_IP_MULTICAST_ADDRESS = 191,
|
||||
OSMO_PFCP_IEI_SOURCE_IP_ADDRESS = 192,
|
||||
OSMO_PFCP_IEI_PACKET_RATE_STATUS = 193,
|
||||
OSMO_PFCP_IEI_CREATE_BRIDGE_INFO_FOR_TSC = 194,
|
||||
OSMO_PFCP_IEI_CREATED_BRIDGE_INFO_FOR_TSC = 195,
|
||||
OSMO_PFCP_IEI_DS_TT_PORT_NUMBER = 196,
|
||||
OSMO_PFCP_IEI_NW_TT_PORT_NUMBER = 197,
|
||||
OSMO_PFCP_IEI_TSN_BRIDGE_ID = 198,
|
||||
OSMO_PFCP_IEI_TSC_MGMT_INFO_IE_IN_SESS_MOD_REQ = 199,
|
||||
OSMO_PFCP_IEI_TSC_MGMT_INFO_IE_IN_SESS_MOD_RESP = 200,
|
||||
OSMO_PFCP_IEI_TSC_MGMT_INFO_IE_IN_SESS_REP_REQ = 201,
|
||||
OSMO_PFCP_IEI_PORT_MGMT_INFO_CONTAINER = 202,
|
||||
OSMO_PFCP_IEI_CLOCK_DRIFT_CTRL_INFO = 203,
|
||||
OSMO_PFCP_IEI_REQUESTED_CLOCK_DRIFT_INFO = 204,
|
||||
OSMO_PFCP_IEI_CLOCK_DRIFT_REP = 205,
|
||||
OSMO_PFCP_IEI_TSN_TIME_DOMAIN_NUMBER = 206,
|
||||
OSMO_PFCP_IEI_TIME_OFFSET_THRESH = 207,
|
||||
OSMO_PFCP_IEI_CUMULATIVE_RATERATIO_THRESH = 208,
|
||||
OSMO_PFCP_IEI_TIME_OFFSET_MEAS = 209,
|
||||
OSMO_PFCP_IEI_CUMULATIVE_RATERATIO_MEAS = 210,
|
||||
OSMO_PFCP_IEI_REMOVE_SRR = 211,
|
||||
OSMO_PFCP_IEI_CREATE_SRR = 212,
|
||||
OSMO_PFCP_IEI_UPD_SRR = 213,
|
||||
OSMO_PFCP_IEI_SESS_REP = 214,
|
||||
OSMO_PFCP_IEI_SRR_ID = 215,
|
||||
OSMO_PFCP_IEI_ACCESS_AVAIL_CTRL_INFO = 216,
|
||||
OSMO_PFCP_IEI_REQUESTED_ACCESS_AVAIL_INFO = 217,
|
||||
OSMO_PFCP_IEI_ACCESS_AVAIL_REP = 218,
|
||||
OSMO_PFCP_IEI_ACCESS_AVAIL_INFO = 219,
|
||||
OSMO_PFCP_IEI_PROVIDE_ATSSS_CTRL_INFO = 220,
|
||||
OSMO_PFCP_IEI_ATSSS_CTRL_PARAMS = 221,
|
||||
OSMO_PFCP_IEI_MPTCP_CTRL_INFO = 222,
|
||||
OSMO_PFCP_IEI_ATSSS_LL_CTRL_INFO = 223,
|
||||
OSMO_PFCP_IEI_PMF_CTRL_INFO = 224,
|
||||
OSMO_PFCP_IEI_MPTCP_PARAMS = 225,
|
||||
OSMO_PFCP_IEI_ATSSS_LL_PARAMS = 226,
|
||||
OSMO_PFCP_IEI_PMF_PARAMS = 227,
|
||||
OSMO_PFCP_IEI_MPTCP_ADDRESS_INFO = 228,
|
||||
OSMO_PFCP_IEI_UE_LINK_SPECIFIC_IP_ADDRESS = 229,
|
||||
OSMO_PFCP_IEI_PMF_ADDRESS_INFO = 230,
|
||||
OSMO_PFCP_IEI_ATSSS_LL_INFO = 231,
|
||||
OSMO_PFCP_IEI_DATA_NETWORK_ACCESS_IDENTIFIER = 232,
|
||||
OSMO_PFCP_IEI_UE_IP_ADDRESS_POOL_INFO = 233,
|
||||
OSMO_PFCP_IEI_AVERAGE_PACKET_DELAY = 234,
|
||||
OSMO_PFCP_IEI_MIN_PACKET_DELAY = 235,
|
||||
OSMO_PFCP_IEI_MAX_PACKET_DELAY = 236,
|
||||
OSMO_PFCP_IEI_QOS_REP_TRIGGER = 237,
|
||||
OSMO_PFCP_IEI_GTP_U_PATH_QOS_CTRL_INFO = 238,
|
||||
OSMO_PFCP_IEI_GTP_U_PATH_QOS_REP_NODE_REP_REQ = 239,
|
||||
OSMO_PFCP_IEI_QOS_INFO_IN_GTP_U_PATH_QOS_REP = 240,
|
||||
OSMO_PFCP_IEI_GTP_U_PATH_IFACE_TYPE = 241,
|
||||
OSMO_PFCP_IEI_QOS_MONITORING_PER_QOS_FLOW_CTRL_INFO = 242,
|
||||
OSMO_PFCP_IEI_REQUESTED_QOS_MONITORING = 243,
|
||||
OSMO_PFCP_IEI_REPORTING_FREQUENCY = 244,
|
||||
OSMO_PFCP_IEI_PACKET_DELAY_THRESHOLDS = 245,
|
||||
OSMO_PFCP_IEI_MIN_WAIT_TIME = 246,
|
||||
OSMO_PFCP_IEI_QOS_MONITORING_REP = 247,
|
||||
OSMO_PFCP_IEI_QOS_MONITORING_MEAS = 248,
|
||||
OSMO_PFCP_IEI_MT_EDT_CTRL_INFO = 249,
|
||||
OSMO_PFCP_IEI_DL_DATA_PACKETS_SIZE = 250,
|
||||
OSMO_PFCP_IEI_QER_CTRL_INDICATIONS = 251,
|
||||
OSMO_PFCP_IEI_PACKET_RATE_STATUS_REP = 252,
|
||||
OSMO_PFCP_IEI_NF_INST_ID = 253,
|
||||
OSMO_PFCP_IEI_ETHERNET_CONTEXT_INFO = 254,
|
||||
OSMO_PFCP_IEI_REDUNDANT_TRANSMISSION_PARAMS = 255,
|
||||
OSMO_PFCP_IEI_UPDATED_PDR = 256,
|
||||
OSMO_PFCP_IEI_S_NSSAI = 257,
|
||||
OSMO_PFCP_IEI_IP_VERSION = 258,
|
||||
OSMO_PFCP_IEI_PFCPASREQ_FLAGS = 259,
|
||||
OSMO_PFCP_IEI_DATA_STATUS = 260,
|
||||
OSMO_PFCP_IEI_PROVIDE_RDS_CONF_INFO = 261,
|
||||
OSMO_PFCP_IEI_RDS_CONF_INFO = 262,
|
||||
OSMO_PFCP_IEI_QUERY_PACKET_RATE_STATUS_IE_IN_SESS_MOD_REQ = 263,
|
||||
OSMO_PFCP_IEI_PACKET_RATE_STATUS_REP_IE_IN_SESS_MOD_RESP = 264,
|
||||
OSMO_PFCP_IEI_MPTCP_APPLICABLE_IND = 265,
|
||||
OSMO_PFCP_IEI_BRIDGE_MGMT_INFO_CONTAINER = 266,
|
||||
OSMO_PFCP_IEI_UE_IP_ADDRESS_USAGE_INFO = 267,
|
||||
OSMO_PFCP_IEI_NUMBER_OF_UE_IP_ADDRS = 268,
|
||||
OSMO_PFCP_IEI_VALIDITY_TIMER = 269,
|
||||
OSMO_PFCP_IEI_REDUNDANT_TRANSMISSION_FORW_PARAMS = 270,
|
||||
OSMO_PFCP_IEI_TRANSPORT_DELAY_REPORTING = 271,
|
||||
};
|
||||
|
||||
/* Section 8.2.1 / Table 8.2.1-1 */
|
||||
enum osmo_pfcp_cause {
|
||||
OSMO_PFCP_CAUSE_RESERVED = 0,
|
||||
OSMO_PFCP_CAUSE_REQUEST_ACCEPTED = 1,
|
||||
OSMO_PFCP_CAUSE_MORE_USAGE_REPORT_TO_SEND = 2,
|
||||
OSMO_PFCP_CAUSE_REQUEST_REJECTED = 64,
|
||||
OSMO_PFCP_CAUSE_SESSION_CTX_NOT_FOUND = 65,
|
||||
OSMO_PFCP_CAUSE_MANDATORY_IE_MISSING = 66,
|
||||
OSMO_PFCP_CAUSE_CONDITIONAL_IE_MISSING = 67,
|
||||
OSMO_PFCP_CAUSE_INVALID_LENGTH = 68,
|
||||
OSMO_PFCP_CAUSE_MANDATORY_IE_INCORRECT = 69,
|
||||
OSMO_PFCP_CAUSE_INVALID_FORW_POLICY = 70,
|
||||
OSMO_PFCP_CAUSE_INVALID_F_TEID_ALLOC_OPTION = 71,
|
||||
OSMO_PFCP_CAUSE_NO_ESTABLISHED_PFCP_ASSOC = 72,
|
||||
OSMO_PFCP_CAUSE_RULE_CREATION_MOD_FAILURE = 73,
|
||||
OSMO_PFCP_CAUSE_PFCP_ENTITY_IN_CONGESTION = 74,
|
||||
OSMO_PFCP_CAUSE_NO_RESOURCES_AVAILABLE = 75,
|
||||
OSMO_PFCP_CAUSE_SERVICE_NOT_SUPPORTED = 76,
|
||||
OSMO_PFCP_CAUSE_SYSTEM_FAILURE = 77,
|
||||
OSMO_PFCP_CAUSE_REDIRECTION_REQUESTED = 78,
|
||||
OSMO_PFCP_CAUSE_ALL_DYNAMIC_ADDRESSES_ARE_OCCUPIED = 79,
|
||||
};
|
||||
|
||||
/* Section 8.2.38 */
|
||||
enum osmo_pfcp_node_id_type {
|
||||
OSMO_PFCP_NODE_ID_T_IPV4 = 0,
|
||||
OSMO_PFCP_NODE_ID_T_IPV6 = 1,
|
||||
OSMO_PFCP_NODE_ID_T_FQDN = 2,
|
||||
};
|
||||
|
||||
enum osmo_pfcp_3gpp_iface_type {
|
||||
OSMO_PFCP_3GPP_IFACE_TYPE_S1_U = 0,
|
||||
OSMO_PFCP_3GPP_IFACE_TYPE_S5_S8_U = 1,
|
||||
OSMO_PFCP_3GPP_IFACE_TYPE_S4_U = 2,
|
||||
OSMO_PFCP_3GPP_IFACE_TYPE_S11_U = 3,
|
||||
OSMO_PFCP_3GPP_IFACE_TYPE_S12_U = 4,
|
||||
OSMO_PFCP_3GPP_IFACE_TYPE_GN_GP_U = 5,
|
||||
OSMO_PFCP_3GPP_IFACE_TYPE_S2A_U = 6,
|
||||
OSMO_PFCP_3GPP_IFACE_TYPE_S2B_U = 7,
|
||||
OSMO_PFCP_3GPP_IFACE_TYPE_ENODEB_GTP_U_INTERFACE_FOR_DL_DATA_FORWARDING = 8,
|
||||
OSMO_PFCP_3GPP_IFACE_TYPE_ENODEB_GTP_U_INTERFACE_FOR_UL_DATA_FORWARDING = 9,
|
||||
OSMO_PFCP_3GPP_IFACE_TYPE_SGW_UPF_GTP_U_INTERFACE_FOR_DL_DATA_FORWARDING = 10,
|
||||
OSMO_PFCP_3GPP_IFACE_TYPE_N3_3GPP_ACCESS = 11,
|
||||
OSMO_PFCP_3GPP_IFACE_TYPE_N3_TRUSTED_NON_3GPP_ACCESS = 12,
|
||||
OSMO_PFCP_3GPP_IFACE_TYPE_N3_UNTRUSTED_NON_3GPP_ACCESS = 13,
|
||||
OSMO_PFCP_3GPP_IFACE_TYPE_N3_FOR_DATA_FORWARDING = 14,
|
||||
OSMO_PFCP_3GPP_IFACE_TYPE_N9 = 15,
|
||||
OSMO_PFCP_3GPP_IFACE_TYPE_SGI = 16,
|
||||
OSMO_PFCP_3GPP_IFACE_TYPE_N6 = 17,
|
||||
OSMO_PFCP_3GPP_IFACE_TYPE_N19 = 18,
|
||||
OSMO_PFCP_3GPP_IFACE_TYPE_S8_U = 19,
|
||||
OSMO_PFCP_3GPP_IFACE_TYPE_GP_U = 20,
|
||||
};
|
||||
|
||||
enum osmo_pfcp_source_iface {
|
||||
OSMO_PFCP_SOURCE_IFACE_ACCESS = 0,
|
||||
OSMO_PFCP_SOURCE_IFACE_CORE = 1,
|
||||
OSMO_PFCP_SOURCE_IFACE_SGI_LAN_N6_LAN = 2,
|
||||
OSMO_PFCP_SOURCE_IFACE_CP_FUNCTION = 3,
|
||||
OSMO_PFCP_SOURCE_IFACE_5G_VN_INTERNAL = 4,
|
||||
};
|
||||
|
||||
enum osmo_pfcp_dest_iface {
|
||||
OSMO_PFCP_DEST_IFACE_ACCESS = 0,
|
||||
OSMO_PFCP_DEST_IFACE_CORE = 1,
|
||||
OSMO_PFCP_DEST_IFACE_SGI_LAN_N6_LAN = 2,
|
||||
OSMO_PFCP_DEST_IFACE_CP_FUNCTION = 3,
|
||||
OSMO_PFCP_DEST_IFACE_LI_FUNCTION = 4,
|
||||
OSMO_PFCP_DEST_IFACE_5G_VN_INTERNAL = 5,
|
||||
};
|
||||
|
||||
/* The enum values correspond to the bit index in the supported features bitmask in the PFCP UP Function Features IE.
|
||||
* 0 means first octet and first bit, "Octet 5 Bit 1" as in spec;
|
||||
* 7 means first octet last bit, "Octet 5 Bit 8";
|
||||
* 8 means second octet first bit, "Octet 6 Bit 1";
|
||||
* and so on.
|
||||
* Intended for use with osmo_pfcp_bits_get(), osmo_pfcp_bits_set(), osmo_pfcp_bits_to_str_c().
|
||||
*/
|
||||
enum osmo_pfcp_up_feature {
|
||||
OSMO_PFCP_UP_FEAT_BUCP = 0,
|
||||
OSMO_PFCP_UP_FEAT_DDND,
|
||||
OSMO_PFCP_UP_FEAT_DLBD,
|
||||
OSMO_PFCP_UP_FEAT_TRST,
|
||||
OSMO_PFCP_UP_FEAT_FTUP,
|
||||
OSMO_PFCP_UP_FEAT_PFDM,
|
||||
OSMO_PFCP_UP_FEAT_HEEU,
|
||||
OSMO_PFCP_UP_FEAT_TREU,
|
||||
OSMO_PFCP_UP_FEAT_EMPU,
|
||||
OSMO_PFCP_UP_FEAT_PDIU,
|
||||
OSMO_PFCP_UP_FEAT_UDBC,
|
||||
OSMO_PFCP_UP_FEAT_QUOAC,
|
||||
OSMO_PFCP_UP_FEAT_TRACE,
|
||||
OSMO_PFCP_UP_FEAT_FRRT,
|
||||
OSMO_PFCP_UP_FEAT_PFDE,
|
||||
OSMO_PFCP_UP_FEAT_EPFAR,
|
||||
OSMO_PFCP_UP_FEAT_DPDRA,
|
||||
OSMO_PFCP_UP_FEAT_ADPDP,
|
||||
OSMO_PFCP_UP_FEAT_UEIP,
|
||||
OSMO_PFCP_UP_FEAT_SSET,
|
||||
OSMO_PFCP_UP_FEAT_MNOP,
|
||||
OSMO_PFCP_UP_FEAT_MTE,
|
||||
OSMO_PFCP_UP_FEAT_BUNDL,
|
||||
OSMO_PFCP_UP_FEAT_GCOM,
|
||||
OSMO_PFCP_UP_FEAT_MPAS,
|
||||
OSMO_PFCP_UP_FEAT_RTTL,
|
||||
OSMO_PFCP_UP_FEAT_VTIME,
|
||||
OSMO_PFCP_UP_FEAT_NORP,
|
||||
OSMO_PFCP_UP_FEAT_IP6PL,
|
||||
OSMO_PFCP_UP_FEAT_TSCU,
|
||||
OSMO_PFCP_UP_FEAT_MPTCP,
|
||||
OSMO_PFCP_UP_FEAT_ATSSSLL,
|
||||
OSMO_PFCP_UP_FEAT_QFQM,
|
||||
OSMO_PFCP_UP_FEAT_GPQM,
|
||||
OSMO_PFCP_UP_FEAT_MTEDT,
|
||||
OSMO_PFCP_UP_FEAT_CIOT,
|
||||
OSMO_PFCP_UP_FEAT_ETHAR,
|
||||
OSMO_PFCP_UP_FEAT_DDDS,
|
||||
OSMO_PFCP_UP_FEAT_RDS,
|
||||
OSMO_PFCP_UP_FEAT_RTTWP,
|
||||
};
|
||||
|
||||
/* The enum values correspond to the bit index in the supported features bitmask in the PFCP CP Function Features IE.
|
||||
* 0 means first octet and first bit, "Octet 5 Bit 1" as in spec;
|
||||
* 7 means first octet last bit, "Octet 5 Bit 8";
|
||||
* 8 means second octet first bit, "Octet 6 Bit 1";
|
||||
* and so on.
|
||||
* Intended for use with osmo_pfcp_bits_get(), osmo_pfcp_bits_set(), osmo_pfcp_bits_to_str_c().
|
||||
*/
|
||||
enum osmo_pfcp_cp_feature {
|
||||
OSMO_PFCP_CP_FEAT_LOAD = 0,
|
||||
OSMO_PFCP_CP_FEAT_OVRL,
|
||||
OSMO_PFCP_CP_FEAT_EPFAR,
|
||||
OSMO_PFCP_CP_FEAT_SSET,
|
||||
OSMO_PFCP_CP_FEAT_BUNDL,
|
||||
OSMO_PFCP_CP_FEAT_MPAS,
|
||||
OSMO_PFCP_CP_FEAT_ARDR,
|
||||
OSMO_PFCP_CP_FEAT_UIAUR,
|
||||
};
|
||||
|
||||
/* The enum values correspond to the bit index in the PFCP Apply Action IE.
|
||||
* 0 means first octet and first bit, "Octet 5 Bit 1" as in spec;
|
||||
* 7 means first octet last bit, "Octet 5 Bit 8";
|
||||
* 8 means second octet first bit, "Octet 6 Bit 1";
|
||||
* and so on.
|
||||
* Intended for use with osmo_pfcp_bits_get(), osmo_pfcp_bits_set(), osmo_pfcp_bits_to_str_c().
|
||||
*/
|
||||
enum osmo_pfcp_apply_action {
|
||||
OSMO_PFCP_APPLY_ACTION_DROP = 0,
|
||||
OSMO_PFCP_APPLY_ACTION_FORW,
|
||||
OSMO_PFCP_APPLY_ACTION_BUFF,
|
||||
OSMO_PFCP_APPLY_ACTION_NOCP,
|
||||
OSMO_PFCP_APPLY_ACTION_DUPL,
|
||||
OSMO_PFCP_APPLY_ACTION_IPMA,
|
||||
OSMO_PFCP_APPLY_ACTION_IPMD,
|
||||
OSMO_PFCP_APPLY_ACTION_DFRT,
|
||||
OSMO_PFCP_APPLY_ACTION_EDRT,
|
||||
OSMO_PFCP_APPLY_ACTION_BDPN,
|
||||
OSMO_PFCP_APPLY_ACTION_DDPN,
|
||||
};
|
||||
|
||||
/* The enum values correspond to the bit index in the description bitmask in the PFCP Outer Header Creation IE.
|
||||
* 0 means first octet and first bit, "Octet 5 Bit 1" as in spec;
|
||||
* 7 means first octet last bit, "Octet 5 Bit 8";
|
||||
* 8 means second octet first bit, "Octet 6 Bit 1";
|
||||
* and so on.
|
||||
* Intended for use with osmo_pfcp_bits_get(), osmo_pfcp_bits_set(), osmo_pfcp_bits_to_str_c().
|
||||
*/
|
||||
enum osmo_pfcp_outer_header_creation {
|
||||
OSMO_PFCP_OUTER_HEADER_CREATION_GTP_U_UDP_IPV4 = 0,
|
||||
OSMO_PFCP_OUTER_HEADER_CREATION_GTP_U_UDP_IPV6,
|
||||
OSMO_PFCP_OUTER_HEADER_CREATION_UDP_IPV4,
|
||||
OSMO_PFCP_OUTER_HEADER_CREATION_UDP_IPV6,
|
||||
OSMO_PFCP_OUTER_HEADER_CREATION_IPV4,
|
||||
OSMO_PFCP_OUTER_HEADER_CREATION_IPV6,
|
||||
OSMO_PFCP_OUTER_HEADER_CREATION_C_TAG,
|
||||
OSMO_PFCP_OUTER_HEADER_CREATION_S_TAG,
|
||||
OSMO_PFCP_OUTER_HEADER_CREATION_N19_INDICATION,
|
||||
OSMO_PFCP_OUTER_HEADER_CREATION_N6_INDICATION,
|
||||
};
|
||||
|
||||
/* 3GPP TS 29.244 8.2.64 */
|
||||
enum osmo_pfcp_outer_header_removal_desc {
|
||||
OSMO_PFCP_OUTER_HEADER_REMOVAL_GTP_U_UDP_IPV4 = 0,
|
||||
OSMO_PFCP_OUTER_HEADER_REMOVAL_GTP_U_UDP_IPV6 = 1,
|
||||
OSMO_PFCP_OUTER_HEADER_REMOVAL_UDP_IPV4 = 2,
|
||||
OSMO_PFCP_OUTER_HEADER_REMOVAL_UDP_IPV6 = 3,
|
||||
OSMO_PFCP_OUTER_HEADER_REMOVAL_IPV4 = 4,
|
||||
OSMO_PFCP_OUTER_HEADER_REMOVAL_IPV6 = 5,
|
||||
OSMO_PFCP_OUTER_HEADER_REMOVAL_GTP_U_UDP_IP = 6,
|
||||
OSMO_PFCP_OUTER_HEADER_REMOVAL_VLAN_S_TAG = 7,
|
||||
OSMO_PFCP_OUTER_HEADER_REMOVAL_S_TAG_AND_C_TAG = 8,
|
||||
};
|
||||
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <osmocom/pfcp/pfcp_proto.h>
|
||||
|
||||
extern const struct value_string osmo_pfcp_message_type_strs[];
|
||||
static inline const char *osmo_pfcp_message_type_str(enum osmo_pfcp_message_type val)
|
||||
{
|
||||
return get_value_string(osmo_pfcp_message_type_strs, val);
|
||||
}
|
||||
|
||||
extern const struct value_string osmo_pfcp_iei_strs[];
|
||||
static inline const char *osmo_pfcp_iei_str(enum osmo_pfcp_iei val)
|
||||
{
|
||||
return get_value_string(osmo_pfcp_iei_strs, val);
|
||||
}
|
||||
|
||||
extern const struct value_string osmo_pfcp_cause_strs[];
|
||||
static inline const char *osmo_pfcp_cause_str(enum osmo_pfcp_cause val)
|
||||
{
|
||||
return get_value_string(osmo_pfcp_cause_strs, val);
|
||||
}
|
||||
|
||||
extern const struct value_string osmo_pfcp_up_feature_strs[];
|
||||
static inline const char *osmo_pfcp_up_feature_str(enum osmo_pfcp_up_feature val)
|
||||
{
|
||||
return get_value_string(osmo_pfcp_up_feature_strs, val);
|
||||
}
|
||||
|
||||
extern const struct value_string osmo_pfcp_cp_feature_strs[];
|
||||
static inline const char *osmo_pfcp_cp_feature_str(enum osmo_pfcp_cp_feature val)
|
||||
{
|
||||
return get_value_string(osmo_pfcp_cp_feature_strs, val);
|
||||
}
|
||||
|
||||
extern const struct value_string osmo_pfcp_apply_action_strs[];
|
||||
static inline const char *osmo_pfcp_apply_action_str(enum osmo_pfcp_apply_action val)
|
||||
{
|
||||
return get_value_string(osmo_pfcp_apply_action_strs, val);
|
||||
}
|
||||
|
||||
extern const struct value_string osmo_pfcp_outer_header_creation_strs[];
|
||||
static inline const char *osmo_pfcp_outer_header_creation_str(enum osmo_pfcp_outer_header_creation val)
|
||||
{
|
||||
return get_value_string(osmo_pfcp_outer_header_creation_strs, val);
|
||||
}
|
||||
|
||||
extern const struct value_string osmo_pfcp_outer_header_removal_desc_strs[];
|
||||
static inline const char *osmo_pfcp_outer_header_removal_desc_str(enum osmo_pfcp_outer_header_removal_desc val)
|
||||
{
|
||||
return get_value_string(osmo_pfcp_outer_header_removal_desc_strs, val);
|
||||
}
|
||||
|
||||
extern const struct value_string osmo_pfcp_source_iface_strs[];
|
||||
static inline const char *osmo_pfcp_source_iface_str(enum osmo_pfcp_source_iface val)
|
||||
{
|
||||
return get_value_string(osmo_pfcp_source_iface_strs, val);
|
||||
}
|
||||
|
||||
extern const struct value_string osmo_pfcp_dest_iface_strs[];
|
||||
static inline const char *osmo_pfcp_dest_iface_str(enum osmo_pfcp_dest_iface val)
|
||||
{
|
||||
return get_value_string(osmo_pfcp_dest_iface_strs, val);
|
||||
}
|
||||
|
||||
extern const struct value_string osmo_pfcp_3gpp_iface_type_strs[];
|
||||
static inline const char *osmo_pfcp_3gpp_iface_type_str(enum osmo_pfcp_3gpp_iface_type val)
|
||||
{
|
||||
return get_value_string(osmo_pfcp_3gpp_iface_type_strs, val);
|
||||
}
|
||||
6
include/osmocom/pfcptool/Makefile.am
Normal file
6
include/osmocom/pfcptool/Makefile.am
Normal file
@@ -0,0 +1,6 @@
|
||||
noinst_HEADERS = \
|
||||
checksum.h \
|
||||
gtp_flood.h \
|
||||
pfcp_tool.h \
|
||||
range.h \
|
||||
$(NULL)
|
||||
13
include/osmocom/pfcptool/checksum.h
Normal file
13
include/osmocom/pfcptool/checksum.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
uint16_t ip_fast_csum(const void *iph, unsigned int ihl);
|
||||
uint32_t csum_partial(const void *buff, int len, uint32_t wsum);
|
||||
uint16_t ip_compute_csum(const void *buff, int len);
|
||||
|
||||
uint16_t csum_ipv6_magic(const struct in6_addr *saddr,
|
||||
const struct in6_addr *daddr,
|
||||
uint32_t len, uint8_t proto, uint32_t csum);
|
||||
|
||||
uint16_t csum_fold(uint32_t csum);
|
||||
34
include/osmocom/pfcptool/gtp_flood.h
Normal file
34
include/osmocom/pfcptool/gtp_flood.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/socket.h>
|
||||
|
||||
/* According to 3GPP TS 29.060. */
|
||||
struct gtp1u_hdr {
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
uint8_t pn:1, s:1, e:1, spare:1, pt:1, version:3;
|
||||
#else
|
||||
uint8_t version:3, pt:1, spare:1, e:1, s:1, pn:1;
|
||||
#endif
|
||||
uint8_t type;
|
||||
uint16_t length;
|
||||
uint32_t tei;
|
||||
};
|
||||
|
||||
struct gtp_flood;
|
||||
struct udp_port;
|
||||
|
||||
struct gtp_flood *gtp_flood_alloc(void *ctx, unsigned int workers);
|
||||
|
||||
struct gtp_flood_flow_cfg {
|
||||
struct udp_port *gtp_local;
|
||||
struct osmo_sockaddr gtp_remote;
|
||||
uint32_t gtp_remote_teid;
|
||||
struct osmo_sockaddr payload_src;
|
||||
struct osmo_sockaddr payload_dst;
|
||||
unsigned int num_packets;
|
||||
};
|
||||
void gtp_flood_add_flow(struct gtp_flood *gtp_flood,
|
||||
const struct gtp_flood_flow_cfg *flow_cfg);
|
||||
|
||||
void gtp_flood_start(struct gtp_flood *gtp_flood);
|
||||
bool gtp_flood_is_busy(struct gtp_flood *gtp_flood);
|
||||
178
include/osmocom/pfcptool/pfcp_tool.h
Normal file
178
include/osmocom/pfcptool/pfcp_tool.h
Normal file
@@ -0,0 +1,178 @@
|
||||
/* Global definitions for osmo-pfcp-tool */
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/tdef.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/sockaddr_str.h>
|
||||
#include <osmocom/vty/command.h>
|
||||
|
||||
#include <osmocom/pfcp/pfcp_msg.h>
|
||||
|
||||
#include <osmocom/upf/up_gtp_action.h>
|
||||
#include <osmocom/pfcptool/range.h>
|
||||
|
||||
struct osmo_tdef;
|
||||
struct ctrl_handle;
|
||||
|
||||
enum pfcp_tool_vty_node {
|
||||
PEER_NODE = _LAST_OSMOVTY_NODE + 1,
|
||||
SESSION_NODE,
|
||||
GTP_FLOOD_NODE,
|
||||
};
|
||||
|
||||
extern struct osmo_tdef g_pfcp_tool_tdefs[];
|
||||
extern struct osmo_tdef_group g_pfcp_tool_tdef_groups[];
|
||||
|
||||
struct pfcp_tool_peer {
|
||||
struct llist_head entry;
|
||||
|
||||
struct osmo_sockaddr remote_addr;
|
||||
struct osmo_pfcp_msg last_req;
|
||||
struct osmo_pfcp_msg last_resp;
|
||||
|
||||
uint64_t next_seid_state;
|
||||
|
||||
struct llist_head sessions;
|
||||
};
|
||||
|
||||
struct pfcp_tool_gtp_tun_ep {
|
||||
struct osmo_sockaddr_str addr;
|
||||
uint32_t teid;
|
||||
};
|
||||
|
||||
struct pfcp_tool_gtp_tun {
|
||||
struct pfcp_tool_gtp_tun_ep local;
|
||||
struct pfcp_tool_gtp_tun_ep remote;
|
||||
};
|
||||
|
||||
struct pfcp_tool_tunend {
|
||||
struct pfcp_tool_gtp_tun access;
|
||||
struct {
|
||||
struct osmo_sockaddr_str ue_local_addr;
|
||||
} core;
|
||||
};
|
||||
|
||||
struct pfcp_tool_tunmap {
|
||||
struct pfcp_tool_gtp_tun access;
|
||||
struct pfcp_tool_gtp_tun core;
|
||||
};
|
||||
|
||||
struct pfcp_tool_session {
|
||||
struct llist_head entry;
|
||||
|
||||
struct pfcp_tool_peer *peer;
|
||||
uint64_t cp_seid;
|
||||
struct osmo_pfcp_ie_f_seid up_f_seid;
|
||||
|
||||
enum up_gtp_action_kind kind;
|
||||
union {
|
||||
/* En-/De-capsulate GTP: add/remove a GTP header and forward the GTP payload from/to plain IP. */
|
||||
struct pfcp_tool_tunend tunend;
|
||||
|
||||
/* Tunnel-map GTP: translate from one TEID to another and forward */
|
||||
struct pfcp_tool_tunmap tunmap;
|
||||
};
|
||||
};
|
||||
|
||||
struct udp_port {
|
||||
struct llist_head entry;
|
||||
|
||||
/* IP address and UDP port from user input */
|
||||
struct osmo_sockaddr osa;
|
||||
|
||||
/* In case this is a locally bound port, this is the fd for the socket. */
|
||||
struct osmo_fd ofd;
|
||||
};
|
||||
|
||||
struct g_pfcp_tool {
|
||||
struct ctrl_handle *ctrl;
|
||||
|
||||
struct {
|
||||
char *local_ip;
|
||||
uint16_t local_port;
|
||||
} vty_cfg;
|
||||
|
||||
struct osmo_pfcp_endpoint *ep;
|
||||
struct llist_head peers;
|
||||
|
||||
uint32_t next_teid_state;
|
||||
|
||||
struct {
|
||||
struct range ip_range;
|
||||
} ue;
|
||||
|
||||
struct {
|
||||
/* list of struct udp_port */
|
||||
struct llist_head gtp_local_addrs;
|
||||
struct udp_port *gtp_local_addrs_next;
|
||||
|
||||
struct {
|
||||
struct {
|
||||
/* source address is always the UE IP address */
|
||||
struct range udp_port_range;
|
||||
} source;
|
||||
struct {
|
||||
struct range ip_range;
|
||||
struct range udp_port_range;
|
||||
} target;
|
||||
} payload;
|
||||
|
||||
struct {
|
||||
unsigned int workers;
|
||||
|
||||
unsigned int flows_per_session;
|
||||
unsigned int packets_per_flow;
|
||||
|
||||
struct gtp_flood *state;
|
||||
} flood;
|
||||
} gtp;
|
||||
};
|
||||
|
||||
extern struct g_pfcp_tool *g_pfcp_tool;
|
||||
|
||||
void g_pfcp_tool_alloc(void *ctx);
|
||||
void pfcp_tool_vty_init_cfg();
|
||||
void pfcp_tool_vty_init_cmds();
|
||||
|
||||
int pfcp_tool_mainloop(int poll);
|
||||
|
||||
struct pfcp_tool_peer *pfcp_tool_peer_find_or_create(const struct osmo_sockaddr *remote_addr);
|
||||
struct pfcp_tool_session *pfcp_tool_session_find_or_create(struct pfcp_tool_peer *peer, uint64_t cp_seid,
|
||||
enum up_gtp_action_kind kind);
|
||||
void pfcp_tool_rx_msg(struct osmo_pfcp_endpoint *ep, struct osmo_pfcp_msg *m, struct osmo_pfcp_msg *req);
|
||||
|
||||
int peer_tx(struct pfcp_tool_peer *peer, struct osmo_pfcp_msg *m);
|
||||
uint64_t peer_new_seid(struct pfcp_tool_peer *peer);
|
||||
|
||||
uint32_t pfcp_tool_new_teid(void);
|
||||
int pfcp_tool_next_ue_addr(struct osmo_sockaddr *dst);
|
||||
|
||||
struct udp_port *pfcp_tool_have_udp_port_by_str(const struct osmo_sockaddr_str *addr, uint16_t fallback_port);
|
||||
|
||||
void pfcp_tool_gtp_flood_start(void);
|
||||
|
||||
int pfcp_tool_vty_go_parent(struct vty *vty);
|
||||
38
include/osmocom/pfcptool/range.h
Normal file
38
include/osmocom/pfcptool/range.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
struct osmo_sockaddr;
|
||||
|
||||
/* A value large enough for an IPv6 address (128bit) */
|
||||
struct range_val {
|
||||
uint64_t buf[2];
|
||||
size_t size;
|
||||
};
|
||||
|
||||
/* Manage stepping through a defined range of values large enough to handle IPv6 addresses. */
|
||||
struct range {
|
||||
struct range_val first;
|
||||
struct range_val last;
|
||||
struct range_val next;
|
||||
bool wrapped;
|
||||
};
|
||||
|
||||
/* struct range r = {};
|
||||
* range_val_set_int(&r.first, 23);
|
||||
* range_val_set_int(&r.last, 42);
|
||||
* while (1) {
|
||||
* range_next();
|
||||
* unsigned int val = range_val_get_int(&r.next);
|
||||
* printf("%u\n", val);
|
||||
* }
|
||||
*/
|
||||
void range_next(struct range *r);
|
||||
void range_val_set_int(struct range_val *rv, uint32_t val);
|
||||
uint32_t range_val_get_int(const struct range_val *rv);
|
||||
void range_val_set_addr(struct range_val *rv, const struct osmo_sockaddr *val);
|
||||
void range_val_get_addr(struct osmo_sockaddr *dst, const struct range_val *rv);
|
||||
void range_val_inc(struct range_val *rv);
|
||||
int range_val_cmp(const struct range_val *a, const struct range_val *b);
|
||||
@@ -1,3 +1,12 @@
|
||||
noinst_HEADERS = \
|
||||
netinst.h \
|
||||
up_endpoint.h \
|
||||
up_peer.h \
|
||||
up_session.h \
|
||||
upf.h \
|
||||
upf_gtp.h \
|
||||
upf_gtpu_echo.h \
|
||||
upf_nft.h \
|
||||
upf_tun.h \
|
||||
up_gtp_action.h \
|
||||
$(NULL)
|
||||
|
||||
44
include/osmocom/upf/netinst.h
Normal file
44
include/osmocom/upf/netinst.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/sockaddr_str.h>
|
||||
|
||||
struct vty;
|
||||
|
||||
struct network_instance {
|
||||
struct llist_head entry;
|
||||
|
||||
char *name;
|
||||
struct osmo_sockaddr_str addr;
|
||||
};
|
||||
|
||||
const struct network_instance *netinst_add(void *ctx, struct llist_head *list, const char *name, const char *addr,
|
||||
const char **errmsg);
|
||||
const struct network_instance *netinst_find(struct llist_head *list, const char *name);
|
||||
const struct network_instance *netinst_first(struct llist_head *list);
|
||||
int netinst_clear(struct llist_head *list);
|
||||
|
||||
int netinst_vty_write(struct vty *vty, struct llist_head *list, const char *indent, const char *name_or_null);
|
||||
48
include/osmocom/upf/up_endpoint.h
Normal file
48
include/osmocom/upf/up_endpoint.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
|
||||
struct osmo_pfcp_msg;
|
||||
struct osmo_pfcp_endpoint;
|
||||
struct osmo_sockaddr;
|
||||
|
||||
#define UP_USE_MSG_RX "msg-rx"
|
||||
#define UP_USE_MSG_TX "msg-tx"
|
||||
|
||||
struct up_endpoint {
|
||||
struct osmo_pfcp_endpoint *pfcp_ep;
|
||||
|
||||
/* list of struct up_peer. */
|
||||
struct llist_head peers;
|
||||
|
||||
uint64_t next_up_seid_state;
|
||||
};
|
||||
|
||||
struct up_endpoint *up_endpoint_alloc(void *ctx, const struct osmo_sockaddr *local_addr);
|
||||
int up_endpoint_bind(struct up_endpoint *up_ep);
|
||||
void up_endpoint_free(struct up_endpoint **ep);
|
||||
|
||||
uint64_t up_endpoint_next_up_seid(struct up_endpoint *ep);
|
||||
73
include/osmocom/upf/up_gtp_action.h
Normal file
73
include/osmocom/upf/up_gtp_action.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/fsm.h>
|
||||
|
||||
#include <osmocom/upf/up_session.h>
|
||||
#include <osmocom/upf/upf_gtp.h>
|
||||
#include <osmocom/upf/upf_nft.h>
|
||||
|
||||
#define LOG_UP_GTP_ACTION(A, LEVEL, FMT, ARGS...) \
|
||||
LOGP(DGTP, LEVEL, "%s: " FMT, up_gtp_action_to_str_c(OTC_SELECT, A), ##ARGS)
|
||||
|
||||
struct up_session;
|
||||
|
||||
enum up_gtp_action_kind {
|
||||
UP_GTP_DROP,
|
||||
UP_GTP_U_TUNEND,
|
||||
UP_GTP_U_TUNMAP,
|
||||
};
|
||||
|
||||
struct up_gtp_action {
|
||||
struct llist_head entry;
|
||||
struct up_session *session;
|
||||
|
||||
uint16_t pdr_access;
|
||||
uint16_t pdr_core;
|
||||
|
||||
enum up_gtp_action_kind kind;
|
||||
union {
|
||||
/* En-/De-capsulate GTP: add/remove a GTP header and forward the GTP payload from/to plain IP. */
|
||||
struct upf_tunend tunend;
|
||||
|
||||
/* Tunnel-map GTP: translate from one TEID to another and forward */
|
||||
struct upf_tunmap tunmap;
|
||||
};
|
||||
|
||||
/* volatile loop variable to match up wanted and actually present GTP actions */
|
||||
void *handle;
|
||||
};
|
||||
|
||||
int up_gtp_action_cmp(const struct up_gtp_action *a, const struct up_gtp_action *b);
|
||||
|
||||
int up_gtp_action_enable(struct up_gtp_action *a);
|
||||
int up_gtp_action_disable(struct up_gtp_action *a);
|
||||
|
||||
int up_gtp_action_to_str_buf(char *buf, size_t buflen, const struct up_gtp_action *a);
|
||||
char *up_gtp_action_to_str_c(void *ctx, const struct up_gtp_action *a);
|
||||
79
include/osmocom/upf/up_peer.h
Normal file
79
include/osmocom/upf/up_peer.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/hashtable.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/use_count.h>
|
||||
|
||||
#include <osmocom/pfcp/pfcp_msg.h>
|
||||
|
||||
enum up_peer_event {
|
||||
UP_PEER_EV_RX_ASSOC_SETUP_REQ,
|
||||
UP_PEER_EV_RX_ASSOC_UPD_REQ,
|
||||
UP_PEER_EV_RX_ASSOC_REL_REQ,
|
||||
UP_PEER_EV_RX_SESSION_EST_REQ,
|
||||
UP_PEER_EV_HEARTBEAT_FAILURE,
|
||||
UP_PEER_EV_USE_COUNT_ZERO,
|
||||
UP_PEER_EV_SESSION_TERM,
|
||||
};
|
||||
|
||||
struct up_peer {
|
||||
struct llist_head entry;
|
||||
|
||||
struct osmo_fsm_inst *fi;
|
||||
/* backpointer */
|
||||
struct up_endpoint *up_endpoint;
|
||||
|
||||
/* peer's remote address */
|
||||
struct osmo_sockaddr remote_addr;
|
||||
struct osmo_pfcp_ie_node_id remote_node_id;
|
||||
uint32_t remote_recovery_timestamp;
|
||||
|
||||
struct osmo_pfcp_ie_up_function_features local_up_features;
|
||||
struct osmo_pfcp_ie_cp_function_features peer_cp_features;
|
||||
|
||||
uint32_t next_seq_nr;
|
||||
|
||||
struct osmo_fsm_inst *heartbeat_fi;
|
||||
|
||||
struct osmo_use_count use_count;
|
||||
struct osmo_use_count_entry use_count_buf[5];
|
||||
|
||||
DECLARE_HASHTABLE(sessions_by_up_seid, 6);
|
||||
DECLARE_HASHTABLE(sessions_by_cp_seid, 6);
|
||||
};
|
||||
|
||||
struct up_peer *up_peer_find_or_add(struct up_endpoint *up_ep, const struct osmo_sockaddr *remote_addr);
|
||||
struct up_peer *up_peer_find(struct up_endpoint *up_ep, const struct osmo_sockaddr *remote_addr);
|
||||
|
||||
void up_peer_set_msg_ctx(struct up_peer *peer, struct osmo_pfcp_msg *m);
|
||||
|
||||
char *up_peer_remote_addr_str(struct up_peer *peer);
|
||||
|
||||
struct osmo_pfcp_msg *up_peer_init_tx(struct up_peer *peer, struct osmo_pfcp_msg *in_reply_to,
|
||||
enum osmo_pfcp_message_type message_type);
|
||||
|
||||
void up_peer_free(struct up_peer *peer);
|
||||
115
include/osmocom/upf/up_session.h
Normal file
115
include/osmocom/upf/up_session.h
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <osmocom/core/hashtable.h>
|
||||
#include <osmocom/core/use_count.h>
|
||||
|
||||
#include <osmocom/pfcp/pfcp_msg.h>
|
||||
|
||||
struct osmo_fsm_inst;
|
||||
struct osmo_pfcp_msg;
|
||||
struct up_peer;
|
||||
|
||||
enum up_session_fsm_event {
|
||||
UP_SESSION_EV_RX_SESSION_EST_REQ,
|
||||
UP_SESSION_EV_RX_SESSION_MOD_REQ,
|
||||
UP_SESSION_EV_RX_SESSION_DEL_REQ,
|
||||
UP_SESSION_EV_USE_COUNT_ZERO,
|
||||
};
|
||||
|
||||
struct up_session {
|
||||
struct hlist_node node_by_up_seid;
|
||||
struct hlist_node node_by_cp_seid;
|
||||
|
||||
struct osmo_fsm_inst *fi;
|
||||
/* backpointer */
|
||||
struct up_peer *up_peer;
|
||||
|
||||
struct osmo_pfcp_ie_f_seid cp_f_seid;
|
||||
uint64_t up_seid;
|
||||
|
||||
struct osmo_use_count use_count;
|
||||
struct osmo_use_count_entry use_count_buf[8];
|
||||
|
||||
/* llist of struct pdr */
|
||||
struct llist_head pdrs;
|
||||
/* llist of struct far */
|
||||
struct llist_head fars;
|
||||
/* llist of struct chosen_f_teid */
|
||||
struct llist_head chosen_f_teids;
|
||||
|
||||
/* llist of struct up_gtp_action */
|
||||
struct llist_head active_gtp_actions;
|
||||
};
|
||||
|
||||
struct up_session *up_session_find_or_add(struct up_peer *peer, const struct osmo_pfcp_ie_f_seid *cp_f_seid);
|
||||
struct up_session *up_session_find_by_up_seid(struct up_peer *peer, uint64_t up_seid);
|
||||
struct up_session *up_session_find_by_cp_f_seid(struct up_peer *peer, const struct osmo_pfcp_ie_f_seid *cp_f_seid);
|
||||
struct up_session *up_session_find_by_local_teid(struct up_peer *peer, uint32_t teid);
|
||||
|
||||
void up_session_set_msg_ctx(struct up_session *session, struct osmo_pfcp_msg *m);
|
||||
|
||||
char *up_session_gtp_status(struct up_session *session);
|
||||
bool up_session_is_active(struct up_session *session);
|
||||
bool up_session_is_fully_active(struct up_session *session, int *active_p, int *inactive_p);
|
||||
|
||||
int up_session_discard(struct up_session *session);
|
||||
|
||||
int up_session_to_str_buf(char *buf, size_t buflen, struct up_session *session);
|
||||
char *up_session_to_str_c(void *ctx, struct up_session *session);
|
||||
|
||||
struct pdr {
|
||||
struct llist_head entry;
|
||||
struct up_session *session;
|
||||
|
||||
struct osmo_pfcp_ie_create_pdr desc;
|
||||
struct osmo_pfcp_ie_f_teid *local_f_teid;
|
||||
struct osmo_pfcp_ie_f_teid _local_f_teid_buf;
|
||||
|
||||
struct far *far;
|
||||
|
||||
bool rx_decaps;
|
||||
bool forw_encaps;
|
||||
bool access_to_core;
|
||||
bool core_to_access;
|
||||
|
||||
struct pdr *reverse_pdr;
|
||||
bool active;
|
||||
|
||||
char *inactive_reason;
|
||||
};
|
||||
|
||||
int pdr_to_str_buf(char *buf, size_t buflen, const struct pdr *pdr);
|
||||
char *pdr_to_str_c(void *ctx, const struct pdr *pdr);
|
||||
|
||||
struct far {
|
||||
struct llist_head entry;
|
||||
struct up_session *session;
|
||||
|
||||
struct osmo_pfcp_ie_create_far desc;
|
||||
bool active;
|
||||
};
|
||||
@@ -1,4 +1,3 @@
|
||||
/* Definitions for decoded message IEs, to be used by the auto-generated myproto_ies_auto.c. */
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
@@ -22,29 +21,3 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
enum myproto_msg_type {
|
||||
MYPROTO_MSGT_MOO = 1,
|
||||
};
|
||||
|
||||
extern const struct value_string myproto_msg_type_names[];
|
||||
|
||||
enum myproto_iei {
|
||||
MYPROTO_IEI_BAR = 1,
|
||||
};
|
||||
|
||||
enum myproto_iei_bar_inst {
|
||||
MYPROTO_IEI_BAR_ALPHA = 2,
|
||||
MYPROTO_IEI_BAR_BETA = 3,
|
||||
MYPROTO_IEI_BAR_GAMMA = 5,
|
||||
};
|
||||
|
||||
extern const struct value_string myproto_iei_names[];
|
||||
|
||||
struct myproto_ie_bar {
|
||||
int a;
|
||||
bool b;
|
||||
};
|
||||
@@ -24,12 +24,122 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/tdef.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
|
||||
struct osmo_tdef;
|
||||
struct ctrl_handle;
|
||||
|
||||
struct upf_gtp_dev;
|
||||
struct nft_ctx;
|
||||
|
||||
#define UPF_PFCP_LISTEN_DEFAULT "0.0.0.0"
|
||||
|
||||
#define PORT_GTP0_C 3386
|
||||
#define PORT_GTP0_U 3386
|
||||
|
||||
#define PORT_GTP1_C 2123
|
||||
#define PORT_GTP1_U 2152
|
||||
|
||||
extern struct osmo_tdef_group g_upf_tdef_groups[];
|
||||
extern struct osmo_tdef g_upf_nft_tdefs[];
|
||||
|
||||
struct pfcp_vty_cfg {
|
||||
char *local_addr;
|
||||
uint16_t local_port;
|
||||
};
|
||||
|
||||
struct tunend_vty_cfg_dev {
|
||||
struct llist_head entry;
|
||||
|
||||
/* If true, osmo-upf creates the GTP device on startup. If false, the GTP device was created by the user, and we
|
||||
* just plug into it. */
|
||||
bool create;
|
||||
|
||||
/* GTP device name as shown by 'ip link', e.g. 'apn0' */
|
||||
char *dev_name;
|
||||
|
||||
/* Which address the GTP device should listen on.
|
||||
* When create == true, the GTP kernel module is created to listen on this address.
|
||||
* Also used to listen for GTP ECHO requests using libgtp. */
|
||||
char *local_addr;
|
||||
};
|
||||
|
||||
struct tunend_vty_cfg {
|
||||
/* list of struct tunend_vty_cfg_dev, GTP devices as in the config file. The actual GTP devices in use are in
|
||||
* g_upf->tunend.devs. */
|
||||
struct llist_head devs;
|
||||
};
|
||||
|
||||
/* Item in an llist of string pointers */
|
||||
struct string_listitem {
|
||||
struct llist_head entry;
|
||||
char *str;
|
||||
};
|
||||
|
||||
struct g_upf {
|
||||
struct ctrl_handle *ctrl;
|
||||
|
||||
struct {
|
||||
struct pfcp_vty_cfg vty_cfg;
|
||||
struct up_endpoint *ep;
|
||||
} pfcp;
|
||||
|
||||
/* Tunnel encaps decaps via GTP kernel module */
|
||||
struct {
|
||||
/* if true, don't actually send commands to the GTP kernel module, just return success. */
|
||||
bool mockup;
|
||||
|
||||
/* GTP devices as in osmo-upf.cfg */
|
||||
struct tunend_vty_cfg vty_cfg;
|
||||
|
||||
/* GTP devices actually in use, list of struct upf_gtp_dev. */
|
||||
struct llist_head devs;
|
||||
|
||||
struct mnl_socket *nl;
|
||||
int32_t genl_id;
|
||||
|
||||
uint8_t recovery_count;
|
||||
} tunend;
|
||||
|
||||
/* Tunnel forwarding via linux netfilter */
|
||||
struct {
|
||||
/* if true, don't actually send commands to nftables, just return success. */
|
||||
bool mockup;
|
||||
|
||||
struct nft_ctx *nft_ctx;
|
||||
char *table_name;
|
||||
int priority_pre;
|
||||
int priority_post;
|
||||
uint32_t next_chain_id_state;
|
||||
} tunmap;
|
||||
|
||||
struct {
|
||||
uint32_t next_local_teid_state;
|
||||
} gtp;
|
||||
|
||||
struct llist_head netinst;
|
||||
};
|
||||
|
||||
extern struct g_upf *g_upf;
|
||||
|
||||
enum upf_log_subsys {
|
||||
DREF,
|
||||
DPEER,
|
||||
DSESSION,
|
||||
DGTP,
|
||||
DNFT,
|
||||
};
|
||||
|
||||
void g_upf_alloc(void *ctx);
|
||||
void upf_vty_init();
|
||||
int upf_pfcp_init(void);
|
||||
int upf_pfcp_listen(void);
|
||||
|
||||
int upf_gtp_devs_open();
|
||||
void upf_gtp_devs_close();
|
||||
|
||||
uint32_t upf_next_local_teid(void);
|
||||
uint32_t upf_next_chain_id(void);
|
||||
|
||||
84
include/osmocom/upf/upf_gtp.h
Normal file
84
include/osmocom/upf/upf_gtp.h
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
#include <osmocom/upf/upf_tun.h>
|
||||
|
||||
#define LOG_GTP_DEV(DEV, LEVEL, FMT, ARGS...) \
|
||||
LOGP(DGTP, LEVEL, "%s: " FMT, upf_gtp_dev_to_str_c(OTC_SELECT, (DEV)), ##ARGS)
|
||||
|
||||
struct upf_gtp_dev {
|
||||
struct llist_head entry;
|
||||
|
||||
/* If true, osmo-upf created this GTP device on startup and will destroy it on program exit. If false, the user
|
||||
* has created the device and osmo-upf will not destroy it. */
|
||||
bool created;
|
||||
|
||||
char *name;
|
||||
struct {
|
||||
bool enabled;
|
||||
struct osmo_sockaddr local_addr;
|
||||
struct osmo_fd ofd;
|
||||
} gtpv0;
|
||||
struct {
|
||||
struct osmo_sockaddr local_addr;
|
||||
struct osmo_fd ofd;
|
||||
} gtpv1;
|
||||
bool sgsn_mode;
|
||||
|
||||
uint32_t ifidx;
|
||||
|
||||
/* list of struct upf_gtp_tunend */
|
||||
struct llist_head tunnels;
|
||||
};
|
||||
|
||||
/* Description of a GTP encapsulation / decapsulation.
|
||||
* The active state to operate the GTP kernel module accordingly is kept in struct upf_gtp_tunend. */
|
||||
struct upf_tunend {
|
||||
struct upf_tun access;
|
||||
struct {
|
||||
struct osmo_sockaddr ue_local_addr;
|
||||
} core;
|
||||
};
|
||||
|
||||
int upf_gtp_tunend_cmp(const struct upf_tunend *a, const struct upf_tunend *b);
|
||||
|
||||
int upf_gtp_genl_ensure_open();
|
||||
void upf_gtp_genl_close();
|
||||
|
||||
int upf_gtp_dev_open(const char *name, bool create_gtp_dev, const char *local_addr, bool listen_for_gtpv0,
|
||||
bool sgsn_mode);
|
||||
struct upf_gtp_dev *upf_gtp_dev_find_by_name(const char *name);
|
||||
struct upf_gtp_dev *upf_gtp_dev_find_by_local_addr(const struct osmo_sockaddr *local_addr);
|
||||
struct upf_gtp_dev *upf_gtp_dev_first();
|
||||
|
||||
int upf_gtp_dev_tunend_add(struct upf_gtp_dev *dev, const struct upf_tunend *t);
|
||||
int upf_gtp_dev_tunend_del(struct upf_gtp_dev *dev, const struct upf_tunend *t);
|
||||
|
||||
int upf_gtp_dev_to_str_buf(char *buf, size_t buflen, const struct upf_gtp_dev *dev);
|
||||
char *upf_gtp_dev_to_str_c(void *ctx, const struct upf_gtp_dev *dev);
|
||||
4
include/osmocom/upf/upf_gtpu_echo.h
Normal file
4
include/osmocom/upf/upf_gtpu_echo.h
Normal file
@@ -0,0 +1,4 @@
|
||||
/* GTP-U ECHO implementation for osmo-upf */
|
||||
#pragma once
|
||||
|
||||
int upf_gtpu_echo_setup(struct upf_gtp_dev *dev);
|
||||
@@ -1,4 +1,3 @@
|
||||
/* Definitions for decoded message IEs, to be used by the auto-generated myproto_ies_auto.c. */
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
@@ -24,47 +23,29 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <stdint.h>
|
||||
|
||||
enum myproto_msg_type {
|
||||
MYPROTO_MSGT_MOO = 1,
|
||||
MYPROTO_MSGT_GOO = 7,
|
||||
#include <osmocom/upf/upf_tun.h>
|
||||
|
||||
struct upf_nft_tun {
|
||||
struct upf_tun tun;
|
||||
uint32_t chain_id;
|
||||
};
|
||||
|
||||
extern const struct value_string myproto_msg_type_names[];
|
||||
|
||||
enum myproto_iei {
|
||||
MYPROTO_IEI_FOO = 1,
|
||||
MYPROTO_IEI_BAR,
|
||||
MYPROTO_IEI_BAZ,
|
||||
MYPROTO_IEI_REPEAT_INT,
|
||||
MYPROTO_IEI_REPEAT_STRUCT,
|
||||
MYPROTO_IEI_MOO_NEST,
|
||||
MYPROTO_IEI_VAL,
|
||||
MYPROTO_IEI_GOO_NEST,
|
||||
struct upf_tunmap {
|
||||
struct upf_nft_tun access;
|
||||
struct upf_nft_tun core;
|
||||
};
|
||||
|
||||
extern const struct value_string myproto_iei_names[];
|
||||
int upf_nft_tunmap_to_str_buf(char *buf, size_t buflen, const struct upf_tunmap *tunmap);
|
||||
char *upf_nft_tunmap_to_str_c(void *ctx, const struct upf_tunmap *tunmap);
|
||||
|
||||
struct myproto_ie_bar {
|
||||
char str[23];
|
||||
};
|
||||
int upf_nft_init();
|
||||
int upf_nft_free();
|
||||
|
||||
struct myproto_ie_baz {
|
||||
int v_int;
|
||||
bool v_bool;
|
||||
};
|
||||
|
||||
enum myproto_repeat_enum {
|
||||
R_A,
|
||||
R_B,
|
||||
R_C,
|
||||
};
|
||||
|
||||
extern const struct value_string myproto_repeat_enum_names[];
|
||||
|
||||
struct myproto_ie_repeat_struct {
|
||||
int v_int;
|
||||
bool v_bool;
|
||||
enum myproto_repeat_enum v_enum;
|
||||
};
|
||||
char *upf_nft_tunmap_get_table_init_str(void *ctx);
|
||||
char *upf_nft_tunmap_get_vmap_init_str(void *ctx);
|
||||
char *upf_nft_tunmap_get_ruleset_str(void *ctx, struct upf_tunmap *tunmap);
|
||||
char *upf_nft_tunmap_get_ruleset_del_str(void *ctx, struct upf_tunmap *tunmap);
|
||||
int upf_nft_tunmap_create(struct upf_tunmap *tunmap);
|
||||
int upf_nft_tunmap_delete(struct upf_tunmap *tunmap);
|
||||
38
include/osmocom/upf/upf_tun.h
Normal file
38
include/osmocom/upf/upf_tun.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <osmocom/core/socket.h>
|
||||
|
||||
struct upf_tun_ep {
|
||||
struct osmo_sockaddr addr;
|
||||
uint32_t teid;
|
||||
};
|
||||
|
||||
struct upf_tun {
|
||||
struct upf_tun_ep local;
|
||||
struct upf_tun_ep remote;
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# (C) 2021-2022 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
|
||||
# (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@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
|
||||
@@ -15,13 +15,13 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
app_configs = {
|
||||
"osmo-upf": ["doc/examples/osmo-upf/osmo-upf.cfg"]
|
||||
"osmo-upf": ["doc/examples/osmo-upf/osmo-upf-mockup.cfg"]
|
||||
}
|
||||
|
||||
apps = [(4275, "src/osmo-upf/osmo-upf", "OsmoUPF", "osmo-upf")
|
||||
]
|
||||
|
||||
vty_command = ["./src/osmo-upf/osmo-upf", "-c",
|
||||
"doc/examples/osmo-upf/osmo-upf.cfg"]
|
||||
"doc/examples/osmo-upf/osmo-upf-mockup.cfg"]
|
||||
|
||||
vty_app = apps[0]
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
SUBDIRS = \
|
||||
libosmo-gtlv \
|
||||
libosmo-pfcp \
|
||||
osmo-upf \
|
||||
osmo-pfcp-tool \
|
||||
$(NULL)
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
AM_CPPFLAGS = \
|
||||
$(all_includes) \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(top_builddir) \
|
||||
-I$(builddir) \
|
||||
$(NULL)
|
||||
|
||||
AM_CFLAGS = \
|
||||
-Wall \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(COVERAGE_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
AM_LDFLAGS = \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(COVERAGE_LDFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
noinst_LIBRARIES = \
|
||||
libosmo-gtlv.a \
|
||||
$(NULL)
|
||||
|
||||
libosmo_gtlv_a_SOURCES = \
|
||||
gtlv.c \
|
||||
gtlv_dec_enc.c \
|
||||
gtlv_gen.c \
|
||||
$(NULL)
|
||||
@@ -1,342 +0,0 @@
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/bits.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/gtlv/gtlv.h>
|
||||
|
||||
int osmo_gtlv_tag_inst_cmp(const struct osmo_gtlv_tag_inst *a, const struct osmo_gtlv_tag_inst *b)
|
||||
{
|
||||
int cmp;
|
||||
if (a == b)
|
||||
return 0;
|
||||
if (!a)
|
||||
return -1;
|
||||
if (!b)
|
||||
return 1;
|
||||
cmp = OSMO_CMP(a->tag, b->tag);
|
||||
if (cmp)
|
||||
return cmp;
|
||||
cmp = OSMO_CMP(a->instance_present ? 1 : 0, b->instance_present ? 1 : 0);
|
||||
if (cmp)
|
||||
return cmp;
|
||||
if (a->instance_present)
|
||||
return OSMO_CMP(a->instance, b->instance);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_gtlv_tag_inst_to_str_buf(char *buf, size_t buflen, const struct osmo_gtlv_tag_inst *ti,
|
||||
const struct value_string *tag_names)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
if (!tag_names)
|
||||
OSMO_STRBUF_PRINTF(sb, "%u", ti->tag);
|
||||
else
|
||||
OSMO_STRBUF_PRINTF(sb, "%s", get_value_string(tag_names, ti->tag));
|
||||
if (ti->instance_present)
|
||||
OSMO_STRBUF_PRINTF(sb, "[%u]", ti->instance);
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
char *osmo_gtlv_tag_inst_to_str_c(void *ctx, const struct osmo_gtlv_tag_inst *ti,
|
||||
const struct value_string *tag_names)
|
||||
{
|
||||
OSMO_NAME_C_IMPL(ctx, 64, "ERROR", osmo_gtlv_tag_inst_to_str_buf, ti, tag_names)
|
||||
}
|
||||
|
||||
static int next_tl_valid(const struct osmo_gtlv_load *gtlv, const uint8_t **ie_start_p, size_t *buflen_left_p)
|
||||
{
|
||||
const uint8_t *ie_start;
|
||||
size_t buflen_left;
|
||||
|
||||
/* Start of next IE, or first IE for first invocation. */
|
||||
if (!gtlv->val)
|
||||
ie_start = gtlv->src.data;
|
||||
else
|
||||
ie_start = gtlv->val + gtlv->len;
|
||||
|
||||
/* Sanity */
|
||||
if (ie_start < gtlv->src.data || ie_start > gtlv->src.data + gtlv->src.len)
|
||||
return -ENOSPC;
|
||||
|
||||
buflen_left = gtlv->src.len - (ie_start - gtlv->src.data);
|
||||
|
||||
/* Too short for parsing an IE? Check also against integer overflow. */
|
||||
if (buflen_left && ((buflen_left < gtlv->cfg->tl_min_size) || (buflen_left > gtlv->src.len)))
|
||||
return -EBADMSG;
|
||||
|
||||
*ie_start_p = ie_start;
|
||||
*buflen_left_p = buflen_left;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return a TLV IE from a message buffer.
|
||||
*
|
||||
* Return the first or next TLV data found in the data buffer, based on the state of the gtlv parameter.
|
||||
* When gtlv->val is NULL, return the first IE in the data buffer.
|
||||
* Otherwise assume that gtlv points at a valid IE in the data structure, and return the subsequent IE.
|
||||
*
|
||||
* Usage example:
|
||||
*
|
||||
* struct osmo_gtlv gtlv = {
|
||||
* .cfg = osmo_t16l16v_cfg,
|
||||
* .src = { .data = msgb_l3(msg), .len = msgb_l3len(msg) },
|
||||
* };
|
||||
* for (;;) {
|
||||
* if (osmo_gtlv_next(>lv)) {
|
||||
* printf("Error\n");
|
||||
* break;
|
||||
* }
|
||||
* if (!gtlv.val) {
|
||||
* printf("End\n");
|
||||
* break;
|
||||
* }
|
||||
* printf("Tag %u: %zu octets: %s\n", gtlv.tag, gtlv.len, osmo_hexdump(gtlv.val, gtlv.len));
|
||||
* }
|
||||
*
|
||||
* \param[inout] gtlv Buffer to return the IE data, and state for TLV parsing position. gtlv->msg should indicate the
|
||||
* overall message buffer. The other gtlv members should be zero initialized before the first call, and
|
||||
* remain unchanged between invocations of this function.
|
||||
* \returns 0 on success, negative on TLV parsing error. The IE data is returned in gtlv->tag, gtlv->len and gtlv->val;
|
||||
* gtlv->val == NULL if no more IEs remain in the buffer.
|
||||
*/
|
||||
int osmo_gtlv_load_next(struct osmo_gtlv_load *gtlv)
|
||||
{
|
||||
const uint8_t *ie_start;
|
||||
const uint8_t *ie_end;
|
||||
size_t buflen_left;
|
||||
int rc;
|
||||
|
||||
rc = next_tl_valid(gtlv, &ie_start, &buflen_left);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* No more IEs? */
|
||||
if (!buflen_left) {
|
||||
gtlv->val = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Locate next IE */
|
||||
OSMO_ASSERT(gtlv->cfg->load_tl);
|
||||
gtlv->ti = (struct osmo_gtlv_tag_inst){};
|
||||
rc = gtlv->cfg->load_tl(gtlv, ie_start, buflen_left);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Sanity */
|
||||
ie_end = gtlv->val + gtlv->len;
|
||||
if (ie_end < gtlv->src.data || ie_end > gtlv->src.data + gtlv->src.len)
|
||||
return -EBADMSG;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return the tag of the IE that osmo_gtlv_next() would yield, do not change the gtlv state.
|
||||
*
|
||||
* \param[in] gtlv state for TLV parsing position; is not modified.
|
||||
* \param[out] tag the tag number on success, if NULL don't return the tag.
|
||||
* \param[out] instance the instance number or OSMO_GTLV_NO_INSTANCE if there is no instance value,
|
||||
* if NULL don't return the instance value.
|
||||
* \returns 0 on success, negative on TLV parsing error, -ENOENT when no more tags follow.
|
||||
*/
|
||||
int osmo_gtlv_load_peek_tag(const struct osmo_gtlv_load *gtlv, struct osmo_gtlv_tag_inst *ti)
|
||||
{
|
||||
const uint8_t *ie_start;
|
||||
size_t buflen_left;
|
||||
int rc;
|
||||
/* Guard against modification by load_tl(). */
|
||||
struct osmo_gtlv_load mtlv = *gtlv;
|
||||
mtlv.ti = (struct osmo_gtlv_tag_inst){};
|
||||
|
||||
rc = next_tl_valid(&mtlv, &ie_start, &buflen_left);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (!buflen_left)
|
||||
return -ENOENT;
|
||||
|
||||
/* Return next IE tag*/
|
||||
OSMO_ASSERT(mtlv.cfg->load_tl);
|
||||
rc = gtlv->cfg->load_tl(&mtlv, ie_start, buflen_left);
|
||||
if (rc)
|
||||
return -EBADMSG;
|
||||
if (ti)
|
||||
*ti = mtlv.ti;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Same as osmo_gtlv_load_next(), but skip any IEs until the given tag is reached. Change the gtlv state only when success
|
||||
* is returned.
|
||||
* \param[out] gtlv Return the next IE's TLV info.
|
||||
* \param[in] tag Tag value to match.
|
||||
* \param[in] instance Instance value to match; For IEs that have no instance value (no TLIV), pass
|
||||
* OSMO_GTLV_NO_INSTANCE.
|
||||
* \return 0 when the tag is found. Return -ENOENT when no such tag follows and keep the gtlv unchanged. */
|
||||
int osmo_gtlv_load_next_by_tag(struct osmo_gtlv_load *gtlv, unsigned int tag)
|
||||
{
|
||||
struct osmo_gtlv_tag_inst ti = { .tag = tag };
|
||||
return osmo_gtlv_load_next_by_tag_inst(gtlv, &ti);
|
||||
}
|
||||
|
||||
int osmo_gtlv_load_next_by_tag_inst(struct osmo_gtlv_load *gtlv, const struct osmo_gtlv_tag_inst *ti)
|
||||
{
|
||||
struct osmo_gtlv_load work = *gtlv;
|
||||
for (;;) {
|
||||
int rc = osmo_gtlv_load_next(&work);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (!work.val)
|
||||
return -ENOENT;
|
||||
if (!osmo_gtlv_tag_inst_cmp(&work.ti, ti)) {
|
||||
*gtlv = work;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Put tag header and length at the end of the msgb, according to gtlv->cfg->store_tl().
|
||||
* If the length is not known yet, it can be passed as 0 at first, and osmo_gtlv_put_update_tl() can determine the
|
||||
* resulting length after the value part was put into the msgb.
|
||||
*
|
||||
* Usage example:
|
||||
*
|
||||
* struct msgb *msg = msgb_alloc(1024, "foo"),
|
||||
* struct osmo_gtlv_put gtlv = {
|
||||
* .cfg = osmo_t16l16v_cfg,
|
||||
* .dst = msg,
|
||||
* }
|
||||
*
|
||||
* osmo_gtlv_put_tl(gtlv, 23, 0); // tag 23, length 0 = not known yet
|
||||
*
|
||||
* msgb_put(msg, 42);
|
||||
* ...
|
||||
* msgb_put(msg, 42);
|
||||
* ...
|
||||
* msgb_put(msg, 42);
|
||||
*
|
||||
* osmo_gtlv_put_update_tl(gtlv);
|
||||
*
|
||||
* Return 0 on success, -EINVAL if the tag value is invalid, -EMSGSIZE if len is too large.
|
||||
*/
|
||||
int osmo_gtlv_put_tl(struct osmo_gtlv_put *gtlv, unsigned int tag, size_t len)
|
||||
{
|
||||
struct osmo_gtlv_tag_inst ti = { .tag = tag };
|
||||
return osmo_gtlv_put_tli(gtlv, &ti, len);
|
||||
}
|
||||
|
||||
/* Put tag header, instance value and length at the end of the msgb, according to gtlv->cfg->store_tl().
|
||||
* This is the same as osmo_gtlv_put_tl(), only osmo_gtlv_put_tl() passes instance = 0.
|
||||
*/
|
||||
int osmo_gtlv_put_tli(struct osmo_gtlv_put *gtlv, const struct osmo_gtlv_tag_inst *ti, size_t len)
|
||||
{
|
||||
int rc;
|
||||
uint8_t *last_tl;
|
||||
OSMO_ASSERT(gtlv->cfg->store_tl);
|
||||
last_tl = gtlv->dst->tail;
|
||||
rc = gtlv->cfg->store_tl(gtlv->dst->tail, msgb_tailroom(gtlv->dst), ti, len, gtlv);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
if (rc > 0)
|
||||
msgb_put(gtlv->dst, rc);
|
||||
gtlv->last_ti = *ti;
|
||||
gtlv->last_tl = last_tl;
|
||||
gtlv->last_val = gtlv->dst->tail;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Update the length of the last put IE header (last call to osmo_gtlv_put_tl()) to match with the current
|
||||
* gtlv->dst->tail.
|
||||
* Return 0 on success, -EMSGSIZE if the amount of data written since osmo_gtlv_put_tl() is too large.
|
||||
*/
|
||||
int osmo_gtlv_put_update_tl(struct osmo_gtlv_put *gtlv)
|
||||
{
|
||||
size_t len = gtlv->dst->tail - gtlv->last_val;
|
||||
int rc = gtlv->cfg->store_tl(gtlv->last_tl, gtlv->last_val - gtlv->last_tl, >lv->last_ti, len, gtlv);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
/* In case the TL has changed in size, hopefully the implementation has moved the msgb data. Make sure last_val
|
||||
* points at the right place now. */
|
||||
gtlv->last_val = gtlv->last_tl + rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int t8l8v_load_tl(struct osmo_gtlv_load *gtlv, const uint8_t *src_data, size_t src_data_len)
|
||||
{
|
||||
/* already validated in next_tl_valid(): src_data_len >= cfg->tl_min_size == 2. */
|
||||
gtlv->ti.tag = src_data[0];
|
||||
gtlv->len = src_data[1];
|
||||
gtlv->val = src_data + 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int t8l8v_store_tl(uint8_t *dst_data, size_t dst_data_avail, const struct osmo_gtlv_tag_inst *ti, size_t len,
|
||||
struct osmo_gtlv_put *gtlv)
|
||||
{
|
||||
if (ti->tag > UINT8_MAX)
|
||||
return -EINVAL;
|
||||
if (len > UINT8_MAX)
|
||||
return -EMSGSIZE;
|
||||
if (dst_data_avail < 2)
|
||||
return -ENOSPC;
|
||||
dst_data[0] = ti->tag;
|
||||
dst_data[1] = len;
|
||||
return 2;
|
||||
}
|
||||
|
||||
const struct osmo_gtlv_cfg osmo_t8l8v_cfg = {
|
||||
.tl_min_size = 2,
|
||||
.load_tl = t8l8v_load_tl,
|
||||
.store_tl = t8l8v_store_tl,
|
||||
};
|
||||
|
||||
static int t16l16v_load_tl(struct osmo_gtlv_load *gtlv, const uint8_t *src_data, size_t src_data_len)
|
||||
{
|
||||
/* already validated in next_tl_valid(): src_data_len >= cfg->tl_min_size == 4. */
|
||||
gtlv->ti.tag = osmo_load16be(src_data);
|
||||
gtlv->len = osmo_load16be(src_data + 2);
|
||||
gtlv->val = src_data + 4;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int t16l16v_store_tl(uint8_t *dst_data, size_t dst_data_avail, const struct osmo_gtlv_tag_inst *ti, size_t len,
|
||||
struct osmo_gtlv_put *gtlv)
|
||||
{
|
||||
if (ti->tag > UINT16_MAX)
|
||||
return -EINVAL;
|
||||
if (len > UINT16_MAX)
|
||||
return -EMSGSIZE;
|
||||
if (dst_data_avail < 4)
|
||||
return -ENOSPC;
|
||||
osmo_store16be(ti->tag, dst_data);
|
||||
osmo_store16be(len, dst_data + 2);
|
||||
return 4;
|
||||
}
|
||||
|
||||
const struct osmo_gtlv_cfg osmo_t16l16v_cfg = {
|
||||
.tl_min_size = 4,
|
||||
.load_tl = t16l16v_load_tl,
|
||||
.store_tl = t16l16v_store_tl,
|
||||
};
|
||||
@@ -1,530 +0,0 @@
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <osmocom/gtlv/gtlv_dec_enc.h>
|
||||
|
||||
/* Reverse offsetof(): return the address of the struct member for a given osmo_gtlv_msg and member ofs_foo value. */
|
||||
#define MEMB(M, MEMB_OFS) ((void *)((char *)(M) + (MEMB_OFS)))
|
||||
|
||||
#define RETURN_ERROR(RC, TAG_INST, FMT, ARGS...) \
|
||||
do {\
|
||||
if (err_cb) { \
|
||||
if ((TAG_INST).instance_present) \
|
||||
err_cb(err_cb_data, (void *)decoded_struct, __FILE__, __LINE__, \
|
||||
"tag 0x%x = %s instance %u: " FMT " (%d: %s)\n", \
|
||||
(TAG_INST).tag, get_value_string(iei_strs, (TAG_INST).tag), \
|
||||
(TAG_INST).instance, ##ARGS, \
|
||||
RC, strerror((RC) > 0 ? (RC) : -(RC))); \
|
||||
else \
|
||||
err_cb(err_cb_data, (void *)decoded_struct, __FILE__, __LINE__, \
|
||||
"tag 0x%x = %s: " FMT " (%d: %s)\n", \
|
||||
(TAG_INST).tag, get_value_string(iei_strs, (TAG_INST).tag), ##ARGS, \
|
||||
RC, strerror((RC) > 0 ? (RC) : -(RC))); \
|
||||
} \
|
||||
return RC; \
|
||||
} while (0)
|
||||
|
||||
|
||||
/*! Decode a TLV structure from raw data to a decoded struct, for unordered TLV IEs.
|
||||
* How to decode IE values and where to place them in the decoded struct, is defined by ie_coding, an array terminated
|
||||
* by a '{}' entry.
|
||||
* The IEs may appear in any ordering in the TLV data.
|
||||
* For unordered decoding, only IEs with has_presence_flag == true or has_count == true may repeat. Other IE definitions
|
||||
* cause the last read TLV to overwrite all previous decodings, all into the first occurrence in ie_coding.
|
||||
* \param[out] decoded_struct Pointer to the struct to write parsed IE data to.
|
||||
* \param[in] obj_ofs Pass as zero. Used for nested IEs: offset added to decoded_struct to get to a sub-struct.
|
||||
* \param[in] gtlv TLV data to parse, as given in gtlv->msg.*. Must be ready for osmo_gtlv_load_start().
|
||||
* \param[in] ie_coding A list of permitted/expected IEI tags and instructions for decoding.
|
||||
* \param[in] err_cb Function to call to report an error message, or NULL.
|
||||
* \param[in] err_cb_data Caller supplied context to pass to the err_cb as 'data' argument.
|
||||
* \param[in] iei_strs value_string array to give IEI names in error messages passed to err_cb(), or NULL.
|
||||
* \return 0 on success, negative on error.
|
||||
*/
|
||||
static int osmo_gtlvs_decode_unordered(void *decoded_struct, unsigned int obj_ofs, struct osmo_gtlv_load *gtlv,
|
||||
const struct osmo_gtlv_coding *ie_coding,
|
||||
osmo_gtlv_err_cb err_cb, void *err_cb_data, const struct value_string *iei_strs)
|
||||
{
|
||||
void *obj = MEMB(decoded_struct, obj_ofs);
|
||||
const struct osmo_gtlv_coding *iec;
|
||||
unsigned int *multi_count_p;
|
||||
|
||||
/* To check for presence of mandatory IEs, need to keep a flag stack of seen ie_coding entries. This array has
|
||||
* to have at least the nr of entries that the ie_coding array has. Let's allow up to this many ie_coding
|
||||
* entries to avoid dynamic allocation. Seems like enough. */
|
||||
bool seen_ie_coding_entries[4096] = {};
|
||||
bool *seen_p;
|
||||
#define CHECK_SEEN(IEC) do { \
|
||||
unsigned int ie_coding_idx = (IEC) - ie_coding; \
|
||||
if (ie_coding_idx >= ARRAY_SIZE(seen_ie_coding_entries)) \
|
||||
RETURN_ERROR(-ENOTSUP, gtlv->ti, \
|
||||
"Too many IE definitions for decoding an unordered TLV structure"); \
|
||||
seen_p = &seen_ie_coding_entries[ie_coding_idx]; \
|
||||
} while (0)
|
||||
|
||||
|
||||
osmo_gtlv_load_start(gtlv);
|
||||
|
||||
/* IEs are allowed to come in any order. So traverse the TLV structure once, and find an IE parser for each (if
|
||||
* any). */
|
||||
for (;;) {
|
||||
int rc;
|
||||
bool *presence_flag_p;
|
||||
unsigned int memb_next_array_idx;
|
||||
unsigned int memb_ofs;
|
||||
unsigned int ie_max_allowed_count;
|
||||
|
||||
rc = osmo_gtlv_load_next(gtlv);
|
||||
if (rc)
|
||||
RETURN_ERROR(rc, gtlv->ti, "Decoding IEs failed on or after this tag");
|
||||
if (!gtlv->val) {
|
||||
/* End of the TLV structure */
|
||||
break;
|
||||
}
|
||||
|
||||
/* ie_max_allowed_count counts how often the same IEI may appear in a message until all struct members
|
||||
* that can store them are filled up. */
|
||||
ie_max_allowed_count = 0;
|
||||
|
||||
do {
|
||||
/* Find the IE coding for this tag */
|
||||
for (iec = ie_coding;
|
||||
!osmo_gtlv_coding_end(iec) && osmo_gtlv_tag_inst_cmp(&iec->ti, >lv->ti);
|
||||
iec++);
|
||||
/* No such IE coding found. */
|
||||
if (osmo_gtlv_coding_end(iec))
|
||||
break;
|
||||
|
||||
/* Keep track how often this tag can occur */
|
||||
ie_max_allowed_count += iec->has_count ? iec->count_max : 1;
|
||||
|
||||
/* Was this iec instance already decoded? Then skip to the next one, if any. */
|
||||
presence_flag_p = iec->has_presence_flag ? MEMB(obj, iec->presence_flag_ofs) : NULL;
|
||||
multi_count_p = iec->has_count ? MEMB(obj, iec->count_ofs) : NULL;
|
||||
if ((presence_flag_p && *presence_flag_p)
|
||||
|| (multi_count_p && *multi_count_p >= iec->count_max))
|
||||
continue;
|
||||
/* For IEs with a presence flag or a multi count, the decoded struct provides the information
|
||||
* whether the IE has already been decoded. Do the same for mandatory IEs, using local state in
|
||||
* seen_ie_coding_entries[]. */
|
||||
CHECK_SEEN(iec);
|
||||
if (*seen_p)
|
||||
continue;
|
||||
} while (0);
|
||||
if (osmo_gtlv_coding_end(iec)) {
|
||||
if (ie_max_allowed_count) {
|
||||
/* There have been IE definitions for this IEI, but all slots to decode it are already
|
||||
* filled. */
|
||||
RETURN_ERROR(-ENOTSUP, gtlv->ti, "Only %u instances of this IE are supported per message",
|
||||
ie_max_allowed_count);
|
||||
}
|
||||
/* No such IE defined in ie_coding, just skip the TLV. */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If this is a repeated IE, decode into the correct array index memb[idx],
|
||||
* next idx == (*multi_count_p). We've already guaranteed above that *multi_count_p < count_max. */
|
||||
memb_next_array_idx = multi_count_p ? *multi_count_p : 0;
|
||||
memb_ofs = iec->memb_ofs + memb_next_array_idx * iec->memb_array_pitch;
|
||||
|
||||
/* Decode IE value part */
|
||||
if (iec->nested_ies) {
|
||||
/* A nested IE: the value part of this TLV is in turn a TLV structure. Decode the inner
|
||||
* IEs. */
|
||||
struct osmo_gtlv_load inner_tlv = {
|
||||
.cfg = iec->nested_ies_cfg ? : gtlv->cfg,
|
||||
.src = {
|
||||
.data = gtlv->val,
|
||||
.len = gtlv->len,
|
||||
}
|
||||
};
|
||||
bool ordered;
|
||||
switch (iec->nested_ies_ordered) {
|
||||
case OSMO_GTLV_NESTED_IES_ORDERED:
|
||||
ordered = true;
|
||||
break;
|
||||
case OSMO_GTLV_NESTED_IES_ORDERING_SAME:
|
||||
case OSMO_GTLV_NESTED_IES_UNORDERED:
|
||||
ordered = false;
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
rc = osmo_gtlvs_decode(decoded_struct, obj_ofs + memb_ofs, &inner_tlv, ordered, iec->nested_ies,
|
||||
err_cb, err_cb_data, iei_strs);
|
||||
if (rc)
|
||||
RETURN_ERROR(rc, gtlv->ti, "Error while decoding TLV structure nested inside this IE");
|
||||
} else {
|
||||
/* Normal IE, decode the specific IE data. */
|
||||
if (!iec->dec_func)
|
||||
RETURN_ERROR(-EIO, gtlv->ti, "IE definition lacks a dec_func()");
|
||||
rc = iec->dec_func(decoded_struct, MEMB(obj, memb_ofs), gtlv);
|
||||
if (rc)
|
||||
RETURN_ERROR(rc, gtlv->ti, "Error while decoding this IE");
|
||||
}
|
||||
|
||||
if (multi_count_p) {
|
||||
/* A repeated IE, record that we've added one entry. This increments the foo_count value in the
|
||||
* decoded osmo_gtlv_msg.ies.*.
|
||||
* For example, multi_count_p points at osmo_gtlv_msg_session_est_req.create_pdr_count,
|
||||
* and memb_ofs points at osmo_gtlv_msg_session_est_req.create_pdr. */
|
||||
(*multi_count_p)++;
|
||||
}
|
||||
if (presence_flag_p) {
|
||||
*presence_flag_p = true;
|
||||
}
|
||||
CHECK_SEEN(iec);
|
||||
*seen_p = true;
|
||||
}
|
||||
|
||||
/* Check presence of mandatory IEs */
|
||||
for (iec = ie_coding; !osmo_gtlv_coding_end(iec); iec++) {
|
||||
if (iec->has_presence_flag)
|
||||
continue;
|
||||
multi_count_p = iec->has_count ? MEMB(obj, iec->count_ofs) : NULL;
|
||||
if (multi_count_p) {
|
||||
if (*multi_count_p < iec->count_mandatory)
|
||||
RETURN_ERROR(-EINVAL, iec->ti, "%u instances of this IE are mandatory, got %u",
|
||||
iec->count_mandatory, *multi_count_p);
|
||||
continue;
|
||||
}
|
||||
/* Neither an optional nor a multi member, hence it must be mandatory. */
|
||||
CHECK_SEEN(iec);
|
||||
if (!*seen_p)
|
||||
RETURN_ERROR(-EINVAL, iec->ti, "Missing mandatory IE");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Decode a TLV structure from raw data to a decoded struct, for ordered TLV IEs.
|
||||
* How to decode IE values and where to place them in the decoded struct, is defined by ie_coding, an array terminated
|
||||
* by a '{}' entry.
|
||||
* The IEs in the TLV structure must appear in the same order as they are defined in ie_coding.
|
||||
* cause the last read TLV to overwrite all previous decodings, all into the first occurrence in ie_coding.
|
||||
* \param[out] decoded_struct Pointer to the struct to write parsed IE data to.
|
||||
* \param[in] obj_ofs Pass as zero. Used for nested IEs: offset added to decoded_struct to get to a sub-struct.
|
||||
* \param[in] gtlv TLV data to parse, as given in gtlv->msg.*. Must be ready for osmo_gtlv_load_start().
|
||||
* \param[in] ie_coding A list of permitted/expected IEI tags and instructions for decoding.
|
||||
* \param[in] err_cb Function to call to report an error message, or NULL.
|
||||
* \param[in] err_cb_data Caller supplied context to pass to the err_cb as 'data' argument.
|
||||
* \param[in] iei_strs value_string array to give IEI names in error messages passed to err_cb(), or NULL.
|
||||
* \return 0 on success, negative on error.
|
||||
*/
|
||||
static int osmo_gtlvs_decode_ordered(void *decoded_struct, unsigned int obj_ofs, struct osmo_gtlv_load *gtlv,
|
||||
const struct osmo_gtlv_coding *ie_coding,
|
||||
osmo_gtlv_err_cb err_cb, void *err_cb_data, const struct value_string *iei_strs)
|
||||
{
|
||||
void *obj = MEMB(decoded_struct, obj_ofs);
|
||||
|
||||
osmo_gtlv_load_start(gtlv);
|
||||
|
||||
for (; !osmo_gtlv_coding_end(ie_coding); ie_coding++) {
|
||||
int rc;
|
||||
bool *presence_flag = ie_coding->has_presence_flag ? MEMB(obj, ie_coding->presence_flag_ofs) : NULL;
|
||||
unsigned int *multi_count = ie_coding->has_count ? MEMB(obj, ie_coding->count_ofs) : NULL;
|
||||
struct osmo_gtlv_tag_inst peek_ti;
|
||||
|
||||
rc = osmo_gtlv_load_next_by_tag_inst(gtlv, &ie_coding->ti);
|
||||
switch (rc) {
|
||||
case 0:
|
||||
break;
|
||||
case -ENOENT:
|
||||
if (!presence_flag && (!multi_count || *multi_count < ie_coding->count_mandatory))
|
||||
RETURN_ERROR(rc, ie_coding->ti, "Missing mandatory IE");
|
||||
if (presence_flag)
|
||||
*presence_flag = false;
|
||||
continue;
|
||||
default:
|
||||
RETURN_ERROR(rc, ie_coding->ti, "Error in TLV structure");
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
/* If this is a repeated IE, decode into the correct array index memb[idx],
|
||||
* next idx == (*multi_count) */
|
||||
unsigned int memb_next_array_idx = multi_count ? *multi_count : 0;
|
||||
unsigned int memb_ofs = ie_coding->memb_ofs + memb_next_array_idx * ie_coding->memb_array_pitch;
|
||||
|
||||
if (multi_count && memb_next_array_idx >= ie_coding->count_max)
|
||||
RETURN_ERROR(-ENOTSUP, ie_coding->ti, "Only %u instances of this IE are supported per message",
|
||||
ie_coding->count_max);
|
||||
|
||||
/* Decode IE value part */
|
||||
if (ie_coding->nested_ies) {
|
||||
/* A nested IE: the value part of this TLV is in turn a TLV structure. Decode the inner
|
||||
* IEs. */
|
||||
struct osmo_gtlv_load inner_tlv = {
|
||||
.cfg = ie_coding->nested_ies_cfg ? : gtlv->cfg,
|
||||
.src = {
|
||||
.data = gtlv->val,
|
||||
.len = gtlv->len,
|
||||
}
|
||||
};
|
||||
bool ordered;
|
||||
switch (ie_coding->nested_ies_ordered) {
|
||||
case OSMO_GTLV_NESTED_IES_ORDERING_SAME:
|
||||
case OSMO_GTLV_NESTED_IES_ORDERED:
|
||||
ordered = true;
|
||||
break;
|
||||
case OSMO_GTLV_NESTED_IES_UNORDERED:
|
||||
ordered = false;
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
rc = osmo_gtlvs_decode(decoded_struct, obj_ofs + memb_ofs, &inner_tlv, ordered,
|
||||
ie_coding->nested_ies, err_cb, err_cb_data, iei_strs);
|
||||
if (rc)
|
||||
RETURN_ERROR(rc, ie_coding->ti,
|
||||
"Error while decoding TLV structure nested inside this IE");
|
||||
} else {
|
||||
/* Normal IE, decode the specific IE data. */
|
||||
if (!ie_coding->dec_func)
|
||||
RETURN_ERROR(-EIO, ie_coding->ti, "IE definition lacks a dec_func()");
|
||||
rc = ie_coding->dec_func(decoded_struct, MEMB(obj, memb_ofs), gtlv);
|
||||
if (rc)
|
||||
RETURN_ERROR(rc, ie_coding->ti, "Error while decoding this IE");
|
||||
}
|
||||
|
||||
if (presence_flag)
|
||||
*presence_flag = true;
|
||||
|
||||
if (!multi_count) {
|
||||
/* Not a repeated IE. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* A repeated IE, record that we've added one entry. This increments the foo_count value in the
|
||||
* decoded osmo_pfcp_msg.ies.*.
|
||||
* For example, multi_count points at osmo_pfcp_msg_session_est_req.create_pdr_count,
|
||||
* and memb_ofs points at osmo_pfcp_msg_session_est_req.create_pdr. */
|
||||
(*multi_count)++;
|
||||
|
||||
/* Does another one of these IEs follow? */
|
||||
if (osmo_gtlv_load_peek_tag(gtlv, &peek_ti)
|
||||
|| osmo_gtlv_tag_inst_cmp(&peek_ti, >lv->ti)) {
|
||||
/* Next tag is a different IE, end the repetition. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* continue, parsing the next repetition of this tag. */
|
||||
rc = osmo_gtlv_load_next(gtlv);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
/* continue parsing the next tag. */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Decode an entire TLV message from raw data to decoded struct.
|
||||
* How to decode IE values and where to put them in the decoded struct is defined by ie_coding, an array terminated by
|
||||
* a '{}' entry.
|
||||
* \param[out] decoded_struct Pointer to the struct to write parsed IE data to.
|
||||
* \param[in] obj_ofs Pass as zero. Used for nested IEs: offset added to decoded_struct to get to a sub-struct.
|
||||
* \param[in] gtlv TLV data to parse, as given in gtlv->msg.*. Must be ready for osmo_gtlv_load_start().
|
||||
* \param[in] ie_coding A list of permitted/expected IEI tags and instructions for decoding.
|
||||
* \param[in] err_cb Function to call to report an error message, or NULL.
|
||||
* \param[in] err_cb_data Caller supplied context to pass to the err_cb as 'data' argument.
|
||||
* \param[in] iei_strs value_string array to give IEI names in error messages passed to err_cb(), or NULL.
|
||||
* \return 0 on success, negative on error.
|
||||
*/
|
||||
int osmo_gtlvs_decode(void *decoded_struct, unsigned int obj_ofs, struct osmo_gtlv_load *gtlv, bool tlv_ordered,
|
||||
const struct osmo_gtlv_coding *ie_coding,
|
||||
osmo_gtlv_err_cb err_cb, void *err_cb_data, const struct value_string *iei_strs)
|
||||
{
|
||||
if (!ie_coding)
|
||||
return -ENOTSUP;
|
||||
if (tlv_ordered)
|
||||
return osmo_gtlvs_decode_ordered(decoded_struct, obj_ofs, gtlv, ie_coding, err_cb, err_cb_data, iei_strs);
|
||||
else
|
||||
return osmo_gtlvs_decode_unordered(decoded_struct, obj_ofs, gtlv, ie_coding, err_cb, err_cb_data,
|
||||
iei_strs);
|
||||
}
|
||||
|
||||
/*! Encode a TLV structure from decoded struct to raw data.
|
||||
* How to encode IE values and where to read them in the decoded struct is defined by ie_coding, an array terminated by
|
||||
* a '{}' entry.
|
||||
* The IEs will be encoded in the order they appear in ie_coding.
|
||||
* \param[out] gtlv Write data using this TLV definition to gtlv->dst.
|
||||
* \param[in] decoded_struct C struct data to encode.
|
||||
* \param[in] obj_ofs Nesting offset, pass as 0.
|
||||
* \param[in] ie_coding A {} terminated list of IEI tags to encode (if present) and instructions for encoding.
|
||||
* \param[in] err_cb Function to call to report an error message, or NULL.
|
||||
* \param[in] err_cb_data Caller supplied context to pass to the err_cb as 'data' argument.
|
||||
* \param[in] iei_strs value_string array to give IEI names in error messages passed to err_cb(), or NULL.
|
||||
* \return 0 on success, negative on error.
|
||||
*/
|
||||
int osmo_gtlvs_encode(struct osmo_gtlv_put *gtlv, const void *decoded_struct, unsigned int obj_ofs,
|
||||
const struct osmo_gtlv_coding *ie_coding,
|
||||
osmo_gtlv_err_cb err_cb, void *err_cb_data, const struct value_string *iei_strs)
|
||||
{
|
||||
void *obj = MEMB(decoded_struct, obj_ofs);
|
||||
|
||||
if (!ie_coding)
|
||||
return -ENOTSUP;
|
||||
|
||||
for (; !osmo_gtlv_coding_end(ie_coding); ie_coding++) {
|
||||
int rc;
|
||||
bool *presence_flag_p = ie_coding->has_presence_flag ? MEMB(obj, ie_coding->presence_flag_ofs) : NULL;
|
||||
unsigned int *multi_count_p = ie_coding->has_count ? MEMB(obj, ie_coding->count_ofs) : NULL;
|
||||
unsigned int n;
|
||||
unsigned int i;
|
||||
|
||||
if (presence_flag_p && !*presence_flag_p)
|
||||
continue;
|
||||
|
||||
if (multi_count_p) {
|
||||
n = *multi_count_p;
|
||||
if (!ie_coding->memb_array_pitch)
|
||||
RETURN_ERROR(-EFAULT, ie_coding->ti,
|
||||
"Error in protocol definition: The ie_coding lacks a memb_array_pitch"
|
||||
" value, cannot be used as multi-IE\n");
|
||||
} else {
|
||||
n = 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
unsigned int memb_ofs;
|
||||
|
||||
osmo_gtlv_put_tli(gtlv, &ie_coding->ti, 0);
|
||||
|
||||
/* If this is a repeated IE, encode from the correct array index */
|
||||
if (multi_count_p && i >= ie_coding->count_max)
|
||||
RETURN_ERROR(-ENOTSUP, ie_coding->ti,
|
||||
"Only %u instances of this IE are supported per message", ie_coding->count_max);
|
||||
memb_ofs = ie_coding->memb_ofs + i * ie_coding->memb_array_pitch;
|
||||
|
||||
if (ie_coding->nested_ies) {
|
||||
struct osmo_gtlv_put nested_tlv = {
|
||||
.cfg = ie_coding->nested_ies_cfg ? : gtlv->cfg,
|
||||
.dst = gtlv->dst,
|
||||
};
|
||||
rc = osmo_gtlvs_encode(&nested_tlv, decoded_struct, obj_ofs + memb_ofs,
|
||||
ie_coding->nested_ies, err_cb, err_cb_data, iei_strs);
|
||||
if (rc)
|
||||
RETURN_ERROR(rc, ie_coding->ti,
|
||||
"Error while encoding TLV structure nested inside this IE");
|
||||
} else {
|
||||
rc = ie_coding->enc_func(gtlv, decoded_struct, MEMB(obj, memb_ofs));
|
||||
if (rc)
|
||||
RETURN_ERROR(rc, ie_coding->ti, "Error while encoding this IE");
|
||||
}
|
||||
|
||||
osmo_gtlv_put_update_tl(gtlv);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Compose a human readable string describing a decoded struct.
|
||||
* How to encode IE values and where to read them in the decoded struct is defined by ie_coding, an array terminated by
|
||||
* a '{}' entry.
|
||||
* The IEs will be encoded in the order they appear in ie_coding.
|
||||
* \param[out] buf Return the string in this buffer.
|
||||
* \param[in] buflen Size of buf.
|
||||
* \param[in] decoded_struct C struct data to encode.
|
||||
* \param[in] obj_ofs Nesting offset, pass as 0.
|
||||
* \param[in] ie_coding A {} terminated list of IEI tags to encode (if present) and instructions for encoding.
|
||||
* \param[in] iei_strs value_string array to give IEI names in tag headers, or NULL.
|
||||
* \return number of characters that would be written if the buffer is large enough, like snprintf().
|
||||
*/
|
||||
int osmo_gtlvs_encode_to_str_buf(char *buf, size_t buflen, const void *decoded_struct, unsigned int obj_ofs,
|
||||
const struct osmo_gtlv_coding *ie_coding, const struct value_string *iei_strs)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
|
||||
void *obj = MEMB(decoded_struct, obj_ofs);
|
||||
|
||||
if (!ie_coding)
|
||||
return -ENOTSUP;
|
||||
|
||||
for (; !osmo_gtlv_coding_end(ie_coding); ie_coding++) {
|
||||
bool *presence_flag_p = ie_coding->has_presence_flag ? MEMB(obj, ie_coding->presence_flag_ofs) : NULL;
|
||||
unsigned int *multi_count_p = ie_coding->has_count ? MEMB(obj, ie_coding->count_ofs) : NULL;
|
||||
unsigned int n;
|
||||
unsigned int i;
|
||||
|
||||
if (presence_flag_p && !*presence_flag_p)
|
||||
continue;
|
||||
|
||||
if (multi_count_p) {
|
||||
n = *multi_count_p;
|
||||
} else {
|
||||
n = 1;
|
||||
}
|
||||
|
||||
if (!n)
|
||||
continue;
|
||||
|
||||
OSMO_STRBUF_PRINTF(sb, " '%s'=", get_value_string(iei_strs, ie_coding->ti.tag));
|
||||
if (multi_count_p)
|
||||
OSMO_STRBUF_PRINTF(sb, "{ ");
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
unsigned int memb_ofs;
|
||||
|
||||
/* If this is a repeated IE, encode from the correct array index */
|
||||
if (multi_count_p && i >= ie_coding->count_max)
|
||||
return -ENOTSUP;
|
||||
if (i > 0)
|
||||
OSMO_STRBUF_PRINTF(sb, ", ");
|
||||
|
||||
memb_ofs = ie_coding->memb_ofs + i * ie_coding->memb_array_pitch;
|
||||
|
||||
if (ie_coding->nested_ies) {
|
||||
OSMO_STRBUF_PRINTF(sb, "{");
|
||||
OSMO_STRBUF_APPEND(sb, osmo_gtlvs_encode_to_str_buf, decoded_struct, obj_ofs + memb_ofs,
|
||||
ie_coding->nested_ies, iei_strs);
|
||||
OSMO_STRBUF_PRINTF(sb, " }");
|
||||
} else {
|
||||
if (ie_coding->enc_to_str_func)
|
||||
OSMO_STRBUF_APPEND(sb, ie_coding->enc_to_str_func, MEMB(obj, memb_ofs));
|
||||
else
|
||||
OSMO_STRBUF_PRINTF(sb, "(enc_to_str_func==NULL)");
|
||||
}
|
||||
}
|
||||
|
||||
if (multi_count_p)
|
||||
OSMO_STRBUF_PRINTF(sb, " }");
|
||||
}
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
/*! Compose a human readable string describing a decoded struct.
|
||||
* Like osmo_gtlvs_encode_to_str_buf() but returns a talloc allocated string.
|
||||
* \param[in] ctx talloc context to allocate from, e.g. OTC_SELECT.
|
||||
* \param[in] decoded_struct C struct data to encode.
|
||||
* \param[in] obj_ofs Nesting offset, pass as 0.
|
||||
* \param[in] ie_coding A {} terminated list of IEI tags to encode (if present) and instructions for encoding.
|
||||
* \param[in] iei_strs value_string array to give IEI names in tag headers, or NULL.
|
||||
* \return human readable string.
|
||||
*/
|
||||
char *osmo_gtlvs_encode_to_str_c(void *ctx, const void *decoded_struct, unsigned int obj_ofs,
|
||||
const struct osmo_gtlv_coding *ie_coding, const struct value_string *iei_strs)
|
||||
{
|
||||
OSMO_NAME_C_IMPL(ctx, 256, "ERROR", osmo_gtlvs_encode_to_str_buf, decoded_struct, obj_ofs, ie_coding, iei_strs)
|
||||
}
|
||||
@@ -1,420 +0,0 @@
|
||||
/* Write h and c source files for TLV protocol definitions, based on very sparse TLV definitions.
|
||||
* For a usage example see tests/libosmo-gtlv/test_gtlv_gen/. */
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <osmocom/gtlv/gtlv_gen.h>
|
||||
|
||||
static const struct osmo_gtlv_gen_cfg *g_cfg = NULL;
|
||||
|
||||
const struct osmo_gtlv_gen_ie osmo_gtlv_gen_ie_auto = {};
|
||||
|
||||
/* Helps avoid redundant definitions of the same type. */
|
||||
struct seen_entry {
|
||||
struct llist_head entry;
|
||||
char str[256];
|
||||
const void *from_def;
|
||||
};
|
||||
static LLIST_HEAD(seen_list);
|
||||
|
||||
static bool seen(const char *str, const void *from_def)
|
||||
{
|
||||
struct seen_entry *s;
|
||||
llist_for_each_entry(s, &seen_list, entry) {
|
||||
if (!strcmp(s->str, str)) {
|
||||
if (from_def != s->from_def) {
|
||||
fprintf(stderr, "ERROR: %s: multiple definitions use the same name: '%s'\n",
|
||||
g_cfg->proto_name, str);
|
||||
exit(1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
s = talloc_zero(NULL, struct seen_entry);
|
||||
OSMO_STRLCPY_ARRAY(s->str, str);
|
||||
s->from_def = from_def;
|
||||
llist_add(&s->entry, &seen_list);
|
||||
return false;
|
||||
}
|
||||
static void clear_seen()
|
||||
{
|
||||
struct seen_entry *s;
|
||||
while ((s = llist_first_entry_or_null(&seen_list, struct seen_entry, entry))) {
|
||||
llist_del(&s->entry);
|
||||
talloc_free(s);
|
||||
}
|
||||
}
|
||||
|
||||
/* Return "struct foo_ie_bar" from g_cfg->decoded_type_prefix and ie. */
|
||||
static inline const char *decoded_type(const struct osmo_gtlv_gen_ie_o *ie_o)
|
||||
{
|
||||
static char b[255];
|
||||
const struct osmo_gtlv_gen_ie *ie = ie_o->ie;
|
||||
const char *tag_name;
|
||||
if (ie && ie->decoded_type)
|
||||
return ie->decoded_type;
|
||||
/* "struct foo_ie_" + "bar" = struct foo_ie_bar*/
|
||||
tag_name = ie ? ie->tag_name : NULL;
|
||||
snprintf(b, sizeof(b), "%s%s", g_cfg->decoded_type_prefix, tag_name ? : ie_o->name);
|
||||
return b;
|
||||
}
|
||||
|
||||
/* --- .h file --- */
|
||||
|
||||
/* Write a listing of struct members like
|
||||
* bool foo_present;
|
||||
* int foo;
|
||||
* struct myproto_ie_bar bar;
|
||||
* struct abc abc[10];
|
||||
* int abc_count;
|
||||
*/
|
||||
static void write_ie_members(const struct osmo_gtlv_gen_ie_o ies[])
|
||||
{
|
||||
const struct osmo_gtlv_gen_ie_o *ie_o;
|
||||
for (ie_o = ies; ie_o->ie; ie_o++) {
|
||||
if (ie_o->optional)
|
||||
printf("\tbool %s_present;\n", ie_o->name);
|
||||
printf("\t%s %s", decoded_type(ie_o), ie_o->name);
|
||||
if (ie_o->multi) {
|
||||
printf("[%u];\n", ie_o->multi);
|
||||
printf("\tunsigned int %s_count", ie_o->name);
|
||||
}
|
||||
printf(";\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Traverse nesting levels in the message definitions and generate the structs for all as needed. */
|
||||
static void write_ie_auto_structs(const struct osmo_gtlv_gen_ie_o ies[])
|
||||
{
|
||||
const struct osmo_gtlv_gen_ie_o *ie_o;
|
||||
if (!ies)
|
||||
return;
|
||||
for (ie_o = ies; ie_o->ie; ie_o++) {
|
||||
const struct osmo_gtlv_gen_ie *ie = ie_o->ie;
|
||||
if (!ie || !ie->nested_ies)
|
||||
continue;
|
||||
/* Recurse to write inner layers first, so that they can be referenced in outer layers. */
|
||||
write_ie_auto_structs(ie->nested_ies);
|
||||
|
||||
/* Various IE definitions can use the same underlying type. Only generate each type once. */
|
||||
if (seen(decoded_type(ie_o), NULL))
|
||||
continue;
|
||||
|
||||
/* Print:
|
||||
*
|
||||
* \* spec ref *\
|
||||
* struct myproto_ie_goo {
|
||||
* bool foo_present;
|
||||
* int foo;
|
||||
* struct myproto_ie_bar bar;
|
||||
* struct abc abc[10];
|
||||
* int abc_count;
|
||||
* };
|
||||
*/
|
||||
printf("\n");
|
||||
if (ie->spec_ref)
|
||||
printf("/* %s%s */\n", g_cfg->spec_ref_prefix, ie->spec_ref);
|
||||
printf("%s {\n", decoded_type(ie_o));
|
||||
write_ie_members(ie->nested_ies);
|
||||
printf("};\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Write all auto-generated structs, starting with the outer message definitions and nesting into all contained IE
|
||||
* definitions. */
|
||||
static void write_auto_structs()
|
||||
{
|
||||
const struct osmo_gtlv_gen_msg *gen_msg;
|
||||
clear_seen();
|
||||
for (gen_msg = g_cfg->msg_defs; gen_msg->name; gen_msg++) {
|
||||
write_ie_auto_structs(gen_msg->ies);
|
||||
}
|
||||
}
|
||||
|
||||
/* Write the struct definitions for each message, i.e. for each entry in the outer PDU's message union, as well as the
|
||||
* union itself.
|
||||
*
|
||||
* struct myproto_msg_foo {
|
||||
* ...
|
||||
* }:
|
||||
* struct myproto_msg_goo {
|
||||
* ...
|
||||
* };
|
||||
* union myproto_ies {
|
||||
* myproto_msg_foo foo;
|
||||
* myproto_msg_goo goo;
|
||||
* };
|
||||
*/
|
||||
static void write_msg_union()
|
||||
{
|
||||
const struct osmo_gtlv_gen_msg *gen_msg;
|
||||
for (gen_msg = g_cfg->msg_defs; gen_msg->name; gen_msg++) {
|
||||
/* "struct foo_msg" + "_%s" { *
|
||||
* struct foo_msg_goo_request { ... }; */
|
||||
printf("\nstruct %s_msg_%s {\n",
|
||||
g_cfg->proto_name,
|
||||
gen_msg->name);
|
||||
write_ie_members(gen_msg->ies);
|
||||
printf("};\n");
|
||||
}
|
||||
|
||||
printf("\nunion %s_ies {\n", g_cfg->proto_name);
|
||||
for (gen_msg = g_cfg->msg_defs; gen_msg->name; gen_msg++) {
|
||||
printf("\tstruct %s_msg_%s %s;\n", g_cfg->proto_name,
|
||||
gen_msg->name, gen_msg->name);
|
||||
}
|
||||
printf("};\n");
|
||||
}
|
||||
|
||||
/* Write the C header, myproto_ies_auto.h */
|
||||
static void write_h()
|
||||
{
|
||||
printf("/* THIS FILE IS GENERATED FROM %s */\n", __FILE__);
|
||||
printf("#include <stdint.h>\n");
|
||||
printf("#include <osmocom/gtlv/gtlv_dec_enc.h>\n");
|
||||
if (g_cfg->h_header)
|
||||
printf("\n%s\n", g_cfg->h_header);
|
||||
write_auto_structs();
|
||||
write_msg_union();
|
||||
printf("\nconst struct osmo_gtlv_coding *%s_get_msg_coding(%s message_type);\n",
|
||||
g_cfg->proto_name, g_cfg->message_type_enum ? : "int");
|
||||
printf("\n"
|
||||
"int %s_ies_decode(union %s_ies *dst, struct osmo_gtlv_load *gtlv, bool tlv_ordered,\n"
|
||||
" %s message_type, osmo_gtlv_err_cb err_cb, void *err_cb_data, const struct value_string *iei_strs);\n",
|
||||
g_cfg->proto_name, g_cfg->proto_name, g_cfg->message_type_enum ? : "int");
|
||||
printf("\n"
|
||||
"int %s_ies_encode(struct osmo_gtlv_put *gtlv, const union %s_ies *src,\n"
|
||||
" %s message_type, osmo_gtlv_err_cb err_cb, void *err_cb_data, const struct value_string *iei_strs);\n",
|
||||
g_cfg->proto_name, g_cfg->proto_name, g_cfg->message_type_enum ? : "int");
|
||||
printf("\n"
|
||||
"int %s_ies_encode_to_str(char *buf, size_t buflen, const union %s_ies *src,\n"
|
||||
" %s message_type, const struct value_string *iei_strs);\n",
|
||||
g_cfg->proto_name, g_cfg->proto_name, g_cfg->message_type_enum ? : "int");
|
||||
}
|
||||
|
||||
/* --- .c file --- */
|
||||
|
||||
/* Write a listing of:
|
||||
* extern int myproto_dec_foo(...);
|
||||
* extern int myproto_enc_foo(...);
|
||||
*/
|
||||
static void write_extern_dec_enc(const struct osmo_gtlv_gen_ie_o *ies)
|
||||
{
|
||||
const struct osmo_gtlv_gen_ie_o *ie_o;
|
||||
for (ie_o = ies; ie_o->ie; ie_o++) {
|
||||
const struct osmo_gtlv_gen_ie *ie = ie_o->ie;
|
||||
const char *dec_enc = ie_o->name;
|
||||
if (ie)
|
||||
dec_enc = ie->dec_enc ? : (ie->tag_name ? : ie_o->name);
|
||||
if (ie && ie->nested_ies) {
|
||||
write_extern_dec_enc(ie->nested_ies);
|
||||
continue;
|
||||
}
|
||||
if (seen(dec_enc, NULL))
|
||||
continue;
|
||||
printf("extern int %s_dec_%s(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *gtlv);\n",
|
||||
g_cfg->proto_name, dec_enc);
|
||||
printf("extern int %s_enc_%s(struct osmo_gtlv_put *gtlv, const void *decoded_struct, const void *encode_from);\n",
|
||||
g_cfg->proto_name, dec_enc);
|
||||
if (g_cfg->add_enc_to_str)
|
||||
printf("extern int %s_enc_to_str_%s(char *buf, size_t buflen, const void *encode_from);\n",
|
||||
g_cfg->proto_name, dec_enc);
|
||||
}
|
||||
}
|
||||
|
||||
/* For a nested IE, write the struct osmo_gtlv_coding array of the inner IEs.
|
||||
* { { MYPROTO_IEI_BAR },
|
||||
* .memb_ofs = offsetof(struct myproto_foo, bar),
|
||||
* .dec_func = myproto_dec_bar,
|
||||
* .enc_func = myproto_enc_bar,
|
||||
* },
|
||||
*/
|
||||
static void write_ies_array(const char *indent, const struct osmo_gtlv_gen_ie_o *ies, const char *obj_type, const char *substruct)
|
||||
{
|
||||
#define printi(FMT, ARGS...) printf("%s" FMT, indent, ##ARGS)
|
||||
|
||||
const struct osmo_gtlv_gen_ie_o *ie_o;
|
||||
for (ie_o = ies; ie_o->ie; ie_o++) {
|
||||
const struct osmo_gtlv_gen_ie *ie = ie_o->ie;
|
||||
const char *tag_name = (ie && ie->tag_name) ? ie->tag_name : ie_o->name;
|
||||
printi("{ { %s%s", g_cfg->tag_prefix, osmo_str_toupper(tag_name));
|
||||
if (ie_o->instance)
|
||||
printf(", true, %s", ie_o->instance);
|
||||
printf(" },\n");
|
||||
printi(" .memb_ofs = offsetof(%s, %s%s),\n", obj_type, substruct, ie_o->name);
|
||||
if (ie->nested_ies) {
|
||||
printi(" .nested_ies = ies_in_%s,\n", tag_name);
|
||||
} else {
|
||||
const char *dec_enc = ie->dec_enc ? : (ie->tag_name ? : ie_o->name);
|
||||
printi(" .dec_func = %s_dec_%s,\n", g_cfg->proto_name, dec_enc);
|
||||
printi(" .enc_func = %s_enc_%s,\n", g_cfg->proto_name, dec_enc);
|
||||
if (g_cfg->add_enc_to_str)
|
||||
printi(" .enc_to_str_func = %s_enc_to_str_%s,\n", g_cfg->proto_name, dec_enc);
|
||||
}
|
||||
if (ie_o->multi) {
|
||||
printi(" .memb_array_pitch = OSMO_MEMB_ARRAY_PITCH(%s, %s%s),\n",
|
||||
obj_type, substruct, ie_o->name);
|
||||
printi(" .has_count = true, .count_max = %u,\n", ie_o->multi);
|
||||
printi(" .count_mandatory = %u,\n", ie_o->multi_mandatory);
|
||||
printi(" .count_ofs = offsetof(%s, %s%s_count),\n", obj_type, substruct, ie_o->name);
|
||||
}
|
||||
if (ie_o->optional) {
|
||||
printi(" .has_presence_flag = true,\n");
|
||||
printi(" .presence_flag_ofs = offsetof(%s, %s%s_present),\n", obj_type, substruct, ie_o->name);
|
||||
}
|
||||
printi("},\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* For a nested IE, write the struct osmo_gtlv_coding array of the inner IEs.
|
||||
* static const struct osmo_gtlv_coding ies_in_foo[] = {
|
||||
* { {MYPROTO_IEI_BAR},
|
||||
* .memb_ofs = offsetof(struct myproto_foo, bar),
|
||||
* .dec_func = myproto_dec_bar,
|
||||
* .enc_func = myproto_enc_bar,
|
||||
* },
|
||||
* ...
|
||||
* };
|
||||
*/
|
||||
static void write_nested_ies_array(const struct osmo_gtlv_gen_ie_o *ies)
|
||||
{
|
||||
const char *indent = "\t";
|
||||
const struct osmo_gtlv_gen_ie_o *ie_o;
|
||||
for (ie_o = ies; ie_o->ie; ie_o++) {
|
||||
const struct osmo_gtlv_gen_ie *ie = ie_o->ie;
|
||||
if (!ie || !ie->nested_ies)
|
||||
continue;
|
||||
write_nested_ies_array(ie->nested_ies);
|
||||
|
||||
const char *ies_in_name = ie->tag_name ? : ie_o->name;
|
||||
if (seen(ies_in_name, ie))
|
||||
continue;
|
||||
|
||||
printf("\nstatic const struct osmo_gtlv_coding ies_in_%s[] = {\n", ies_in_name);
|
||||
write_ies_array(indent, ie->nested_ies, decoded_type(ie_o), "");
|
||||
printi("{}\n");
|
||||
printf("};\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Write the bulk of the C code: on the basis of the list of messages (g_cfg->msg_defs), write all dec/enc function
|
||||
* declarations, all IEs arrays as well as the list of message types, first triggering to write the C code for any inner
|
||||
* layers. */
|
||||
static void write_c()
|
||||
{
|
||||
const struct osmo_gtlv_gen_msg *gen_msg;
|
||||
|
||||
printf("/* THIS FILE IS GENERATED FROM %s */\n", __FILE__);
|
||||
printf("#include <stddef.h>\n");
|
||||
printf("#include <errno.h>\n");
|
||||
printf("#include <osmocom/core/utils.h>\n");
|
||||
printf("#include <osmocom/gtlv/gtlv.h>\n");
|
||||
printf("#include <osmocom/gtlv/gtlv_dec_enc.h>\n");
|
||||
printf("#include <osmocom/gtlv/gtlv_gen.h>\n");
|
||||
if (g_cfg->c_header)
|
||||
printf("\n%s\n", g_cfg->c_header);
|
||||
|
||||
printf("\n");
|
||||
clear_seen();
|
||||
for (gen_msg = g_cfg->msg_defs; gen_msg->name; gen_msg++) {
|
||||
write_extern_dec_enc(gen_msg->ies);
|
||||
}
|
||||
|
||||
clear_seen();
|
||||
for (gen_msg = g_cfg->msg_defs; gen_msg->name; gen_msg++) {
|
||||
write_nested_ies_array(gen_msg->ies);
|
||||
}
|
||||
|
||||
for (gen_msg = g_cfg->msg_defs; gen_msg->name; gen_msg++) {
|
||||
char *obj_type = talloc_asprintf(NULL, "union %s_ies", g_cfg->proto_name);
|
||||
char *substruct = talloc_asprintf(NULL, "%s.", gen_msg->name);
|
||||
printf("\nstatic const struct osmo_gtlv_coding ies_in_msg_%s[] = {\n", gen_msg->name);
|
||||
write_ies_array("\t", gen_msg->ies, obj_type, substruct);
|
||||
printf("\t{}\n};\n");
|
||||
talloc_free(substruct);
|
||||
talloc_free(obj_type);
|
||||
}
|
||||
printf("\nstatic const struct osmo_gtlv_coding *msg_defs[] = {\n");
|
||||
for (gen_msg = g_cfg->msg_defs; gen_msg->name; gen_msg++) {
|
||||
printf("\t[%s%s] = ies_in_msg_%s,\n", g_cfg->message_type_prefix, osmo_str_toupper(gen_msg->name), gen_msg->name);
|
||||
}
|
||||
printf("};\n");
|
||||
|
||||
/* print this code snippet into the .c file, because only there can we do ARRAY_SIZE(foo_msg_coding). */
|
||||
printf("\n"
|
||||
"const struct osmo_gtlv_coding *%s_get_msg_coding(%s message_type)\n"
|
||||
"{\n"
|
||||
" if (message_type >= ARRAY_SIZE(msg_defs))\n"
|
||||
" return NULL;\n"
|
||||
" return msg_defs[message_type];\n"
|
||||
"}\n",
|
||||
g_cfg->proto_name, g_cfg->message_type_enum ? : "int");
|
||||
|
||||
printf("\n"
|
||||
"int %s_ies_decode(union %s_ies *dst, struct osmo_gtlv_load *gtlv, bool tlv_ordered,\n"
|
||||
" %s message_type,\n"
|
||||
" osmo_gtlv_err_cb err_cb, void *err_cb_data, const struct value_string *iei_strs)\n"
|
||||
"{\n"
|
||||
" return osmo_gtlvs_decode(dst, 0, gtlv, tlv_ordered, %s_get_msg_coding(message_type), err_cb, err_cb_data, iei_strs);\n"
|
||||
"}\n",
|
||||
g_cfg->proto_name, g_cfg->proto_name, g_cfg->message_type_enum ? : "int", g_cfg->proto_name);
|
||||
printf("\n"
|
||||
"int %s_ies_encode(struct osmo_gtlv_put *gtlv, const union %s_ies *src,\n"
|
||||
" %s message_type, osmo_gtlv_err_cb err_cb, void *err_cb_data, const struct value_string *iei_strs)\n"
|
||||
"{\n"
|
||||
" return osmo_gtlvs_encode(gtlv, src, 0, %s_get_msg_coding(message_type), err_cb, err_cb_data, iei_strs);\n"
|
||||
"}\n",
|
||||
g_cfg->proto_name, g_cfg->proto_name, g_cfg->message_type_enum ? : "int", g_cfg->proto_name);
|
||||
printf("\n"
|
||||
"int %s_ies_encode_to_str(char *buf, size_t buflen, const union %s_ies *src,\n"
|
||||
" %s message_type, const struct value_string *iei_strs)\n"
|
||||
"{\n"
|
||||
" return osmo_gtlvs_encode_to_str_buf(buf, buflen, src, 0, %s_get_msg_coding(message_type), iei_strs);\n"
|
||||
"}\n",
|
||||
g_cfg->proto_name, g_cfg->proto_name, g_cfg->message_type_enum ? : "int", g_cfg->proto_name);
|
||||
}
|
||||
|
||||
/* Call this from your main(). */
|
||||
int osmo_gtlv_gen_main(const struct osmo_gtlv_gen_cfg *cfg, int argc, const char **argv)
|
||||
{
|
||||
if (argc < 2)
|
||||
return 1;
|
||||
|
||||
g_cfg = cfg;
|
||||
|
||||
if (strcmp(argv[1], "h") == 0)
|
||||
write_h();
|
||||
else if (strcmp(argv[1], "c") == 0)
|
||||
write_c();
|
||||
else
|
||||
return 1;
|
||||
|
||||
clear_seen();
|
||||
return 0;
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
AM_CPPFLAGS = \
|
||||
$(all_includes) \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(top_builddir)/include \
|
||||
-I$(top_builddir) \
|
||||
-I$(builddir) \
|
||||
$(NULL)
|
||||
|
||||
AM_CFLAGS = \
|
||||
-Wall \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(LIBOSMOVTY_CFLAGS) \
|
||||
$(COVERAGE_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
AM_LDFLAGS = \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(COVERAGE_LDFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
noinst_LIBRARIES = \
|
||||
libosmo-pfcp.a \
|
||||
$(NULL)
|
||||
|
||||
libosmo_pfcp_a_SOURCES = \
|
||||
pfcp_ies_custom.c \
|
||||
pfcp_strs.c \
|
||||
\
|
||||
pfcp_ies_auto.c \
|
||||
$(NULL)
|
||||
|
||||
BUILT_SOURCES = \
|
||||
pfcp_ies_auto.c \
|
||||
$(NULL)
|
||||
|
||||
CLEANFILES = \
|
||||
pfcp_ies_auto.c \
|
||||
$(NULL)
|
||||
|
||||
pfcp_ies_auto.c: $(srcdir)/gen__pfcp_ies_auto.c \
|
||||
$(top_srcdir)/src/libosmo-gtlv/gtlv_gen.c \
|
||||
$(top_srcdir)/include/osmocom/gtlv/gtlv_gen.h
|
||||
$(MAKE) -C $(top_builddir)/src/libosmo-gtlv
|
||||
$(MAKE) gen__pfcp_ies_auto
|
||||
$(builddir)/gen__pfcp_ies_auto c > $(builddir)/pfcp_ies_auto.c
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
gen__pfcp_ies_auto \
|
||||
$(NULL)
|
||||
|
||||
gen__pfcp_ies_auto_SOURCES = \
|
||||
gen__pfcp_ies_auto.c \
|
||||
$(NULL)
|
||||
|
||||
gen__pfcp_ies_auto_LDADD = \
|
||||
$(top_builddir)/src/libosmo-gtlv/libosmo-gtlv.a \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(COVERAGE_LDFLAGS) \
|
||||
$(NULL)
|
||||
@@ -1,365 +0,0 @@
|
||||
/* Tool to generate C source code of structs and IE arrays for de- and encoding PFCP messages. */
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/gtlv/gtlv_gen.h>
|
||||
|
||||
#define O OSMO_GTLV_GEN_O
|
||||
#define M OSMO_GTLV_GEN_M
|
||||
#define O_MULTI OSMO_GTLV_GEN_O_MULTI
|
||||
#define M_MULTI OSMO_GTLV_GEN_M_MULTI
|
||||
#define ALL_FROM_NAME osmo_gtlv_gen_ie_auto
|
||||
|
||||
#define Ms(MEMB_NAME) M(MEMB_NAME, #MEMB_NAME)
|
||||
#define Os(MEMB_NAME) O(MEMB_NAME, #MEMB_NAME)
|
||||
#define O_MULTIs(N, MEMB_NAME) O_MULTI(N, MEMB_NAME, #MEMB_NAME)
|
||||
#define M_MULTIs(N, M, MEMB_NAME) M_MULTI(N, M, MEMB_NAME, #MEMB_NAME)
|
||||
|
||||
static const struct osmo_gtlv_gen_ie recovery_time_stamp = {
|
||||
"uint32_t",
|
||||
.dec_enc = "32be",
|
||||
.spec_ref = "7.4.2",
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie cause = {
|
||||
"enum osmo_pfcp_cause",
|
||||
.spec_ref = "8.2.1",
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie offending_ie = {
|
||||
.decoded_type = "enum osmo_pfcp_iei",
|
||||
.spec_ref = "8.2.22",
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie f_seid = {
|
||||
.tag_name = "f_seid",
|
||||
.spec_ref = "8.2.37",
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie pdr_id = {
|
||||
.decoded_type = "uint16_t",
|
||||
.dec_enc = "16be",
|
||||
.spec_ref = "8.2.36",
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie precedence = {
|
||||
.decoded_type = "uint32_t",
|
||||
.dec_enc = "32be",
|
||||
.spec_ref = "8.2.11",
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie source_iface = {
|
||||
.decoded_type = "enum osmo_pfcp_source_iface",
|
||||
.spec_ref = "8.2.2",
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie f_teid = {
|
||||
.tag_name = "f_teid",
|
||||
.spec_ref = "8.2.3",
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie traffic_endpoint_id = {
|
||||
.tag_name = "traffic_endpoint_id",
|
||||
.decoded_type = "uint8_t",
|
||||
.dec_enc = "8",
|
||||
.spec_ref = "8.2.92",
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie iface_type = {
|
||||
.decoded_type = "enum osmo_pfcp_3gpp_iface_type",
|
||||
.tag_name = "3gpp_iface_type",
|
||||
.spec_ref = "8.2.118",
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie_o ies_in_pdi[] = {
|
||||
Ms(source_iface),
|
||||
O(f_teid, "local_f_teid"),
|
||||
O(ALL_FROM_NAME, "ue_ip_address"),
|
||||
Os(traffic_endpoint_id),
|
||||
O(iface_type, "source_iface_type"),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie pdi = {
|
||||
.nested_ies = ies_in_pdi,
|
||||
.spec_ref = "7.5.2.2-2",
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie far_id = {
|
||||
.decoded_type = "uint32_t",
|
||||
.dec_enc = "32be",
|
||||
.spec_ref = "8.2.74",
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie_o ies_in_create_pdr[] = {
|
||||
Ms(pdr_id),
|
||||
Ms(precedence),
|
||||
Ms(pdi),
|
||||
O(ALL_FROM_NAME, "outer_header_removal"),
|
||||
Os(far_id),
|
||||
O(ALL_FROM_NAME, "activate_predefined_rules"),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie create_pdr = {
|
||||
.nested_ies = ies_in_create_pdr,
|
||||
.spec_ref = "7.5.2.2",
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie_o ies_in_created_pdr[] = {
|
||||
Ms(pdr_id),
|
||||
O(f_teid, "local_f_teid"),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie created_pdr = {
|
||||
.nested_ies = ies_in_created_pdr,
|
||||
.spec_ref = "7.5.3.2",
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie_o ies_in_upd_pdr[] = {
|
||||
Ms(pdr_id),
|
||||
O(ALL_FROM_NAME, "outer_header_removal"),
|
||||
Os(pdi),
|
||||
Os(far_id),
|
||||
O(ALL_FROM_NAME, "activate_predefined_rules"),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie upd_pdr = {
|
||||
.nested_ies = ies_in_upd_pdr,
|
||||
.spec_ref = "7.5.4.2",
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie_o ies_in_updated_pdr[] = {
|
||||
Ms(pdr_id),
|
||||
O(f_teid, "local_f_teid"),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie updated_pdr = {
|
||||
.nested_ies = ies_in_updated_pdr,
|
||||
.spec_ref = "7.5.9.3",
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie_o ies_in_remove_pdr[] = {
|
||||
Ms(pdr_id),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie remove_pdr = {
|
||||
.nested_ies = ies_in_remove_pdr,
|
||||
.spec_ref = "7.5.4.6",
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie destination_iface = {
|
||||
.decoded_type = "enum osmo_pfcp_dest_iface",
|
||||
.dec_enc = "dest_iface",
|
||||
.spec_ref = "8.2.24",
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie_o ies_in_forw_params[] = {
|
||||
Ms(destination_iface),
|
||||
O(ALL_FROM_NAME, "network_inst"),
|
||||
O(ALL_FROM_NAME, "outer_header_creation"),
|
||||
O(traffic_endpoint_id, "linked_te_id"),
|
||||
O(iface_type, "destination_iface_type"),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie forw_params = {
|
||||
.nested_ies = ies_in_forw_params,
|
||||
.spec_ref = "7.5.2.3-2",
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie_o ies_in_upd_forw_params[] = {
|
||||
Os(destination_iface),
|
||||
O(ALL_FROM_NAME, "network_inst"),
|
||||
O(ALL_FROM_NAME, "outer_header_creation"),
|
||||
O(traffic_endpoint_id, "linked_te_id"),
|
||||
O(iface_type, "destination_iface_type"),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie upd_forw_params = {
|
||||
.nested_ies = ies_in_upd_forw_params,
|
||||
.spec_ref = "7.5.4.3-2",
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie_o ies_in_create_far[] = {
|
||||
Ms(far_id),
|
||||
M(ALL_FROM_NAME, "apply_action"),
|
||||
Os(forw_params),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie create_far = {
|
||||
.nested_ies = ies_in_create_far,
|
||||
.spec_ref = "7.5.2.3",
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie_o ies_in_remove_far[] = {
|
||||
Ms(far_id),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie remove_far = {
|
||||
.nested_ies = ies_in_remove_far,
|
||||
.spec_ref = "7.5.4.6",
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie_o ies_in_upd_far[] = {
|
||||
Ms(far_id),
|
||||
O(ALL_FROM_NAME, "apply_action"),
|
||||
Os(upd_forw_params),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie upd_far = {
|
||||
.nested_ies = ies_in_upd_far,
|
||||
.spec_ref = "7.5.4.3",
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie_o ies_in_msg_heartbeat_req[] = {
|
||||
Ms(recovery_time_stamp),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie_o ies_in_msg_heartbeat_resp[] = {
|
||||
Ms(recovery_time_stamp),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie_o ies_in_msg_assoc_setup_req[] = {
|
||||
M(ALL_FROM_NAME, "node_id"),
|
||||
Ms(recovery_time_stamp),
|
||||
O(ALL_FROM_NAME, "up_function_features"),
|
||||
O(ALL_FROM_NAME, "cp_function_features"),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie_o ies_in_msg_assoc_setup_resp[] = {
|
||||
M(ALL_FROM_NAME, "node_id"),
|
||||
Ms(cause),
|
||||
Ms(recovery_time_stamp),
|
||||
O(ALL_FROM_NAME, "up_function_features"),
|
||||
O(ALL_FROM_NAME, "cp_function_features"),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie_o ies_in_msg_assoc_release_req[] = {
|
||||
M(ALL_FROM_NAME, "node_id"),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie_o ies_in_msg_assoc_release_resp[] = {
|
||||
M(ALL_FROM_NAME, "node_id"),
|
||||
Ms(cause),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie_o ies_in_msg_session_est_req[] = {
|
||||
M(ALL_FROM_NAME, "node_id"),
|
||||
O(f_seid, "cp_f_seid"),
|
||||
M_MULTIs(32, 1, create_pdr),
|
||||
M_MULTI(32, 1, create_far, "create_far"),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie_o ies_in_msg_session_est_resp[] = {
|
||||
M(ALL_FROM_NAME, "node_id"),
|
||||
Ms(cause),
|
||||
Os(offending_ie),
|
||||
O(f_seid, "up_f_seid"),
|
||||
O_MULTIs(32, created_pdr),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie_o ies_in_msg_session_mod_req[] = {
|
||||
O(f_seid, "cp_f_seid"),
|
||||
O_MULTIs(32, remove_pdr),
|
||||
O_MULTIs(32, remove_far),
|
||||
O_MULTIs(32, create_pdr),
|
||||
O_MULTIs(32, create_far),
|
||||
O_MULTIs(32, upd_pdr),
|
||||
O_MULTIs(32, upd_far),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie_o ies_in_msg_session_mod_resp[] = {
|
||||
Ms(cause),
|
||||
Os(offending_ie),
|
||||
O_MULTIs(32, created_pdr),
|
||||
O_MULTIs(32, updated_pdr),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie_o ies_in_msg_session_del_req[] = {
|
||||
/* no IEs */
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie_o ies_in_msg_session_del_resp[] = {
|
||||
Ms(cause),
|
||||
{}
|
||||
};
|
||||
|
||||
#define MSG(NAME) { #NAME, ies_in_msg_##NAME }
|
||||
static const struct osmo_gtlv_gen_msg pfcp_msg_defs[] = {
|
||||
MSG(heartbeat_req),
|
||||
MSG(heartbeat_resp),
|
||||
MSG(assoc_setup_req),
|
||||
MSG(assoc_setup_resp),
|
||||
MSG(assoc_release_req),
|
||||
MSG(assoc_release_resp),
|
||||
MSG(session_est_req),
|
||||
MSG(session_est_resp),
|
||||
MSG(session_mod_req),
|
||||
MSG(session_mod_resp),
|
||||
MSG(session_del_req),
|
||||
MSG(session_del_resp),
|
||||
{}
|
||||
};
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
struct osmo_gtlv_gen_cfg cfg = {
|
||||
.proto_name = "osmo_pfcp",
|
||||
.spec_ref_prefix = "3GPP TS 29.244 ",
|
||||
.message_type_enum = "enum osmo_pfcp_message_type",
|
||||
.message_type_prefix = "OSMO_PFCP_MSGT_",
|
||||
.tag_enum = "enum osmo_pfcp_iei",
|
||||
.tag_prefix = "OSMO_PFCP_IEI_",
|
||||
.decoded_type_prefix = "struct osmo_pfcp_ie_",
|
||||
.h_header = "#include <osmocom/pfcp/pfcp_ies_custom.h>",
|
||||
.c_header = "#include <osmocom/pfcp/pfcp_ies_auto.h>",
|
||||
.msg_defs = pfcp_msg_defs,
|
||||
.add_enc_to_str = true,
|
||||
};
|
||||
return osmo_gtlv_gen_main(&cfg, argc, argv);
|
||||
}
|
||||
@@ -1,989 +0,0 @@
|
||||
/* Decoded PFCP IEs, to be used by the auto-generated pfcp_ies_auto.c. */
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
|
||||
#include <osmocom/gtlv/gtlv.h>
|
||||
|
||||
#include <osmocom/pfcp/pfcp_ies_custom.h>
|
||||
#include <osmocom/pfcp/pfcp_strs.h>
|
||||
|
||||
/* Assumes presence of local variable osmo_pfcp_msg *m. m->log_ctx may be NULL. */
|
||||
#define RETURN_ERROR(RC, FMT, ARGS...) \
|
||||
do {\
|
||||
OSMO_ASSERT(decoded_struct); \
|
||||
LOGP(DLPFCP, LOGL_ERROR, FMT " (%d: %s)\n", ##ARGS, RC, \
|
||||
strerror((RC) > 0 ? (RC) : -(RC))); \
|
||||
return RC; \
|
||||
} while (0)
|
||||
|
||||
/* Assumes presence of local variable osmo_gtlv_load *tlv. Usage:
|
||||
* ENSURE_LENGTH_IS_EXACTLY(2);
|
||||
*/
|
||||
#define ENSURE_LENGTH_IS_EXACTLY(VAL) \
|
||||
do { \
|
||||
if (!(tlv->len == VAL)) \
|
||||
RETURN_ERROR(-EINVAL, "IE has length = %zu, expected length == " #VAL, tlv->len); \
|
||||
} while (0)
|
||||
|
||||
/* Assumes presence of local variable osmo_gtlv_load *tlv. Usage:
|
||||
* ENSURE_LENGTH_IS_AT_LEAST(1);
|
||||
*/
|
||||
#define ENSURE_LENGTH_IS_AT_LEAST(VAL) \
|
||||
do { \
|
||||
if (!(tlv->len >= VAL)) \
|
||||
RETURN_ERROR(-EINVAL, "IE has length = %zu, expected length >= " #VAL, tlv->len); \
|
||||
} while (0)
|
||||
|
||||
/* Assumes presence of local variable osmo_gtlv_load *tlv. Usage:
|
||||
* const uint8_t *pos = tlv->val;
|
||||
* ENSURE_REMAINING_LENGTH_IS_AT_LEAST("first part", pos, 23);
|
||||
* <parse first part>
|
||||
* pos += 23;
|
||||
* ENSURE_REMAINING_LENGTH_IS_AT_LEAST("very long part", pos, 235);
|
||||
* <parse very long part>
|
||||
* pos += 235;
|
||||
*/
|
||||
#define ENSURE_REMAINING_LENGTH_IS_AT_LEAST(NAME, POS, MIN_VAL) \
|
||||
do { \
|
||||
if (!((tlv->len - ((POS) - tlv->val)) >= MIN_VAL)) \
|
||||
RETURN_ERROR(-EINVAL, \
|
||||
"at value octet %d: %zu octets remaining, but " #NAME " requires length >= " #MIN_VAL, \
|
||||
(int)((POS) - tlv->val), \
|
||||
tlv->len - ((POS) - tlv->val)); \
|
||||
} while (0)
|
||||
|
||||
void osmo_pfcp_ie_f_seid_set(struct osmo_pfcp_ie_f_seid *f_seid, uint64_t seid, const struct osmo_sockaddr *remote_addr)
|
||||
{
|
||||
*f_seid = (struct osmo_pfcp_ie_f_seid) {
|
||||
.seid = seid,
|
||||
};
|
||||
osmo_pfcp_ip_addrs_set(&f_seid->ip_addr, remote_addr);
|
||||
}
|
||||
|
||||
int osmo_pfcp_dec_cause(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
||||
{
|
||||
enum osmo_pfcp_cause *cause = decode_to;
|
||||
ENSURE_LENGTH_IS_EXACTLY(1);
|
||||
*cause = *tlv->val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_cause(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
||||
{
|
||||
const enum osmo_pfcp_cause *cause = encode_from;
|
||||
msgb_put_u8(tlv->dst, *cause);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_to_str_cause(char *buf, size_t buflen, const void *encode_from)
|
||||
{
|
||||
const enum osmo_pfcp_cause *cause = encode_from;
|
||||
return snprintf(buf, buflen, "%s", osmo_pfcp_cause_str(*cause));
|
||||
}
|
||||
|
||||
int osmo_pfcp_dec_offending_ie(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
||||
{
|
||||
enum osmo_pfcp_iei *offending_ie = decode_to;
|
||||
ENSURE_LENGTH_IS_EXACTLY(2);
|
||||
*offending_ie = osmo_load16be(tlv->val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_offending_ie(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
||||
{
|
||||
const enum osmo_pfcp_iei *offending_ie = encode_from;
|
||||
msgb_put_u16(tlv->dst, *offending_ie);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_to_str_offending_ie(char *buf, size_t buflen, const void *encode_from)
|
||||
{
|
||||
const enum osmo_pfcp_iei *offending_ie = encode_from;
|
||||
return snprintf(buf, buflen, "%s", osmo_pfcp_iei_str(*offending_ie));
|
||||
}
|
||||
|
||||
int osmo_pfcp_dec_8(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
||||
{
|
||||
uint8_t *u8 = decode_to;
|
||||
ENSURE_LENGTH_IS_AT_LEAST(1);
|
||||
*u8 = tlv->val[0];
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_8(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
||||
{
|
||||
const uint8_t *u8 = encode_from;
|
||||
msgb_put_u8(tlv->dst, *u8);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_to_str_8(char *buf, size_t buflen, const void *encode_from)
|
||||
{
|
||||
const uint8_t *u8 = encode_from;
|
||||
return snprintf(buf, buflen, "%u", *u8);
|
||||
}
|
||||
|
||||
int osmo_pfcp_dec_16be(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
||||
{
|
||||
uint16_t *u16 = decode_to;
|
||||
ENSURE_LENGTH_IS_AT_LEAST(2);
|
||||
*u16 = osmo_load16be(tlv->val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_16be(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
||||
{
|
||||
const uint16_t *u16 = encode_from;
|
||||
msgb_put_u16(tlv->dst, *u16);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_to_str_16be(char *buf, size_t buflen, const void *encode_from)
|
||||
{
|
||||
const uint16_t *u16 = encode_from;
|
||||
return snprintf(buf, buflen, "%u", *u16);
|
||||
}
|
||||
|
||||
int osmo_pfcp_dec_32be(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
||||
{
|
||||
uint32_t *u32 = decode_to;
|
||||
ENSURE_LENGTH_IS_AT_LEAST(4);
|
||||
*u32 = osmo_load32be(tlv->val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_32be(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
||||
{
|
||||
const uint32_t *u32 = encode_from;
|
||||
msgb_put_u32(tlv->dst, *u32);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_to_str_32be(char *buf, size_t buflen, const void *encode_from)
|
||||
{
|
||||
const uint32_t *u32 = encode_from;
|
||||
return snprintf(buf, buflen, "%u", *u32);
|
||||
}
|
||||
|
||||
int osmo_pfcp_dec_3gpp_iface_type(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
||||
{
|
||||
enum osmo_pfcp_3gpp_iface_type *_3gpp_iface_type = decode_to;
|
||||
ENSURE_LENGTH_IS_AT_LEAST(1);
|
||||
*_3gpp_iface_type = tlv->val[0] & 0x3f;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_3gpp_iface_type(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
||||
{
|
||||
const enum osmo_pfcp_3gpp_iface_type *_3gpp_iface_type = encode_from;
|
||||
msgb_put_u8(tlv->dst, (uint8_t)(*_3gpp_iface_type) & 0x3f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_to_str_3gpp_iface_type(char *buf, size_t buflen, const void *encode_from)
|
||||
{
|
||||
const enum osmo_pfcp_3gpp_iface_type *_3gpp_iface_type = encode_from;
|
||||
return snprintf(buf, buflen, "%s", osmo_pfcp_3gpp_iface_type_str(*_3gpp_iface_type));
|
||||
}
|
||||
|
||||
int osmo_pfcp_dec_source_iface(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
||||
{
|
||||
enum osmo_pfcp_source_iface *source_iface = decode_to;
|
||||
ENSURE_LENGTH_IS_AT_LEAST(1);
|
||||
*source_iface = tlv->val[0] & 0xf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_source_iface(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
||||
{
|
||||
const enum osmo_pfcp_source_iface *source_iface = encode_from;
|
||||
msgb_put_u8(tlv->dst, (uint8_t)(*source_iface) & 0xf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_to_str_source_iface(char *buf, size_t buflen, const void *encode_from)
|
||||
{
|
||||
const enum osmo_pfcp_source_iface *source_iface = encode_from;
|
||||
return snprintf(buf, buflen, "%s", osmo_pfcp_source_iface_str(*source_iface));
|
||||
}
|
||||
|
||||
int osmo_pfcp_dec_dest_iface(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
||||
{
|
||||
enum osmo_pfcp_dest_iface *dest_interface = decode_to;
|
||||
ENSURE_LENGTH_IS_AT_LEAST(1);
|
||||
*dest_interface = tlv->val[0] & 0xf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_dest_iface(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
||||
{
|
||||
const enum osmo_pfcp_dest_iface *dest_interface = encode_from;
|
||||
msgb_put_u8(tlv->dst, (uint8_t)(*dest_interface) & 0xf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_to_str_dest_iface(char *buf, size_t buflen, const void *encode_from)
|
||||
{
|
||||
const enum osmo_pfcp_dest_iface *dest_iface = encode_from;
|
||||
return snprintf(buf, buflen, "%s", osmo_pfcp_dest_iface_str(*dest_iface));
|
||||
}
|
||||
|
||||
int osmo_pfcp_dec_node_id(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
||||
{
|
||||
struct osmo_pfcp_ie_node_id *node_id = decode_to;
|
||||
const void *ip;
|
||||
unsigned int ip_len;
|
||||
unsigned int want_len;
|
||||
ENSURE_LENGTH_IS_AT_LEAST(1);
|
||||
node_id->type = *(uint8_t *)tlv->val;
|
||||
ip = &tlv->val[1];
|
||||
ip_len = tlv->len - 1;
|
||||
|
||||
switch (node_id->type) {
|
||||
case OSMO_PFCP_NODE_ID_T_IPV4:
|
||||
want_len = sizeof(node_id->ip.u.sin.sin_addr);
|
||||
if (ip_len != want_len)
|
||||
RETURN_ERROR(-EINVAL, "Node ID: wrong IPv4 address value length %u, expected %u",
|
||||
ip_len, want_len);
|
||||
osmo_sockaddr_from_octets(&node_id->ip, ip, ip_len);
|
||||
break;
|
||||
case OSMO_PFCP_NODE_ID_T_IPV6:
|
||||
want_len = sizeof(node_id->ip.u.sin6.sin6_addr);
|
||||
if (ip_len != want_len)
|
||||
RETURN_ERROR(-EINVAL, "Node ID: wrong IPv6 address value length %u, expected %u",
|
||||
ip_len, want_len);
|
||||
osmo_sockaddr_from_octets(&node_id->ip, ip, ip_len);
|
||||
break;
|
||||
case OSMO_PFCP_NODE_ID_T_FQDN:
|
||||
/* Copy and add a trailing nul */
|
||||
OSMO_STRLCPY_ARRAY(node_id->fqdn, ip);
|
||||
break;
|
||||
default:
|
||||
RETURN_ERROR(-EINVAL, "Invalid Node ID Type: %d", node_id->type);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_node_id(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
||||
{
|
||||
unsigned int l;
|
||||
const struct osmo_pfcp_ie_node_id *node_id = encode_from;
|
||||
msgb_put_u8(tlv->dst, node_id->type);
|
||||
switch (node_id->type) {
|
||||
case OSMO_PFCP_NODE_ID_T_IPV4:
|
||||
l = sizeof(node_id->ip.u.sin.sin_addr);
|
||||
osmo_sockaddr_to_octets(msgb_put(tlv->dst, l), l, &node_id->ip);
|
||||
break;
|
||||
case OSMO_PFCP_NODE_ID_T_IPV6:
|
||||
l = sizeof(node_id->ip.u.sin6.sin6_addr);
|
||||
osmo_sockaddr_to_octets(msgb_put(tlv->dst, l), l, &node_id->ip);
|
||||
break;
|
||||
case OSMO_PFCP_NODE_ID_T_FQDN:
|
||||
l = strnlen(node_id->fqdn, sizeof(node_id->fqdn));
|
||||
/* Copy without trailing nul */
|
||||
memcpy((char *)msgb_put(tlv->dst, l), node_id->fqdn, l);
|
||||
break;
|
||||
default:
|
||||
RETURN_ERROR(-EINVAL, "Invalid Node ID Type: %d", node_id->type);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_to_str_node_id(char *buf, size_t buflen, const void *encode_from)
|
||||
{
|
||||
const struct osmo_pfcp_ie_node_id *node_id = encode_from;
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
|
||||
switch (node_id->type) {
|
||||
case OSMO_PFCP_NODE_ID_T_IPV4:
|
||||
OSMO_STRBUF_PRINTF(sb, "v4:");
|
||||
break;
|
||||
case OSMO_PFCP_NODE_ID_T_IPV6:
|
||||
OSMO_STRBUF_PRINTF(sb, "v6:");
|
||||
break;
|
||||
case OSMO_PFCP_NODE_ID_T_FQDN:
|
||||
OSMO_STRBUF_PRINTF(sb, "fqdn:");
|
||||
OSMO_STRBUF_APPEND(sb, osmo_quote_str_buf3,
|
||||
node_id->fqdn, strnlen(node_id->fqdn, sizeof(node_id->fqdn)));
|
||||
return sb.chars_needed;
|
||||
default:
|
||||
OSMO_STRBUF_PRINTF(sb, "unknown-node-id-type-%u", node_id->type);
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &node_id->ip);
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
bool osmo_pfcp_bits_get(const uint8_t *bits, unsigned int bitpos)
|
||||
{
|
||||
unsigned int bytenum = bitpos / 8;
|
||||
unsigned int bitmask = 1 << (bitpos % 8);
|
||||
|
||||
return (bool)(bits[bytenum] & bitmask);
|
||||
}
|
||||
|
||||
void osmo_pfcp_bits_set(uint8_t *bits, unsigned int bitpos, bool val)
|
||||
{
|
||||
unsigned int bytenum = bitpos / 8;
|
||||
unsigned int bitmask = 1 << (bitpos % 8);
|
||||
|
||||
if (val)
|
||||
bits[bytenum] |= bitmask;
|
||||
else
|
||||
bits[bytenum] &= ~bitmask;
|
||||
}
|
||||
|
||||
int osmo_pfcp_bits_to_str_buf(char *buf, size_t buflen, const uint8_t *bits, const struct value_string *bit_strs)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
OSMO_STRBUF_PRINTF(sb, "(");
|
||||
for (; bit_strs->str; bit_strs++) {
|
||||
if (osmo_pfcp_bits_get(bits, bit_strs->value)) {
|
||||
OSMO_STRBUF_PRINTF(sb, " %s", bit_strs->str);
|
||||
}
|
||||
}
|
||||
OSMO_STRBUF_PRINTF(sb, " )");
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
char *osmo_pfcp_bits_to_str_c(void *ctx, const uint8_t *bits, const struct value_string *bit_str)
|
||||
{
|
||||
OSMO_NAME_C_IMPL(ctx, 64, "ERROR", osmo_pfcp_bits_to_str_buf, bits, bit_str)
|
||||
}
|
||||
|
||||
int osmo_pfcp_dec_up_function_features(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
||||
{
|
||||
struct osmo_pfcp_ie_up_function_features *up_function_features = decode_to;
|
||||
ENSURE_LENGTH_IS_AT_LEAST(6);
|
||||
memcpy(up_function_features->bits, tlv->val, 6);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_up_function_features(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
||||
{
|
||||
const struct osmo_pfcp_ie_up_function_features *up_function_features = encode_from;
|
||||
memcpy(msgb_put(tlv->dst, 6), up_function_features->bits, 6);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_to_str_up_function_features(char *buf, size_t buflen, const void *encode_from)
|
||||
{
|
||||
const struct osmo_pfcp_ie_up_function_features *up_function_features = encode_from;
|
||||
return osmo_pfcp_bits_to_str_buf(buf, buflen, up_function_features->bits, osmo_pfcp_up_feature_strs);
|
||||
}
|
||||
|
||||
int osmo_pfcp_dec_cp_function_features(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
||||
{
|
||||
struct osmo_pfcp_ie_cp_function_features *cp_function_features = decode_to;
|
||||
ENSURE_LENGTH_IS_AT_LEAST(sizeof(cp_function_features->bits));
|
||||
memcpy(cp_function_features->bits, tlv->val, sizeof(cp_function_features->bits));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_cp_function_features(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
||||
{
|
||||
const struct osmo_pfcp_ie_cp_function_features *cp_function_features = encode_from;
|
||||
memcpy(msgb_put(tlv->dst, sizeof(cp_function_features->bits)),
|
||||
cp_function_features->bits, sizeof(cp_function_features->bits));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_to_str_cp_function_features(char *buf, size_t buflen, const void *encode_from)
|
||||
{
|
||||
const struct osmo_pfcp_ie_cp_function_features *cp_function_features = encode_from;
|
||||
return osmo_pfcp_bits_to_str_buf(buf, buflen, cp_function_features->bits, osmo_pfcp_cp_feature_strs);
|
||||
}
|
||||
|
||||
int osmo_pfcp_dec_f_seid(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
||||
{
|
||||
struct osmo_pfcp_ie_f_seid *f_seid = decode_to;
|
||||
uint8_t flags;
|
||||
uint8_t pos;
|
||||
unsigned int l;
|
||||
/* flags and 8 octet seid */
|
||||
ENSURE_LENGTH_IS_AT_LEAST(9);
|
||||
flags = tlv->val[0];
|
||||
f_seid->ip_addr.v6_present = flags & 1;
|
||||
f_seid->ip_addr.v4_present = flags & 2;
|
||||
f_seid->seid = osmo_load64be(&tlv->val[1]);
|
||||
pos = 9;
|
||||
if (f_seid->ip_addr.v4_present) {
|
||||
l = sizeof(f_seid->ip_addr.v4.u.sin.sin_addr);
|
||||
if (pos + l > tlv->len)
|
||||
RETURN_ERROR(-EINVAL, "F-SEID IE is too short for the IPv4 address: %zu", tlv->len);
|
||||
osmo_sockaddr_from_octets(&f_seid->ip_addr.v4, &tlv->val[pos], l);
|
||||
pos += l;
|
||||
}
|
||||
if (f_seid->ip_addr.v6_present) {
|
||||
l = sizeof(f_seid->ip_addr.v4.u.sin6.sin6_addr);
|
||||
if (pos + l > tlv->len)
|
||||
RETURN_ERROR(-EINVAL, "F-SEID IE is too short for the IPv6 address: %zu", tlv->len);
|
||||
osmo_sockaddr_from_octets(&f_seid->ip_addr.v6, &tlv->val[pos], l);
|
||||
pos += l;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_f_seid(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
||||
{
|
||||
const struct osmo_pfcp_ie_f_seid *f_seid = encode_from;
|
||||
unsigned int l;
|
||||
uint8_t flags = (f_seid->ip_addr.v6_present ? 1 : 0) + (f_seid->ip_addr.v4_present ? 2 : 0);
|
||||
/* flags and 8 octet seid */
|
||||
msgb_put_u8(tlv->dst, flags);
|
||||
osmo_store64be(f_seid->seid, msgb_put(tlv->dst, 8));
|
||||
|
||||
if (f_seid->ip_addr.v4_present) {
|
||||
if (f_seid->ip_addr.v4.u.sin.sin_family != AF_INET)
|
||||
RETURN_ERROR(-EINVAL,
|
||||
"f_seid IE indicates IPv4 address, but there is no ipv4_addr");
|
||||
l = sizeof(f_seid->ip_addr.v4.u.sin.sin_addr);
|
||||
osmo_sockaddr_to_octets(msgb_put(tlv->dst, l), l, &f_seid->ip_addr.v4);
|
||||
}
|
||||
if (f_seid->ip_addr.v6_present) {
|
||||
if (f_seid->ip_addr.v6.u.sin6.sin6_family != AF_INET6)
|
||||
RETURN_ERROR(-EINVAL,
|
||||
"f_seid IE indicates IPv6 address, but there is no ipv6_addr");
|
||||
l = sizeof(f_seid->ip_addr.v6.u.sin6.sin6_addr);
|
||||
osmo_sockaddr_to_octets(msgb_put(tlv->dst, l), l, &f_seid->ip_addr.v6);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ip_addrs_to_str_buf(char *buf, size_t buflen, const struct osmo_pfcp_ip_addrs *addrs)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
if (addrs->v4_present) {
|
||||
OSMO_STRBUF_PRINTF(sb, ",v4:");
|
||||
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &addrs->v4);
|
||||
}
|
||||
if (addrs->v6_present) {
|
||||
OSMO_STRBUF_PRINTF(sb, ",v6:");
|
||||
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &addrs->v6);
|
||||
}
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_to_str_f_seid(char *buf, size_t buflen, const void *encode_from)
|
||||
{
|
||||
const struct osmo_pfcp_ie_f_seid *f_seid = encode_from;
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
OSMO_STRBUF_PRINTF(sb, "0x%"PRIx64, f_seid->seid);
|
||||
OSMO_STRBUF_APPEND(sb, ip_addrs_to_str_buf, &f_seid->ip_addr);
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
int osmo_pfcp_dec_f_teid(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
||||
{
|
||||
struct osmo_pfcp_ie_f_teid *f_teid = decode_to;
|
||||
uint8_t flags;
|
||||
const uint8_t *pos;
|
||||
|
||||
*f_teid = (struct osmo_pfcp_ie_f_teid){};
|
||||
|
||||
pos = tlv->val;
|
||||
|
||||
ENSURE_REMAINING_LENGTH_IS_AT_LEAST("flags", pos, 1);
|
||||
flags = *pos;
|
||||
pos++;
|
||||
f_teid->choose_flag = flags & 4;
|
||||
|
||||
if (!f_teid->choose_flag) {
|
||||
/* A fixed TEID and address are provided */
|
||||
f_teid->fixed.ip_addr.v4_present = flags & 1;
|
||||
f_teid->fixed.ip_addr.v6_present = flags & 2;
|
||||
|
||||
ENSURE_REMAINING_LENGTH_IS_AT_LEAST("TEID", pos, 4);
|
||||
f_teid->fixed.teid = osmo_load32be(pos);
|
||||
pos += 4;
|
||||
|
||||
if (f_teid->fixed.ip_addr.v4_present) {
|
||||
osmo_static_assert(sizeof(f_teid->fixed.ip_addr.v4.u.sin.sin_addr) == 4, sin_addr_size_is_4);
|
||||
ENSURE_REMAINING_LENGTH_IS_AT_LEAST("IPv4 address", pos, 4);
|
||||
osmo_sockaddr_from_octets(&f_teid->fixed.ip_addr.v4, pos, 4);
|
||||
pos += 4;
|
||||
}
|
||||
if (f_teid->fixed.ip_addr.v6_present) {
|
||||
osmo_static_assert(sizeof(f_teid->fixed.ip_addr.v6.u.sin6.sin6_addr) == 16, sin6_addr_size_is_16);
|
||||
ENSURE_REMAINING_LENGTH_IS_AT_LEAST("IPv6 address", pos, 16);
|
||||
osmo_sockaddr_from_octets(&f_teid->fixed.ip_addr.v6, pos, 16);
|
||||
pos += 16;
|
||||
}
|
||||
} else {
|
||||
/* CH flag is 1, choose an F-TEID. */
|
||||
f_teid->choose.ipv4_addr = flags & 1;
|
||||
f_teid->choose.ipv6_addr = flags & 2;
|
||||
f_teid->choose.choose_id_present = flags & 8;
|
||||
|
||||
if (f_teid->choose.choose_id_present) {
|
||||
ENSURE_REMAINING_LENGTH_IS_AT_LEAST("CHOOSE ID", pos, 1);
|
||||
f_teid->choose.choose_id = *pos;
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_f_teid(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
||||
{
|
||||
const struct osmo_pfcp_ie_f_teid *f_teid = encode_from;
|
||||
uint8_t flags;
|
||||
|
||||
flags = (f_teid->choose_flag ? 4 : 0);
|
||||
|
||||
if (!f_teid->choose_flag) {
|
||||
/* A fixed TEID and address are provided */
|
||||
flags |= (f_teid->fixed.ip_addr.v4_present ? 1 : 0)
|
||||
+ (f_teid->fixed.ip_addr.v6_present ? 2 : 0);
|
||||
|
||||
msgb_put_u8(tlv->dst, flags);
|
||||
msgb_put_u32(tlv->dst, f_teid->fixed.teid);
|
||||
|
||||
if (f_teid->fixed.ip_addr.v4_present) {
|
||||
if (f_teid->fixed.ip_addr.v4.u.sin.sin_family != AF_INET)
|
||||
RETURN_ERROR(-EINVAL,
|
||||
"f_teid IE indicates IPv4 address, but there is no ipv4_addr"
|
||||
" (sin_family = %d != AF_INET)", f_teid->fixed.ip_addr.v4.u.sin.sin_family);
|
||||
osmo_sockaddr_to_octets(msgb_put(tlv->dst, 4), 4, &f_teid->fixed.ip_addr.v4);
|
||||
}
|
||||
if (f_teid->fixed.ip_addr.v6_present) {
|
||||
if (f_teid->fixed.ip_addr.v6.u.sin6.sin6_family != AF_INET6)
|
||||
RETURN_ERROR(-EINVAL,
|
||||
"f_teid IE indicates IPv6 address, but there is no ipv6_addr"
|
||||
" (sin6_family = %d != AF_INET6)", f_teid->fixed.ip_addr.v6.u.sin6.sin6_family);
|
||||
osmo_sockaddr_to_octets(msgb_put(tlv->dst, 16), 16, &f_teid->fixed.ip_addr.v6);
|
||||
}
|
||||
} else {
|
||||
flags |= (f_teid->choose.ipv4_addr ? 1 : 0)
|
||||
+ (f_teid->choose.ipv6_addr ? 2 : 0)
|
||||
+ (f_teid->choose.choose_id_present ? 8 : 0);
|
||||
msgb_put_u8(tlv->dst, flags);
|
||||
if (f_teid->choose.choose_id_present)
|
||||
msgb_put_u8(tlv->dst, f_teid->choose.choose_id);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_ie_f_teid_to_str_buf(char *buf, size_t buflen, const struct osmo_pfcp_ie_f_teid *ft)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
if (ft->choose_flag) {
|
||||
OSMO_STRBUF_PRINTF(sb, "CHOOSE");
|
||||
if (ft->choose.ipv4_addr)
|
||||
OSMO_STRBUF_PRINTF(sb, "-v4");
|
||||
if (ft->choose.ipv6_addr)
|
||||
OSMO_STRBUF_PRINTF(sb, "-v6");
|
||||
if (ft->choose.choose_id_present)
|
||||
OSMO_STRBUF_PRINTF(sb, "-id%u", ft->choose.choose_id);
|
||||
} else {
|
||||
OSMO_STRBUF_PRINTF(sb, "TEID-0x%x", ft->fixed.teid);
|
||||
OSMO_STRBUF_APPEND(sb, ip_addrs_to_str_buf, &ft->fixed.ip_addr);
|
||||
}
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
char *osmo_pfcp_ie_f_teid_to_str_c(void *ctx, const struct osmo_pfcp_ie_f_teid *ft)
|
||||
{
|
||||
OSMO_NAME_C_IMPL(ctx, 64, "ERROR", osmo_pfcp_ie_f_teid_to_str_buf, ft)
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_to_str_f_teid(char *buf, size_t buflen, const void *encode_from)
|
||||
{
|
||||
const struct osmo_pfcp_ie_f_teid *f_teid = encode_from;
|
||||
return osmo_pfcp_ie_f_teid_to_str_buf(buf, buflen, f_teid);
|
||||
}
|
||||
|
||||
int osmo_pfcp_dec_apply_action(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
||||
{
|
||||
struct osmo_pfcp_ie_apply_action *apply_action = decode_to;
|
||||
ENSURE_LENGTH_IS_AT_LEAST(1);
|
||||
*apply_action = (struct osmo_pfcp_ie_apply_action){};
|
||||
memcpy(apply_action->bits, tlv->val, OSMO_MIN(tlv->len, sizeof(apply_action->bits)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_apply_action(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
||||
{
|
||||
const struct osmo_pfcp_ie_apply_action *apply_action = encode_from;
|
||||
memcpy(msgb_put(tlv->dst, sizeof(apply_action->bits)),
|
||||
apply_action->bits, sizeof(apply_action->bits));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_to_str_apply_action(char *buf, size_t buflen, const void *encode_from)
|
||||
{
|
||||
const struct osmo_pfcp_ie_apply_action *apply_action = encode_from;
|
||||
return osmo_pfcp_bits_to_str_buf(buf, buflen, apply_action->bits, osmo_pfcp_apply_action_strs);
|
||||
}
|
||||
|
||||
int osmo_pfcp_dec_network_inst(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
||||
{
|
||||
struct osmo_pfcp_ie_network_inst *network_inst = decode_to;
|
||||
osmo_strlcpy(network_inst->str, (const char *)tlv->val, OSMO_MIN(sizeof(network_inst->str), tlv->len+1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_network_inst(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
||||
{
|
||||
const struct osmo_pfcp_ie_network_inst *network_inst = encode_from;
|
||||
unsigned int l = strlen(network_inst->str);
|
||||
if (l)
|
||||
memcpy(msgb_put(tlv->dst, l), network_inst->str, l);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_to_str_network_inst(char *buf, size_t buflen, const void *encode_from)
|
||||
{
|
||||
const struct osmo_pfcp_ie_network_inst *network_inst = encode_from;
|
||||
return osmo_quote_str_buf3(buf, buflen, network_inst->str,
|
||||
strnlen(network_inst->str, sizeof(network_inst->str)));
|
||||
}
|
||||
|
||||
int osmo_pfcp_dec_outer_header_creation(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
||||
{
|
||||
struct osmo_pfcp_ie_outer_header_creation *ohc = decode_to;
|
||||
const uint8_t *pos;
|
||||
bool gtp_u_udp_ipv4;
|
||||
bool gtp_u_udp_ipv6;
|
||||
bool udp_ipv4;
|
||||
bool udp_ipv6;
|
||||
bool ipv4;
|
||||
bool ipv6;
|
||||
bool c_tag;
|
||||
bool s_tag;
|
||||
|
||||
*ohc = (struct osmo_pfcp_ie_outer_header_creation){};
|
||||
|
||||
ENSURE_LENGTH_IS_AT_LEAST(2);
|
||||
|
||||
memcpy(ohc->desc_bits, tlv->val, 2);
|
||||
|
||||
gtp_u_udp_ipv4 = osmo_pfcp_bits_get(ohc->desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_GTP_U_UDP_IPV4);
|
||||
udp_ipv4 = osmo_pfcp_bits_get(ohc->desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_UDP_IPV4);
|
||||
ipv4 = osmo_pfcp_bits_get(ohc->desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_IPV4);
|
||||
gtp_u_udp_ipv6 = osmo_pfcp_bits_get(ohc->desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_GTP_U_UDP_IPV6);
|
||||
udp_ipv6 = osmo_pfcp_bits_get(ohc->desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_UDP_IPV6);
|
||||
ipv6 = osmo_pfcp_bits_get(ohc->desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_IPV6);
|
||||
c_tag = osmo_pfcp_bits_get(ohc->desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_C_TAG);
|
||||
s_tag = osmo_pfcp_bits_get(ohc->desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_S_TAG);
|
||||
|
||||
pos = tlv->val + 2;
|
||||
if (gtp_u_udp_ipv4 || gtp_u_udp_ipv6) {
|
||||
ENSURE_REMAINING_LENGTH_IS_AT_LEAST("TEID", pos, 4);
|
||||
ohc->teid_present = true;
|
||||
ohc->teid = osmo_load32be(pos);
|
||||
pos += 4;
|
||||
}
|
||||
if (gtp_u_udp_ipv4 || udp_ipv4 || ipv4) {
|
||||
ENSURE_REMAINING_LENGTH_IS_AT_LEAST("IPv4 address", pos, 4);
|
||||
ohc->ip_addr.v4_present = true;
|
||||
osmo_sockaddr_from_octets(&ohc->ip_addr.v4, pos, 4);
|
||||
pos += 4;
|
||||
}
|
||||
if (gtp_u_udp_ipv6 || udp_ipv6 || ipv6) {
|
||||
ENSURE_REMAINING_LENGTH_IS_AT_LEAST("IPv6 address", pos, 16);
|
||||
ohc->ip_addr.v6_present = true;
|
||||
osmo_sockaddr_from_octets(&ohc->ip_addr.v6, pos, 16);
|
||||
pos += 16;
|
||||
}
|
||||
if (udp_ipv4 || udp_ipv6) {
|
||||
ENSURE_REMAINING_LENGTH_IS_AT_LEAST("UDP port number", pos, 2);
|
||||
ohc->port_number_present = true;
|
||||
ohc->port_number = osmo_load16be(pos);
|
||||
pos += 2;
|
||||
}
|
||||
if (c_tag) {
|
||||
ohc->c_tag_present = true;
|
||||
ENSURE_REMAINING_LENGTH_IS_AT_LEAST("C-TAG", pos, 3);
|
||||
ohc->c_tag_present = true;
|
||||
ohc->c_tag = osmo_load32be_ext_2(pos, 3);
|
||||
pos += 3;
|
||||
}
|
||||
if (s_tag) {
|
||||
ohc->s_tag_present = true;
|
||||
ENSURE_REMAINING_LENGTH_IS_AT_LEAST("S-TAG", pos, 3);
|
||||
ohc->s_tag_present = true;
|
||||
ohc->s_tag = osmo_load32be_ext_2(pos, 3);
|
||||
pos += 3;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_outer_header_creation(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
||||
{
|
||||
const struct osmo_pfcp_ie_outer_header_creation *ohc = encode_from;
|
||||
bool gtp_u_udp_ipv4;
|
||||
bool gtp_u_udp_ipv6;
|
||||
bool udp_ipv4;
|
||||
bool udp_ipv6;
|
||||
bool ipv4;
|
||||
bool ipv6;
|
||||
bool c_tag;
|
||||
bool s_tag;
|
||||
|
||||
memcpy(msgb_put(tlv->dst, sizeof(ohc->desc_bits)), ohc->desc_bits, sizeof(ohc->desc_bits));
|
||||
|
||||
gtp_u_udp_ipv4 = osmo_pfcp_bits_get(ohc->desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_GTP_U_UDP_IPV4);
|
||||
udp_ipv4 = osmo_pfcp_bits_get(ohc->desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_UDP_IPV4);
|
||||
ipv4 = osmo_pfcp_bits_get(ohc->desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_IPV4);
|
||||
gtp_u_udp_ipv6 = osmo_pfcp_bits_get(ohc->desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_GTP_U_UDP_IPV6);
|
||||
udp_ipv6 = osmo_pfcp_bits_get(ohc->desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_UDP_IPV6);
|
||||
ipv6 = osmo_pfcp_bits_get(ohc->desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_IPV6);
|
||||
c_tag = osmo_pfcp_bits_get(ohc->desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_C_TAG);
|
||||
s_tag = osmo_pfcp_bits_get(ohc->desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_S_TAG);
|
||||
|
||||
if ((gtp_u_udp_ipv4 || gtp_u_udp_ipv6) != (ohc->teid_present))
|
||||
RETURN_ERROR(-EINVAL, "teid_present = %s does not match the description bits 0x%02x\n",
|
||||
ohc->teid_present ? "true" : "false",
|
||||
ohc->desc_bits[0]);
|
||||
if (ohc->teid_present)
|
||||
msgb_put_u32(tlv->dst, ohc->teid);
|
||||
|
||||
if ((gtp_u_udp_ipv4 || udp_ipv4 || ipv4) != ohc->ip_addr.v4_present)
|
||||
RETURN_ERROR(-EINVAL, "ipv4_addr_present = %s does not match the description bits 0x%02x\n",
|
||||
ohc->ip_addr.v4_present ? "true" : "false",
|
||||
ohc->desc_bits[0]);
|
||||
if (ohc->ip_addr.v4_present)
|
||||
osmo_sockaddr_to_octets(msgb_put(tlv->dst, 4), 4, &ohc->ip_addr.v4);
|
||||
|
||||
if ((gtp_u_udp_ipv6 || udp_ipv6 || ipv6) != ohc->ip_addr.v6_present)
|
||||
RETURN_ERROR(-EINVAL, "ipv6_addr_present = %s does not match the description bits 0x%02x\n",
|
||||
ohc->ip_addr.v6_present ? "true" : "false",
|
||||
ohc->desc_bits[0]);
|
||||
if (ohc->ip_addr.v6_present)
|
||||
osmo_sockaddr_to_octets(msgb_put(tlv->dst, 16), 16, &ohc->ip_addr.v6);
|
||||
|
||||
if ((udp_ipv4 || udp_ipv6) != ohc->port_number_present)
|
||||
RETURN_ERROR(-EINVAL, "port_number_present = %s does not match the description bits 0x%02x\n",
|
||||
ohc->port_number_present ? "true" : "false",
|
||||
ohc->desc_bits[0]);
|
||||
if (ohc->port_number_present)
|
||||
msgb_put_u16(tlv->dst, ohc->port_number);
|
||||
|
||||
if (c_tag != ohc->c_tag_present)
|
||||
RETURN_ERROR(-EINVAL, "c_tag_present = %s does not match the description bits 0x%02x%02x\n",
|
||||
ohc->c_tag_present ? "true" : "false",
|
||||
ohc->desc_bits[1], ohc->desc_bits[0]);
|
||||
if (ohc->c_tag_present)
|
||||
osmo_store32be_ext(ohc->c_tag, msgb_put(tlv->dst, 3), 3);
|
||||
|
||||
if (s_tag != ohc->s_tag_present)
|
||||
RETURN_ERROR(-EINVAL, "s_tag_present = %s does not match the description bits 0x%02x%02x\n",
|
||||
ohc->s_tag_present ? "true" : "false",
|
||||
ohc->desc_bits[1], ohc->desc_bits[0]);
|
||||
if (ohc->s_tag_present)
|
||||
osmo_store32be_ext(ohc->s_tag, msgb_put(tlv->dst, 3), 3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_to_str_outer_header_creation(char *buf, size_t buflen, const void *encode_from)
|
||||
{
|
||||
const struct osmo_pfcp_ie_outer_header_creation *ohc = encode_from;
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
OSMO_STRBUF_APPEND(sb, osmo_pfcp_bits_to_str_buf, ohc->desc_bits, osmo_pfcp_outer_header_creation_strs);
|
||||
if (ohc->teid_present)
|
||||
OSMO_STRBUF_PRINTF(sb, ",TEID:0x%x", ohc->teid);
|
||||
OSMO_STRBUF_APPEND(sb, ip_addrs_to_str_buf, &ohc->ip_addr);
|
||||
if (ohc->port_number_present)
|
||||
OSMO_STRBUF_PRINTF(sb, ",port:%u", ohc->port_number);
|
||||
if (ohc->c_tag_present)
|
||||
OSMO_STRBUF_PRINTF(sb, ",c-tag:%u", ohc->c_tag);
|
||||
if (ohc->s_tag_present)
|
||||
OSMO_STRBUF_PRINTF(sb, ",s-tag:%u", ohc->s_tag);
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
int osmo_pfcp_dec_activate_predefined_rules(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
||||
{
|
||||
struct osmo_pfcp_ie_activate_predefined_rules *activate_predefined_rules = decode_to;
|
||||
osmo_strlcpy(activate_predefined_rules->str, (const char *)tlv->val, OSMO_MIN(sizeof(activate_predefined_rules->str), tlv->len+1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_activate_predefined_rules(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
||||
{
|
||||
const struct osmo_pfcp_ie_activate_predefined_rules *activate_predefined_rules = encode_from;
|
||||
unsigned int l = strlen(activate_predefined_rules->str);
|
||||
if (l)
|
||||
memcpy(msgb_put(tlv->dst, l), activate_predefined_rules->str, l);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_to_str_activate_predefined_rules(char *buf, size_t buflen, const void *encode_from)
|
||||
{
|
||||
const struct osmo_pfcp_ie_activate_predefined_rules *activate_predefined_rules = encode_from;
|
||||
return osmo_quote_str_buf3(buf, buflen, activate_predefined_rules->str,
|
||||
strnlen(activate_predefined_rules->str, sizeof(activate_predefined_rules->str)));
|
||||
}
|
||||
|
||||
int osmo_pfcp_dec_outer_header_removal(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
||||
{
|
||||
struct osmo_pfcp_ie_outer_header_removal *outer_header_removal = decode_to;
|
||||
ENSURE_LENGTH_IS_AT_LEAST(1);
|
||||
outer_header_removal->desc = tlv->val[0];
|
||||
|
||||
if (tlv->len > 1) {
|
||||
outer_header_removal->gtp_u_extension_header_del_present = true;
|
||||
memcpy(outer_header_removal->gtp_u_extension_header_del_bits, &tlv->val[1],
|
||||
sizeof(outer_header_removal->gtp_u_extension_header_del_bits));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_outer_header_removal(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
||||
{
|
||||
const struct osmo_pfcp_ie_outer_header_removal *outer_header_removal = encode_from;
|
||||
msgb_put_u8(tlv->dst, outer_header_removal->desc);
|
||||
if (outer_header_removal->gtp_u_extension_header_del_present) {
|
||||
memcpy(msgb_put(tlv->dst, sizeof(outer_header_removal->gtp_u_extension_header_del_bits)),
|
||||
outer_header_removal->gtp_u_extension_header_del_bits,
|
||||
sizeof(outer_header_removal->gtp_u_extension_header_del_bits));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_to_str_outer_header_removal(char *buf, size_t buflen, const void *encode_from)
|
||||
{
|
||||
const struct osmo_pfcp_ie_outer_header_removal *outer_header_removal = encode_from;
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
OSMO_STRBUF_PRINTF(sb, "%s", osmo_pfcp_outer_header_removal_desc_str(outer_header_removal->desc));
|
||||
if (outer_header_removal->gtp_u_extension_header_del_present)
|
||||
OSMO_STRBUF_PRINTF(sb, ",ext-hdr-del:0x%x", outer_header_removal->gtp_u_extension_header_del_bits[0]);
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
int osmo_pfcp_dec_ue_ip_address(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
||||
{
|
||||
struct osmo_pfcp_ie_ue_ip_address *ue_ip_address = decode_to;
|
||||
const uint8_t *pos;
|
||||
uint8_t flags;
|
||||
|
||||
pos = tlv->val;
|
||||
|
||||
ENSURE_REMAINING_LENGTH_IS_AT_LEAST("flags", pos, 1);
|
||||
flags = *pos;
|
||||
pos++;
|
||||
|
||||
ue_ip_address->ipv6_prefix_length_present = flags & (1 << 6);
|
||||
ue_ip_address->chv6 = flags & (1 << 5);
|
||||
ue_ip_address->chv4 = flags & (1 << 4);
|
||||
ue_ip_address->ipv6_prefix_delegation_bits_present = flags & (1 << 3);
|
||||
ue_ip_address->ip_is_destination = flags & (1 << 2);
|
||||
ue_ip_address->ip_addr.v4_present = flags & (1 << 1);
|
||||
ue_ip_address->ip_addr.v6_present = flags & (1 << 0);
|
||||
|
||||
if (ue_ip_address->ip_addr.v4_present) {
|
||||
ENSURE_REMAINING_LENGTH_IS_AT_LEAST("IPv4 address", pos, 4);
|
||||
osmo_sockaddr_from_octets(&ue_ip_address->ip_addr.v4, pos, 4);
|
||||
pos += 4;
|
||||
}
|
||||
if (ue_ip_address->ip_addr.v6_present) {
|
||||
ENSURE_REMAINING_LENGTH_IS_AT_LEAST("IPv6 address", pos, 16);
|
||||
osmo_sockaddr_from_octets(&ue_ip_address->ip_addr.v6, pos, 16);
|
||||
pos += 16;
|
||||
}
|
||||
|
||||
if (ue_ip_address->ipv6_prefix_delegation_bits_present) {
|
||||
ENSURE_REMAINING_LENGTH_IS_AT_LEAST("IPv6 prefix delegation bits", pos, 1);
|
||||
ue_ip_address->ipv6_prefix_delegation_bits = *pos;
|
||||
pos++;
|
||||
}
|
||||
if (ue_ip_address->ipv6_prefix_length_present) {
|
||||
ENSURE_REMAINING_LENGTH_IS_AT_LEAST("IPv6 prefix length", pos, 1);
|
||||
ue_ip_address->ipv6_prefix_length = *pos;
|
||||
pos++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_ue_ip_address(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
||||
{
|
||||
const struct osmo_pfcp_ie_ue_ip_address *ue_ip_address = encode_from;
|
||||
unsigned int l;
|
||||
uint8_t flags;
|
||||
|
||||
flags = 0
|
||||
| (ue_ip_address->ipv6_prefix_length_present ? (1 << 6) : 0)
|
||||
| (ue_ip_address->chv6 ? (1 << 5) : 0)
|
||||
| (ue_ip_address->chv4 ? (1 << 4) : 0)
|
||||
| (ue_ip_address->ipv6_prefix_delegation_bits_present ? (1 << 3) : 0)
|
||||
| (ue_ip_address->ip_is_destination ? (1 << 2) : 0)
|
||||
| (ue_ip_address->ip_addr.v4_present ? (1 << 1) : 0)
|
||||
| (ue_ip_address->ip_addr.v6_present ? (1 << 0) : 0)
|
||||
;
|
||||
|
||||
msgb_put_u8(tlv->dst, flags);
|
||||
|
||||
if (ue_ip_address->ip_addr.v4_present) {
|
||||
if (ue_ip_address->ip_addr.v4.u.sin.sin_family != AF_INET)
|
||||
RETURN_ERROR(-EINVAL,
|
||||
"ue_ip_address IE indicates IPv4 address, but there is no ipv4_addr");
|
||||
l = sizeof(ue_ip_address->ip_addr.v4.u.sin.sin_addr);
|
||||
osmo_sockaddr_to_octets(msgb_put(tlv->dst, l), l, &ue_ip_address->ip_addr.v4);
|
||||
}
|
||||
if (ue_ip_address->ip_addr.v6_present) {
|
||||
if (ue_ip_address->ip_addr.v6.u.sin6.sin6_family != AF_INET6)
|
||||
RETURN_ERROR(-EINVAL,
|
||||
"ue_ip_address IE indicates IPv6 address, but there is no ipv6_addr");
|
||||
l = sizeof(ue_ip_address->ip_addr.v6.u.sin6.sin6_addr);
|
||||
osmo_sockaddr_to_octets(msgb_put(tlv->dst, l), l, &ue_ip_address->ip_addr.v6);
|
||||
}
|
||||
|
||||
if (ue_ip_address->ipv6_prefix_delegation_bits_present)
|
||||
msgb_put_u8(tlv->dst, ue_ip_address->ipv6_prefix_delegation_bits);
|
||||
|
||||
if (ue_ip_address->ipv6_prefix_length_present)
|
||||
msgb_put_u8(tlv->dst, ue_ip_address->ipv6_prefix_length);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pfcp_enc_to_str_ue_ip_address(char *buf, size_t buflen, const void *encode_from)
|
||||
{
|
||||
const struct osmo_pfcp_ie_ue_ip_address *uia = encode_from;
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
if (uia->chv4)
|
||||
OSMO_STRBUF_PRINTF(sb, "chv4");
|
||||
if (uia->chv6)
|
||||
OSMO_STRBUF_PRINTF(sb, "%schv4", sb.pos ? "," : "");
|
||||
if (uia->ip_is_destination)
|
||||
OSMO_STRBUF_PRINTF(sb, "%sdst", sb.pos ? "," : "");
|
||||
OSMO_STRBUF_APPEND(sb, ip_addrs_to_str_buf, &uia->ip_addr);
|
||||
if (uia->ipv6_prefix_delegation_bits_present)
|
||||
OSMO_STRBUF_PRINTF(sb, ",ipv6-prefix-deleg:%x", uia->ipv6_prefix_delegation_bits);
|
||||
if (uia->ipv6_prefix_length_present)
|
||||
OSMO_STRBUF_PRINTF(sb, ",ipv6-prefix-len:%u", uia->ipv6_prefix_length);
|
||||
return sb.chars_needed;
|
||||
}
|
||||
@@ -1,492 +0,0 @@
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/pfcp/pfcp_strs.h>
|
||||
|
||||
const struct value_string osmo_pfcp_message_type_strs[] = {
|
||||
{ OSMO_PFCP_MSGT_HEARTBEAT_REQ, "HEARTBEAT_REQ" },
|
||||
{ OSMO_PFCP_MSGT_HEARTBEAT_RESP, "HEARTBEAT_RESP" },
|
||||
{ OSMO_PFCP_MSGT_PFD_MGMT_REQ, "PFD_MGMT_REQ" },
|
||||
{ OSMO_PFCP_MSGT_PFD_MGMT_RESP, "PFD_MGMT_RESP" },
|
||||
{ OSMO_PFCP_MSGT_ASSOC_SETUP_REQ, "ASSOC_SETUP_REQ" },
|
||||
{ OSMO_PFCP_MSGT_ASSOC_SETUP_RESP, "ASSOC_SETUP_RESP" },
|
||||
{ OSMO_PFCP_MSGT_ASSOC_UPDATE_REQ, "ASSOC_UPDATE_REQ" },
|
||||
{ OSMO_PFCP_MSGT_ASSOC_UPDATE_RESP, "ASSOC_UPDATE_RESP" },
|
||||
{ OSMO_PFCP_MSGT_ASSOC_RELEASE_REQ, "ASSOC_RELEASE_REQ" },
|
||||
{ OSMO_PFCP_MSGT_ASSOC_RELEASE_RESP, "ASSOC_RELEASE_RESP" },
|
||||
{ OSMO_PFCP_MSGT_VERSION_NOT_SUPP_RESP, "VERSION_NOT_SUPP_RESP" },
|
||||
{ OSMO_PFCP_MSGT_NODE_REPORT_REQ, "NODE_REPORT_REQ" },
|
||||
{ OSMO_PFCP_MSGT_NODE_REPORT_RESP, "NODE_REPORT_RESP" },
|
||||
{ OSMO_PFCP_MSGT_SESSION_SET_DEL_REQ, "SESSION_SET_DEL_REQ" },
|
||||
{ OSMO_PFCP_MSGT_SESSION_SET_DEL_RESP, "SESSION_SET_DEL_RESP" },
|
||||
{ OSMO_PFCP_MSGT_SESSION_EST_REQ, "SESSION_EST_REQ" },
|
||||
{ OSMO_PFCP_MSGT_SESSION_EST_RESP, "SESSION_EST_RESP" },
|
||||
{ OSMO_PFCP_MSGT_SESSION_MOD_REQ, "SESSION_MOD_REQ" },
|
||||
{ OSMO_PFCP_MSGT_SESSION_MOD_RESP, "SESSION_MOD_RESP" },
|
||||
{ OSMO_PFCP_MSGT_SESSION_DEL_REQ, "SESSION_DEL_REQ" },
|
||||
{ OSMO_PFCP_MSGT_SESSION_DEL_RESP, "SESSION_DEL_RESP" },
|
||||
{ OSMO_PFCP_MSGT_SESSION_REP_REQ, "SESSION_REP_REQ" },
|
||||
{ OSMO_PFCP_MSGT_SESSION_REP_RESP, "SESSION_REP_RESP" },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
const struct value_string osmo_pfcp_iei_strs[] = {
|
||||
{ OSMO_PFCP_IEI_CREATE_PDR, "Create PDR" },
|
||||
{ OSMO_PFCP_IEI_PDI, "PDI" },
|
||||
{ OSMO_PFCP_IEI_CREATE_FAR, "Create FAR" },
|
||||
{ OSMO_PFCP_IEI_FORW_PARAMS, "Forwarding Parameters" },
|
||||
{ OSMO_PFCP_IEI_DUPL_PARAMS, "Duplicating Parameters" },
|
||||
{ OSMO_PFCP_IEI_CREATE_URR, "Create URR" },
|
||||
{ OSMO_PFCP_IEI_CREATE_QER, "Create QER" },
|
||||
{ OSMO_PFCP_IEI_CREATED_PDR, "Created PDR" },
|
||||
{ OSMO_PFCP_IEI_UPD_PDR, "Update PDR" },
|
||||
{ OSMO_PFCP_IEI_UPD_FAR, "Update FAR" },
|
||||
{ OSMO_PFCP_IEI_UPD_FORW_PARAMS, "Update Forwarding Parameters" },
|
||||
{ OSMO_PFCP_IEI_UPD_BAR_SESS_REP_RESP, "Update BAR (PFCP Session Report Response)" },
|
||||
{ OSMO_PFCP_IEI_UPD_URR, "Update URR" },
|
||||
{ OSMO_PFCP_IEI_UPD_QER, "Update QER" },
|
||||
{ OSMO_PFCP_IEI_REMOVE_PDR, "Remove PDR" },
|
||||
{ OSMO_PFCP_IEI_REMOVE_FAR, "Remove FAR" },
|
||||
{ OSMO_PFCP_IEI_REMOVE_URR, "Remove URR" },
|
||||
{ OSMO_PFCP_IEI_REMOVE_QER, "Remove QER" },
|
||||
{ OSMO_PFCP_IEI_CAUSE, "Cause" },
|
||||
{ OSMO_PFCP_IEI_SOURCE_IFACE, "Source Interface" },
|
||||
{ OSMO_PFCP_IEI_F_TEID, "F-TEID" },
|
||||
{ OSMO_PFCP_IEI_NETWORK_INST, "Network Instance" },
|
||||
{ OSMO_PFCP_IEI_SDF_FILTER, "SDF Filter" },
|
||||
{ OSMO_PFCP_IEI_APPLICATION_ID, "Application ID" },
|
||||
{ OSMO_PFCP_IEI_GATE_STATUS, "Gate Status" },
|
||||
{ OSMO_PFCP_IEI_MBR, "MBR" },
|
||||
{ OSMO_PFCP_IEI_GBR, "GBR" },
|
||||
{ OSMO_PFCP_IEI_QER_CORRELATION_ID, "QER Correlation ID" },
|
||||
{ OSMO_PFCP_IEI_PRECEDENCE, "Precedence" },
|
||||
{ OSMO_PFCP_IEI_TRANSPORT_LEVEL_MARKING, "Transport Level Marking" },
|
||||
{ OSMO_PFCP_IEI_VOLUME_THRESH, "Volume Threshold" },
|
||||
{ OSMO_PFCP_IEI_TIME_THRESH, "Time Threshold" },
|
||||
{ OSMO_PFCP_IEI_MONITORING_TIME, "Monitoring Time" },
|
||||
{ OSMO_PFCP_IEI_SUBSEQUENT_VOLUME_THRESH, "Subsequent Volume Threshold" },
|
||||
{ OSMO_PFCP_IEI_SUBSEQUENT_TIME_THRESH, "Subsequent Time Threshold" },
|
||||
{ OSMO_PFCP_IEI_INACT_DETECTION_TIME, "Inactivity Detection Time" },
|
||||
{ OSMO_PFCP_IEI_REPORTING_TRIGGERS, "Reporting Triggers" },
|
||||
{ OSMO_PFCP_IEI_REDIRECT_INFO, "Redirect Information" },
|
||||
{ OSMO_PFCP_IEI_REP_TYPE, "Report Type" },
|
||||
{ OSMO_PFCP_IEI_OFFENDING_IE, "Offending IE" },
|
||||
{ OSMO_PFCP_IEI_FORW_POLICY, "Forwarding Policy" },
|
||||
{ OSMO_PFCP_IEI_DESTINATION_IFACE, "Destination Interface" },
|
||||
{ OSMO_PFCP_IEI_UP_FUNCTION_FEATURES, "UP Function Features" },
|
||||
{ OSMO_PFCP_IEI_APPLY_ACTION, "Apply Action" },
|
||||
{ OSMO_PFCP_IEI_DL_DATA_SERVICE_INFO, "Downlink Data Service Information" },
|
||||
{ OSMO_PFCP_IEI_DL_DATA_NOTIFICATION_DELAY, "Downlink Data Notification Delay" },
|
||||
{ OSMO_PFCP_IEI_DL_BUFF_DURATION, "DL Buffering Duration" },
|
||||
{ OSMO_PFCP_IEI_DL_BUFF_SUGGESTED_PACKET_COUNT, "DL Buffering Suggested Packet Count" },
|
||||
{ OSMO_PFCP_IEI_PFCPSMREQ_FLAGS, "PFCPSMReq-Flags" },
|
||||
{ OSMO_PFCP_IEI_PFCPSRRSP_FLAGS, "PFCPSRRsp-Flags" },
|
||||
{ OSMO_PFCP_IEI_LOAD_CTRL_INFO, "Load Control Information" },
|
||||
{ OSMO_PFCP_IEI_SEQUENCE_NUMBER, "Sequence Number" },
|
||||
{ OSMO_PFCP_IEI_METRIC, "Metric" },
|
||||
{ OSMO_PFCP_IEI_OVERLOAD_CTRL_INFO, "Overload Control Information" },
|
||||
{ OSMO_PFCP_IEI_TIMER, "Timer" },
|
||||
{ OSMO_PFCP_IEI_PDR_ID, "PDR ID" },
|
||||
{ OSMO_PFCP_IEI_F_SEID, "F-SEID" },
|
||||
{ OSMO_PFCP_IEI_APPLICATION_IDS_PFDS, "Application ID's PFDs" },
|
||||
{ OSMO_PFCP_IEI_PFD_CONTEXT, "PFD context" },
|
||||
{ OSMO_PFCP_IEI_NODE_ID, "Node ID" },
|
||||
{ OSMO_PFCP_IEI_PFD_CONTENTS, "PFD contents" },
|
||||
{ OSMO_PFCP_IEI_MEAS_METHOD, "Measurement Method" },
|
||||
{ OSMO_PFCP_IEI_USAGE_REP_TRIGGER, "Usage Report Trigger" },
|
||||
{ OSMO_PFCP_IEI_MEAS_PERIOD, "Measurement Period" },
|
||||
{ OSMO_PFCP_IEI_FQ_CSID, "FQ-CSID" },
|
||||
{ OSMO_PFCP_IEI_VOLUME_MEAS, "Volume Measurement" },
|
||||
{ OSMO_PFCP_IEI_DURATION_MEAS, "Duration Measurement" },
|
||||
{ OSMO_PFCP_IEI_APPLICATION_DETECTION_INFO, "Application Detection Information" },
|
||||
{ OSMO_PFCP_IEI_TIME_OF_FIRST_PACKET, "Time of First Packet" },
|
||||
{ OSMO_PFCP_IEI_TIME_OF_LAST_PACKET, "Time of Last Packet" },
|
||||
{ OSMO_PFCP_IEI_QUOTA_HOLDING_TIME, "Quota Holding Time" },
|
||||
{ OSMO_PFCP_IEI_DROPPED_DL_TRAFFIC_THRESH, "Dropped DL Traffic Threshold" },
|
||||
{ OSMO_PFCP_IEI_VOLUME_QUOTA, "Volume Quota" },
|
||||
{ OSMO_PFCP_IEI_TIME_QUOTA, "Time Quota" },
|
||||
{ OSMO_PFCP_IEI_START_TIME, "Start Time" },
|
||||
{ OSMO_PFCP_IEI_END_TIME, "End Time" },
|
||||
{ OSMO_PFCP_IEI_QUERY_URR, "Query URR" },
|
||||
{ OSMO_PFCP_IEI_USAGE_REP_SESS_MOD_RESP, "Usage Report (Session Modification Response)" },
|
||||
{ OSMO_PFCP_IEI_USAGE_REP_SESS_DEL_RESP, "Usage Report (Session Deletion Response)" },
|
||||
{ OSMO_PFCP_IEI_USAGE_REP_SESS_REP_REQ, "Usage Report (Session Report Request)" },
|
||||
{ OSMO_PFCP_IEI_URR_ID, "URR ID" },
|
||||
{ OSMO_PFCP_IEI_LINKED_URR_ID, "Linked URR ID" },
|
||||
{ OSMO_PFCP_IEI_DL_DATA_REP, "Downlink Data Report" },
|
||||
{ OSMO_PFCP_IEI_OUTER_HEADER_CREATION, "Outer Header Creation" },
|
||||
{ OSMO_PFCP_IEI_CREATE_BAR, "Create BAR" },
|
||||
{ OSMO_PFCP_IEI_UPD_BAR_SESS_MOD_REQ, "Update BAR (Session Modification Request)" },
|
||||
{ OSMO_PFCP_IEI_REMOVE_BAR, "Remove BAR" },
|
||||
{ OSMO_PFCP_IEI_BAR_ID, "BAR ID" },
|
||||
{ OSMO_PFCP_IEI_CP_FUNCTION_FEATURES, "CP Function Features" },
|
||||
{ OSMO_PFCP_IEI_USAGE_INFO, "Usage Information" },
|
||||
{ OSMO_PFCP_IEI_APPLICATION_INST_ID, "Application Instance ID" },
|
||||
{ OSMO_PFCP_IEI_FLOW_INFO, "Flow Information" },
|
||||
{ OSMO_PFCP_IEI_UE_IP_ADDRESS, "UE IP Address" },
|
||||
{ OSMO_PFCP_IEI_PACKET_RATE, "Packet Rate" },
|
||||
{ OSMO_PFCP_IEI_OUTER_HEADER_REMOVAL, "Outer Header Removal" },
|
||||
{ OSMO_PFCP_IEI_RECOVERY_TIME_STAMP, "Recovery Time Stamp" },
|
||||
{ OSMO_PFCP_IEI_DL_FLOW_LEVEL_MARKING, "DL Flow Level Marking" },
|
||||
{ OSMO_PFCP_IEI_HEADER_ENRICHMENT, "Header Enrichment" },
|
||||
{ OSMO_PFCP_IEI_ERROR_IND_REP, "Error Indication Report" },
|
||||
{ OSMO_PFCP_IEI_MEAS_INFO, "Measurement Information" },
|
||||
{ OSMO_PFCP_IEI_NODE_REP_TYPE, "Node Report Type" },
|
||||
{ OSMO_PFCP_IEI_USER_PLANE_PATH_FAILURE_REP, "User Plane Path Failure Report" },
|
||||
{ OSMO_PFCP_IEI_REMOTE_GTP_U_PEER, "Remote GTP-U Peer" },
|
||||
{ OSMO_PFCP_IEI_UR_SEQN, "UR-SEQN" },
|
||||
{ OSMO_PFCP_IEI_UPD_DUPL_PARAMS, "Update Duplicating Parameters" },
|
||||
{ OSMO_PFCP_IEI_ACTIVATE_PREDEFINED_RULES, "Activate Predefined Rules" },
|
||||
{ OSMO_PFCP_IEI_DEACTIVATE_PREDEFINED_RULES, "Deactivate Predefined Rules" },
|
||||
{ OSMO_PFCP_IEI_FAR_ID, "FAR ID" },
|
||||
{ OSMO_PFCP_IEI_QER_ID, "QER ID" },
|
||||
{ OSMO_PFCP_IEI_OCI_FLAGS, "OCI Flags" },
|
||||
{ OSMO_PFCP_IEI_PFCP_ASSOC_RELEASE_REQ, "PFCP Association Release Request" },
|
||||
{ OSMO_PFCP_IEI_GRACEFUL_RELEASE_PERIOD, "Graceful Release Period" },
|
||||
{ OSMO_PFCP_IEI_PDN_TYPE, "PDN Type" },
|
||||
{ OSMO_PFCP_IEI_FAILED_RULE_ID, "Failed Rule ID" },
|
||||
{ OSMO_PFCP_IEI_TIME_QUOTA_MECHANISM, "Time Quota Mechanism" },
|
||||
{ OSMO_PFCP_IEI_RESERVED, "Reserved" },
|
||||
{ OSMO_PFCP_IEI_USER_PLANE_INACT_TIMER, "User Plane Inactivity Timer" },
|
||||
{ OSMO_PFCP_IEI_AGGREGATED_URRS, "Aggregated URRs" },
|
||||
{ OSMO_PFCP_IEI_MULTIPLIER, "Multiplier" },
|
||||
{ OSMO_PFCP_IEI_AGGREGATED_URR_ID, "Aggregated URR ID" },
|
||||
{ OSMO_PFCP_IEI_SUBSEQUENT_VOLUME_QUOTA, "Subsequent Volume Quota" },
|
||||
{ OSMO_PFCP_IEI_SUBSEQUENT_TIME_QUOTA, "Subsequent Time Quota" },
|
||||
{ OSMO_PFCP_IEI_RQI, "RQI" },
|
||||
{ OSMO_PFCP_IEI_QFI, "QFI" },
|
||||
{ OSMO_PFCP_IEI_QUERY_URR_REFERENCE, "Query URR Reference" },
|
||||
{ OSMO_PFCP_IEI_ADDITIONAL_USAGE_REPS_INFO, "Additional Usage Reports Information" },
|
||||
{ OSMO_PFCP_IEI_CREATE_TRAFFIC_ENDPOINT, "Create Traffic Endpoint" },
|
||||
{ OSMO_PFCP_IEI_CREATED_TRAFFIC_ENDPOINT, "Created Traffic Endpoint" },
|
||||
{ OSMO_PFCP_IEI_UPD_TRAFFIC_ENDPOINT, "Update Traffic Endpoint" },
|
||||
{ OSMO_PFCP_IEI_REMOVE_TRAFFIC_ENDPOINT, "Remove Traffic Endpoint" },
|
||||
{ OSMO_PFCP_IEI_TRAFFIC_ENDPOINT_ID, "Traffic Endpoint ID" },
|
||||
{ OSMO_PFCP_IEI_ETHERNET_PACKET_FILTER, "Ethernet Packet Filter" },
|
||||
{ OSMO_PFCP_IEI_MAC_ADDRESS, "MAC address" },
|
||||
{ OSMO_PFCP_IEI_C_TAG, "C-TAG" },
|
||||
{ OSMO_PFCP_IEI_S_TAG, "S-TAG" },
|
||||
{ OSMO_PFCP_IEI_ETHERTYPE, "Ethertype" },
|
||||
{ OSMO_PFCP_IEI_PROXYING, "Proxying" },
|
||||
{ OSMO_PFCP_IEI_ETHERNET_FILTER_ID, "Ethernet Filter ID" },
|
||||
{ OSMO_PFCP_IEI_ETHERNET_FILTER_PROPERTIES, "Ethernet Filter Properties" },
|
||||
{ OSMO_PFCP_IEI_SUGGESTED_BUFF_PACKETS_COUNT, "Suggested Buffering Packets Count" },
|
||||
{ OSMO_PFCP_IEI_USER_ID, "User ID" },
|
||||
{ OSMO_PFCP_IEI_ETHERNET_PDU_SESS_INFO, "Ethernet PDU Session Information" },
|
||||
{ OSMO_PFCP_IEI_ETHERNET_TRAFFIC_INFO, "Ethernet Traffic Information" },
|
||||
{ OSMO_PFCP_IEI_MAC_ADDRS_DETECTED, "MAC Addresses Detected" },
|
||||
{ OSMO_PFCP_IEI_MAC_ADDRS_REMOVED, "MAC Addresses Removed" },
|
||||
{ OSMO_PFCP_IEI_ETHERNET_INACT_TIMER, "Ethernet Inactivity Timer" },
|
||||
{ OSMO_PFCP_IEI_ADDITIONAL_MONITORING_TIME, "Additional Monitoring Time" },
|
||||
{ OSMO_PFCP_IEI_EVENT_QUOTA, "Event Quota" },
|
||||
{ OSMO_PFCP_IEI_EVENT_THRESH, "Event Threshold" },
|
||||
{ OSMO_PFCP_IEI_SUBSEQUENT_EVENT_QUOTA, "Subsequent Event Quota" },
|
||||
{ OSMO_PFCP_IEI_SUBSEQUENT_EVENT_THRESH, "Subsequent Event Threshold" },
|
||||
{ OSMO_PFCP_IEI_TRACE_INFO, "Trace Information" },
|
||||
{ OSMO_PFCP_IEI_FRAMED_ROUTE, "Framed-Route" },
|
||||
{ OSMO_PFCP_IEI_FRAMED_ROUTING, "Framed-Routing" },
|
||||
{ OSMO_PFCP_IEI_FRAMED_IPV6_ROUTE, "Framed-IPv6-Route" },
|
||||
{ OSMO_PFCP_IEI_TIME_STAMP, "Time Stamp" },
|
||||
{ OSMO_PFCP_IEI_AVERAGING_WINDOW, "Averaging Window" },
|
||||
{ OSMO_PFCP_IEI_PAGING_POLICY_INDICATOR, "Paging Policy Indicator" },
|
||||
{ OSMO_PFCP_IEI_APN_DNN, "APN/DNN" },
|
||||
{ OSMO_PFCP_IEI_3GPP_IFACE_TYPE, "3GPP Interface Type" },
|
||||
{ OSMO_PFCP_IEI_PFCPSRREQ_FLAGS, "PFCPSRReq-Flags" },
|
||||
{ OSMO_PFCP_IEI_PFCPAUREQ_FLAGS, "PFCPAUReq-Flags" },
|
||||
{ OSMO_PFCP_IEI_ACTIVATION_TIME, "Activation Time" },
|
||||
{ OSMO_PFCP_IEI_DEACTIVATION_TIME, "Deactivation Time" },
|
||||
{ OSMO_PFCP_IEI_CREATE_MAR, "Create MAR" },
|
||||
{ OSMO_PFCP_IEI_3GPP_ACCESS_FORW_ACTION_INFO, "3GPP Access Forwarding Action Information" },
|
||||
{ OSMO_PFCP_IEI_NON_3GPP_ACCESS_FORW_ACTION_INFO, "Non-3GPP Access Forwarding Action Information" },
|
||||
{ OSMO_PFCP_IEI_REMOVE_MAR, "Remove MAR" },
|
||||
{ OSMO_PFCP_IEI_UPD_MAR, "Update MAR" },
|
||||
{ OSMO_PFCP_IEI_MAR_ID, "MAR ID" },
|
||||
{ OSMO_PFCP_IEI_STEERING_FUNCTIONALITY, "Steering Functionality" },
|
||||
{ OSMO_PFCP_IEI_STEERING_MODE, "Steering Mode" },
|
||||
{ OSMO_PFCP_IEI_WEIGHT, "Weight" },
|
||||
{ OSMO_PFCP_IEI_PRIORITY, "Priority" },
|
||||
{ OSMO_PFCP_IEI_UPD_3GPP_ACCESS_FORW_ACTION_INFO, "Update 3GPP Access Forwarding Action Information" },
|
||||
{ OSMO_PFCP_IEI_UPD_NON_3GPP_ACCESS_FORW_ACTION_INFO, "Update Non 3GPP Access Forwarding Action Information" },
|
||||
{ OSMO_PFCP_IEI_UE_IP_ADDRESS_POOL_IDENTITY, "UE IP address Pool Identity" },
|
||||
{ OSMO_PFCP_IEI_ALTERNATIVE_SMF_IP_ADDRESS, "Alternative SMF IP Address" },
|
||||
{ OSMO_PFCP_IEI_PACKET_REPLICATION_AND_DETECTION_CARRY_ON_INFO, "Packet Replication and Detection Carry-On Information" },
|
||||
{ OSMO_PFCP_IEI_SMF_SET_ID, "SMF Set ID" },
|
||||
{ OSMO_PFCP_IEI_QUOTA_VALIDITY_TIME, "Quota Validity Time" },
|
||||
{ OSMO_PFCP_IEI_NUMBER_OF_REPS, "Number of Reports" },
|
||||
{ OSMO_PFCP_IEI_PFCP_SESS_RETENTION_INFO_IN_ASSOC_SETUP_REQ, "PFCP Session Retention Information (within PFCP Association Setup Request)" },
|
||||
{ OSMO_PFCP_IEI_PFCPASRSP_FLAGS, "PFCPASRsp-Flags" },
|
||||
{ OSMO_PFCP_IEI_CP_ENTITY_IP_ADDRESS, "CP PFCP Entity IP Address" },
|
||||
{ OSMO_PFCP_IEI_PFCPSEREQ_FLAGS, "PFCPSEReq-Flags" },
|
||||
{ OSMO_PFCP_IEI_USER_PLANE_PATH_RECOVERY_REP, "User Plane Path Recovery Report" },
|
||||
{ OSMO_PFCP_IEI_IP_MULTICAST_ADDR_INFO_IN_SESS_EST_REQ, "IP Multicast Addressing Info within PFCP Session Establishment Request" },
|
||||
{ OSMO_PFCP_IEI_JOIN_IP_MULTICAST_INFO_IE_IN_USAGE_REP, "Join IP Multicast Information IE within Usage Report" },
|
||||
{ OSMO_PFCP_IEI_LEAVE_IP_MULTICAST_INFO_IE_IN_USAGE_REP, "Leave IP Multicast Information IE within Usage Report" },
|
||||
{ OSMO_PFCP_IEI_IP_MULTICAST_ADDRESS, "IP Multicast Address" },
|
||||
{ OSMO_PFCP_IEI_SOURCE_IP_ADDRESS, "Source IP Address" },
|
||||
{ OSMO_PFCP_IEI_PACKET_RATE_STATUS, "Packet Rate Status" },
|
||||
{ OSMO_PFCP_IEI_CREATE_BRIDGE_INFO_FOR_TSC, "Create Bridge Info for TSC" },
|
||||
{ OSMO_PFCP_IEI_CREATED_BRIDGE_INFO_FOR_TSC, "Created Bridge Info for TSC" },
|
||||
{ OSMO_PFCP_IEI_DS_TT_PORT_NUMBER, "DS-TT Port Number" },
|
||||
{ OSMO_PFCP_IEI_NW_TT_PORT_NUMBER, "NW-TT Port Number" },
|
||||
{ OSMO_PFCP_IEI_TSN_BRIDGE_ID, "TSN Bridge ID" },
|
||||
{ OSMO_PFCP_IEI_TSC_MGMT_INFO_IE_IN_SESS_MOD_REQ, "TSC Management Information IE within PFCP Session Modification Request" },
|
||||
{ OSMO_PFCP_IEI_TSC_MGMT_INFO_IE_IN_SESS_MOD_RESP, "TSC Management Information IE within PFCP Session Modification Response" },
|
||||
{ OSMO_PFCP_IEI_TSC_MGMT_INFO_IE_IN_SESS_REP_REQ, "TSC Management Information IE within PFCP Session Report Request" },
|
||||
{ OSMO_PFCP_IEI_PORT_MGMT_INFO_CONTAINER, "Port Management Information Container" },
|
||||
{ OSMO_PFCP_IEI_CLOCK_DRIFT_CTRL_INFO, "Clock Drift Control Information" },
|
||||
{ OSMO_PFCP_IEI_REQUESTED_CLOCK_DRIFT_INFO, "Requested Clock Drift Information" },
|
||||
{ OSMO_PFCP_IEI_CLOCK_DRIFT_REP, "Clock Drift Report" },
|
||||
{ OSMO_PFCP_IEI_TSN_TIME_DOMAIN_NUMBER, "TSN Time Domain Number" },
|
||||
{ OSMO_PFCP_IEI_TIME_OFFSET_THRESH, "Time Offset Threshold" },
|
||||
{ OSMO_PFCP_IEI_CUMULATIVE_RATERATIO_THRESH, "Cumulative rateRatio Threshold" },
|
||||
{ OSMO_PFCP_IEI_TIME_OFFSET_MEAS, "Time Offset Measurement" },
|
||||
{ OSMO_PFCP_IEI_CUMULATIVE_RATERATIO_MEAS, "Cumulative rateRatio Measurement" },
|
||||
{ OSMO_PFCP_IEI_REMOVE_SRR, "Remove SRR" },
|
||||
{ OSMO_PFCP_IEI_CREATE_SRR, "Create SRR" },
|
||||
{ OSMO_PFCP_IEI_UPD_SRR, "Update SRR" },
|
||||
{ OSMO_PFCP_IEI_SESS_REP, "Session Report" },
|
||||
{ OSMO_PFCP_IEI_SRR_ID, "SRR ID" },
|
||||
{ OSMO_PFCP_IEI_ACCESS_AVAIL_CTRL_INFO, "Access Availability Control Information" },
|
||||
{ OSMO_PFCP_IEI_REQUESTED_ACCESS_AVAIL_INFO, "Requested Access Availability Information" },
|
||||
{ OSMO_PFCP_IEI_ACCESS_AVAIL_REP, "Access Availability Report" },
|
||||
{ OSMO_PFCP_IEI_ACCESS_AVAIL_INFO, "Access Availability Information" },
|
||||
{ OSMO_PFCP_IEI_PROVIDE_ATSSS_CTRL_INFO, "Provide ATSSS Control Information" },
|
||||
{ OSMO_PFCP_IEI_ATSSS_CTRL_PARAMS, "ATSSS Control Parameters" },
|
||||
{ OSMO_PFCP_IEI_MPTCP_CTRL_INFO, "MPTCP Control Information" },
|
||||
{ OSMO_PFCP_IEI_ATSSS_LL_CTRL_INFO, "ATSSS-LL Control Information" },
|
||||
{ OSMO_PFCP_IEI_PMF_CTRL_INFO, "PMF Control Information" },
|
||||
{ OSMO_PFCP_IEI_MPTCP_PARAMS, "MPTCP Parameters" },
|
||||
{ OSMO_PFCP_IEI_ATSSS_LL_PARAMS, "ATSSS-LL Parameters" },
|
||||
{ OSMO_PFCP_IEI_PMF_PARAMS, "PMF Parameters" },
|
||||
{ OSMO_PFCP_IEI_MPTCP_ADDRESS_INFO, "MPTCP Address Information" },
|
||||
{ OSMO_PFCP_IEI_UE_LINK_SPECIFIC_IP_ADDRESS, "UE Link-Specific IP Address" },
|
||||
{ OSMO_PFCP_IEI_PMF_ADDRESS_INFO, "PMF Address Information" },
|
||||
{ OSMO_PFCP_IEI_ATSSS_LL_INFO, "ATSSS-LL Information" },
|
||||
{ OSMO_PFCP_IEI_DATA_NETWORK_ACCESS_IDENTIFIER, "Data Network Access Identifier" },
|
||||
{ OSMO_PFCP_IEI_UE_IP_ADDRESS_POOL_INFO, "UE IP address Pool Information" },
|
||||
{ OSMO_PFCP_IEI_AVERAGE_PACKET_DELAY, "Average Packet Delay" },
|
||||
{ OSMO_PFCP_IEI_MIN_PACKET_DELAY, "Minimum Packet Delay" },
|
||||
{ OSMO_PFCP_IEI_MAX_PACKET_DELAY, "Maximum Packet Delay" },
|
||||
{ OSMO_PFCP_IEI_QOS_REP_TRIGGER, "QoS Report Trigger" },
|
||||
{ OSMO_PFCP_IEI_GTP_U_PATH_QOS_CTRL_INFO, "GTP-U Path QoS Control Information" },
|
||||
{ OSMO_PFCP_IEI_GTP_U_PATH_QOS_REP_NODE_REP_REQ, "GTP-U Path QoS Report (PFCP Node Report Request)" },
|
||||
{ OSMO_PFCP_IEI_QOS_INFO_IN_GTP_U_PATH_QOS_REP, "QoS Information in GTP-U Path QoS Report" },
|
||||
{ OSMO_PFCP_IEI_GTP_U_PATH_IFACE_TYPE, "GTP-U Path Interface Type" },
|
||||
{ OSMO_PFCP_IEI_QOS_MONITORING_PER_QOS_FLOW_CTRL_INFO, "QoS Monitoring per QoS flow Control Information" },
|
||||
{ OSMO_PFCP_IEI_REQUESTED_QOS_MONITORING, "Requested QoS Monitoring" },
|
||||
{ OSMO_PFCP_IEI_REPORTING_FREQUENCY, "Reporting Frequency" },
|
||||
{ OSMO_PFCP_IEI_PACKET_DELAY_THRESHOLDS, "Packet Delay Thresholds" },
|
||||
{ OSMO_PFCP_IEI_MIN_WAIT_TIME, "Minimum Wait Time" },
|
||||
{ OSMO_PFCP_IEI_QOS_MONITORING_REP, "QoS Monitoring Report" },
|
||||
{ OSMO_PFCP_IEI_QOS_MONITORING_MEAS, "QoS Monitoring Measurement" },
|
||||
{ OSMO_PFCP_IEI_MT_EDT_CTRL_INFO, "MT-EDT Control Information" },
|
||||
{ OSMO_PFCP_IEI_DL_DATA_PACKETS_SIZE, "DL Data Packets Size" },
|
||||
{ OSMO_PFCP_IEI_QER_CTRL_INDICATIONS, "QER Control Indications" },
|
||||
{ OSMO_PFCP_IEI_PACKET_RATE_STATUS_REP, "Packet Rate Status Report" },
|
||||
{ OSMO_PFCP_IEI_NF_INST_ID, "NF Instance ID" },
|
||||
{ OSMO_PFCP_IEI_ETHERNET_CONTEXT_INFO, "Ethernet Context Information" },
|
||||
{ OSMO_PFCP_IEI_REDUNDANT_TRANSMISSION_PARAMS, "Redundant Transmission Parameters" },
|
||||
{ OSMO_PFCP_IEI_UPDATED_PDR, "Updated PDR" },
|
||||
{ OSMO_PFCP_IEI_S_NSSAI, "S-NSSAI" },
|
||||
{ OSMO_PFCP_IEI_IP_VERSION, "IP version" },
|
||||
{ OSMO_PFCP_IEI_PFCPASREQ_FLAGS, "PFCPASReq-Flags" },
|
||||
{ OSMO_PFCP_IEI_DATA_STATUS, "Data Status" },
|
||||
{ OSMO_PFCP_IEI_PROVIDE_RDS_CONF_INFO, "Provide RDS configuration information" },
|
||||
{ OSMO_PFCP_IEI_RDS_CONF_INFO, "RDS configuration information" },
|
||||
{ OSMO_PFCP_IEI_QUERY_PACKET_RATE_STATUS_IE_IN_SESS_MOD_REQ, "Query Packet Rate Status IE within PFCP Session Modification Request" },
|
||||
{ OSMO_PFCP_IEI_PACKET_RATE_STATUS_REP_IE_IN_SESS_MOD_RESP, "Packet Rate Status Report IE within PFCP Session Modification Response" },
|
||||
{ OSMO_PFCP_IEI_MPTCP_APPLICABLE_IND, "MPTCP Applicable Indication" },
|
||||
{ OSMO_PFCP_IEI_BRIDGE_MGMT_INFO_CONTAINER, "Bridge Management Information Container" },
|
||||
{ OSMO_PFCP_IEI_UE_IP_ADDRESS_USAGE_INFO, "UE IP Address Usage Information" },
|
||||
{ OSMO_PFCP_IEI_NUMBER_OF_UE_IP_ADDRS, "Number of UE IP Addresses" },
|
||||
{ OSMO_PFCP_IEI_VALIDITY_TIMER, "Validity Timer" },
|
||||
{ OSMO_PFCP_IEI_REDUNDANT_TRANSMISSION_FORW_PARAMS, "Redundant Transmission Forwarding Parameters" },
|
||||
{ OSMO_PFCP_IEI_TRANSPORT_DELAY_REPORTING, "Transport Delay Reporting" },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
const struct value_string osmo_pfcp_cause_strs[] = {
|
||||
{ OSMO_PFCP_CAUSE_RESERVED, "0" },
|
||||
{ OSMO_PFCP_CAUSE_REQUEST_ACCEPTED, "Request accepted (success)" },
|
||||
{ OSMO_PFCP_CAUSE_MORE_USAGE_REPORT_TO_SEND, "More Usage Report to send" },
|
||||
{ OSMO_PFCP_CAUSE_REQUEST_REJECTED, "Request rejected (reason not specified)" },
|
||||
{ OSMO_PFCP_CAUSE_SESSION_CTX_NOT_FOUND, "Session context not found" },
|
||||
{ OSMO_PFCP_CAUSE_MANDATORY_IE_MISSING, "Mandatory IE missing" },
|
||||
{ OSMO_PFCP_CAUSE_CONDITIONAL_IE_MISSING, "Conditional IE missing" },
|
||||
{ OSMO_PFCP_CAUSE_INVALID_LENGTH, "Invalid length" },
|
||||
{ OSMO_PFCP_CAUSE_MANDATORY_IE_INCORRECT, "Mandatory IE incorrect" },
|
||||
{ OSMO_PFCP_CAUSE_INVALID_FORW_POLICY, "Invalid Forwarding Policy" },
|
||||
{ OSMO_PFCP_CAUSE_INVALID_F_TEID_ALLOC_OPTION, "Invalid F-TEID allocation option" },
|
||||
{ OSMO_PFCP_CAUSE_NO_ESTABLISHED_PFCP_ASSOC, "No established PFCP Association" },
|
||||
{ OSMO_PFCP_CAUSE_RULE_CREATION_MOD_FAILURE, "Rule creation/modification Failure" },
|
||||
{ OSMO_PFCP_CAUSE_PFCP_ENTITY_IN_CONGESTION, "PFCP entity in congestion" },
|
||||
{ OSMO_PFCP_CAUSE_NO_RESOURCES_AVAILABLE, "No resources available" },
|
||||
{ OSMO_PFCP_CAUSE_SERVICE_NOT_SUPPORTED, "Service not supported" },
|
||||
{ OSMO_PFCP_CAUSE_SYSTEM_FAILURE, "System failure" },
|
||||
{ OSMO_PFCP_CAUSE_REDIRECTION_REQUESTED, "Redirection Requested" },
|
||||
{ OSMO_PFCP_CAUSE_ALL_DYNAMIC_ADDRESSES_ARE_OCCUPIED, "All dynamic addresses are occupied" },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
const struct value_string osmo_pfcp_up_feature_strs[] = {
|
||||
{ OSMO_PFCP_UP_FEAT_BUCP, "BUCP" },
|
||||
{ OSMO_PFCP_UP_FEAT_DDND, "DDND" },
|
||||
{ OSMO_PFCP_UP_FEAT_DLBD, "DLBD" },
|
||||
{ OSMO_PFCP_UP_FEAT_TRST, "TRST" },
|
||||
{ OSMO_PFCP_UP_FEAT_FTUP, "FTUP" },
|
||||
{ OSMO_PFCP_UP_FEAT_PFDM, "PFDM" },
|
||||
{ OSMO_PFCP_UP_FEAT_HEEU, "HEEU" },
|
||||
{ OSMO_PFCP_UP_FEAT_TREU, "TREU" },
|
||||
{ OSMO_PFCP_UP_FEAT_EMPU, "EMPU" },
|
||||
{ OSMO_PFCP_UP_FEAT_PDIU, "PDIU" },
|
||||
{ OSMO_PFCP_UP_FEAT_UDBC, "UDBC" },
|
||||
{ OSMO_PFCP_UP_FEAT_QUOAC, "QUOAC" },
|
||||
{ OSMO_PFCP_UP_FEAT_TRACE, "TRACE" },
|
||||
{ OSMO_PFCP_UP_FEAT_FRRT, "FRRT" },
|
||||
{ OSMO_PFCP_UP_FEAT_PFDE, "PFDE" },
|
||||
{ OSMO_PFCP_UP_FEAT_EPFAR, "EPFAR" },
|
||||
{ OSMO_PFCP_UP_FEAT_DPDRA, "DPDRA" },
|
||||
{ OSMO_PFCP_UP_FEAT_ADPDP, "ADPDP" },
|
||||
{ OSMO_PFCP_UP_FEAT_UEIP, "UEIP" },
|
||||
{ OSMO_PFCP_UP_FEAT_SSET, "SSET" },
|
||||
{ OSMO_PFCP_UP_FEAT_MNOP, "MNOP" },
|
||||
{ OSMO_PFCP_UP_FEAT_MTE, "MTE" },
|
||||
{ OSMO_PFCP_UP_FEAT_BUNDL, "BUNDL" },
|
||||
{ OSMO_PFCP_UP_FEAT_GCOM, "GCOM" },
|
||||
{ OSMO_PFCP_UP_FEAT_MPAS, "MPAS" },
|
||||
{ OSMO_PFCP_UP_FEAT_RTTL, "RTTL" },
|
||||
{ OSMO_PFCP_UP_FEAT_VTIME, "VTIME" },
|
||||
{ OSMO_PFCP_UP_FEAT_NORP, "NORP" },
|
||||
{ OSMO_PFCP_UP_FEAT_IP6PL, "IP6PL" },
|
||||
{ OSMO_PFCP_UP_FEAT_TSCU, "TSCU" },
|
||||
{ OSMO_PFCP_UP_FEAT_MPTCP, "MPTCP" },
|
||||
{ OSMO_PFCP_UP_FEAT_ATSSSLL, "ATSSSLL" },
|
||||
{ OSMO_PFCP_UP_FEAT_QFQM, "QFQM" },
|
||||
{ OSMO_PFCP_UP_FEAT_GPQM, "GPQM" },
|
||||
{ OSMO_PFCP_UP_FEAT_MTEDT, "MTEDT" },
|
||||
{ OSMO_PFCP_UP_FEAT_CIOT, "CIOT" },
|
||||
{ OSMO_PFCP_UP_FEAT_ETHAR, "ETHAR" },
|
||||
{ OSMO_PFCP_UP_FEAT_DDDS, "DDDS" },
|
||||
{ OSMO_PFCP_UP_FEAT_RDS, "RDS" },
|
||||
{ OSMO_PFCP_UP_FEAT_RTTWP, "RTTWP" },
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
const struct value_string osmo_pfcp_cp_feature_strs[] = {
|
||||
{ OSMO_PFCP_CP_FEAT_LOAD, "LOAD" },
|
||||
{ OSMO_PFCP_CP_FEAT_OVRL, "OVRL" },
|
||||
{ OSMO_PFCP_CP_FEAT_EPFAR, "EPFAR" },
|
||||
{ OSMO_PFCP_CP_FEAT_SSET, "SSET" },
|
||||
{ OSMO_PFCP_CP_FEAT_BUNDL, "BUNDL" },
|
||||
{ OSMO_PFCP_CP_FEAT_MPAS, "MPAS" },
|
||||
{ OSMO_PFCP_CP_FEAT_ARDR, "ARDR" },
|
||||
{ OSMO_PFCP_CP_FEAT_UIAUR, "UIAUR" },
|
||||
{}
|
||||
};
|
||||
|
||||
const struct value_string osmo_pfcp_apply_action_strs[] = {
|
||||
{ OSMO_PFCP_APPLY_ACTION_DROP, "DROP" },
|
||||
{ OSMO_PFCP_APPLY_ACTION_FORW, "FORW" },
|
||||
{ OSMO_PFCP_APPLY_ACTION_BUFF, "BUFF" },
|
||||
{ OSMO_PFCP_APPLY_ACTION_NOCP, "NOCP" },
|
||||
{ OSMO_PFCP_APPLY_ACTION_DUPL, "DUPL" },
|
||||
{ OSMO_PFCP_APPLY_ACTION_IPMA, "IPMA" },
|
||||
{ OSMO_PFCP_APPLY_ACTION_IPMD, "IPMD" },
|
||||
{ OSMO_PFCP_APPLY_ACTION_DFRT, "DFRT" },
|
||||
{ OSMO_PFCP_APPLY_ACTION_EDRT, "EDRT" },
|
||||
{ OSMO_PFCP_APPLY_ACTION_BDPN, "BDPN" },
|
||||
{ OSMO_PFCP_APPLY_ACTION_DDPN, "DDPN" },
|
||||
{}
|
||||
};
|
||||
|
||||
const struct value_string osmo_pfcp_outer_header_creation_strs[] = {
|
||||
{ OSMO_PFCP_OUTER_HEADER_CREATION_GTP_U_UDP_IPV4, "GTP_U_UDP_IPV4" },
|
||||
{ OSMO_PFCP_OUTER_HEADER_CREATION_GTP_U_UDP_IPV6, "GTP_U_UDP_IPV6" },
|
||||
{ OSMO_PFCP_OUTER_HEADER_CREATION_UDP_IPV4, "UDP_IPV4" },
|
||||
{ OSMO_PFCP_OUTER_HEADER_CREATION_UDP_IPV6, "UDP_IPV6" },
|
||||
{ OSMO_PFCP_OUTER_HEADER_CREATION_IPV4, "IPV4" },
|
||||
{ OSMO_PFCP_OUTER_HEADER_CREATION_IPV6, "IPV6" },
|
||||
{ OSMO_PFCP_OUTER_HEADER_CREATION_C_TAG, "C_TAG" },
|
||||
{ OSMO_PFCP_OUTER_HEADER_CREATION_S_TAG, "S_TAG" },
|
||||
{ OSMO_PFCP_OUTER_HEADER_CREATION_N19_INDICATION, "N19_INDICATION" },
|
||||
{ OSMO_PFCP_OUTER_HEADER_CREATION_N6_INDICATION, "N6_INDICATION" },
|
||||
{}
|
||||
};
|
||||
|
||||
const struct value_string osmo_pfcp_outer_header_removal_desc_strs[] = {
|
||||
{ OSMO_PFCP_OUTER_HEADER_REMOVAL_GTP_U_UDP_IPV4, "GTP_U_UDP_IPV4" },
|
||||
{ OSMO_PFCP_OUTER_HEADER_REMOVAL_GTP_U_UDP_IPV6, "GTP_U_UDP_IPV6" },
|
||||
{ OSMO_PFCP_OUTER_HEADER_REMOVAL_UDP_IPV4, "UDP_IPV4" },
|
||||
{ OSMO_PFCP_OUTER_HEADER_REMOVAL_UDP_IPV6, "UDP_IPV6" },
|
||||
{ OSMO_PFCP_OUTER_HEADER_REMOVAL_IPV4, "IPV4" },
|
||||
{ OSMO_PFCP_OUTER_HEADER_REMOVAL_IPV6, "IPV6" },
|
||||
{ OSMO_PFCP_OUTER_HEADER_REMOVAL_GTP_U_UDP_IP, "GTP_U_UDP_IP" },
|
||||
{ OSMO_PFCP_OUTER_HEADER_REMOVAL_VLAN_S_TAG, "VLAN_S_TAG" },
|
||||
{ OSMO_PFCP_OUTER_HEADER_REMOVAL_S_TAG_AND_C_TAG, "S_TAG_AND_C_TAG" },
|
||||
{}
|
||||
};
|
||||
|
||||
const struct value_string osmo_pfcp_source_iface_strs[] = {
|
||||
{ OSMO_PFCP_SOURCE_IFACE_ACCESS, "Access" },
|
||||
{ OSMO_PFCP_SOURCE_IFACE_CORE, "Core" },
|
||||
{ OSMO_PFCP_SOURCE_IFACE_SGI_LAN_N6_LAN, "SGi-LAN/N6-LAN" },
|
||||
{ OSMO_PFCP_SOURCE_IFACE_CP_FUNCTION, "CP-function" },
|
||||
{ OSMO_PFCP_SOURCE_IFACE_5G_VN_INTERNAL, "5G-VN-Internal" },
|
||||
{}
|
||||
};
|
||||
|
||||
const struct value_string osmo_pfcp_dest_iface_strs[] = {
|
||||
{ OSMO_PFCP_DEST_IFACE_ACCESS, "Access" },
|
||||
{ OSMO_PFCP_DEST_IFACE_CORE, "Core" },
|
||||
{ OSMO_PFCP_DEST_IFACE_SGI_LAN_N6_LAN, "SGi-LAN/N6-LAN" },
|
||||
{ OSMO_PFCP_DEST_IFACE_CP_FUNCTION, "CP-function" },
|
||||
{ OSMO_PFCP_DEST_IFACE_LI_FUNCTION, "LI-function" },
|
||||
{ OSMO_PFCP_DEST_IFACE_5G_VN_INTERNAL, "5G-VN-Internal" },
|
||||
{}
|
||||
};
|
||||
|
||||
const struct value_string osmo_pfcp_3gpp_iface_type_strs[] = {
|
||||
{ OSMO_PFCP_3GPP_IFACE_TYPE_S1_U, "S1_U" },
|
||||
{ OSMO_PFCP_3GPP_IFACE_TYPE_S5_S8_U, "S5_S8_U" },
|
||||
{ OSMO_PFCP_3GPP_IFACE_TYPE_S4_U, "S4_U" },
|
||||
{ OSMO_PFCP_3GPP_IFACE_TYPE_S11_U, "S11_U" },
|
||||
{ OSMO_PFCP_3GPP_IFACE_TYPE_S12_U, "S12_U" },
|
||||
{ OSMO_PFCP_3GPP_IFACE_TYPE_GN_GP_U, "GN_GP_U" },
|
||||
{ OSMO_PFCP_3GPP_IFACE_TYPE_S2A_U, "S2A_U" },
|
||||
{ OSMO_PFCP_3GPP_IFACE_TYPE_S2B_U, "S2B_U" },
|
||||
{ OSMO_PFCP_3GPP_IFACE_TYPE_ENODEB_GTP_U_INTERFACE_FOR_DL_DATA_FORWARDING, "ENODEB_GTP_U_INTERFACE_FOR_DL_DATA_FORWARDING" },
|
||||
{ OSMO_PFCP_3GPP_IFACE_TYPE_ENODEB_GTP_U_INTERFACE_FOR_UL_DATA_FORWARDING, "ENODEB_GTP_U_INTERFACE_FOR_UL_DATA_FORWARDING" },
|
||||
{ OSMO_PFCP_3GPP_IFACE_TYPE_SGW_UPF_GTP_U_INTERFACE_FOR_DL_DATA_FORWARDING, "SGW_UPF_GTP_U_INTERFACE_FOR_DL_DATA_FORWARDING" },
|
||||
{ OSMO_PFCP_3GPP_IFACE_TYPE_N3_3GPP_ACCESS, "N3_3GPP_ACCESS" },
|
||||
{ OSMO_PFCP_3GPP_IFACE_TYPE_N3_TRUSTED_NON_3GPP_ACCESS, "N3_TRUSTED_NON_3GPP_ACCESS" },
|
||||
{ OSMO_PFCP_3GPP_IFACE_TYPE_N3_UNTRUSTED_NON_3GPP_ACCESS, "N3_UNTRUSTED_NON_3GPP_ACCESS" },
|
||||
{ OSMO_PFCP_3GPP_IFACE_TYPE_N3_FOR_DATA_FORWARDING, "N3_FOR_DATA_FORWARDING" },
|
||||
{ OSMO_PFCP_3GPP_IFACE_TYPE_N9, "N9" },
|
||||
{ OSMO_PFCP_3GPP_IFACE_TYPE_SGI, "SGI" },
|
||||
{ OSMO_PFCP_3GPP_IFACE_TYPE_N6, "N6" },
|
||||
{ OSMO_PFCP_3GPP_IFACE_TYPE_N19, "N19" },
|
||||
{ OSMO_PFCP_3GPP_IFACE_TYPE_S8_U, "S8_U" },
|
||||
{ OSMO_PFCP_3GPP_IFACE_TYPE_GP_U, "GP_U" },
|
||||
{}
|
||||
};
|
||||
38
src/osmo-pfcp-tool/Makefile.am
Normal file
38
src/osmo-pfcp-tool/Makefile.am
Normal file
@@ -0,0 +1,38 @@
|
||||
AM_CPPFLAGS = \
|
||||
$(all_includes) \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(top_builddir)/include \
|
||||
-I$(top_builddir) \
|
||||
$(NULL)
|
||||
|
||||
AM_CFLAGS = \
|
||||
-Wall \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(LIBOSMOVTY_CFLAGS) \
|
||||
$(LIBOSMOCTRL_CFLAGS) \
|
||||
$(LIBOSMOPFCP_CFLAGS) \
|
||||
$(LIBURING_CFLAGS) \
|
||||
$(COVERAGE_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
AM_LDFLAGS = \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(LIBOSMOCTRL_LIBS) \
|
||||
$(LIBOSMOPFCP_LIBS) \
|
||||
$(LIBURING_LIBS) \
|
||||
$(COVERAGE_LDFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
bin_PROGRAMS = \
|
||||
osmo-pfcp-tool \
|
||||
$(NULL)
|
||||
|
||||
osmo_pfcp_tool_SOURCES = \
|
||||
checksum.c \
|
||||
gtp_flood.c \
|
||||
osmo_pfcp_tool_main.c \
|
||||
pfcp_tool.c \
|
||||
pfcp_tool_vty.c \
|
||||
range.c \
|
||||
$(NULL)
|
||||
211
src/osmo-pfcp-tool/checksum.c
Normal file
211
src/osmo-pfcp-tool/checksum.c
Normal file
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
*
|
||||
* INET An implementation of the TCP/IP protocol suite for the LINUX
|
||||
* operating system. INET is implemented using the BSD Socket
|
||||
* interface as the means of communication with the user level.
|
||||
*
|
||||
* IP/TCP/UDP checksumming routines
|
||||
*
|
||||
* Authors: Jorge Cwik, <jorge@laser.satlink.net>
|
||||
* Arnt Gulbrandsen, <agulbra@nvg.unit.no>
|
||||
* Tom May, <ftom@netcom.com>
|
||||
* Andreas Schwab, <schwab@issan.informatik.uni-dortmund.de>
|
||||
* Lots of code moved from tcp.c and ip.c; see those files
|
||||
* for more names.
|
||||
*
|
||||
* 03/02/96 Jes Sorensen, Andreas Schwab, Roman Hodek:
|
||||
* Fixed some nasty bugs, causing some horrible crashes.
|
||||
* A: At some points, the sum (%0) was used as
|
||||
* length-counter instead of the length counter
|
||||
* (%1). Thanks to Roman Hodek for pointing this out.
|
||||
* B: GCC seems to mess up if one uses too many
|
||||
* data-registers to hold input values and one tries to
|
||||
* specify d0 and d1 as scratch registers. Letting gcc
|
||||
* choose these registers itself solves the problem.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
/* Revised by Kenneth Albanowski for m68knommu. Basic problem: unaligned access
|
||||
kills, so most of the assembly has to go. */
|
||||
|
||||
#if defined(__FreeBSD__)
|
||||
#define _KERNEL /* needed on FreeBSD 10.x for s6_addr32 */
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/endian.h>
|
||||
#endif
|
||||
|
||||
#include <osmocom/pfcptool/checksum.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
static inline unsigned short from32to16(unsigned int x)
|
||||
{
|
||||
/* add up 16-bit and 16-bit for 16+c bit */
|
||||
x = (x & 0xffff) + (x >> 16);
|
||||
/* add up carry.. */
|
||||
x = (x & 0xffff) + (x >> 16);
|
||||
return x;
|
||||
}
|
||||
|
||||
static unsigned int do_csum(const unsigned char *buff, int len)
|
||||
{
|
||||
int odd;
|
||||
unsigned int result = 0;
|
||||
|
||||
if (len <= 0)
|
||||
goto out;
|
||||
odd = 1 & (unsigned long) buff;
|
||||
if (odd) {
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN
|
||||
result += (*buff << 8);
|
||||
#else
|
||||
result = *buff;
|
||||
#endif
|
||||
len--;
|
||||
buff++;
|
||||
}
|
||||
if (len >= 2) {
|
||||
if (2 & (unsigned long) buff) {
|
||||
result += *(unsigned short *) buff;
|
||||
len -= 2;
|
||||
buff += 2;
|
||||
}
|
||||
if (len >= 4) {
|
||||
const unsigned char *end = buff + ((unsigned)len & ~3);
|
||||
unsigned int carry = 0;
|
||||
do {
|
||||
unsigned int w = *(unsigned int *) buff;
|
||||
buff += 4;
|
||||
result += carry;
|
||||
result += w;
|
||||
carry = (w > result);
|
||||
} while (buff < end);
|
||||
result += carry;
|
||||
result = (result & 0xffff) + (result >> 16);
|
||||
}
|
||||
if (len & 2) {
|
||||
result += *(unsigned short *) buff;
|
||||
buff += 2;
|
||||
}
|
||||
}
|
||||
if (len & 1)
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN
|
||||
result += *buff;
|
||||
#else
|
||||
result += (*buff << 8);
|
||||
#endif
|
||||
result = from32to16(result);
|
||||
if (odd)
|
||||
result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a version of ip_compute_csum() optimized for IP headers,
|
||||
* which always checksum on 4 octet boundaries.
|
||||
*/
|
||||
uint16_t ip_fast_csum(const void *iph, unsigned int ihl)
|
||||
{
|
||||
return (uint16_t)~do_csum(iph, ihl*4);
|
||||
}
|
||||
|
||||
/*
|
||||
* computes the checksum of a memory block at buff, length len,
|
||||
* and adds in "sum" (32-bit)
|
||||
*
|
||||
* returns a 32-bit number suitable for feeding into itself
|
||||
* or csum_tcpudp_magic
|
||||
*
|
||||
* this function must be called with even lengths, except
|
||||
* for the last fragment, which may be odd
|
||||
*
|
||||
* it's best to have buff aligned on a 32-bit boundary
|
||||
*/
|
||||
uint32_t csum_partial(const void *buff, int len, uint32_t wsum)
|
||||
{
|
||||
unsigned int sum = (unsigned int)wsum;
|
||||
unsigned int result = do_csum(buff, len);
|
||||
|
||||
/* add in old sum, and carry.. */
|
||||
result += sum;
|
||||
if (sum > result)
|
||||
result += 1;
|
||||
return (uint32_t)result;
|
||||
}
|
||||
|
||||
/*
|
||||
* this routine is used for miscellaneous IP-like checksums, mainly
|
||||
* in icmp.c
|
||||
*/
|
||||
uint16_t ip_compute_csum(const void *buff, int len)
|
||||
{
|
||||
return (uint16_t)~do_csum(buff, len);
|
||||
}
|
||||
|
||||
uint16_t csum_ipv6_magic(const struct in6_addr *saddr,
|
||||
const struct in6_addr *daddr,
|
||||
uint32_t len, uint8_t proto, uint32_t csum)
|
||||
{
|
||||
int carry;
|
||||
uint32_t ulen;
|
||||
uint32_t uproto;
|
||||
uint32_t sum = (uint32_t)csum;
|
||||
|
||||
sum += (uint32_t)saddr->s6_addr32[0];
|
||||
carry = (sum < (uint32_t)saddr->s6_addr32[0]);
|
||||
sum += carry;
|
||||
|
||||
sum += (uint32_t)saddr->s6_addr32[1];
|
||||
carry = (sum < (uint32_t)saddr->s6_addr32[1]);
|
||||
sum += carry;
|
||||
|
||||
sum += (uint32_t)saddr->s6_addr32[2];
|
||||
carry = (sum < (uint32_t)saddr->s6_addr32[2]);
|
||||
sum += carry;
|
||||
|
||||
sum += (uint32_t)saddr->s6_addr32[3];
|
||||
carry = (sum < (uint32_t)saddr->s6_addr32[3]);
|
||||
sum += carry;
|
||||
|
||||
sum += (uint32_t)daddr->s6_addr32[0];
|
||||
carry = (sum < (uint32_t)daddr->s6_addr32[0]);
|
||||
sum += carry;
|
||||
|
||||
sum += (uint32_t)daddr->s6_addr32[1];
|
||||
carry = (sum < (uint32_t)daddr->s6_addr32[1]);
|
||||
sum += carry;
|
||||
|
||||
sum += (uint32_t)daddr->s6_addr32[2];
|
||||
carry = (sum < (uint32_t)daddr->s6_addr32[2]);
|
||||
sum += carry;
|
||||
|
||||
sum += (uint32_t)daddr->s6_addr32[3];
|
||||
carry = (sum < (uint32_t)daddr->s6_addr32[3]);
|
||||
sum += carry;
|
||||
|
||||
ulen = (uint32_t)htonl((uint32_t) len);
|
||||
sum += ulen;
|
||||
carry = (sum < ulen);
|
||||
sum += carry;
|
||||
|
||||
uproto = (uint32_t)htonl(proto);
|
||||
sum += uproto;
|
||||
carry = (sum < uproto);
|
||||
sum += carry;
|
||||
|
||||
return csum_fold((uint32_t)sum);
|
||||
}
|
||||
|
||||
/* fold a partial checksum */
|
||||
uint16_t csum_fold(uint32_t csum)
|
||||
{
|
||||
uint32_t sum = (uint32_t)csum;
|
||||
sum = (sum & 0xffff) + (sum >> 16);
|
||||
sum = (sum & 0xffff) + (sum >> 16);
|
||||
return (uint16_t)~sum;
|
||||
}
|
||||
368
src/osmo-pfcp-tool/gtp_flood.c
Normal file
368
src/osmo-pfcp-tool/gtp_flood.c
Normal file
@@ -0,0 +1,368 @@
|
||||
/* GTP-U traffic/load generator. Generates a configurable amount of UDP/IP flows using io_uring.
|
||||
*
|
||||
* Based on gtp-load-gen.c from https://gitea.osmocom.org/cellular-infrastructure/gtp-load-gen
|
||||
* which is marked (C) 2021 by Harald Welte <laforge@osmocom.org>
|
||||
*/
|
||||
/*
|
||||
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include "config.h"
|
||||
|
||||
#include <osmocom/pfcptool/gtp_flood.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
#if HAVE_URING
|
||||
|
||||
#include <liburing.h>
|
||||
#include <pthread.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/ip6.h>
|
||||
#include <netinet/udp.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/pfcptool/pfcp_tool.h>
|
||||
#include <osmocom/pfcptool/checksum.h>
|
||||
|
||||
/* number of packets per submit */
|
||||
#define SLOW 0
|
||||
#if SLOW
|
||||
#define SWEET_SPOT 3
|
||||
#else
|
||||
#define SWEET_SPOT 4000
|
||||
#endif
|
||||
|
||||
#define BUF_SIZE 1024
|
||||
|
||||
struct gtp_flood_worker;
|
||||
|
||||
struct gtp_flood {
|
||||
/* allocated array */
|
||||
struct gtp_flood_worker *workers;
|
||||
unsigned int workers_count;
|
||||
unsigned int next_worker;
|
||||
|
||||
int workers_started;
|
||||
int workers_running;
|
||||
};
|
||||
|
||||
struct gtp_flood_worker {
|
||||
/* backpointer */
|
||||
struct gtp_flood *gtp_flood;
|
||||
|
||||
/* list of struct gtp_flood_flow */
|
||||
struct llist_head flows;
|
||||
|
||||
struct io_uring ring;
|
||||
int fd;
|
||||
|
||||
pthread_t worker;
|
||||
};
|
||||
|
||||
struct gtp_flood_flow {
|
||||
struct llist_head entry;
|
||||
|
||||
/* backpointer */
|
||||
struct gtp_flood_worker *worker;
|
||||
|
||||
struct gtp_flood_flow_cfg cfg;
|
||||
|
||||
/* for logging and included in generated payloads */
|
||||
unsigned int id;
|
||||
|
||||
/* must live until completion */
|
||||
struct iovec iov[1];
|
||||
struct msghdr msgh;
|
||||
|
||||
/* flow-private packet buffer */
|
||||
uint8_t *pkt_buf;
|
||||
|
||||
unsigned int sent_gtp_packets;
|
||||
|
||||
bool stop;
|
||||
};
|
||||
|
||||
static void gtp_flood_worker_init(struct gtp_flood *gtp_flood, struct gtp_flood_worker *worker)
|
||||
{
|
||||
worker->gtp_flood = gtp_flood;
|
||||
INIT_LLIST_HEAD(&worker->flows);
|
||||
}
|
||||
|
||||
struct gtp_flood *gtp_flood_alloc(void *ctx, unsigned int workers)
|
||||
{
|
||||
struct gtp_flood *gtp_flood;
|
||||
int i;
|
||||
workers = OSMO_MAX(1, workers);
|
||||
|
||||
gtp_flood = talloc_zero(ctx, struct gtp_flood);
|
||||
*gtp_flood = (struct gtp_flood){
|
||||
.workers = talloc_array(gtp_flood, struct gtp_flood_worker, workers),
|
||||
.workers_count = workers,
|
||||
};
|
||||
for (i = 0; i < gtp_flood->workers_count; i++)
|
||||
gtp_flood_worker_init(gtp_flood, >p_flood->workers[i]);
|
||||
LOGP(DLGLOBAL, LOGL_NOTICE, "workers: %u\n", gtp_flood->workers_count);
|
||||
return gtp_flood;
|
||||
}
|
||||
|
||||
static void gtp_flood_flow_init_payload(struct gtp_flood_flow *flow)
|
||||
{
|
||||
struct gtp1u_hdr *gtp_hdr = (void *)flow->pkt_buf;
|
||||
|
||||
*gtp_hdr = (struct gtp1u_hdr) {
|
||||
.pn = 0,
|
||||
.s = 0,
|
||||
.e = 0,
|
||||
.spare = 0,
|
||||
.pt = 1,
|
||||
.version = 1,
|
||||
.type = 0xff, /* G-PDU */
|
||||
.length = 0, /* filled in later */
|
||||
.tei = htonl(flow->cfg.gtp_remote_teid),
|
||||
};
|
||||
|
||||
uint8_t *cur = flow->pkt_buf + sizeof(*gtp_hdr);
|
||||
|
||||
/* FIXME: randomize this */
|
||||
unsigned int udp_len = 1024;
|
||||
|
||||
struct iphdr *iph;
|
||||
struct ip6_hdr *ip6h;
|
||||
struct udphdr *uh;
|
||||
struct osmo_sockaddr *src = &flow->cfg.payload_src;
|
||||
struct osmo_sockaddr *dst = &flow->cfg.payload_dst;
|
||||
|
||||
if (src->u.sa.sa_family == AF_INET) {
|
||||
iph = (struct iphdr *) cur;
|
||||
cur += sizeof(*iph);
|
||||
|
||||
iph->ihl = 5;
|
||||
iph->version = 4;
|
||||
iph->tos = 0;
|
||||
iph->tot_len = htons(udp_len + sizeof(struct udphdr) + sizeof(*iph));
|
||||
iph->id = 0;
|
||||
iph->frag_off = 0;
|
||||
iph->ttl = 32;
|
||||
iph->protocol = IPPROTO_UDP;
|
||||
iph->saddr = src->u.sin.sin_addr.s_addr;
|
||||
iph->daddr = dst->u.sin.sin_addr.s_addr;
|
||||
iph->check = ip_fast_csum(iph, iph->ihl);
|
||||
} else {
|
||||
ip6h = (struct ip6_hdr *) cur;
|
||||
cur += sizeof(*ip6h);
|
||||
|
||||
ip6h->ip6_flow = htonl((6 << 28));
|
||||
ip6h->ip6_plen = htons(udp_len + sizeof(struct udphdr));
|
||||
ip6h->ip6_nxt = IPPROTO_UDP;
|
||||
ip6h->ip6_hlim = 32;
|
||||
ip6h->ip6_src = src->u.sin6.sin6_addr;
|
||||
ip6h->ip6_dst = dst->u.sin6.sin6_addr;
|
||||
}
|
||||
|
||||
uh = (struct udphdr *) cur;
|
||||
cur += sizeof(*uh);
|
||||
|
||||
uh->source = htons(osmo_sockaddr_port(&src->u.sa));
|
||||
uh->dest = htons(osmo_sockaddr_port(&dst->u.sa));
|
||||
uh->len = htons(udp_len);
|
||||
uh->check = 0; // TODO
|
||||
|
||||
gtp_hdr->length = htons(udp_len + (cur - flow->pkt_buf) - sizeof(*gtp_hdr));
|
||||
|
||||
/* initialize this once, so we have it ready for each transmit */
|
||||
flow->msgh.msg_name = &flow->cfg.gtp_remote.u.sa;
|
||||
flow->msgh.msg_namelen = sizeof(flow->cfg.gtp_remote.u.sa);
|
||||
flow->msgh.msg_iov = flow->iov;
|
||||
flow->msgh.msg_iovlen = ARRAY_SIZE(flow->iov);
|
||||
flow->msgh.msg_control = NULL;
|
||||
flow->msgh.msg_controllen = 0;
|
||||
flow->msgh.msg_flags = 0;
|
||||
|
||||
flow->iov[0].iov_base = flow->pkt_buf;
|
||||
flow->iov[0].iov_len = udp_len + (cur - flow->pkt_buf);
|
||||
|
||||
/* write some payload */
|
||||
struct osmo_strbuf sb = { .buf = (void *)cur, .len = udp_len };
|
||||
OSMO_STRBUF_PRINTF(sb, "osmo-pfcp-tool gtp flood, emitted from %s to %s teid 0x%08x flow %u\n",
|
||||
osmo_sockaddr_to_str_c(OTC_SELECT, &flow->cfg.gtp_local->osa),
|
||||
osmo_sockaddr_to_str_c(OTC_SELECT, &flow->cfg.gtp_remote),
|
||||
flow->cfg.gtp_remote_teid,
|
||||
flow->id);
|
||||
}
|
||||
|
||||
void gtp_flood_add_flow(struct gtp_flood *gtp_flood,
|
||||
const struct gtp_flood_flow_cfg *flow_cfg)
|
||||
{
|
||||
static unsigned int next_flow_id = 0;
|
||||
|
||||
struct gtp_flood_worker *worker;
|
||||
gtp_flood->next_worker %= gtp_flood->workers_count;
|
||||
worker = >p_flood->workers[gtp_flood->next_worker];
|
||||
gtp_flood->next_worker++;
|
||||
|
||||
struct gtp_flood_flow *flow = talloc_zero(gtp_flood, struct gtp_flood_flow);
|
||||
flow->cfg = *flow_cfg;
|
||||
flow->id = next_flow_id++;
|
||||
|
||||
flow->pkt_buf = talloc_zero_size(flow, BUF_SIZE);
|
||||
OSMO_ASSERT(flow->pkt_buf);
|
||||
|
||||
flow->worker = worker;
|
||||
llist_add_tail(&flow->entry, &worker->flows);
|
||||
|
||||
gtp_flood_flow_init_payload(flow);
|
||||
}
|
||||
|
||||
/* transmit one packet for a given flow */
|
||||
static void gtp_flow_tx_one(struct gtp_flood_flow *flow)
|
||||
{
|
||||
struct gtp_flood_worker *worker = flow->worker;
|
||||
struct io_uring_sqe *sqe;
|
||||
|
||||
sqe = io_uring_get_sqe(&worker->ring);
|
||||
OSMO_ASSERT(sqe);
|
||||
io_uring_prep_sendmsg(sqe, flow->cfg.gtp_local->ofd.fd, &flow->msgh, 0);
|
||||
//io_uring_sqe_set_data(sqe, flow);
|
||||
|
||||
flow->sent_gtp_packets++;
|
||||
}
|
||||
|
||||
static void *gtp_flood_worker_thread(void *_worker)
|
||||
{
|
||||
struct gtp_flood_worker *worker = (struct gtp_flood_worker *)_worker;
|
||||
struct gtp_flood *gtp_flood = worker->gtp_flood;
|
||||
|
||||
osmo_ctx_init(__func__);
|
||||
|
||||
gtp_flood->workers_started++;
|
||||
gtp_flood->workers_running++;
|
||||
|
||||
LOGP(DLGLOBAL, LOGL_INFO, "gtp flood worker starting (%u started, %u running)\n",
|
||||
gtp_flood->workers_started,
|
||||
gtp_flood->workers_running);
|
||||
|
||||
while (1) {
|
||||
uint32_t num_submitted = 0;
|
||||
int num_pending;
|
||||
bool all_ended = false;
|
||||
|
||||
#if SLOW
|
||||
sleep(1);
|
||||
#endif
|
||||
|
||||
/* prepare transmit submissions */
|
||||
while (num_submitted < SWEET_SPOT) {
|
||||
int submitted_was = num_submitted;
|
||||
|
||||
struct gtp_flood_flow *flow;
|
||||
llist_for_each_entry (flow, &worker->flows, entry) {
|
||||
if (flow->stop)
|
||||
continue;
|
||||
if (flow->cfg.num_packets
|
||||
&& flow->sent_gtp_packets >= flow->cfg.num_packets) {
|
||||
flow->stop = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
gtp_flow_tx_one(flow);
|
||||
num_submitted++;
|
||||
}
|
||||
|
||||
/* No change in number of submitted PDUs, all flows are done. */
|
||||
if (submitted_was == num_submitted) {
|
||||
all_ended = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* actually submit; determines completions */
|
||||
num_pending = io_uring_submit(&worker->ring);
|
||||
|
||||
/* process all completions */
|
||||
for (int j = 0; j < num_pending; j++) {
|
||||
struct io_uring_cqe *cqe;
|
||||
int rc;
|
||||
|
||||
rc = io_uring_wait_cqe(&worker->ring, &cqe);
|
||||
OSMO_ASSERT(rc >= 0);
|
||||
//handle_completion(worker, cqe);
|
||||
io_uring_cqe_seen(&worker->ring, cqe);
|
||||
}
|
||||
|
||||
if (all_ended)
|
||||
break;
|
||||
}
|
||||
|
||||
gtp_flood->workers_running--;
|
||||
LOGP(DLGLOBAL, LOGL_INFO, "gtp flood worker done (%u started, %u running)\n",
|
||||
gtp_flood->workers_started,
|
||||
gtp_flood->workers_running);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void gtp_flood_worker_start(struct gtp_flood_worker *worker)
|
||||
{
|
||||
int rc;
|
||||
rc = io_uring_queue_init(4000, &worker->ring, 0);
|
||||
OSMO_ASSERT(rc >= 0);
|
||||
rc = pthread_create(&worker->worker, NULL, gtp_flood_worker_thread, worker);
|
||||
OSMO_ASSERT(rc >= 0);
|
||||
}
|
||||
|
||||
void gtp_flood_start(struct gtp_flood *gtp_flood)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < gtp_flood->workers_count; i++)
|
||||
gtp_flood_worker_start(>p_flood->workers[i]);
|
||||
}
|
||||
|
||||
bool gtp_flood_is_busy(struct gtp_flood *gtp_flood)
|
||||
{
|
||||
if (!gtp_flood)
|
||||
return false;
|
||||
return gtp_flood->workers_started && gtp_flood->workers_running;
|
||||
}
|
||||
|
||||
#else /* HAVE_URING */
|
||||
|
||||
struct gtp_flood *gtp_flood_alloc(void *ctx, unsigned int workers)
|
||||
{
|
||||
LOGP(DLGLOBAL, LOGL_ERROR, "Cannot start GTP flood: built without liburing support\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void gtp_flood_add_flow(struct gtp_flood *gtp_flood,
|
||||
const struct gtp_flood_flow_cfg *flow_cfg)
|
||||
{
|
||||
}
|
||||
|
||||
void gtp_flood_start(struct gtp_flood *gtp_flood)
|
||||
{
|
||||
}
|
||||
|
||||
bool gtp_flood_is_busy(struct gtp_flood *gtp_flood)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif /* HAVE_URING */
|
||||
373
src/osmo-pfcp-tool/osmo_pfcp_tool_main.c
Normal file
373
src/osmo-pfcp-tool/osmo_pfcp_tool_main.c
Normal file
@@ -0,0 +1,373 @@
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/signal.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/fsm.h>
|
||||
#include <osmocom/core/stats.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/vty/logging.h>
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/vty/misc.h>
|
||||
#include <osmocom/vty/cpu_sched_vty.h>
|
||||
#include <osmocom/vty/telnet_interface.h>
|
||||
#include <osmocom/vty/ports.h>
|
||||
#include <osmocom/vty/tdef_vty.h>
|
||||
#include <osmocom/ctrl/control_if.h>
|
||||
#include <osmocom/ctrl/control_vty.h>
|
||||
#include <osmocom/ctrl/ports.h>
|
||||
#include <osmocom/core/sockaddr_str.h>
|
||||
|
||||
#include <osmocom/pfcp/pfcp_endpoint.h>
|
||||
|
||||
#include <osmocom/pfcptool/pfcp_tool.h>
|
||||
#include <osmocom/pfcptool/gtp_flood.h>
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <getopt.h>
|
||||
|
||||
/* build switches from the configure script */
|
||||
#include "config.h"
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
extern void *tall_vty_ctx;
|
||||
|
||||
void *tall_pfcp_tool_ctx = NULL;
|
||||
static int quit = 0;
|
||||
|
||||
static struct {
|
||||
const char *config_file;
|
||||
int daemonize;
|
||||
enum vty_ref_gen_mode vty_ref_gen_mode;
|
||||
const char *command_file;
|
||||
} pfcp_tool_cmdline_config = {
|
||||
.config_file = "osmo-pfcp-tool.cfg",
|
||||
.vty_ref_gen_mode = VTY_REF_GEN_MODE_DEFAULT,
|
||||
};
|
||||
|
||||
static void print_usage()
|
||||
{
|
||||
printf("Usage: osmo-pfcp-tool [command-file.vty]\n telnet localhost %d\n", OSMO_VTY_PORT_PFCP_TOOL);
|
||||
}
|
||||
|
||||
static void print_help()
|
||||
{
|
||||
const struct value_string *vty_ref_gen_mode_name;
|
||||
|
||||
printf("Some useful options:\n");
|
||||
printf(" -h --help This text.\n");
|
||||
printf(" -D --daemonize Fork the process into a background daemon.\n");
|
||||
printf(" -c --config-file filename The config file to use, for logging etc.\n");
|
||||
printf(" -V --version Print the version of OsmoMSC.\n");
|
||||
|
||||
printf("\nVTY reference generation:\n");
|
||||
printf(" --vty-ref-xml Generate the VTY reference XML output and exit.\n");
|
||||
printf(" --vty-ref-mode MODE Mode for --vty-ref-xml:\n");
|
||||
/* List all VTY ref gen modes */
|
||||
for (vty_ref_gen_mode_name = vty_ref_gen_mode_names; vty_ref_gen_mode_name->str; vty_ref_gen_mode_name++)
|
||||
printf(" %s: %s\n",
|
||||
vty_ref_gen_mode_name->str,
|
||||
get_value_string(vty_ref_gen_mode_desc, vty_ref_gen_mode_name->value));
|
||||
}
|
||||
|
||||
static void handle_long_options(const char *prog_name, const int long_option)
|
||||
{
|
||||
switch (long_option) {
|
||||
case 1:
|
||||
pfcp_tool_cmdline_config.vty_ref_gen_mode = get_string_value(vty_ref_gen_mode_names, optarg);
|
||||
if (pfcp_tool_cmdline_config.vty_ref_gen_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, pfcp_tool_cmdline_config.vty_ref_gen_mode),
|
||||
get_value_string(vty_ref_gen_mode_desc, pfcp_tool_cmdline_config.vty_ref_gen_mode));
|
||||
vty_dump_xml_ref_mode(stdout, pfcp_tool_cmdline_config.vty_ref_gen_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'},
|
||||
{"daemonize", 0, 0, 'D'},
|
||||
{"config-file", 1, 0, 'c'},
|
||||
{"version", 0, 0, 'V' },
|
||||
{"vty-ref-mode", 1, &long_option, 1},
|
||||
{"vty-ref-xml", 0, &long_option, 2},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
c = getopt_long(argc, argv, "hDc:V", long_options, &option_index);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'h':
|
||||
print_usage();
|
||||
print_help();
|
||||
exit(0);
|
||||
case 0:
|
||||
handle_long_options(argv[0], long_option);
|
||||
break;
|
||||
case 'D':
|
||||
pfcp_tool_cmdline_config.daemonize = 1;
|
||||
break;
|
||||
case 'c':
|
||||
pfcp_tool_cmdline_config.config_file = optarg;
|
||||
break;
|
||||
case 'V':
|
||||
print_version(1);
|
||||
exit(0);
|
||||
break;
|
||||
default:
|
||||
/* catch unknown options *as well as* missing arguments. */
|
||||
fprintf(stderr, "%s: Error in command line options. Exiting.\n", argv[0]);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
if (argc > optind) {
|
||||
pfcp_tool_cmdline_config.command_file = argv[optind];
|
||||
optind++;
|
||||
}
|
||||
|
||||
if (argc > optind) {
|
||||
fprintf(stderr, "%s: Unsupported positional arguments on command line\n", argv[optind]);
|
||||
exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
static void signal_handler(int signum)
|
||||
{
|
||||
fprintf(stdout, "signal %u received\n", signum);
|
||||
|
||||
switch (signum) {
|
||||
case SIGINT:
|
||||
case SIGTERM:
|
||||
LOGP(DLGLOBAL, LOGL_NOTICE, "Terminating due to signal %d\n", signum);
|
||||
quit++;
|
||||
break;
|
||||
case SIGABRT:
|
||||
osmo_generate_backtrace();
|
||||
/* in case of abort, we want to obtain a talloc report and
|
||||
* then run default SIGABRT handler, who will generate coredump
|
||||
* and abort the process. abort() should do this for us after we
|
||||
* return, but program wouldn't exit if an external SIGABRT is
|
||||
* received.
|
||||
*/
|
||||
talloc_report(tall_vty_ctx, stderr);
|
||||
talloc_report_full(tall_pfcp_tool_ctx, stderr);
|
||||
signal(SIGABRT, SIG_DFL);
|
||||
raise(SIGABRT);
|
||||
break;
|
||||
case SIGUSR1:
|
||||
talloc_report(tall_vty_ctx, stderr);
|
||||
talloc_report_full(tall_pfcp_tool_ctx, stderr);
|
||||
break;
|
||||
case SIGUSR2:
|
||||
talloc_report_full(tall_vty_ctx, stderr);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static struct vty_app_info pfcp_tool_vty_app_info = {
|
||||
.name = "osmo-pfcp-tool",
|
||||
.version = PACKAGE_VERSION,
|
||||
.copyright =
|
||||
"OsmoPFCPTool - Osmocom Packet Forwarding Control Protocol tool for testing\r\n"
|
||||
"Copyright (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>\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",
|
||||
.go_parent_cb = pfcp_tool_vty_go_parent,
|
||||
};
|
||||
|
||||
static const struct log_info_cat pfcp_tool_default_categories[] = {
|
||||
};
|
||||
|
||||
const struct log_info log_info = {
|
||||
.cat = pfcp_tool_default_categories,
|
||||
.num_cat = ARRAY_SIZE(pfcp_tool_default_categories),
|
||||
};
|
||||
|
||||
int pfcp_tool_mainloop(int poll)
|
||||
{
|
||||
int rc;
|
||||
log_reset_context();
|
||||
rc = osmo_select_main_ctx(poll);
|
||||
|
||||
/* If the user hits Ctrl-C the third time, just terminate immediately. */
|
||||
if (quit >= 3)
|
||||
return -1;
|
||||
|
||||
/* Has SIGTERM been received (and not yet been handled)? */
|
||||
if (quit && !osmo_select_shutdown_requested()) {
|
||||
osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL);
|
||||
|
||||
/* Request write-only mode in osmo_select_main_ctx() */
|
||||
osmo_select_shutdown_request();
|
||||
/* continue the main select loop until all write queues are serviced. */
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* Track the use of talloc NULL memory contexts */
|
||||
talloc_enable_null_tracking();
|
||||
|
||||
osmo_fsm_set_dealloc_ctx(OTC_SELECT);
|
||||
|
||||
tall_pfcp_tool_ctx = talloc_named_const(NULL, 1, "osmo-pfcp-tool");
|
||||
pfcp_tool_vty_app_info.tall_ctx = tall_pfcp_tool_ctx;
|
||||
|
||||
msgb_talloc_ctx_init(tall_pfcp_tool_ctx, 0);
|
||||
osmo_signal_talloc_ctx_init(tall_pfcp_tool_ctx);
|
||||
|
||||
osmo_init_logging2(tall_pfcp_tool_ctx, &log_info);
|
||||
log_set_print_category_hex(osmo_stderr_target, 0);
|
||||
log_set_print_category(osmo_stderr_target, 1);
|
||||
log_set_print_level(osmo_stderr_target, 1);
|
||||
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_BASENAME);
|
||||
log_set_print_filename_pos(osmo_stderr_target, LOG_FILENAME_POS_LINE_END);
|
||||
log_set_print_extended_timestamp(osmo_stderr_target, 1);
|
||||
|
||||
osmo_fsm_log_timeouts(true);
|
||||
osmo_fsm_log_addr(true);
|
||||
|
||||
osmo_stats_init(tall_pfcp_tool_ctx);
|
||||
|
||||
g_pfcp_tool_alloc(tall_pfcp_tool_ctx);
|
||||
|
||||
/* For --version, vty_init() must be called before handling options */
|
||||
vty_init(&pfcp_tool_vty_app_info);
|
||||
|
||||
ctrl_vty_init(tall_pfcp_tool_ctx);
|
||||
logging_vty_add_cmds();
|
||||
osmo_talloc_vty_add_cmds();
|
||||
osmo_cpu_sched_vty_init(tall_pfcp_tool_ctx);
|
||||
osmo_fsm_vty_add_cmds();
|
||||
osmo_tdef_vty_groups_init(CONFIG_NODE, g_pfcp_tool_tdef_groups);
|
||||
|
||||
pfcp_tool_vty_init_cfg();
|
||||
|
||||
/* Parse options */
|
||||
handle_options(argc, argv);
|
||||
|
||||
if (pfcp_tool_cmdline_config.config_file) {
|
||||
rc = vty_read_config_file(pfcp_tool_cmdline_config.config_file, NULL);
|
||||
if (rc < 0) {
|
||||
LOGP(DLGLOBAL, LOGL_ERROR, "Failed to parse the config file: '%s'\n",
|
||||
pfcp_tool_cmdline_config.config_file);
|
||||
}
|
||||
}
|
||||
|
||||
/* start telnet VTY */
|
||||
rc = telnet_init_default(tall_pfcp_tool_ctx, &g_pfcp_tool, OSMO_VTY_PORT_PFCP_TOOL);
|
||||
if (rc < 0)
|
||||
return 2;
|
||||
|
||||
/* start control interface, after reading config for ctrl_vty_get_bind_addr() */
|
||||
g_pfcp_tool->ctrl = ctrl_interface_setup(g_pfcp_tool, OSMO_CTRL_PORT_PFCP_TOOL, NULL);
|
||||
if (!g_pfcp_tool->ctrl) {
|
||||
fprintf(stderr, "Failed to initialize control interface. Exiting.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
signal(SIGINT, &signal_handler);
|
||||
signal(SIGTERM, &signal_handler);
|
||||
signal(SIGABRT, &signal_handler);
|
||||
signal(SIGUSR1, &signal_handler);
|
||||
signal(SIGUSR2, &signal_handler);
|
||||
osmo_init_ignore_signals();
|
||||
|
||||
if (pfcp_tool_cmdline_config.daemonize) {
|
||||
rc = osmo_daemonize();
|
||||
if (rc < 0) {
|
||||
perror("Error during daemonize");
|
||||
return 6;
|
||||
}
|
||||
}
|
||||
|
||||
pfcp_tool_mainloop(1);
|
||||
|
||||
pfcp_tool_vty_init_cmds();
|
||||
|
||||
if (pfcp_tool_cmdline_config.command_file) {
|
||||
printf("Reading '%s'\n", pfcp_tool_cmdline_config.command_file);
|
||||
rc = vty_read_config_file(pfcp_tool_cmdline_config.command_file, NULL);
|
||||
if (rc < 0) {
|
||||
LOGP(DLGLOBAL, LOGL_FATAL, "Failed to parse the command file: '%s'\n",
|
||||
pfcp_tool_cmdline_config.command_file);
|
||||
return 1;
|
||||
}
|
||||
printf("Done reading '%s', waiting for tasks to conclude...\n",
|
||||
pfcp_tool_cmdline_config.command_file);
|
||||
do {
|
||||
if (pfcp_tool_mainloop(0) == -1)
|
||||
break;
|
||||
} while (osmo_pfcp_endpoint_retrans_queue_is_busy(g_pfcp_tool->ep)
|
||||
|| gtp_flood_is_busy(g_pfcp_tool->gtp.flood.state));
|
||||
LOGP(DLGLOBAL, LOGL_NOTICE, "Done\n");
|
||||
} else {
|
||||
printf("Listening for commands on VTY...\n");
|
||||
do {
|
||||
if (pfcp_tool_mainloop(0) == -1)
|
||||
break;
|
||||
} while (!osmo_select_shutdown_done());
|
||||
}
|
||||
|
||||
osmo_pfcp_endpoint_free(&g_pfcp_tool->ep);
|
||||
|
||||
log_fini();
|
||||
|
||||
/* Report the heap state of talloc contexts, then free, so both ASAN and Valgrind are happy... */
|
||||
//talloc_report_full(tall_pfcp_tool_ctx, stderr);
|
||||
talloc_free(tall_pfcp_tool_ctx);
|
||||
|
||||
//talloc_report_full(tall_vty_ctx, stderr);
|
||||
talloc_free(tall_vty_ctx);
|
||||
|
||||
//talloc_report_full(NULL, stderr);
|
||||
talloc_disable_null_tracking();
|
||||
return 0;
|
||||
}
|
||||
346
src/osmo-pfcp-tool/pfcp_tool.c
Normal file
346
src/osmo-pfcp-tool/pfcp_tool.c
Normal file
@@ -0,0 +1,346 @@
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
|
||||
#include <osmocom/pfcp/pfcp_endpoint.h>
|
||||
|
||||
#include <osmocom/pfcptool/pfcp_tool.h>
|
||||
#include <osmocom/pfcptool/gtp_flood.h>
|
||||
|
||||
struct g_pfcp_tool *g_pfcp_tool = NULL;
|
||||
|
||||
struct osmo_tdef_group g_pfcp_tool_tdef_groups[] = {
|
||||
{ .name = "pfcp", .tdefs = osmo_pfcp_tdefs, .desc = "PFCP" },
|
||||
{}
|
||||
};
|
||||
|
||||
void g_pfcp_tool_alloc(void *ctx)
|
||||
{
|
||||
OSMO_ASSERT(g_pfcp_tool == NULL);
|
||||
g_pfcp_tool = talloc_zero(ctx, struct g_pfcp_tool);
|
||||
|
||||
*g_pfcp_tool = (struct g_pfcp_tool){
|
||||
.vty_cfg = {
|
||||
.local_ip = talloc_strdup(g_pfcp_tool, "0.0.0.0"),
|
||||
.local_port = OSMO_PFCP_PORT,
|
||||
},
|
||||
.next_teid_state = 23,
|
||||
.gtp.flood = {
|
||||
.workers = 1,
|
||||
.flows_per_session = 1,
|
||||
.packets_per_flow = 0,
|
||||
},
|
||||
};
|
||||
|
||||
INIT_LLIST_HEAD(&g_pfcp_tool->peers);
|
||||
INIT_LLIST_HEAD(&g_pfcp_tool->gtp.gtp_local_addrs);
|
||||
}
|
||||
|
||||
struct pfcp_tool_peer *pfcp_tool_peer_find(const struct osmo_sockaddr *remote_addr)
|
||||
{
|
||||
struct pfcp_tool_peer *peer;
|
||||
llist_for_each_entry(peer, &g_pfcp_tool->peers, entry) {
|
||||
if (osmo_sockaddr_cmp(&peer->remote_addr, remote_addr) == 0)
|
||||
return peer;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct pfcp_tool_peer *pfcp_tool_peer_find_or_create(const struct osmo_sockaddr *remote_addr)
|
||||
{
|
||||
struct pfcp_tool_peer *peer = pfcp_tool_peer_find(remote_addr);
|
||||
if (peer)
|
||||
return peer;
|
||||
|
||||
peer = talloc_zero(g_pfcp_tool, struct pfcp_tool_peer);
|
||||
peer->remote_addr = *remote_addr;
|
||||
peer->next_seid_state = 0x1234567;
|
||||
INIT_LLIST_HEAD(&peer->sessions);
|
||||
llist_add(&peer->entry, &g_pfcp_tool->peers);
|
||||
return peer;
|
||||
}
|
||||
|
||||
struct pfcp_tool_session *pfcp_tool_session_find(struct pfcp_tool_peer *peer, uint64_t cp_seid)
|
||||
{
|
||||
struct pfcp_tool_session *session;
|
||||
llist_for_each_entry(session, &peer->sessions, entry) {
|
||||
if (session->cp_seid == cp_seid)
|
||||
return session;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct pfcp_tool_session *pfcp_tool_session_find_or_create(struct pfcp_tool_peer *peer, uint64_t cp_seid,
|
||||
enum up_gtp_action_kind kind)
|
||||
{
|
||||
struct pfcp_tool_session *session = pfcp_tool_session_find(peer, cp_seid);
|
||||
if (session)
|
||||
return session;
|
||||
|
||||
session = talloc(peer, struct pfcp_tool_session);
|
||||
*session = (struct pfcp_tool_session){
|
||||
.peer = peer,
|
||||
.cp_seid = cp_seid,
|
||||
.kind = kind,
|
||||
};
|
||||
llist_add(&session->entry, &peer->sessions);
|
||||
return session;
|
||||
}
|
||||
|
||||
static void rx_assoc_setup_resp(struct osmo_pfcp_endpoint *ep, struct osmo_pfcp_msg *m)
|
||||
{
|
||||
if (m->ies.assoc_setup_resp.up_function_features_present)
|
||||
OSMO_LOG_PFCP_MSG(m, LOGL_NOTICE, "Associated. UP Peer features: %s\n",
|
||||
osmo_pfcp_bits_to_str_c(OTC_SELECT,
|
||||
m->ies.assoc_setup_resp.up_function_features.bits,
|
||||
osmo_pfcp_up_feature_strs));
|
||||
|
||||
if (m->ies.assoc_setup_resp.cp_function_features_present)
|
||||
OSMO_LOG_PFCP_MSG(m, LOGL_NOTICE, "Associated. CP Peer features: %s\n",
|
||||
osmo_pfcp_bits_to_str_c(OTC_SELECT,
|
||||
m->ies.assoc_setup_resp.cp_function_features.bits,
|
||||
osmo_pfcp_cp_feature_strs));
|
||||
}
|
||||
|
||||
static void rx_session_est_resp(struct osmo_pfcp_endpoint *ep, struct osmo_pfcp_msg *m)
|
||||
{
|
||||
struct pfcp_tool_peer *peer;
|
||||
struct pfcp_tool_session *session;
|
||||
enum osmo_pfcp_cause *cause = osmo_pfcp_msg_cause(m);
|
||||
if (!cause) {
|
||||
OSMO_LOG_PFCP_MSG(m, LOGL_ERROR, "Session Establishment Response should contain a Cause\n");
|
||||
return;
|
||||
}
|
||||
if (*cause != OSMO_PFCP_CAUSE_REQUEST_ACCEPTED) {
|
||||
OSMO_LOG_PFCP_MSG(m, LOGL_ERROR, "Peer responds that Session Establishment failed\n");
|
||||
return;
|
||||
}
|
||||
if (!m->h.seid_present) {
|
||||
OSMO_LOG_PFCP_MSG(m, LOGL_ERROR, "Session Establishment Response should contain a SEID\n");
|
||||
return;
|
||||
}
|
||||
if (!m->ies.session_est_resp.up_f_seid_present) {
|
||||
OSMO_LOG_PFCP_MSG(m, LOGL_ERROR, "Session Establishment Response without UP F-SEID\n");
|
||||
return;
|
||||
}
|
||||
peer = pfcp_tool_peer_find(&m->remote_addr);
|
||||
if (!peer)
|
||||
return;
|
||||
session = pfcp_tool_session_find(peer, m->h.seid);
|
||||
if (!session)
|
||||
return;
|
||||
session->up_f_seid = m->ies.session_est_resp.up_f_seid;
|
||||
}
|
||||
|
||||
void pfcp_tool_rx_msg(struct osmo_pfcp_endpoint *ep, struct osmo_pfcp_msg *m, struct osmo_pfcp_msg *req)
|
||||
{
|
||||
switch (m->h.message_type) {
|
||||
case OSMO_PFCP_MSGT_ASSOC_SETUP_RESP:
|
||||
rx_assoc_setup_resp(ep, m);
|
||||
break;
|
||||
case OSMO_PFCP_MSGT_SESSION_EST_RESP:
|
||||
rx_session_est_resp(ep, m);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
static void copy_msg(struct osmo_pfcp_msg *dst, const struct osmo_pfcp_msg *m)
|
||||
{
|
||||
*dst = *m;
|
||||
dst->encoded = NULL;
|
||||
dst->ctx.peer_use_token = NULL;
|
||||
dst->ctx.session_use_token = NULL;
|
||||
dst->ctx.resp_cb = NULL;
|
||||
}
|
||||
|
||||
int peer_tx(struct pfcp_tool_peer *peer, struct osmo_pfcp_msg *m)
|
||||
{
|
||||
int rc;
|
||||
if (m->is_response)
|
||||
copy_msg(&peer->last_resp, m);
|
||||
else
|
||||
copy_msg(&peer->last_req, m);
|
||||
rc = osmo_pfcp_endpoint_tx(g_pfcp_tool->ep, m);
|
||||
if (rc)
|
||||
LOGP(DLPFCP, LOGL_ERROR, "Failed to transmit PFCP: %s\n", strerror(-rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
uint64_t peer_new_seid(struct pfcp_tool_peer *peer)
|
||||
{
|
||||
return peer->next_seid_state++;
|
||||
}
|
||||
|
||||
uint32_t pfcp_tool_new_teid(void)
|
||||
{
|
||||
return g_pfcp_tool->next_teid_state++;
|
||||
}
|
||||
|
||||
int pfcp_tool_next_ue_addr(struct osmo_sockaddr *dst)
|
||||
{
|
||||
range_next(&g_pfcp_tool->ue.ip_range);
|
||||
if (g_pfcp_tool->ue.ip_range.wrapped) {
|
||||
LOGP(DLGLOBAL, LOGL_ERROR, "insufficient UE IP addresses, wrapped back to first\n");
|
||||
g_pfcp_tool->ue.ip_range.wrapped = false;
|
||||
}
|
||||
range_val_get_addr(dst, &g_pfcp_tool->ue.ip_range.next);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct udp_port *pfcp_tool_have_udp_port_by_osa(const struct osmo_sockaddr *_osa, uint16_t fallback_port)
|
||||
{
|
||||
struct udp_port *port;
|
||||
/* copy osa and have a non-const pointer */
|
||||
struct osmo_sockaddr osa_mutable = *_osa;
|
||||
struct osmo_sockaddr *osa = &osa_mutable;
|
||||
|
||||
if (!osmo_sockaddr_port(&osa->u.sa))
|
||||
osmo_sockaddr_set_port(&osa->u.sa, fallback_port);
|
||||
OSMO_ASSERT(osmo_sockaddr_port(&osa->u.sa));
|
||||
|
||||
llist_for_each_entry (port, &g_pfcp_tool->gtp.gtp_local_addrs, entry) {
|
||||
if (osmo_sockaddr_cmp(&port->osa, osa) == 0)
|
||||
return port;
|
||||
}
|
||||
|
||||
LOGP(DLGLOBAL, LOGL_NOTICE, "new port UDP %s\n", osmo_sockaddr_to_str_c(OTC_SELECT, osa));
|
||||
|
||||
port = talloc_zero(g_pfcp_tool, struct udp_port);
|
||||
port->osa = *osa;
|
||||
|
||||
/* create and bind socket */
|
||||
int rc;
|
||||
rc = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP, &port->osa, NULL, OSMO_SOCK_F_BIND);
|
||||
if (rc < 0) {
|
||||
LOGP(DLGLOBAL, LOGL_ERROR, "Failed to bind socket on UDP %s\n",
|
||||
osmo_sockaddr_to_str_c(OTC_SELECT, &port->osa));
|
||||
exit(1);
|
||||
}
|
||||
port->ofd.fd = rc;
|
||||
LOGP(DLGLOBAL, LOGL_NOTICE, "bound UDP %s fd=%d\n",
|
||||
osmo_sockaddr_to_str_c(OTC_SELECT, &port->osa), rc);
|
||||
|
||||
llist_add_tail(&port->entry, &g_pfcp_tool->gtp.gtp_local_addrs);
|
||||
return port;
|
||||
}
|
||||
|
||||
struct udp_port *pfcp_tool_have_udp_port_by_str(const struct osmo_sockaddr_str *addr, uint16_t fallback_port)
|
||||
{
|
||||
struct osmo_sockaddr osa;
|
||||
if (osmo_sockaddr_str_to_osa(addr, &osa))
|
||||
return NULL;
|
||||
return pfcp_tool_have_udp_port_by_osa(&osa, fallback_port);
|
||||
}
|
||||
|
||||
static bool add_session_to_gtp_flow(struct gtp_flood *gf, struct pfcp_tool_session *session)
|
||||
{
|
||||
struct range *r;
|
||||
struct gtp_flood_flow_cfg cfg = {};
|
||||
const struct pfcp_tool_gtp_tun *tun_access;
|
||||
const struct osmo_sockaddr_str *ue_local_addr = NULL;
|
||||
|
||||
switch (session->kind) {
|
||||
case UP_GTP_U_TUNEND:
|
||||
tun_access = &session->tunend.access;
|
||||
ue_local_addr = &session->tunend.core.ue_local_addr;
|
||||
break;
|
||||
case UP_GTP_U_TUNMAP:
|
||||
tun_access = &session->tunmap.access;
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
|
||||
/* The 'local' and 'remote' naming clashes here. struct pfcp_tool_gtp_tun names its items from the UPF's point
|
||||
* of view: so the GTP port of osmo-pfcp-tool is 'remote'.
|
||||
* Here we are setting up a port to emit GTP from osmo-pfcp-tool, the same port is called 'gtp_local'.
|
||||
*/
|
||||
cfg.gtp_local = pfcp_tool_have_udp_port_by_str(&tun_access->remote.addr, 2152);
|
||||
if (!cfg.gtp_local) {
|
||||
LOGP(DLGLOBAL, LOGL_ERROR, "Cannot set up GTP flow for session, failed to setup local GTP port\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* The 'local' and 'remote' naming clashes here. struct pfcp_tool_gtp_tun names its items from the UPF's point
|
||||
* of view: so the GTP port of UPF is 'local'.
|
||||
* Here we are reading the GTP port that the UPF has opened to receive GTP traffic, the same port is called
|
||||
* 'gtp_remote' in cfg.
|
||||
*/
|
||||
if (osmo_sockaddr_str_to_osa(&tun_access->local.addr, &cfg.gtp_remote)) {
|
||||
LOGP(DLGLOBAL, LOGL_ERROR, "Cannot set up GTP flow for session, failed to identify UPF's GTP port\n");
|
||||
return false;
|
||||
}
|
||||
if (!osmo_sockaddr_port(&cfg.gtp_remote.u.sa))
|
||||
osmo_sockaddr_set_port(&cfg.gtp_remote.u.sa, 2152);
|
||||
cfg.gtp_remote_teid = tun_access->local.teid;
|
||||
|
||||
if (ue_local_addr) {
|
||||
osmo_sockaddr_str_to_osa(ue_local_addr, &cfg.payload_src);
|
||||
} else if (pfcp_tool_next_ue_addr(&cfg.payload_src)) {
|
||||
LOGP(DLGLOBAL, LOGL_ERROR, "Cannot set up GTP flow for session, failed to identify UE's IP address\n");
|
||||
return false;
|
||||
}
|
||||
r = &g_pfcp_tool->gtp.payload.source.udp_port_range;
|
||||
range_next(r);
|
||||
osmo_sockaddr_set_port(&cfg.payload_src.u.sa, range_val_get_int(&r->next));
|
||||
|
||||
r = &g_pfcp_tool->gtp.payload.target.ip_range;
|
||||
range_next(r);
|
||||
range_val_get_addr(&cfg.payload_dst, &r->next);
|
||||
r = &g_pfcp_tool->gtp.payload.target.udp_port_range;
|
||||
range_next(r);
|
||||
osmo_sockaddr_set_port(&cfg.payload_dst.u.sa, range_val_get_int(&r->next));
|
||||
|
||||
cfg.num_packets = g_pfcp_tool->gtp.flood.packets_per_flow;
|
||||
|
||||
gtp_flood_add_flow(gf, &cfg);
|
||||
return true;
|
||||
}
|
||||
|
||||
void pfcp_tool_gtp_flood_start(void)
|
||||
{
|
||||
struct gtp_flood *gf;
|
||||
struct pfcp_tool_peer *peer;
|
||||
|
||||
if (g_pfcp_tool->gtp.flood.state) {
|
||||
LOGP(DLGLOBAL, LOGL_ERROR,
|
||||
"another 'gtp flood' is still running; currently we can run only one gtp flood at a time\n");
|
||||
return;
|
||||
}
|
||||
|
||||
gf = g_pfcp_tool->gtp.flood.state = gtp_flood_alloc(g_pfcp_tool, g_pfcp_tool->gtp.flood.workers);
|
||||
if (!gf)
|
||||
return;
|
||||
|
||||
llist_for_each_entry (peer, &g_pfcp_tool->peers, entry) {
|
||||
struct pfcp_tool_session *session;
|
||||
llist_for_each_entry (session, &peer->sessions, entry) {
|
||||
add_session_to_gtp_flow(gf, session);
|
||||
}
|
||||
}
|
||||
|
||||
gtp_flood_start(gf);
|
||||
}
|
||||
1394
src/osmo-pfcp-tool/pfcp_tool_vty.c
Normal file
1394
src/osmo-pfcp-tool/pfcp_tool_vty.c
Normal file
File diff suppressed because it is too large
Load Diff
111
src/osmo-pfcp-tool/range.c
Normal file
111
src/osmo-pfcp-tool/range.c
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <osmocom/pfcptool/range.h>
|
||||
|
||||
void range_val_set_int(struct range_val *rv, uint32_t val)
|
||||
{
|
||||
*rv = (struct range_val){
|
||||
.buf = { (uint64_t)val, 0 },
|
||||
.size = sizeof(val),
|
||||
};
|
||||
}
|
||||
|
||||
uint32_t range_val_get_int(const struct range_val *rv)
|
||||
{
|
||||
return rv->buf[0];
|
||||
}
|
||||
|
||||
void range_val_set_addr(struct range_val *rv, const struct osmo_sockaddr *val)
|
||||
{
|
||||
*rv = (struct range_val){};
|
||||
rv->size = osmo_sockaddr_to_octets((void *)rv->buf, sizeof(rv->buf), val);
|
||||
|
||||
#if !OSMO_IS_BIG_ENDIAN
|
||||
for (int i = 0; i < rv->size / 2; i++) {
|
||||
uint8_t *rvbuf = (void *)rv->buf;
|
||||
uint8_t tmp = rvbuf[i];
|
||||
rvbuf[i] = rvbuf[rv->size - 1 - i];
|
||||
rvbuf[rv->size - 1 - i] = tmp;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void range_val_get_addr(struct osmo_sockaddr *dst, const struct range_val *rv)
|
||||
{
|
||||
void *buf;
|
||||
#if OSMO_IS_BIG_ENDIAN
|
||||
buf = rv->buf;
|
||||
#else
|
||||
int i;
|
||||
uint8_t rev[sizeof(rv->buf)];
|
||||
uint8_t *val = (void *)rv->buf;
|
||||
for (i = 0; i < rv->size; i++)
|
||||
rev[i] = val[rv->size - 1 - i];
|
||||
buf = rev;
|
||||
#endif
|
||||
osmo_sockaddr_from_octets(dst, buf, rv->size);
|
||||
}
|
||||
|
||||
void range_val_inc(struct range_val *rv)
|
||||
{
|
||||
uint64_t was = rv->buf[0];
|
||||
rv->buf[0]++;
|
||||
if (rv->buf[0] < was)
|
||||
rv->buf[1]++;
|
||||
}
|
||||
|
||||
int range_val_cmp(const struct range_val *a, const struct range_val *b)
|
||||
{
|
||||
int rc;
|
||||
if (a == b)
|
||||
return 0;
|
||||
if (!a)
|
||||
return -1;
|
||||
if (!b)
|
||||
return 1;
|
||||
rc = OSMO_CMP(a->buf[0], b->buf[0]);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = OSMO_CMP(a->buf[1], b->buf[1]);
|
||||
if (rc)
|
||||
return rc;
|
||||
return OSMO_CMP(a->size, b->size);
|
||||
}
|
||||
|
||||
void range_next(struct range *r)
|
||||
{
|
||||
if (range_val_cmp(&r->next, &r->first) < 0) {
|
||||
r->next = r->first;
|
||||
return;
|
||||
}
|
||||
if (range_val_cmp(&r->next, &r->last) >= 0) {
|
||||
r->next = r->first;
|
||||
r->wrapped = true;
|
||||
return;
|
||||
}
|
||||
range_val_inc(&r->next);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
AM_CPPFLAGS = \
|
||||
$(all_includes) \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(top_builddir)/include \
|
||||
-I$(top_builddir) \
|
||||
$(NULL)
|
||||
|
||||
@@ -9,6 +10,10 @@ AM_CFLAGS = \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(LIBOSMOVTY_CFLAGS) \
|
||||
$(LIBOSMOCTRL_CFLAGS) \
|
||||
$(LIBOSMOPFCP_CFLAGS) \
|
||||
$(LIBGTPNL_CFLAGS) \
|
||||
$(LIBNFTNL_CFLAGS) \
|
||||
$(LIBNFTABLES_CFLAGS) \
|
||||
$(COVERAGE_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
@@ -16,18 +21,41 @@ AM_LDFLAGS = \
|
||||
$(COVERAGE_LDFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
noinst_LTLIBRARIES = \
|
||||
libupf.la \
|
||||
$(NULL)
|
||||
|
||||
libupf_la_SOURCES = \
|
||||
netinst.c \
|
||||
up_endpoint.c \
|
||||
up_gtp_action.c \
|
||||
up_peer.c \
|
||||
up_session.c \
|
||||
upf.c \
|
||||
upf_gtp.c \
|
||||
upf_gtpu_echo.c \
|
||||
upf_nft.c \
|
||||
upf_vty.c \
|
||||
$(NULL)
|
||||
|
||||
libupf_la_LIBADD = \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(LIBOSMOCTRL_LIBS) \
|
||||
$(LIBOSMOPFCP_LIBS) \
|
||||
$(LIBGTPNL_LIBS) \
|
||||
$(LIBNFTNL_LIBS) \
|
||||
$(LIBNFTABLES_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
bin_PROGRAMS = \
|
||||
osmo-upf \
|
||||
$(NULL)
|
||||
|
||||
osmo_upf_SOURCES = \
|
||||
osmo_upf_main.c \
|
||||
upf.c \
|
||||
$(NULL)
|
||||
|
||||
osmo_upf_LDADD = \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(LIBOSMOCTRL_LIBS) \
|
||||
$(COVERAGE_LDFLAGS) \
|
||||
libupf.la \
|
||||
$(NULL)
|
||||
|
||||
124
src/osmo-upf/netinst.c
Normal file
124
src/osmo-upf/netinst.c
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/vty/vty.h>
|
||||
|
||||
#include <osmocom/upf/netinst.h>
|
||||
|
||||
/* Add a new netinst entry to the given list.
|
||||
* \param ctx talloc allocate new entry from ctx.
|
||||
* \param list append to this list.
|
||||
* \param name The Network Instance name as given in PFCP Network Instance IEs.
|
||||
* \param addr IP address string of local interface to associate with the Network Instance.
|
||||
* \param errmsg On error, an error description is returned in this out-argument.
|
||||
* \return new network_instance entry, or NULL on error.
|
||||
*/
|
||||
const struct network_instance *netinst_add(void *ctx, struct llist_head *list, const char *name, const char *addr,
|
||||
const char **errmsg)
|
||||
{
|
||||
struct network_instance *netinst;
|
||||
if (errmsg)
|
||||
*errmsg = NULL;
|
||||
|
||||
if (!name || !*name) {
|
||||
if (errmsg)
|
||||
*errmsg = "Network Instance name must not be empty";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (netinst_find(list, name)) {
|
||||
if (errmsg)
|
||||
*errmsg = "Network Instance entry with this name already exists";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
netinst = talloc(ctx, struct network_instance);
|
||||
*netinst = (struct network_instance){
|
||||
.name = talloc_strdup(netinst, name),
|
||||
};
|
||||
if (osmo_sockaddr_str_from_str(&netinst->addr, addr, 0)) {
|
||||
if (errmsg)
|
||||
*errmsg = "Network Instance address is not a valid IP address string";
|
||||
talloc_free(netinst);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
llist_add_tail(&netinst->entry, list);
|
||||
|
||||
return netinst;
|
||||
}
|
||||
|
||||
const struct network_instance *netinst_find(struct llist_head *list, const char *name)
|
||||
{
|
||||
const struct network_instance *netinst;
|
||||
|
||||
if (!name)
|
||||
return NULL;
|
||||
|
||||
llist_for_each_entry(netinst, list, entry)
|
||||
if (!strcmp(netinst->name, name))
|
||||
return netinst;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct network_instance *netinst_first(struct llist_head *list)
|
||||
{
|
||||
return llist_first_entry_or_null(list, struct network_instance, entry);
|
||||
}
|
||||
|
||||
/* Clear the list of Network Instance entries, return the nr of entries that were removed. */
|
||||
int netinst_clear(struct llist_head *list)
|
||||
{
|
||||
int count = 0;
|
||||
while (1) {
|
||||
struct network_instance *netinst = llist_first_entry_or_null(list, struct network_instance, entry);
|
||||
if (!netinst)
|
||||
break;
|
||||
llist_del(&netinst->entry);
|
||||
talloc_free(netinst);
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/* Write one or all netinst entries to the VTY output.
|
||||
* If name_or_null is NULL, print all entries. Else, print only the entry matching that name.
|
||||
* Return number of printed entries. */
|
||||
int netinst_vty_write(struct vty *vty, struct llist_head *list, const char *indent, const char *name_or_null)
|
||||
{
|
||||
const struct network_instance *netinst;
|
||||
int count = 0;
|
||||
|
||||
llist_for_each_entry(netinst, list, entry) {
|
||||
if (name_or_null && strcmp(netinst->name, name_or_null))
|
||||
continue;
|
||||
vty_out(vty, "%sadd %s %s%s", indent, netinst->name, netinst->addr.ip, VTY_NEWLINE);
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
@@ -34,11 +34,17 @@
|
||||
#include <osmocom/vty/cpu_sched_vty.h>
|
||||
#include <osmocom/vty/telnet_interface.h>
|
||||
#include <osmocom/vty/ports.h>
|
||||
#include <osmocom/vty/tdef_vty.h>
|
||||
#include <osmocom/ctrl/control_if.h>
|
||||
#include <osmocom/ctrl/control_vty.h>
|
||||
#include <osmocom/ctrl/ports.h>
|
||||
|
||||
#include <osmocom/pfcp/pfcp_endpoint.h>
|
||||
|
||||
#include <osmocom/upf/upf.h>
|
||||
#include <osmocom/upf/up_endpoint.h>
|
||||
#include <osmocom/upf/upf_gtp.h>
|
||||
#include <osmocom/upf/upf_nft.h>
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <getopt.h>
|
||||
@@ -48,6 +54,7 @@
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
extern void *tall_vty_ctx;
|
||||
|
||||
@@ -207,6 +214,36 @@ static struct vty_app_info upf_vty_app_info = {
|
||||
};
|
||||
|
||||
static const struct log_info_cat upf_default_categories[] = {
|
||||
[DREF] = {
|
||||
.name = "DREF",
|
||||
.description = "Reference Counting",
|
||||
.enabled = 0, .loglevel = LOGL_NOTICE,
|
||||
.color = OSMO_LOGCOLOR_DARKGREY,
|
||||
},
|
||||
[DPEER] = {
|
||||
.name = "DPEER",
|
||||
.description = "PFCP peer association",
|
||||
.enabled = 0, .loglevel = LOGL_NOTICE,
|
||||
.color = OSMO_LOGCOLOR_YELLOW,
|
||||
},
|
||||
[DSESSION] = {
|
||||
.name = "DSESSION",
|
||||
.description = "PFCP sessions",
|
||||
.enabled = 0, .loglevel = LOGL_NOTICE,
|
||||
.color = OSMO_LOGCOLOR_BLUE,
|
||||
},
|
||||
[DGTP] = {
|
||||
.name = "DGTP",
|
||||
.description = "GTP tunneling",
|
||||
.enabled = 0, .loglevel = LOGL_NOTICE,
|
||||
.color = OSMO_LOGCOLOR_PURPLE,
|
||||
},
|
||||
[DNFT] = {
|
||||
.name = "DNFT",
|
||||
.description = "GTP forwarding rules via linux netfilter",
|
||||
.enabled = 0, .loglevel = LOGL_NOTICE,
|
||||
.color = OSMO_LOGCOLOR_PURPLE,
|
||||
},
|
||||
};
|
||||
|
||||
const struct log_info log_info = {
|
||||
@@ -217,19 +254,27 @@ const struct log_info log_info = {
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int rc;
|
||||
void *tall_infra_ctx;
|
||||
|
||||
/* Track the use of talloc NULL memory contexts */
|
||||
talloc_enable_null_tracking();
|
||||
|
||||
osmo_fsm_set_dealloc_ctx(OTC_SELECT);
|
||||
|
||||
tall_upf_ctx = talloc_named_const(NULL, 1, "osmo-upf");
|
||||
tall_infra_ctx = talloc_named_const(NULL, 1, "osmo-upf");
|
||||
tall_upf_ctx = talloc_named_const(tall_infra_ctx, 1, "osmo-upf-main");
|
||||
upf_vty_app_info.tall_ctx = tall_upf_ctx;
|
||||
|
||||
msgb_talloc_ctx_init(tall_upf_ctx, 0);
|
||||
osmo_signal_talloc_ctx_init(tall_upf_ctx);
|
||||
|
||||
osmo_init_logging2(tall_upf_ctx, &log_info);
|
||||
osmo_init_logging2(tall_infra_ctx, &log_info);
|
||||
log_set_print_category_hex(osmo_stderr_target, 0);
|
||||
log_set_print_category(osmo_stderr_target, 1);
|
||||
log_set_print_level(osmo_stderr_target, 1);
|
||||
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_BASENAME);
|
||||
log_set_print_filename_pos(osmo_stderr_target, LOG_FILENAME_POS_LINE_END);
|
||||
log_set_print_extended_timestamp(osmo_stderr_target, 1);
|
||||
|
||||
osmo_fsm_log_timeouts(true);
|
||||
osmo_fsm_log_addr(true);
|
||||
@@ -246,6 +291,9 @@ int main(int argc, char **argv)
|
||||
osmo_talloc_vty_add_cmds();
|
||||
osmo_cpu_sched_vty_init(tall_upf_ctx);
|
||||
|
||||
upf_vty_init();
|
||||
osmo_tdef_vty_groups_init(CONFIG_NODE, g_upf_tdef_groups);
|
||||
|
||||
/* Parse options */
|
||||
handle_options(argc, argv);
|
||||
|
||||
@@ -256,13 +304,13 @@ int main(int argc, char **argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* start telnet, after reading config for vty_get_bind_addr() */
|
||||
rc = telnet_init_dynif(tall_upf_ctx, &g_upf, vty_get_bind_addr(), OSMO_VTY_PORT_UPF);
|
||||
/* start telnet VTY */
|
||||
rc = telnet_init_default(tall_upf_ctx, &g_upf, OSMO_VTY_PORT_UPF);
|
||||
if (rc < 0)
|
||||
return 2;
|
||||
|
||||
/* start control interface, after reading config for ctrl_vty_get_bind_addr() */
|
||||
g_upf->ctrl = ctrl_interface_setup_dynip(g_upf, ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_UPF, NULL);
|
||||
g_upf->ctrl = ctrl_interface_setup(g_upf, OSMO_CTRL_PORT_UPF, NULL);
|
||||
if (!g_upf->ctrl) {
|
||||
fprintf(stderr, "Failed to initialize control interface. Exiting.\n");
|
||||
return -1;
|
||||
@@ -283,6 +331,15 @@ int main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
if (upf_gtp_devs_open())
|
||||
return -1;
|
||||
|
||||
if (upf_nft_init())
|
||||
return -1;
|
||||
|
||||
if (upf_pfcp_listen())
|
||||
return -1;
|
||||
|
||||
do {
|
||||
log_reset_context();
|
||||
osmo_select_main_ctx(0);
|
||||
@@ -301,13 +358,23 @@ int main(int argc, char **argv)
|
||||
}
|
||||
} while (!osmo_select_shutdown_done());
|
||||
|
||||
log_fini();
|
||||
up_endpoint_free(&g_upf->pfcp.ep);
|
||||
upf_gtp_devs_close();
|
||||
|
||||
upf_gtp_genl_close();
|
||||
|
||||
upf_nft_free();
|
||||
|
||||
/* Report the heap state of talloc contexts, then free, so both ASAN and Valgrind are happy... */
|
||||
talloc_report_full(tall_upf_ctx, stderr);
|
||||
talloc_free(tall_upf_ctx);
|
||||
|
||||
talloc_report_full(tall_vty_ctx, stderr);
|
||||
log_fini();
|
||||
|
||||
talloc_report_full(tall_infra_ctx, stderr);
|
||||
talloc_free(tall_infra_ctx);
|
||||
|
||||
talloc_report(tall_vty_ctx, stderr);
|
||||
talloc_free(tall_vty_ctx);
|
||||
|
||||
talloc_report_full(NULL, stderr);
|
||||
|
||||
297
src/osmo-upf/up_endpoint.c
Normal file
297
src/osmo-upf/up_endpoint.c
Normal file
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/pfcp/pfcp_endpoint.h>
|
||||
#include <osmocom/pfcp/pfcp_msg.h>
|
||||
|
||||
#include <osmocom/upf/up_endpoint.h>
|
||||
#include <osmocom/upf/up_peer.h>
|
||||
#include <osmocom/upf/up_session.h>
|
||||
|
||||
static void up_endpoint_set_msg_ctx(struct osmo_pfcp_endpoint *ep, struct osmo_pfcp_msg *m, struct osmo_pfcp_msg *req)
|
||||
{
|
||||
struct up_endpoint *up_ep = osmo_pfcp_endpoint_get_cfg(ep)->priv;
|
||||
struct up_peer *peer;
|
||||
|
||||
/* If this is a response to an earlier request, just take the msg context from the request message. */
|
||||
if (req) {
|
||||
if (!m->ctx.peer_fi && req->ctx.peer_fi)
|
||||
up_peer_set_msg_ctx(req->ctx.peer_fi->priv, m);
|
||||
if (!m->ctx.session_fi && req->ctx.session_fi)
|
||||
up_session_set_msg_ctx(req->ctx.session_fi->priv, m);
|
||||
}
|
||||
|
||||
/* From the remote address, find the matching peer instance */
|
||||
if (!m->ctx.peer_fi) {
|
||||
peer = up_peer_find(up_ep, &m->remote_addr);
|
||||
if (peer) {
|
||||
up_peer_set_msg_ctx(peer, m);
|
||||
}
|
||||
} else {
|
||||
peer = m->ctx.peer_fi->priv;
|
||||
}
|
||||
|
||||
/* Find a session, if the header is parsed yet and contains a SEID */
|
||||
if (peer && !m->ctx.session_fi && m->h.seid_present) {
|
||||
struct up_session *session;
|
||||
session = up_session_find_by_up_seid(peer, m->h.seid);
|
||||
if (session) {
|
||||
up_session_set_msg_ctx(session, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void up_ep_rx_not_impl_req(struct up_endpoint *up_ep, const struct osmo_pfcp_msg *m,
|
||||
enum osmo_pfcp_message_type resp_msgt, enum osmo_pfcp_cause cause)
|
||||
{
|
||||
struct osmo_pfcp_msg *tx;
|
||||
enum osmo_pfcp_cause *tx_cause;
|
||||
OSMO_LOG_PFCP_MSG(m, LOGL_ERROR, "message type not implemented\n");
|
||||
tx = osmo_pfcp_msg_alloc_tx_resp(OTC_SELECT, m, resp_msgt);
|
||||
tx_cause = osmo_pfcp_msg_cause(tx);
|
||||
if (tx_cause)
|
||||
*tx_cause = cause;
|
||||
osmo_pfcp_endpoint_tx(up_ep->pfcp_ep, tx);
|
||||
}
|
||||
|
||||
static void up_ep_rx_pfd_mgmt_req(struct up_endpoint *up_ep, const struct osmo_pfcp_msg *m)
|
||||
{
|
||||
up_ep_rx_not_impl_req(up_ep, m, OSMO_PFCP_MSGT_PFD_MGMT_RESP, OSMO_PFCP_CAUSE_SERVICE_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
static void up_ep_rx_assoc_setup_req(struct up_endpoint *up_ep, const struct osmo_pfcp_msg *m)
|
||||
{
|
||||
struct up_peer *peer = m->ctx.peer_fi ? m->ctx.peer_fi->priv : NULL;
|
||||
if (!peer) {
|
||||
peer = up_peer_find_or_add(up_ep, &m->remote_addr);
|
||||
OSMO_ASSERT(peer);
|
||||
}
|
||||
osmo_fsm_inst_dispatch(peer->fi, UP_PEER_EV_RX_ASSOC_SETUP_REQ, (void *)m);
|
||||
}
|
||||
|
||||
static void up_ep_rx_assoc_upd_req(struct up_endpoint *up_ep, const struct osmo_pfcp_msg *m)
|
||||
{
|
||||
if (!m->ctx.peer_fi) {
|
||||
struct osmo_pfcp_msg *tx;
|
||||
OSMO_LOG_PFCP_MSG(m, LOGL_ERROR, "Peer is not associated, cannot update association\n");
|
||||
tx = osmo_pfcp_msg_alloc_tx_resp(OTC_SELECT, m, OSMO_PFCP_MSGT_ASSOC_UPDATE_RESP);
|
||||
osmo_pfcp_endpoint_tx(up_ep->pfcp_ep, tx);
|
||||
return;
|
||||
}
|
||||
osmo_fsm_inst_dispatch(m->ctx.peer_fi, UP_PEER_EV_RX_ASSOC_UPD_REQ, (void *)m);
|
||||
}
|
||||
|
||||
static void up_ep_rx_assoc_rel_req(struct up_endpoint *up_ep, const struct osmo_pfcp_msg *m)
|
||||
{
|
||||
if (!m->ctx.peer_fi) {
|
||||
struct osmo_pfcp_msg *tx;
|
||||
OSMO_LOG_PFCP_MSG(m, LOGL_ERROR, "Peer is not associated. Sending ACK response anyway\n");
|
||||
tx = osmo_pfcp_msg_alloc_tx_resp(OTC_SELECT, m, OSMO_PFCP_MSGT_ASSOC_RELEASE_RESP);
|
||||
osmo_pfcp_endpoint_tx(up_ep->pfcp_ep, tx);
|
||||
return;
|
||||
}
|
||||
osmo_fsm_inst_dispatch(m->ctx.peer_fi, UP_PEER_EV_RX_ASSOC_REL_REQ, (void *)m);
|
||||
}
|
||||
|
||||
static void up_ep_rx_node_report_req(struct up_endpoint *up_ep, const struct osmo_pfcp_msg *m)
|
||||
{
|
||||
up_ep_rx_not_impl_req(up_ep, m, OSMO_PFCP_MSGT_NODE_REPORT_RESP, OSMO_PFCP_CAUSE_SERVICE_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
static void up_ep_rx_session_set_del_req(struct up_endpoint *up_ep, const struct osmo_pfcp_msg *m)
|
||||
{
|
||||
up_ep_rx_not_impl_req(up_ep, m, OSMO_PFCP_MSGT_SESSION_SET_DEL_RESP,
|
||||
OSMO_PFCP_CAUSE_SERVICE_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
static void up_ep_rx_session_est_req(struct up_endpoint *up_ep, const struct osmo_pfcp_msg *m)
|
||||
{
|
||||
if (!m->ctx.peer_fi) {
|
||||
struct osmo_pfcp_msg *tx;
|
||||
OSMO_LOG_PFCP_MSG(m, LOGL_ERROR, "Peer is not associated, cannot establish session\n");
|
||||
tx = osmo_pfcp_msg_alloc_tx_resp(OTC_SELECT, m, OSMO_PFCP_MSGT_SESSION_EST_RESP);
|
||||
tx->ies.session_est_resp.cause = OSMO_PFCP_CAUSE_NO_ESTABLISHED_PFCP_ASSOC;
|
||||
osmo_pfcp_endpoint_tx(up_ep->pfcp_ep, tx);
|
||||
return;
|
||||
}
|
||||
osmo_fsm_inst_dispatch(m->ctx.peer_fi, UP_PEER_EV_RX_SESSION_EST_REQ, (void *)m);
|
||||
}
|
||||
|
||||
static void up_ep_rx_session_mod_req(struct up_endpoint *up_ep, const struct osmo_pfcp_msg *m)
|
||||
{
|
||||
if (!m->ctx.session_fi) {
|
||||
/* Session not found. */
|
||||
struct osmo_pfcp_msg *tx;
|
||||
tx = osmo_pfcp_msg_alloc_tx_resp(OTC_SELECT, m, OSMO_PFCP_MSGT_SESSION_MOD_RESP);
|
||||
if (!m->ctx.peer_fi) {
|
||||
/* Not even the peer is associated. */
|
||||
OSMO_LOG_PFCP_MSG(m, LOGL_ERROR, "Peer is not associated, cannot modify session\n");
|
||||
tx->ies.session_mod_resp.cause = OSMO_PFCP_CAUSE_NO_ESTABLISHED_PFCP_ASSOC;
|
||||
} else {
|
||||
OSMO_LOG_PFCP_MSG(m, LOGL_ERROR,
|
||||
"No established session with SEID=0x%"PRIx64", cannot modify\n",
|
||||
m->h.seid);
|
||||
tx->ies.session_mod_resp.cause = OSMO_PFCP_CAUSE_SESSION_CTX_NOT_FOUND;
|
||||
}
|
||||
osmo_pfcp_endpoint_tx(up_ep->pfcp_ep, tx);
|
||||
return;
|
||||
}
|
||||
osmo_fsm_inst_dispatch(m->ctx.session_fi, UP_SESSION_EV_RX_SESSION_MOD_REQ, (void *)m);
|
||||
}
|
||||
|
||||
static void up_ep_rx_session_del_req(struct up_endpoint *up_ep, const struct osmo_pfcp_msg *m)
|
||||
{
|
||||
if (!m->ctx.session_fi) {
|
||||
/* Session not found. */
|
||||
struct osmo_pfcp_msg *tx;
|
||||
tx = osmo_pfcp_msg_alloc_tx_resp(OTC_SELECT, m, OSMO_PFCP_MSGT_SESSION_DEL_RESP);
|
||||
if (!m->ctx.peer_fi) {
|
||||
/* Not even the peer is associated. */
|
||||
OSMO_LOG_PFCP_MSG(m, LOGL_ERROR, "Peer is not associated, cannot delete session\n");
|
||||
tx->ies.session_del_resp.cause = OSMO_PFCP_CAUSE_NO_ESTABLISHED_PFCP_ASSOC;
|
||||
} else {
|
||||
OSMO_LOG_PFCP_MSG(m, LOGL_ERROR,
|
||||
"No established session with SEID=0x%"PRIx64", cannot delete\n",
|
||||
m->h.seid);
|
||||
tx->ies.session_del_resp.cause = OSMO_PFCP_CAUSE_SESSION_CTX_NOT_FOUND;
|
||||
}
|
||||
osmo_pfcp_endpoint_tx(up_ep->pfcp_ep, tx);
|
||||
return;
|
||||
}
|
||||
osmo_fsm_inst_dispatch(m->ctx.session_fi, UP_SESSION_EV_RX_SESSION_DEL_REQ, (void *)m);
|
||||
}
|
||||
|
||||
static void up_ep_rx_session_rep_req(struct up_endpoint *up_ep, const struct osmo_pfcp_msg *m)
|
||||
{
|
||||
up_ep_rx_not_impl_req(up_ep, m, OSMO_PFCP_MSGT_SESSION_REP_RESP, OSMO_PFCP_CAUSE_SERVICE_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
static void up_endpoint_rx_cb(struct osmo_pfcp_endpoint *ep, struct osmo_pfcp_msg *m, struct osmo_pfcp_msg *req)
|
||||
{
|
||||
struct up_endpoint *up_ep = osmo_pfcp_endpoint_get_priv(ep);
|
||||
|
||||
switch (m->h.message_type) {
|
||||
case OSMO_PFCP_MSGT_PFD_MGMT_REQ:
|
||||
up_ep_rx_pfd_mgmt_req(up_ep, m);
|
||||
return;
|
||||
case OSMO_PFCP_MSGT_ASSOC_SETUP_REQ:
|
||||
up_ep_rx_assoc_setup_req(up_ep, m);
|
||||
return;
|
||||
case OSMO_PFCP_MSGT_ASSOC_UPDATE_REQ:
|
||||
up_ep_rx_assoc_upd_req(up_ep, m);
|
||||
return;
|
||||
case OSMO_PFCP_MSGT_ASSOC_RELEASE_REQ:
|
||||
up_ep_rx_assoc_rel_req(up_ep, m);
|
||||
return;
|
||||
case OSMO_PFCP_MSGT_NODE_REPORT_REQ:
|
||||
up_ep_rx_node_report_req(up_ep, m);
|
||||
return;
|
||||
case OSMO_PFCP_MSGT_SESSION_SET_DEL_REQ:
|
||||
up_ep_rx_session_set_del_req(up_ep, m);
|
||||
return;
|
||||
case OSMO_PFCP_MSGT_SESSION_EST_REQ:
|
||||
up_ep_rx_session_est_req(up_ep, m);
|
||||
return;
|
||||
case OSMO_PFCP_MSGT_SESSION_MOD_REQ:
|
||||
up_ep_rx_session_mod_req(up_ep, m);
|
||||
return;
|
||||
case OSMO_PFCP_MSGT_SESSION_DEL_REQ:
|
||||
up_ep_rx_session_del_req(up_ep, m);
|
||||
return;
|
||||
case OSMO_PFCP_MSGT_SESSION_REP_REQ:
|
||||
up_ep_rx_session_rep_req(up_ep, m);
|
||||
return;
|
||||
case OSMO_PFCP_MSGT_HEARTBEAT_REQ:
|
||||
case OSMO_PFCP_MSGT_HEARTBEAT_RESP:
|
||||
/* Heartbeat is already handled in osmo_pfcp_endpoint_handle_rx() in pfcp_endpoint.c. The heartbeat
|
||||
* messages are also dispatched here, to the rx_cb, "on informtional basis", nothing needs to happen
|
||||
* here. */
|
||||
return;
|
||||
default:
|
||||
OSMO_LOG_PFCP_MSG(m, LOGL_ERROR, "Unknown message type\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
struct up_endpoint *up_endpoint_alloc(void *ctx, const struct osmo_sockaddr *local_addr)
|
||||
{
|
||||
struct osmo_pfcp_endpoint_cfg cfg;
|
||||
struct up_endpoint *up_ep;
|
||||
up_ep = talloc_zero(ctx, struct up_endpoint);
|
||||
INIT_LLIST_HEAD(&up_ep->peers);
|
||||
|
||||
cfg = (struct osmo_pfcp_endpoint_cfg){
|
||||
.local_addr = *local_addr,
|
||||
.set_msg_ctx_cb = up_endpoint_set_msg_ctx,
|
||||
.rx_msg_cb = up_endpoint_rx_cb,
|
||||
.priv = up_ep,
|
||||
};
|
||||
osmo_pfcp_ie_node_id_from_osmo_sockaddr(&cfg.local_node_id, local_addr);
|
||||
|
||||
up_ep->pfcp_ep = osmo_pfcp_endpoint_create(up_ep, &cfg);
|
||||
OSMO_ASSERT(up_ep->pfcp_ep);
|
||||
|
||||
return up_ep;
|
||||
}
|
||||
|
||||
int up_endpoint_bind(struct up_endpoint *up_ep)
|
||||
{
|
||||
OSMO_ASSERT(up_ep);
|
||||
OSMO_ASSERT(up_ep->pfcp_ep);
|
||||
return osmo_pfcp_endpoint_bind(up_ep->pfcp_ep);
|
||||
}
|
||||
|
||||
static struct up_session *up_endpoint_find_session(struct up_endpoint *ep, uint64_t up_seid)
|
||||
{
|
||||
struct up_peer *peer;
|
||||
llist_for_each_entry(peer, &ep->peers, entry) {
|
||||
struct up_session *session = up_session_find_by_up_seid(peer, up_seid);
|
||||
if (session)
|
||||
return session;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint64_t up_endpoint_next_up_seid(struct up_endpoint *ep)
|
||||
{
|
||||
uint64_t sanity;
|
||||
for (sanity = 2342; sanity; sanity--) {
|
||||
uint64_t next_seid = osmo_pfcp_next_seid(&ep->next_up_seid_state);
|
||||
if (up_endpoint_find_session(ep, next_seid))
|
||||
continue;
|
||||
return next_seid;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void up_endpoint_free(struct up_endpoint **_ep)
|
||||
{
|
||||
struct up_peer *peer;
|
||||
struct up_endpoint *ep = *_ep;
|
||||
|
||||
while ((peer = llist_first_entry_or_null(&ep->peers, struct up_peer, entry)))
|
||||
up_peer_free(peer);
|
||||
|
||||
osmo_pfcp_endpoint_free(&ep->pfcp_ep);
|
||||
*_ep = NULL;
|
||||
}
|
||||
200
src/osmo-upf/up_gtp_action.c
Normal file
200
src/osmo-upf/up_gtp_action.c
Normal file
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <osmocom/upf/upf.h>
|
||||
#include <osmocom/upf/up_gtp_action.h>
|
||||
#include <osmocom/upf/up_peer.h>
|
||||
#include <osmocom/upf/up_session.h>
|
||||
|
||||
int up_gtp_action_cmp(const struct up_gtp_action *a, const struct up_gtp_action *b)
|
||||
{
|
||||
int cmp;
|
||||
if (a == b)
|
||||
return 0;
|
||||
if (!a)
|
||||
return -1;
|
||||
if (!b)
|
||||
return 1;
|
||||
|
||||
#define CMP_MEMB(MEMB) OSMO_CMP(a->MEMB, b->MEMB)
|
||||
|
||||
if ((cmp = CMP_MEMB(kind)))
|
||||
return cmp;
|
||||
|
||||
switch (a->kind) {
|
||||
case UP_GTP_U_TUNEND:
|
||||
if ((cmp = CMP_MEMB(tunend.access.local.teid)))
|
||||
return cmp;
|
||||
if ((cmp = CMP_MEMB(tunend.access.remote.teid)))
|
||||
return cmp;
|
||||
cmp = osmo_sockaddr_cmp(&a->tunend.access.remote.addr, &b->tunend.access.remote.addr);
|
||||
if (cmp)
|
||||
return cmp;
|
||||
cmp = osmo_sockaddr_cmp(&a->tunend.core.ue_local_addr, &b->tunend.core.ue_local_addr);
|
||||
if (cmp)
|
||||
return cmp;
|
||||
break;
|
||||
|
||||
case UP_GTP_U_TUNMAP:
|
||||
if ((cmp = CMP_MEMB(tunmap.access.tun.local.teid)))
|
||||
return cmp;
|
||||
if ((cmp = CMP_MEMB(tunmap.access.tun.remote.teid)))
|
||||
return cmp;
|
||||
if ((cmp = CMP_MEMB(tunmap.core.tun.local.teid)))
|
||||
return cmp;
|
||||
if ((cmp = CMP_MEMB(tunmap.core.tun.remote.teid)))
|
||||
return cmp;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int up_gtp_action_enable_disable(struct up_gtp_action *a, bool enable)
|
||||
{
|
||||
struct upf_gtp_dev *gtp_dev;
|
||||
const struct osmo_sockaddr *gtp_addr;
|
||||
int rc;
|
||||
|
||||
switch (a->kind) {
|
||||
case UP_GTP_U_TUNEND:
|
||||
if (g_upf->tunend.mockup) {
|
||||
LOG_UP_GTP_ACTION(a, LOGL_NOTICE, "tunend/mockup active, skipping GTP action %s\n",
|
||||
enable ? "enable" : "disable");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Pick GTP device matching the local F-TEID set up for the GTP tunnel (it is on the Access side) */
|
||||
gtp_addr = &a->tunend.access.local.addr;
|
||||
gtp_dev = upf_gtp_dev_find_by_local_addr(gtp_addr);
|
||||
if (!gtp_dev) {
|
||||
LOG_UP_GTP_ACTION(a, LOGL_ERROR, "No GTP device open for local address %s, cannot %s"
|
||||
" -- consider configuring 'tunend' / 'dev (create|use) foo %s'\n",
|
||||
osmo_sockaddr_to_str_c(OTC_SELECT, gtp_addr),
|
||||
enable ? "enable" : "disable",
|
||||
osmo_sockaddr_to_str_c(OTC_SELECT, gtp_addr));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (enable)
|
||||
rc = upf_gtp_dev_tunend_add(gtp_dev, &a->tunend);
|
||||
else
|
||||
rc = upf_gtp_dev_tunend_del(gtp_dev, &a->tunend);
|
||||
if (rc) {
|
||||
LOG_UP_GTP_ACTION(a, LOGL_ERROR, "Failed to %s GTP tunnel (rc=%d)\n",
|
||||
enable ? "enable" : "disable", rc);
|
||||
return rc;
|
||||
}
|
||||
LOG_UP_GTP_ACTION(a, LOGL_NOTICE, "%s tunend on dev %s\n", enable ? "Enabled" : "Disabled",
|
||||
gtp_dev->name);
|
||||
return 0;
|
||||
|
||||
case UP_GTP_U_TUNMAP:
|
||||
if (g_upf->tunmap.mockup) {
|
||||
LOG_UP_GTP_ACTION(a, LOGL_NOTICE, "tunmap/mockup active, skipping nftables ruleset %s\n",
|
||||
enable ? "enable" : "disable");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (enable)
|
||||
rc = upf_nft_tunmap_create(&a->tunmap);
|
||||
else
|
||||
rc = upf_nft_tunmap_delete(&a->tunmap);
|
||||
if (rc) {
|
||||
LOG_UP_GTP_ACTION(a, LOGL_ERROR, "Failed to %s nft GTP tunnel mapping (rc=%d)\n",
|
||||
enable ? "enable" : "disable", rc);
|
||||
return rc;
|
||||
}
|
||||
LOG_UP_GTP_ACTION(a, LOGL_NOTICE, "%s tunmap, nft chain IDs: access--%u-> <-%u--core\n",
|
||||
enable ? "Enabled" : "Disabled",
|
||||
a->tunmap.access.chain_id, a->tunmap.core.chain_id);
|
||||
return 0;
|
||||
|
||||
default:
|
||||
LOG_UP_GTP_ACTION(a, LOGL_ERROR, "Invalid action\n");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
}
|
||||
|
||||
int up_gtp_action_enable(struct up_gtp_action *a)
|
||||
{
|
||||
return up_gtp_action_enable_disable(a, true);
|
||||
}
|
||||
|
||||
int up_gtp_action_disable(struct up_gtp_action *a)
|
||||
{
|
||||
return up_gtp_action_enable_disable(a, false);
|
||||
}
|
||||
|
||||
int up_gtp_action_to_str_buf(char *buf, size_t buflen, const struct up_gtp_action *a)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
switch (a->kind) {
|
||||
case UP_GTP_U_TUNEND:
|
||||
OSMO_STRBUF_PRINTF(sb, "GTP:tunend GTP-access-r:");
|
||||
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &a->tunend.access.remote.addr);
|
||||
OSMO_STRBUF_PRINTF(sb, " TEID-access-r:0x%"PRIx32, a->tunend.access.remote.teid);
|
||||
OSMO_STRBUF_PRINTF(sb, " GTP-access-l:");
|
||||
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &a->tunend.access.local.addr);
|
||||
OSMO_STRBUF_PRINTF(sb, " TEID-access-l:0x%"PRIx32" IP-core-l:", a->tunend.access.local.teid);
|
||||
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &a->tunend.core.ue_local_addr);
|
||||
break;
|
||||
case UP_GTP_U_TUNMAP:
|
||||
OSMO_STRBUF_PRINTF(sb, "GTP:tunmap GTP-access-r:");
|
||||
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &a->tunmap.access.tun.remote.addr);
|
||||
OSMO_STRBUF_PRINTF(sb, " TEID-access-r:0x%"PRIx32, a->tunmap.access.tun.remote.teid);
|
||||
OSMO_STRBUF_PRINTF(sb, " GTP-access-l:");
|
||||
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &a->tunmap.access.tun.local.addr);
|
||||
OSMO_STRBUF_PRINTF(sb, " TEID-access-l:0x%"PRIx32, a->tunmap.access.tun.local.teid);
|
||||
OSMO_STRBUF_PRINTF(sb, " GTP-core-r:");
|
||||
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &a->tunmap.core.tun.remote.addr);
|
||||
OSMO_STRBUF_PRINTF(sb, " TEID-core-r:0x%"PRIx32, a->tunmap.core.tun.remote.teid);
|
||||
OSMO_STRBUF_PRINTF(sb, " GTP-core-l:");
|
||||
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &a->tunmap.core.tun.local.addr);
|
||||
OSMO_STRBUF_PRINTF(sb, " TEID-core-l:0x%"PRIx32, a->tunmap.core.tun.local.teid);
|
||||
break;
|
||||
case UP_GTP_DROP:
|
||||
OSMO_STRBUF_PRINTF(sb, "GTP:drop");
|
||||
break;
|
||||
default:
|
||||
OSMO_STRBUF_PRINTF(sb, "GTP:?");
|
||||
break;
|
||||
}
|
||||
if (a->session)
|
||||
OSMO_STRBUF_PRINTF(sb, " PFCP-peer:%s SEID-l:0x%"PRIx64,
|
||||
up_peer_remote_addr_str(a->session->up_peer), a->session->up_seid);
|
||||
OSMO_STRBUF_PRINTF(sb, " PDR-access:%d", a->pdr_access);
|
||||
OSMO_STRBUF_PRINTF(sb, " PDR-core:%d", a->pdr_core);
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
char *up_gtp_action_to_str_c(void *ctx, const struct up_gtp_action *a)
|
||||
{
|
||||
OSMO_NAME_C_IMPL(ctx, 128, "ERROR", up_gtp_action_to_str_buf, a)
|
||||
}
|
||||
544
src/osmo-upf/up_peer.c
Normal file
544
src/osmo-upf/up_peer.c
Normal file
@@ -0,0 +1,544 @@
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/fsm.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/tdef.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <osmocom/pfcp/pfcp_msg.h>
|
||||
#include <osmocom/pfcp/pfcp_endpoint.h>
|
||||
|
||||
#include <osmocom/upf/upf.h>
|
||||
#include <osmocom/upf/up_peer.h>
|
||||
#include <osmocom/upf/up_endpoint.h>
|
||||
#include <osmocom/upf/up_session.h>
|
||||
|
||||
enum up_peer_fsm_state {
|
||||
UP_PEER_ST_NOT_ASSOCIATED,
|
||||
UP_PEER_ST_ASSOCIATED,
|
||||
UP_PEER_ST_GRACEFUL_RELEASE,
|
||||
UP_PEER_ST_WAIT_USE_COUNT,
|
||||
};
|
||||
|
||||
static const struct value_string up_peer_fsm_event_names[] = {
|
||||
OSMO_VALUE_STRING(UP_PEER_EV_RX_ASSOC_SETUP_REQ),
|
||||
OSMO_VALUE_STRING(UP_PEER_EV_RX_ASSOC_UPD_REQ),
|
||||
OSMO_VALUE_STRING(UP_PEER_EV_RX_ASSOC_REL_REQ),
|
||||
OSMO_VALUE_STRING(UP_PEER_EV_RX_SESSION_EST_REQ),
|
||||
OSMO_VALUE_STRING(UP_PEER_EV_HEARTBEAT_FAILURE),
|
||||
OSMO_VALUE_STRING(UP_PEER_EV_USE_COUNT_ZERO),
|
||||
OSMO_VALUE_STRING(UP_PEER_EV_SESSION_TERM),
|
||||
{}
|
||||
};
|
||||
|
||||
static struct osmo_fsm up_peer_fsm;
|
||||
|
||||
static const struct osmo_tdef_state_timeout up_peer_fsm_timeouts[32] = {
|
||||
[UP_PEER_ST_GRACEFUL_RELEASE] = { .T = -21 },
|
||||
};
|
||||
|
||||
/* Transition to a state, using the T timer defined in up_peer_fsm_timeouts.
|
||||
* Assumes local variable fi exists. */
|
||||
#define up_peer_fsm_state_chg(state) \
|
||||
osmo_tdef_fsm_inst_state_chg(fi, state, \
|
||||
up_peer_fsm_timeouts, \
|
||||
osmo_pfcp_tdefs, \
|
||||
5)
|
||||
|
||||
static int up_peer_use_cb(struct osmo_use_count_entry *e, int32_t old_use_count, const char *file, int line)
|
||||
{
|
||||
struct up_peer *peer = e->use_count->talloc_object;
|
||||
int32_t total;
|
||||
int level;
|
||||
|
||||
if (!e->use)
|
||||
return -EINVAL;
|
||||
|
||||
total = osmo_use_count_total(&peer->use_count);
|
||||
|
||||
if (total == 0
|
||||
|| (total == 1 && old_use_count == 0 && e->count == 1))
|
||||
level = LOGL_INFO;
|
||||
else
|
||||
level = LOGL_DEBUG;
|
||||
|
||||
LOGPFSMSLSRC(peer->fi, DREF, level, file, line,
|
||||
"%s %s: now used by %s\n",
|
||||
(e->count - old_use_count) > 0 ? "+" : "-", e->use,
|
||||
osmo_use_count_to_str_c(OTC_SELECT, &peer->use_count));
|
||||
|
||||
if (e->count < 0)
|
||||
return -ERANGE;
|
||||
|
||||
if (total == 0)
|
||||
osmo_fsm_inst_dispatch(peer->fi, UP_PEER_EV_USE_COUNT_ZERO, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *up_peer_remote_addr_str(struct up_peer *peer)
|
||||
{
|
||||
struct osmo_sockaddr remote_addr = peer->remote_addr;
|
||||
|
||||
/* Zero the port, it is not interesting information. The port for PFCP is defined fixed, and there is no use
|
||||
* printing it in the logs */
|
||||
osmo_sockaddr_set_port(&remote_addr.u.sa, 0);
|
||||
|
||||
return osmo_sockaddr_to_str_c(OTC_SELECT, &remote_addr);
|
||||
}
|
||||
|
||||
static void up_peer_update_id(struct up_peer *peer)
|
||||
{
|
||||
osmo_fsm_inst_update_id_f_sanitize(peer->fi, '-', "%s", up_peer_remote_addr_str(peer));
|
||||
LOGPFSML(peer->fi, LOGL_DEBUG, "Updated id\n");
|
||||
}
|
||||
|
||||
static struct up_peer *up_peer_add(struct up_endpoint *up_endpoint, const struct osmo_sockaddr *remote_addr)
|
||||
{
|
||||
struct up_peer *peer;
|
||||
|
||||
struct osmo_fsm_inst *fi = osmo_fsm_inst_alloc(&up_peer_fsm, up_endpoint, NULL, LOGL_DEBUG, NULL);
|
||||
OSMO_ASSERT(fi);
|
||||
|
||||
peer = talloc(fi, struct up_peer);
|
||||
OSMO_ASSERT(peer);
|
||||
fi->priv = peer;
|
||||
|
||||
*peer = (struct up_peer) {
|
||||
.fi = fi,
|
||||
.up_endpoint = up_endpoint,
|
||||
.remote_addr = *remote_addr,
|
||||
.heartbeat_fi = NULL /* FIXME */,
|
||||
.use_count = {
|
||||
.talloc_object = peer,
|
||||
.use_cb = up_peer_use_cb,
|
||||
},
|
||||
};
|
||||
osmo_use_count_make_static_entries(&peer->use_count, peer->use_count_buf, ARRAY_SIZE(peer->use_count_buf));
|
||||
hash_init(peer->sessions_by_up_seid);
|
||||
hash_init(peer->sessions_by_cp_seid);
|
||||
|
||||
osmo_pfcp_bits_set(peer->local_up_features.bits, OSMO_PFCP_UP_FEAT_BUNDL, true);
|
||||
osmo_pfcp_bits_set(peer->local_up_features.bits, OSMO_PFCP_UP_FEAT_RTTL, true);
|
||||
osmo_pfcp_bits_set(peer->local_up_features.bits, OSMO_PFCP_UP_FEAT_FTUP, true);
|
||||
|
||||
up_peer_update_id(peer);
|
||||
|
||||
llist_add(&peer->entry, &up_endpoint->peers);
|
||||
return peer;
|
||||
}
|
||||
|
||||
struct up_peer *up_peer_find(struct up_endpoint *up_endpoint, const struct osmo_sockaddr *remote_addr)
|
||||
{
|
||||
struct up_peer *peer;
|
||||
llist_for_each_entry(peer, &up_endpoint->peers, entry) {
|
||||
if (osmo_sockaddr_cmp(&peer->remote_addr, remote_addr))
|
||||
continue;
|
||||
return peer;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct up_peer *up_peer_find_or_add(struct up_endpoint *up_endpoint, const struct osmo_sockaddr *remote_addr)
|
||||
{
|
||||
struct up_peer *peer = up_peer_find(up_endpoint, remote_addr);
|
||||
if (peer)
|
||||
return peer;
|
||||
return up_peer_add(up_endpoint, remote_addr);
|
||||
}
|
||||
|
||||
static int up_peer_fsm_timer_cb(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
//struct up_peer *peer = fi->priv;
|
||||
/* Return 1 to terminate FSM instance, 0 to keep running */
|
||||
return 1;
|
||||
}
|
||||
|
||||
void up_peer_set_msg_ctx(struct up_peer *peer, struct osmo_pfcp_msg *m)
|
||||
{
|
||||
OSMO_ASSERT(!m->ctx.peer_fi);
|
||||
|
||||
m->ctx.peer_fi = peer->fi;
|
||||
m->ctx.peer_use_count = &peer->use_count;
|
||||
m->ctx.peer_use_token = (m->rx ? UP_USE_MSG_RX : UP_USE_MSG_TX);
|
||||
OSMO_ASSERT(osmo_use_count_get_put(m->ctx.peer_use_count, m->ctx.peer_use_token, 1) == 0);
|
||||
}
|
||||
|
||||
struct osmo_pfcp_msg *up_peer_init_tx(struct up_peer *peer, struct osmo_pfcp_msg *in_reply_to,
|
||||
enum osmo_pfcp_message_type message_type)
|
||||
{
|
||||
struct osmo_pfcp_msg *tx;
|
||||
if (in_reply_to)
|
||||
tx = osmo_pfcp_msg_alloc_tx_resp(OTC_SELECT, in_reply_to, message_type);
|
||||
else
|
||||
tx = osmo_pfcp_msg_alloc_tx_req(OTC_SELECT, &peer->remote_addr, message_type);
|
||||
up_peer_set_msg_ctx(peer, tx);
|
||||
return tx;
|
||||
}
|
||||
|
||||
static int up_peer_tx_assoc_setup_resp(struct up_peer *peer, struct osmo_pfcp_msg *m, enum osmo_pfcp_cause cause)
|
||||
{
|
||||
struct osmo_pfcp_msg *resp;
|
||||
|
||||
resp = up_peer_init_tx(peer, m, OSMO_PFCP_MSGT_ASSOC_SETUP_RESP);
|
||||
|
||||
resp->ies.assoc_setup_resp = (struct osmo_pfcp_msg_assoc_setup_resp) {
|
||||
.cause = cause,
|
||||
.recovery_time_stamp = osmo_pfcp_endpoint_get_recovery_timestamp(g_upf->pfcp.ep->pfcp_ep),
|
||||
.up_function_features_present = true,
|
||||
.up_function_features = peer->local_up_features,
|
||||
};
|
||||
|
||||
if (osmo_pfcp_endpoint_tx(peer->up_endpoint->pfcp_ep, resp)) {
|
||||
OSMO_LOG_PFCP_MSG(m, LOGL_ERROR, "Error sending response to this message,"
|
||||
" cannot associate with peer\n");
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int up_peer_tx_assoc_rel_resp(struct up_peer *peer, struct osmo_pfcp_msg *m, enum osmo_pfcp_cause cause)
|
||||
{
|
||||
struct osmo_pfcp_msg *resp;
|
||||
|
||||
resp = up_peer_init_tx(peer, m, OSMO_PFCP_MSGT_ASSOC_RELEASE_RESP);
|
||||
|
||||
resp->ies.assoc_release_resp = (struct osmo_pfcp_msg_assoc_release_resp) {
|
||||
.cause = cause,
|
||||
};
|
||||
|
||||
if (osmo_pfcp_endpoint_tx(peer->up_endpoint->pfcp_ep, resp)) {
|
||||
OSMO_LOG_PFCP_MSG(m, LOGL_ERROR, "Error sending response to this message\n");
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void up_peer_clear_sessions(struct up_peer *peer)
|
||||
{
|
||||
struct up_session *session;
|
||||
int bkt;
|
||||
struct hlist_node *tmp;
|
||||
int count = 0;
|
||||
hash_for_each_safe(peer->sessions_by_up_seid, bkt, tmp, session, node_by_up_seid) {
|
||||
count += up_session_discard(session);
|
||||
}
|
||||
if (count)
|
||||
LOGPFSML(peer->fi, LOGL_NOTICE, "terminated %d sessions\n", count);
|
||||
}
|
||||
|
||||
static void up_peer_rx_assoc_setup_req(struct up_peer *peer, struct osmo_pfcp_msg *m)
|
||||
{
|
||||
struct osmo_fsm_inst *fi = peer->fi;
|
||||
enum osmo_pfcp_cause cause = OSMO_PFCP_CAUSE_REQUEST_ACCEPTED;
|
||||
|
||||
if (fi->state == UP_PEER_ST_ASSOCIATED) {
|
||||
/* Retransmissions of the ACK response happen in pfcp_endpoint.c. So if we get this, it is a genuine
|
||||
* duplicate association setup request. We could reject it. But why. Just "replace" with the new
|
||||
* association. Continue. */
|
||||
/* If the peer has restarted, it has forgotten about all sessions. */
|
||||
if (peer->remote_recovery_timestamp != m->ies.assoc_setup_req.recovery_time_stamp) {
|
||||
LOGPFSML(fi, LOGL_NOTICE, "another Association Setup Request, with different Recovery Timestamp."
|
||||
" Clearing sessions, sending ACK.\n");
|
||||
up_peer_clear_sessions(peer);
|
||||
} else {
|
||||
LOGPFSML(fi, LOGL_NOTICE, "another Association Setup Request, with same Recovery Timestamp."
|
||||
" Keeping sessions, sending ACK.\n");
|
||||
}
|
||||
} else if (up_peer_fsm_state_chg(UP_PEER_ST_ASSOCIATED)) {
|
||||
/* Not allowed to transition to ST_ASSOCIATED */
|
||||
cause = OSMO_PFCP_CAUSE_REQUEST_REJECTED;
|
||||
} else {
|
||||
/* Successfully transitioned to ST_ASSOCIATED */
|
||||
peer->remote_recovery_timestamp = m->ies.assoc_setup_req.recovery_time_stamp;
|
||||
peer->remote_node_id = m->ies.assoc_setup_req.node_id;
|
||||
if (m->ies.assoc_setup_req.cp_function_features_present)
|
||||
peer->peer_cp_features = m->ies.assoc_setup_req.cp_function_features;
|
||||
}
|
||||
|
||||
if (up_peer_tx_assoc_setup_resp(peer, m, cause)
|
||||
|| cause != OSMO_PFCP_CAUSE_REQUEST_ACCEPTED)
|
||||
up_peer_fsm_state_chg(UP_PEER_ST_WAIT_USE_COUNT);
|
||||
|
||||
LOGPFSML(fi, LOGL_NOTICE, "Peer associated, Node-Id=%s. Local UP features: [%s]; Peer CP features: [%s]\n",
|
||||
osmo_pfcp_ie_node_id_to_str_c(OTC_SELECT, &peer->remote_node_id),
|
||||
osmo_pfcp_bits_to_str_c(OTC_SELECT, peer->local_up_features.bits, osmo_pfcp_up_feature_strs),
|
||||
osmo_pfcp_bits_to_str_c(OTC_SELECT, peer->peer_cp_features.bits, osmo_pfcp_cp_feature_strs));
|
||||
}
|
||||
|
||||
static void up_peer_rx_assoc_rel_req(struct up_peer *peer, struct osmo_pfcp_msg *m)
|
||||
{
|
||||
struct osmo_fsm_inst *fi = peer->fi;
|
||||
up_peer_tx_assoc_rel_resp(peer, m, OSMO_PFCP_CAUSE_REQUEST_ACCEPTED);
|
||||
up_peer_fsm_state_chg(UP_PEER_ST_WAIT_USE_COUNT);
|
||||
}
|
||||
|
||||
static void up_peer_rx_session_est_req(struct up_peer *peer, struct osmo_pfcp_msg *m)
|
||||
{
|
||||
enum osmo_pfcp_cause cause = OSMO_PFCP_CAUSE_REQUEST_ACCEPTED;
|
||||
struct osmo_pfcp_msg *resp;
|
||||
struct up_session *session = up_session_find_or_add(peer, &m->ies.session_est_req.cp_f_seid);
|
||||
|
||||
if (!session) {
|
||||
cause = OSMO_PFCP_CAUSE_NO_RESOURCES_AVAILABLE;
|
||||
goto nack_response;
|
||||
}
|
||||
|
||||
up_session_set_msg_ctx(session, m);
|
||||
|
||||
if (osmo_fsm_inst_dispatch(session->fi, UP_SESSION_EV_RX_SESSION_EST_REQ, m)) {
|
||||
cause = OSMO_PFCP_CAUSE_REQUEST_REJECTED;
|
||||
goto nack_response;
|
||||
}
|
||||
return;
|
||||
|
||||
nack_response:
|
||||
resp = up_peer_init_tx(peer, m, OSMO_PFCP_MSGT_SESSION_EST_RESP);
|
||||
resp->h.seid = m->ies.session_est_req.cp_f_seid.seid;
|
||||
resp->h.seid_present = true;
|
||||
resp->ies.session_est_resp = (struct osmo_pfcp_msg_session_est_resp){
|
||||
.cause = cause,
|
||||
};
|
||||
osmo_pfcp_endpoint_tx(peer->up_endpoint->pfcp_ep, resp);
|
||||
if (session)
|
||||
up_session_discard(session);
|
||||
}
|
||||
|
||||
static void up_peer_not_associated_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct up_peer *peer = fi->priv;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case UP_PEER_EV_RX_ASSOC_SETUP_REQ:
|
||||
up_peer_rx_assoc_setup_req(peer, data);
|
||||
break;
|
||||
|
||||
case UP_PEER_EV_USE_COUNT_ZERO:
|
||||
/* Not associated and no pending messages. discard peer. */
|
||||
up_peer_fsm_state_chg(UP_PEER_ST_WAIT_USE_COUNT);
|
||||
return;
|
||||
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
static void up_peer_associated_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct up_peer *peer = fi->priv;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case UP_PEER_EV_RX_ASSOC_SETUP_REQ:
|
||||
up_peer_rx_assoc_setup_req(peer, data);
|
||||
break;
|
||||
|
||||
case UP_PEER_EV_RX_ASSOC_UPD_REQ:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
case UP_PEER_EV_RX_SESSION_EST_REQ:
|
||||
up_peer_rx_session_est_req(peer, data);
|
||||
break;
|
||||
|
||||
case UP_PEER_EV_HEARTBEAT_FAILURE:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
case UP_PEER_EV_USE_COUNT_ZERO:
|
||||
/* Stay associated. */
|
||||
return;
|
||||
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
static void up_peer_associated_onleave(struct osmo_fsm_inst *fi, uint32_t next_state)
|
||||
{
|
||||
struct up_peer *peer = fi->priv;
|
||||
if (next_state != UP_PEER_ST_ASSOCIATED)
|
||||
LOGPFSML(fi, LOGL_NOTICE, "Peer %s released\n",
|
||||
osmo_pfcp_ie_node_id_to_str_c(OTC_SELECT, &peer->remote_node_id));
|
||||
}
|
||||
|
||||
static void up_peer_graceful_release_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
//struct up_peer *peer = fi->priv;
|
||||
// FIXME
|
||||
}
|
||||
|
||||
static void up_peer_graceful_release_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct up_peer *peer = fi->priv;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case UP_PEER_EV_HEARTBEAT_FAILURE:
|
||||
up_peer_fsm_state_chg(UP_PEER_ST_WAIT_USE_COUNT);
|
||||
break;
|
||||
|
||||
case UP_PEER_EV_USE_COUNT_ZERO:
|
||||
/* When there are still sessions, stay around. */
|
||||
if (!hash_empty(peer->sessions_by_up_seid))
|
||||
return;
|
||||
up_peer_fsm_state_chg(UP_PEER_ST_WAIT_USE_COUNT);
|
||||
return;
|
||||
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
static void up_peer_wait_use_count_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
struct up_peer *peer = fi->priv;
|
||||
up_peer_clear_sessions(peer);
|
||||
if (!osmo_use_count_total(&peer->use_count))
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
|
||||
}
|
||||
|
||||
static void up_peer_wait_use_count_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct up_peer *peer = fi->priv;
|
||||
switch (event) {
|
||||
|
||||
case UP_PEER_EV_USE_COUNT_ZERO:
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
|
||||
return;
|
||||
|
||||
case UP_PEER_EV_RX_ASSOC_SETUP_REQ:
|
||||
up_peer_rx_assoc_setup_req(peer, data);
|
||||
break;
|
||||
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
#define S(x) (1 << (x))
|
||||
|
||||
static const struct osmo_fsm_state up_peer_fsm_states[] = {
|
||||
[UP_PEER_ST_NOT_ASSOCIATED] = {
|
||||
.name = "NOT_ASSOCIATED",
|
||||
.in_event_mask = 0
|
||||
| S(UP_PEER_EV_RX_ASSOC_SETUP_REQ)
|
||||
| S(UP_PEER_EV_USE_COUNT_ZERO)
|
||||
,
|
||||
.out_state_mask = 0
|
||||
| S(UP_PEER_ST_ASSOCIATED)
|
||||
| S(UP_PEER_ST_WAIT_USE_COUNT)
|
||||
,
|
||||
.action = up_peer_not_associated_action,
|
||||
},
|
||||
[UP_PEER_ST_ASSOCIATED] = {
|
||||
.name = "ASSOCIATED",
|
||||
.in_event_mask = 0
|
||||
| S(UP_PEER_EV_RX_ASSOC_SETUP_REQ)
|
||||
| S(UP_PEER_EV_RX_ASSOC_UPD_REQ)
|
||||
| S(UP_PEER_EV_RX_SESSION_EST_REQ)
|
||||
| S(UP_PEER_EV_HEARTBEAT_FAILURE)
|
||||
| S(UP_PEER_EV_USE_COUNT_ZERO)
|
||||
,
|
||||
.out_state_mask = 0
|
||||
| S(UP_PEER_ST_ASSOCIATED)
|
||||
| S(UP_PEER_ST_GRACEFUL_RELEASE)
|
||||
| S(UP_PEER_ST_WAIT_USE_COUNT)
|
||||
,
|
||||
.action = up_peer_associated_action,
|
||||
.onleave = up_peer_associated_onleave,
|
||||
},
|
||||
[UP_PEER_ST_GRACEFUL_RELEASE] = {
|
||||
.name = "GRACEFUL_RELEASE",
|
||||
.in_event_mask = 0
|
||||
| S(UP_PEER_EV_HEARTBEAT_FAILURE)
|
||||
| S(UP_PEER_EV_USE_COUNT_ZERO)
|
||||
,
|
||||
.out_state_mask = 0
|
||||
| S(UP_PEER_ST_WAIT_USE_COUNT)
|
||||
,
|
||||
.onenter = up_peer_graceful_release_onenter,
|
||||
.action = up_peer_graceful_release_action,
|
||||
},
|
||||
[UP_PEER_ST_WAIT_USE_COUNT] = {
|
||||
.name = "WAIT_USE_COUNT",
|
||||
.in_event_mask = 0
|
||||
| S(UP_PEER_EV_USE_COUNT_ZERO)
|
||||
| S(UP_PEER_EV_RX_ASSOC_SETUP_REQ)
|
||||
,
|
||||
.out_state_mask = 0
|
||||
| S(UP_PEER_ST_ASSOCIATED)
|
||||
,
|
||||
.onenter = up_peer_wait_use_count_onenter,
|
||||
.action = up_peer_wait_use_count_action,
|
||||
},
|
||||
};
|
||||
|
||||
void up_peer_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
|
||||
{
|
||||
struct up_peer *peer = fi->priv;
|
||||
LOGPFSML(fi, LOGL_NOTICE, "Peer removed\n");
|
||||
llist_del(&peer->entry);
|
||||
}
|
||||
|
||||
static void up_peer_allstate_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case UP_PEER_EV_SESSION_TERM:
|
||||
/* ignore */
|
||||
return;
|
||||
case UP_PEER_EV_RX_ASSOC_REL_REQ:
|
||||
up_peer_rx_assoc_rel_req(fi->priv, data);
|
||||
return;
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
static struct osmo_fsm up_peer_fsm = {
|
||||
.name = "up_peer",
|
||||
.log_subsys = DPEER,
|
||||
.states = up_peer_fsm_states,
|
||||
.num_states = ARRAY_SIZE(up_peer_fsm_states),
|
||||
.event_names = up_peer_fsm_event_names,
|
||||
.timer_cb = up_peer_fsm_timer_cb,
|
||||
.cleanup = up_peer_fsm_cleanup,
|
||||
.allstate_event_mask = 0
|
||||
| S(UP_PEER_EV_RX_ASSOC_REL_REQ)
|
||||
| S(UP_PEER_EV_SESSION_TERM)
|
||||
,
|
||||
.allstate_action = up_peer_allstate_action,
|
||||
};
|
||||
|
||||
static __attribute__((constructor)) void up_peer_fsm_register(void)
|
||||
{
|
||||
OSMO_ASSERT(osmo_fsm_register(&up_peer_fsm) == 0);
|
||||
}
|
||||
|
||||
void up_peer_free(struct up_peer *peer)
|
||||
{
|
||||
osmo_fsm_inst_term(peer->fi, OSMO_FSM_TERM_REGULAR, NULL);
|
||||
}
|
||||
196
src/osmo-upf/up_peer_fsm.c
Normal file
196
src/osmo-upf/up_peer_fsm.c
Normal file
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/fsm.h>
|
||||
|
||||
#include <osmocom/upf/up_peer.h>
|
||||
|
||||
enum up_peer_fsm_state {
|
||||
UP_PEER_ST_NOT_ASSOCIATED,
|
||||
UP_PEER_ST_ASSOCIATED,
|
||||
UP_PEER_ST_GRACEFUL_RELEASE,
|
||||
};
|
||||
|
||||
static const struct value_string up_peer_fsm_event_names[] = {
|
||||
OSMO_VALUE_STRING(UP_PEER_EV_RX_ASSOC_SETUP_REQ),
|
||||
OSMO_VALUE_STRING(UP_PEER_EV_RX_ASSOC_UPD_REQ),
|
||||
OSMO_VALUE_STRING(UP_PEER_EV_RX_SESSION_EST_REQ),
|
||||
OSMO_VALUE_STRING(UP_PEER_EV_HEARTBEAT_FAILURE),
|
||||
{}
|
||||
};
|
||||
|
||||
static struct osmo_fsm up_peer_fsm;
|
||||
|
||||
static const struct osmo_tdef_state_timeout up_peer_fsm_timeouts[32] = {
|
||||
[UP_PEER_ST_GRACEFUL_RELEASE] = { .T = -21 },
|
||||
};
|
||||
|
||||
/* Transition to a state, using the T timer defined in up_peer_fsm_timeouts.
|
||||
* Assumes local variable fi exists. */
|
||||
#define up_peer_fsm_state_chg(state) \
|
||||
osmo_tdef_fsm_inst_state_chg(fi, state, \
|
||||
up_peer_fsm_timeouts, \
|
||||
g_upf_tdefs, \
|
||||
5)
|
||||
|
||||
struct up_peer *up_peer_alloc(struct osmo_fsm_inst *parent_fi, uint32_t parent_event_term)
|
||||
{
|
||||
struct up_peer *up_peer;
|
||||
|
||||
struct osmo_fsm_inst *fi = osmo_fsm_inst_alloc_child(&up_peer_fsm, parent_fi, parent_event_term);
|
||||
OSMO_ASSERT(fi);
|
||||
|
||||
up_peer = talloc(fi, struct up_peer);
|
||||
OSMO_ASSERT(up_peer);
|
||||
fi->priv = up_peer;
|
||||
*up_peer = (struct up_peer){
|
||||
.fi = fi,
|
||||
};
|
||||
|
||||
return up_peer;
|
||||
}
|
||||
|
||||
static int up_peer_fsm_timer_cb(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
//struct up_peer *up_peer = fi->priv;
|
||||
/* Return 1 to terminate FSM instance, 0 to keep running */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void up_peer_not_associated_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
//struct up_peer *up_peer = fi->priv;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case UP_PEER_EV_RX_ASSOC_SETUP_REQ:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
static void up_peer_associated_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
//struct up_peer *up_peer = fi->priv;
|
||||
// FIXME
|
||||
}
|
||||
|
||||
static void up_peer_associated_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
//struct up_peer *up_peer = fi->priv;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case UP_PEER_EV_RX_ASSOC_UPD_REQ:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
case UP_PEER_EV_RX_SESSION_EST_REQ:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
case UP_PEER_EV_HEARTBEAT_FAILURE:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
static void up_peer_graceful_release_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
//struct up_peer *up_peer = fi->priv;
|
||||
// FIXME
|
||||
}
|
||||
|
||||
static void up_peer_graceful_release_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
//struct up_peer *up_peer = fi->priv;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case UP_PEER_EV_HEARTBEAT_FAILURE:
|
||||
// FIXME
|
||||
break;
|
||||
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
#define S(x) (1 << (x))
|
||||
|
||||
static const struct osmo_fsm_state up_peer_fsm_states[] = {
|
||||
[UP_PEER_ST_NOT_ASSOCIATED] = {
|
||||
.name = "not_associated",
|
||||
.in_event_mask = 0
|
||||
| S(UP_PEER_EV_RX_ASSOC_SETUP_REQ)
|
||||
,
|
||||
.out_state_mask = 0
|
||||
| S(UP_PEER_ST_ASSOCIATED)
|
||||
,
|
||||
.action = up_peer_not_associated_action,
|
||||
},
|
||||
[UP_PEER_ST_ASSOCIATED] = {
|
||||
.name = "associated",
|
||||
.in_event_mask = 0
|
||||
| S(UP_PEER_EV_RX_ASSOC_UPD_REQ)
|
||||
| S(UP_PEER_EV_RX_SESSION_EST_REQ)
|
||||
| S(UP_PEER_EV_HEARTBEAT_FAILURE)
|
||||
,
|
||||
.out_state_mask = 0
|
||||
| S(UP_PEER_ST_GRACEFUL_RELEASE)
|
||||
,
|
||||
.onenter = up_peer_associated_onenter,
|
||||
.action = up_peer_associated_action,
|
||||
},
|
||||
[UP_PEER_ST_GRACEFUL_RELEASE] = {
|
||||
.name = "graceful_release",
|
||||
.in_event_mask = 0
|
||||
| S(UP_PEER_EV_HEARTBEAT_FAILURE)
|
||||
,
|
||||
.out_state_mask = 0
|
||||
,
|
||||
.onenter = up_peer_graceful_release_onenter,
|
||||
.action = up_peer_graceful_release_action,
|
||||
},
|
||||
};
|
||||
|
||||
static struct osmo_fsm up_peer_fsm = {
|
||||
.name = "up_peer",
|
||||
.states = up_peer_fsm_states,
|
||||
.num_states = ARRAY_SIZE(up_peer_fsm_states),
|
||||
.log_subsys = DSESSION,
|
||||
.event_names = up_peer_fsm_event_names,
|
||||
.timer_cb = up_peer_fsm_timer_cb,
|
||||
};
|
||||
|
||||
static __attribute__((constructor)) void up_peer_fsm_register(void)
|
||||
{
|
||||
OSMO_ASSERT(osmo_fsm_register(&up_peer_fsm) == 0);
|
||||
}
|
||||
1580
src/osmo-upf/up_session.c
Normal file
1580
src/osmo-upf/up_session.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -23,13 +23,194 @@
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/sockaddr_str.h>
|
||||
|
||||
#include <osmocom/pfcp/pfcp_endpoint.h>
|
||||
|
||||
#include <osmocom/upf/upf.h>
|
||||
#include <osmocom/upf/up_endpoint.h>
|
||||
#include <osmocom/upf/up_peer.h>
|
||||
#include <osmocom/upf/up_session.h>
|
||||
#include <osmocom/upf/up_gtp_action.h>
|
||||
#include <osmocom/upf/upf_gtp.h>
|
||||
|
||||
struct g_upf *g_upf = NULL;
|
||||
|
||||
struct osmo_tdef g_upf_nft_tdefs[] = {
|
||||
{ .T = -32, .default_val = 1000, .unit = OSMO_TDEF_MS,
|
||||
.desc = "How long to wait for more nft rulesets before flushing in batch",
|
||||
},
|
||||
{ .T = -33, .default_val = 1, .unit = OSMO_TDEF_CUSTOM,
|
||||
.desc = "When reaching this nr of queued nft rulesets, flush the queue",
|
||||
.max_val = 128,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
struct osmo_tdef_group g_upf_tdef_groups[] = {
|
||||
{ "pfcp", "PFCP endpoint timers", osmo_pfcp_tdefs, },
|
||||
{ "nft", "netfilter timers", g_upf_nft_tdefs, },
|
||||
{}
|
||||
};
|
||||
|
||||
void g_upf_alloc(void *ctx)
|
||||
{
|
||||
OSMO_ASSERT(g_upf == NULL);
|
||||
g_upf = talloc_zero(ctx, struct g_upf);
|
||||
|
||||
*g_upf = (struct g_upf){
|
||||
.pfcp = {
|
||||
.vty_cfg = {
|
||||
.local_addr = talloc_strdup(g_upf, UPF_PFCP_LISTEN_DEFAULT),
|
||||
.local_port = OSMO_PFCP_PORT,
|
||||
},
|
||||
},
|
||||
.tunmap = {
|
||||
.priority_pre = -300,
|
||||
.priority_post = 400,
|
||||
},
|
||||
.tunend = {
|
||||
/* TODO: recovery count state file; use lower byte of current time, poor person's random. */
|
||||
.recovery_count = time(NULL),
|
||||
},
|
||||
};
|
||||
|
||||
INIT_LLIST_HEAD(&g_upf->tunend.vty_cfg.devs);
|
||||
INIT_LLIST_HEAD(&g_upf->tunend.devs);
|
||||
INIT_LLIST_HEAD(&g_upf->netinst);
|
||||
}
|
||||
|
||||
int upf_pfcp_init(void)
|
||||
{
|
||||
struct osmo_sockaddr_str local_addr_str;
|
||||
struct osmo_sockaddr local_addr;
|
||||
|
||||
OSMO_ASSERT(g_upf);
|
||||
OSMO_ASSERT(g_upf->pfcp.ep == NULL);
|
||||
|
||||
/* Translate address string from VTY config to osmo_sockaddr: first read into osmo_sockaddr_str, then write to
|
||||
* osmo_sockaddr. */
|
||||
osmo_sockaddr_str_from_str(&local_addr_str, g_upf->pfcp.vty_cfg.local_addr, g_upf->pfcp.vty_cfg.local_port);
|
||||
osmo_sockaddr_str_to_sockaddr(&local_addr_str, &local_addr.u.sas);
|
||||
|
||||
g_upf->pfcp.ep = up_endpoint_alloc(g_upf, &local_addr);
|
||||
if (!g_upf->pfcp.ep) {
|
||||
fprintf(stderr, "Failed to allocate PFCP endpoint.\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int upf_pfcp_listen(void)
|
||||
{
|
||||
int rc;
|
||||
if (!g_upf->pfcp.ep) {
|
||||
rc = upf_pfcp_init();
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = up_endpoint_bind(g_upf->pfcp.ep);
|
||||
if (rc) {
|
||||
LOGP(DLPFCP, LOGL_ERROR, "PFCP: failed to listen on %s\n",
|
||||
osmo_sockaddr_to_str_c(OTC_SELECT, osmo_pfcp_endpoint_get_local_addr(g_upf->pfcp.ep->pfcp_ep)));
|
||||
return rc;
|
||||
}
|
||||
LOGP(DLPFCP, LOGL_NOTICE, "PFCP: Listening on %s\n",
|
||||
osmo_sockaddr_to_str_c(OTC_SELECT, osmo_pfcp_endpoint_get_local_addr(g_upf->pfcp.ep->pfcp_ep)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int upf_gtp_devs_open()
|
||||
{
|
||||
struct tunend_vty_cfg *c = &g_upf->tunend.vty_cfg;
|
||||
struct tunend_vty_cfg_dev *d;
|
||||
|
||||
llist_for_each_entry(d, &c->devs, entry) {
|
||||
if (upf_gtp_dev_open(d->dev_name, d->create, d->local_addr, false, false))
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool upf_is_local_teid_in_use(uint32_t teid)
|
||||
{
|
||||
struct up_peer *peer;
|
||||
llist_for_each_entry(peer, &g_upf->pfcp.ep->peers, entry) {
|
||||
struct up_session *session = up_session_find_by_local_teid(peer, teid);
|
||||
if (session)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static uint32_t upf_next_local_teid_inc(void)
|
||||
{
|
||||
g_upf->gtp.next_local_teid_state++;
|
||||
if (!g_upf->gtp.next_local_teid_state)
|
||||
g_upf->gtp.next_local_teid_state++;
|
||||
return g_upf->gtp.next_local_teid_state;
|
||||
}
|
||||
|
||||
uint32_t upf_next_local_teid(void)
|
||||
{
|
||||
uint32_t sanity;
|
||||
for (sanity = 2342; sanity; sanity--) {
|
||||
uint32_t next_teid = upf_next_local_teid_inc();
|
||||
if (upf_is_local_teid_in_use(next_teid))
|
||||
continue;
|
||||
return next_teid;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t upf_next_chain_id_inc(void)
|
||||
{
|
||||
g_upf->tunmap.next_chain_id_state++;
|
||||
if (!g_upf->tunmap.next_chain_id_state)
|
||||
g_upf->tunmap.next_chain_id_state++;
|
||||
return g_upf->tunmap.next_chain_id_state;
|
||||
}
|
||||
|
||||
/* Return an unused chain_id, or 0 if none is found with sane effort. */
|
||||
uint32_t upf_next_chain_id(void)
|
||||
{
|
||||
uint32_t sanity;
|
||||
|
||||
/* Make sure the new chain_id is not used anywhere */
|
||||
for (sanity = 2342; sanity; sanity--) {
|
||||
struct up_peer *peer;
|
||||
uint32_t chain_id = upf_next_chain_id_inc();
|
||||
bool taken = false;
|
||||
|
||||
if (!g_upf->pfcp.ep)
|
||||
return chain_id;
|
||||
|
||||
llist_for_each_entry(peer, &g_upf->pfcp.ep->peers, entry) {
|
||||
struct up_session *session;
|
||||
int bkt;
|
||||
hash_for_each(peer->sessions_by_up_seid, bkt, session, node_by_up_seid) {
|
||||
struct up_gtp_action *a;
|
||||
llist_for_each_entry(a, &session->active_gtp_actions, entry) {
|
||||
if (a->kind != UP_GTP_U_TUNMAP)
|
||||
continue;
|
||||
if (a->tunmap.access.chain_id == chain_id
|
||||
|| a->tunmap.core.chain_id == chain_id) {
|
||||
taken = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (taken)
|
||||
break;
|
||||
}
|
||||
if (taken)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!taken)
|
||||
return chain_id;
|
||||
}
|
||||
|
||||
/* finding a chain_id became insane, return invalid = 0 */
|
||||
return 0;
|
||||
}
|
||||
|
||||
511
src/osmo-upf/upf_gtp.c
Normal file
511
src/osmo-upf/upf_gtp.c
Normal file
@@ -0,0 +1,511 @@
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <net/if.h>
|
||||
#include <linux/gtp.h>
|
||||
|
||||
#include <libgtpnl/gtpnl.h>
|
||||
#include <libgtpnl/gtp.h>
|
||||
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/sockaddr_str.h>
|
||||
|
||||
#include <osmocom/upf/upf.h>
|
||||
#include <osmocom/upf/upf_gtp.h>
|
||||
#include <osmocom/upf/upf_gtpu_echo.h>
|
||||
|
||||
#define LOG_GTP_TUN(TUN, LEVEL, FMT, ARGS...) \
|
||||
LOGP(DGTP, LEVEL, "%s: " FMT, upf_gtp_tunend_to_str_c(OTC_SELECT, (TUN)), ##ARGS)
|
||||
|
||||
int upf_gtp_dev_to_str_buf(char *buf, size_t buflen, const struct upf_gtp_dev *dev)
|
||||
{
|
||||
uint16_t v0_port;
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
OSMO_STRBUF_PRINTF(sb, "%s", dev->name ? : "null");
|
||||
if (dev->name && dev->ifidx)
|
||||
OSMO_STRBUF_PRINTF(sb, " [%u]", dev->ifidx);
|
||||
if (dev->sgsn_mode)
|
||||
OSMO_STRBUF_PRINTF(sb, " (SGSN)");
|
||||
OSMO_STRBUF_PRINTF(sb, " ");
|
||||
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &dev->gtpv1.local_addr);
|
||||
v0_port = osmo_sockaddr_port(&dev->gtpv0.local_addr.u.sa);
|
||||
if (dev->gtpv0.enabled && v0_port)
|
||||
OSMO_STRBUF_PRINTF(sb, "/%u", v0_port);
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
char *upf_gtp_dev_to_str_c(void *ctx, const struct upf_gtp_dev *dev)
|
||||
{
|
||||
OSMO_NAME_C_IMPL(ctx, 64, "ERROR", upf_gtp_dev_to_str_buf, dev)
|
||||
}
|
||||
|
||||
struct upf_gtp_dev *upf_gtp_dev_find_by_name(const char *name)
|
||||
{
|
||||
struct upf_gtp_dev *dev;
|
||||
llist_for_each_entry(dev, &g_upf->tunend.devs, entry) {
|
||||
if (!strcmp(name, dev->name))
|
||||
return dev;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct upf_gtp_dev *upf_gtp_dev_find_by_local_addr(const struct osmo_sockaddr *local_addr)
|
||||
{
|
||||
struct upf_gtp_dev *dev;
|
||||
struct upf_gtp_dev *dev_any = NULL;
|
||||
struct osmo_sockaddr needle = *local_addr;
|
||||
|
||||
llist_for_each_entry(dev, &g_upf->tunend.devs, entry) {
|
||||
/* To leave the port number out of the cmp, set the needle's port to match */
|
||||
osmo_sockaddr_set_port(&needle.u.sa, osmo_sockaddr_port(&dev->gtpv1.local_addr.u.sa));
|
||||
|
||||
if (!osmo_sockaddr_cmp(&needle, &dev->gtpv1.local_addr))
|
||||
return dev;
|
||||
if (osmo_sockaddr_is_any(&dev->gtpv1.local_addr) == 1)
|
||||
dev_any = dev;
|
||||
}
|
||||
/* No 1:1 match found, but there is a dev listening on ANY? Return that.
|
||||
* If there is no such dev, return NULL. */
|
||||
return dev_any;
|
||||
}
|
||||
|
||||
struct upf_gtp_dev *upf_gtp_dev_first()
|
||||
{
|
||||
return llist_first_entry_or_null(&g_upf->tunend.devs, struct upf_gtp_dev, entry);
|
||||
}
|
||||
|
||||
/* Tell the kernel to remove the GTP device. Called implicitly by talloc_free() (see upf_gtp_dev_destruct()). */
|
||||
static int upf_gtp_dev_delete(struct upf_gtp_dev *dev)
|
||||
{
|
||||
int rc;
|
||||
if (!dev->name)
|
||||
return 0;
|
||||
rc = gtp_dev_destroy(dev->name);
|
||||
if (rc < 0) {
|
||||
LOG_GTP_DEV(dev, LOGL_ERROR, "Error while deleting device: %s\n", strerror(errno));
|
||||
return rc;
|
||||
}
|
||||
LOG_GTP_DEV(dev, LOGL_NOTICE, "Deleted GTP device\n");
|
||||
dev->name = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int upf_gtp_dev_destruct(struct upf_gtp_dev *dev);
|
||||
|
||||
/* Allocate state for one GTP device, add to g_upf->tunend.devs and return the created device. If state for the device of
|
||||
* that name already exists, do nothing and return NULL. */
|
||||
static struct upf_gtp_dev *upf_gtp_dev_alloc(const char *name, const char *local_addr)
|
||||
{
|
||||
struct upf_gtp_dev *dev = upf_gtp_dev_find_by_name(name);
|
||||
struct osmo_sockaddr_str addr_conv;
|
||||
local_addr = local_addr ? : "0.0.0.0";
|
||||
if (dev) {
|
||||
LOG_GTP_DEV(dev, LOGL_ERROR, "Device already exists. Cannot create %s %s\n", name, local_addr);
|
||||
return NULL;
|
||||
}
|
||||
dev = talloc(g_upf, struct upf_gtp_dev);
|
||||
*dev = (struct upf_gtp_dev){
|
||||
.name = talloc_strdup(dev, name),
|
||||
.gtpv0.ofd.fd = -1,
|
||||
.gtpv1.ofd.fd = -1,
|
||||
};
|
||||
INIT_LLIST_HEAD(&dev->tunnels);
|
||||
|
||||
osmo_sockaddr_str_from_str(&addr_conv, local_addr, PORT_GTP0_U);
|
||||
|
||||
osmo_sockaddr_str_to_sockaddr(&addr_conv, &dev->gtpv0.local_addr.u.sas);
|
||||
|
||||
addr_conv.port = PORT_GTP1_U;
|
||||
osmo_sockaddr_str_to_sockaddr(&addr_conv, &dev->gtpv1.local_addr.u.sas);
|
||||
|
||||
/* Need to add to list before setting up the destructor. A talloc_free() does automagically remove from the
|
||||
* list. */
|
||||
llist_add(&dev->entry, &g_upf->tunend.devs);
|
||||
|
||||
talloc_set_destructor(dev, upf_gtp_dev_destruct);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
static int dev_resolve_ifidx(struct upf_gtp_dev *dev)
|
||||
{
|
||||
int rc;
|
||||
|
||||
dev->ifidx = if_nametoindex(dev->name);
|
||||
if (dev->ifidx == 0) {
|
||||
LOG_GTP_DEV(dev, LOGL_ERROR, "No such device: '%s'\n", dev->name);
|
||||
talloc_free(dev);
|
||||
return -EIO;
|
||||
}
|
||||
/* Let's try something to see if talking to the device works. */
|
||||
errno = 0;
|
||||
rc = gtp_list_tunnel(g_upf->tunend.genl_id, g_upf->tunend.nl);
|
||||
if (errno)
|
||||
rc = -errno;
|
||||
else if (rc)
|
||||
rc = -EINVAL;
|
||||
if (rc) {
|
||||
LOG_GTP_DEV(dev, LOGL_ERROR, "Failed to open GTP device: %s\n", strerror(-rc));
|
||||
talloc_free(dev);
|
||||
return rc;
|
||||
}
|
||||
|
||||
LOG_GTP_DEV(dev, LOGL_NOTICE, "GTP device ready (ifidx=%u)\n", dev->ifidx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int upf_gtp_dev_open(const char *name, bool create_gtp_dev, const char *local_addr, bool listen_for_gtpv0, bool sgsn_mode)
|
||||
{
|
||||
const struct osmo_sockaddr any = {
|
||||
.u.sin = {
|
||||
.sin_family = AF_INET,
|
||||
.sin_port = 0,
|
||||
.sin_addr = {
|
||||
.s_addr = INADDR_ANY,
|
||||
},
|
||||
},
|
||||
};
|
||||
int rc;
|
||||
struct upf_gtp_dev *dev;
|
||||
|
||||
if (g_upf->tunend.mockup) {
|
||||
LOGP(DGTP, LOGL_NOTICE, "tunend/mockup active: not opening GTP device '%s'\n", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev = upf_gtp_dev_alloc(name, local_addr);
|
||||
if (!dev)
|
||||
return -EIO;
|
||||
|
||||
dev->sgsn_mode = sgsn_mode;
|
||||
|
||||
rc = upf_gtp_genl_ensure_open();
|
||||
if (rc) {
|
||||
LOG_GTP_DEV(dev, LOGL_ERROR, "Cannot set up GTP device, failed to open mnl_socket\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (listen_for_gtpv0) {
|
||||
rc = osmo_sock_init_osa_ofd(&dev->gtpv0.ofd, SOCK_DGRAM, 0, &dev->gtpv0.local_addr, &any,
|
||||
OSMO_SOCK_F_BIND);
|
||||
if (rc < 0) {
|
||||
LOG_GTP_DEV(dev, LOGL_ERROR, "Cannot bind GTPv0 on %s (rc=%d)\n",
|
||||
osmo_sockaddr_to_str_c(OTC_SELECT, &dev->gtpv0.local_addr), rc);
|
||||
talloc_free(dev);
|
||||
return -EIO;
|
||||
}
|
||||
dev->gtpv0.enabled = true;
|
||||
LOG_GTP_DEV(dev, LOGL_DEBUG, "GTPv0 bound\n");
|
||||
}
|
||||
|
||||
/* GTPv1 */
|
||||
rc = osmo_sock_init_osa_ofd(&dev->gtpv1.ofd, SOCK_DGRAM, 0, &dev->gtpv1.local_addr, &any,
|
||||
OSMO_SOCK_F_BIND);
|
||||
if (rc < 0) {
|
||||
LOG_GTP_DEV(dev, LOGL_ERROR, "Cannot bind GTPv1 (rc=%d)\n", rc);
|
||||
talloc_free(dev);
|
||||
return -EIO;
|
||||
}
|
||||
LOG_GTP_DEV(dev, LOGL_DEBUG, "GTPv1 bound\n");
|
||||
|
||||
if (create_gtp_dev) {
|
||||
int gtp0_fd = listen_for_gtpv0 ? dev->gtpv0.ofd.fd : -1;
|
||||
int gtp1_fd = dev->gtpv1.ofd.fd;
|
||||
if (dev->sgsn_mode)
|
||||
rc = gtp_dev_create_sgsn(-1, dev->name, gtp0_fd, gtp1_fd);
|
||||
else
|
||||
rc = gtp_dev_create(-1, dev->name, gtp0_fd, gtp1_fd);
|
||||
if (rc < 0) {
|
||||
LOG_GTP_DEV(dev, LOGL_ERROR, "Cannot create GTP device: rc=%d\n", rc);
|
||||
/* name = NULL: signal to the destructor that it does not need to delete the device */
|
||||
dev->name = NULL;
|
||||
talloc_free(dev);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
LOG_GTP_DEV(dev, LOGL_NOTICE, "created GTP device\n");
|
||||
dev->created = true;
|
||||
}
|
||||
|
||||
rc = dev_resolve_ifidx(dev);
|
||||
if (rc) {
|
||||
talloc_free(dev);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
upf_gtpu_echo_setup(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void upf_gtp_devs_close()
|
||||
{
|
||||
struct upf_gtp_dev *dev;
|
||||
while ((dev = llist_first_entry_or_null(&g_upf->tunend.devs, struct upf_gtp_dev, entry)))
|
||||
talloc_free(dev);
|
||||
}
|
||||
|
||||
void upf_gtp_genl_close()
|
||||
{
|
||||
if (!g_upf->tunend.nl)
|
||||
return;
|
||||
genl_socket_close(g_upf->tunend.nl);
|
||||
g_upf->tunend.nl = NULL;
|
||||
g_upf->tunend.genl_id = -1;
|
||||
|
||||
LOGP(DGTP, LOGL_NOTICE, "Closed mnl_socket\n");
|
||||
}
|
||||
|
||||
/* Open an MNL socket which allows to create and remove GTP devices (requires CAP_NET_ADMIN). */
|
||||
int upf_gtp_genl_ensure_open()
|
||||
{
|
||||
/* Already open? */
|
||||
if (g_upf->tunend.nl && g_upf->tunend.genl_id >= 0)
|
||||
return 0;
|
||||
|
||||
/* sanity / paranoia: if re-opening, make sure the previous socket is closed */
|
||||
if (g_upf->tunend.nl)
|
||||
upf_gtp_genl_close();
|
||||
|
||||
g_upf->tunend.nl = genl_socket_open();
|
||||
if (!g_upf->tunend.nl) {
|
||||
LOGP(DGTP, LOGL_ERROR, "Cannot open mnl_socket: %s\n", strerror(errno));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
g_upf->tunend.genl_id = genl_lookup_family(g_upf->tunend.nl, "gtp");
|
||||
if (g_upf->tunend.genl_id < 0) {
|
||||
LOGP(DGTP, LOGL_ERROR, "genl family 'gtp' not found\n");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
LOGP(DGTP, LOGL_NOTICE, "Opened mnl_socket\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct upf_gtp_tunend {
|
||||
struct llist_head entry;
|
||||
|
||||
struct upf_gtp_dev *dev;
|
||||
struct upf_tunend desc;
|
||||
bool active;
|
||||
};
|
||||
|
||||
static int upf_gtp_tunend_to_str_buf(char *buf, size_t buflen, const struct upf_gtp_tunend *tun)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
/* "tunend{dev=apn0 access(GTP-r=1.2.3.4 TEID:l=0x1234,r=0x5678) core(UE-l=10.9.8.7)}" */
|
||||
OSMO_STRBUF_PRINTF(sb, "tunend{dev=%s access(GTP-r=", tun->dev->name);
|
||||
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &tun->desc.access.remote.addr);
|
||||
OSMO_STRBUF_PRINTF(sb, " TEID:l=0x%x,r=0x%x) core(UE-l=",
|
||||
tun->desc.access.local.teid, tun->desc.access.remote.teid);
|
||||
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &tun->desc.core.ue_local_addr);
|
||||
OSMO_STRBUF_PRINTF(sb, ")}");
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
static char *upf_gtp_tunend_to_str_c(void *ctx, const struct upf_gtp_tunend *tun)
|
||||
{
|
||||
OSMO_NAME_C_IMPL(ctx, 64, "ERROR", upf_gtp_tunend_to_str_buf, tun)
|
||||
}
|
||||
|
||||
static int upf_gtp_tunend_deactivate(struct upf_gtp_tunend *tun);
|
||||
|
||||
static int upf_gtp_tunend_destruct(struct upf_gtp_tunend *tun)
|
||||
{
|
||||
if (tun->active)
|
||||
upf_gtp_tunend_deactivate(tun);
|
||||
llist_del(&tun->entry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define tunend_validate(TUNEND) \
|
||||
do { \
|
||||
OSMO_ASSERT(osmo_sockaddr_port(&(TUNEND)->access.local.addr.u.sa) == 0); \
|
||||
OSMO_ASSERT(osmo_sockaddr_port(&(TUNEND)->access.remote.addr.u.sa) == 0); \
|
||||
OSMO_ASSERT(osmo_sockaddr_port(&(TUNEND)->core.ue_local_addr.u.sa) == 0); \
|
||||
} while (0)
|
||||
|
||||
static struct upf_gtp_tunend *upf_gtp_tunend_alloc(struct upf_gtp_dev *dev, const struct upf_tunend *desc)
|
||||
{
|
||||
struct upf_gtp_tunend *tun = talloc(dev, struct upf_gtp_tunend);
|
||||
OSMO_ASSERT(tun);
|
||||
tunend_validate(desc);
|
||||
*tun = (struct upf_gtp_tunend){
|
||||
.dev = dev,
|
||||
.desc = *desc,
|
||||
};
|
||||
llist_add(&tun->entry, &dev->tunnels);
|
||||
talloc_set_destructor(tun, upf_gtp_tunend_destruct);
|
||||
return tun;
|
||||
}
|
||||
|
||||
static struct gtp_tunnel *upf_gtp_tunend_to_gtp_tunnel(struct upf_gtp_tunend *tun)
|
||||
{
|
||||
struct gtp_tunnel *t;
|
||||
|
||||
if (tun->desc.core.ue_local_addr.u.sas.ss_family != AF_INET
|
||||
|| tun->desc.access.remote.addr.u.sas.ss_family != AF_INET) {
|
||||
LOG_GTP_TUN(tun, LOGL_ERROR, "Only capabale of IPv4\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
t = gtp_tunnel_alloc();
|
||||
OSMO_ASSERT(t);
|
||||
gtp_tunnel_set_ifidx(t, tun->dev->ifidx);
|
||||
gtp_tunnel_set_version(t, GTP_V1);
|
||||
gtp_tunnel_set_i_tei(t, tun->desc.access.local.teid);
|
||||
gtp_tunnel_set_o_tei(t, tun->desc.access.remote.teid);
|
||||
gtp_tunnel_set_sgsn_ip4(t, &tun->desc.access.remote.addr.u.sin.sin_addr);
|
||||
gtp_tunnel_set_ms_ip4(t, &tun->desc.core.ue_local_addr.u.sin.sin_addr);
|
||||
return t;
|
||||
}
|
||||
|
||||
int upf_gtp_tunend_activate(struct upf_gtp_tunend *tun)
|
||||
{
|
||||
int rc;
|
||||
struct gtp_tunnel *t;
|
||||
|
||||
if (tun->active)
|
||||
return -EALREADY;
|
||||
|
||||
t = upf_gtp_tunend_to_gtp_tunnel(tun);
|
||||
if (!t)
|
||||
return -ENOTSUP;
|
||||
|
||||
errno = 0;
|
||||
rc = gtp_add_tunnel(g_upf->tunend.genl_id, g_upf->tunend.nl, t);
|
||||
if (errno) {
|
||||
rc = -errno;
|
||||
} else if (rc) {
|
||||
rc = -EINVAL;
|
||||
} else {
|
||||
tun->active = true;
|
||||
}
|
||||
|
||||
gtp_tunnel_free(t);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct upf_gtp_tunend *upf_gtp_dev_tunend_find(struct upf_gtp_dev *dev, const struct upf_tunend *tunend)
|
||||
{
|
||||
struct upf_gtp_tunend *tun;
|
||||
tunend_validate(tunend);
|
||||
llist_for_each_entry(tun, &dev->tunnels, entry) {
|
||||
if (upf_gtp_tunend_cmp(tunend, &tun->desc))
|
||||
continue;
|
||||
return tun;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int upf_gtp_dev_tunend_add(struct upf_gtp_dev *dev, const struct upf_tunend *tunend)
|
||||
{
|
||||
struct upf_gtp_tunend *tun;
|
||||
tunend_validate(tunend);
|
||||
tun = upf_gtp_dev_tunend_find(dev, tunend);
|
||||
if (!tun)
|
||||
tun = upf_gtp_tunend_alloc(dev, tunend);
|
||||
if (tun->active)
|
||||
return 0;
|
||||
return upf_gtp_tunend_activate(tun);
|
||||
}
|
||||
|
||||
int upf_gtp_dev_tunend_del(struct upf_gtp_dev *dev, const struct upf_tunend *tunend)
|
||||
{
|
||||
struct upf_gtp_tunend *tun;
|
||||
int rc;
|
||||
tunend_validate(tunend);
|
||||
tun = upf_gtp_dev_tunend_find(dev, tunend);
|
||||
if (!tun)
|
||||
return 0;
|
||||
if (tun->active) {
|
||||
rc = upf_gtp_tunend_deactivate(tun);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
talloc_free(tun);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int upf_gtp_tunend_deactivate(struct upf_gtp_tunend *tun)
|
||||
{
|
||||
int rc;
|
||||
struct gtp_tunnel *t;
|
||||
|
||||
if (!tun->active) {
|
||||
LOG_GTP_TUN(tun, LOGL_ERROR, "Cannot deactivate, not active\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
t = upf_gtp_tunend_to_gtp_tunnel(tun);
|
||||
if (!t)
|
||||
return -EINVAL;
|
||||
|
||||
rc = gtp_del_tunnel(g_upf->tunend.genl_id, g_upf->tunend.nl, t);
|
||||
if (rc)
|
||||
LOG_GTP_TUN(tun, LOGL_ERROR, "Failed to delete tunnel\n");
|
||||
else
|
||||
tun->active = false;
|
||||
|
||||
gtp_tunnel_free(t);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int upf_gtp_dev_destruct(struct upf_gtp_dev *dev)
|
||||
{
|
||||
struct upf_gtp_tunend *t;
|
||||
/* Destruct and clean up all active tunnels before deleting the device */
|
||||
while ((t = llist_first_entry_or_null(&dev->tunnels, struct upf_gtp_tunend, entry)))
|
||||
talloc_free(t);
|
||||
llist_del(&dev->entry);
|
||||
/* osmo_fd_close() is a noop if ofd.fd == -1 */
|
||||
osmo_fd_close(&dev->gtpv0.ofd);
|
||||
osmo_fd_close(&dev->gtpv1.ofd);
|
||||
if (dev->created)
|
||||
upf_gtp_dev_delete(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int upf_gtp_tunend_cmp(const struct upf_tunend *a, const struct upf_tunend *b)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (a == b)
|
||||
return 0;
|
||||
if (!a)
|
||||
return -1;
|
||||
if (!b)
|
||||
return 1;
|
||||
|
||||
#define CMP_MEMB(MEMB) OSMO_CMP(a->MEMB, b->MEMB)
|
||||
if ((r = CMP_MEMB(access.local.teid)))
|
||||
return r;
|
||||
if ((r = CMP_MEMB(access.remote.teid)))
|
||||
return r;
|
||||
return osmo_sockaddr_cmp(&a->access.remote.addr, &b->access.remote.addr);
|
||||
}
|
||||
167
src/osmo-upf/upf_gtpu_echo.c
Normal file
167
src/osmo-upf/upf_gtpu_echo.c
Normal file
@@ -0,0 +1,167 @@
|
||||
/* GTP-U ECHO implementation for osmo-upf */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/endian.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
|
||||
#include <osmocom/upf/upf.h>
|
||||
#include <osmocom/upf/upf_gtp.h>
|
||||
|
||||
#define GTP1U_PORT 2152
|
||||
|
||||
enum gtp1u_msgt {
|
||||
GTP1U_MSGTYPE_ECHO_REQ = 1,
|
||||
GTP1U_MSGTYPE_ECHO_RSP = 2,
|
||||
};
|
||||
|
||||
enum gtp1u_iei {
|
||||
GTP1U_IEI_RECOVERY = 14,
|
||||
};
|
||||
|
||||
/* 3GPP TS 29.281 */
|
||||
struct gtp1u_hdr {
|
||||
#if OSMO_IS_LITTLE_ENDIAN
|
||||
uint8_t pn:1, /*< N-PDU Number flag */
|
||||
s:1, /*< Sequence number flag */
|
||||
e:1, /*< Extension header flag */
|
||||
spare:1,
|
||||
pt:1, /*< Protocol Type: GTP=1, GTP'=0 */
|
||||
version:3; /*< Version: 1 */
|
||||
#elif OSMO_IS_BIG_ENDIAN
|
||||
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
|
||||
uint8_t version:3, pt:1, spare:1, e:1, s:1, pn:1;
|
||||
#endif
|
||||
uint8_t msg_type;
|
||||
uint16_t length;
|
||||
uint32_t tei; /*< 05 - 08 Tunnel Endpoint ID */
|
||||
union {
|
||||
uint8_t data1[0];
|
||||
struct {
|
||||
uint16_t seq_nr;
|
||||
uint8_t n_pdu_nr;
|
||||
uint8_t next_ext_type;
|
||||
} ext;
|
||||
};
|
||||
uint8_t data2[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
static int tx_echo_resp(struct upf_gtp_dev *dev, const struct osmo_sockaddr *remote, uint16_t seq_nr);
|
||||
|
||||
static int rx_echo_req(struct upf_gtp_dev *dev, const struct osmo_sockaddr *remote, const struct gtp1u_hdr *rx_h)
|
||||
{
|
||||
if (!rx_h->s) {
|
||||
LOG_GTP_DEV(dev, LOGL_ERROR, "rx GTPv1-U ECHO REQ without sequence nr\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return tx_echo_resp(dev, remote, rx_h->ext.seq_nr);
|
||||
}
|
||||
|
||||
static int tx_echo_resp(struct upf_gtp_dev *dev, const struct osmo_sockaddr *remote, uint16_t seq_nr)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct gtp1u_hdr *tx_h;
|
||||
int rc;
|
||||
|
||||
msg = msgb_alloc_headroom(1024, 128, "GTP-echo-resp");
|
||||
tx_h = (void *)msgb_put(msg, sizeof(*tx_h));
|
||||
|
||||
*tx_h = (struct gtp1u_hdr){
|
||||
/* 3GPP TS 29.281 5.1 defines that the ECHO REQ & RESP shall contain a sequence nr */
|
||||
.s = 1,
|
||||
.pt = 1,
|
||||
.version = 1,
|
||||
.msg_type = GTP1U_MSGTYPE_ECHO_RSP,
|
||||
.ext = {
|
||||
.seq_nr = seq_nr,
|
||||
},
|
||||
};
|
||||
|
||||
OSMO_ASSERT(msg->tail == tx_h->data2);
|
||||
|
||||
/* ECHO RESPONSE shall contain a recovery counter */
|
||||
msgb_put_u8(msg, GTP1U_IEI_RECOVERY);
|
||||
msgb_put_u8(msg, g_upf->tunend.recovery_count);
|
||||
|
||||
osmo_store16be(msg->tail - tx_h->data1, &tx_h->length);
|
||||
|
||||
rc = sendto(dev->gtpv1.ofd.fd, msgb_data(msg), msgb_length(msg), 0, &remote->u.sa, sizeof(*remote));
|
||||
if (rc < 0) {
|
||||
int err = errno;
|
||||
LOG_GTP_DEV(dev, LOGL_ERROR, "GTP1-U sendto(len=%d, to=%s): %s\n", msgb_length(msg),
|
||||
osmo_sockaddr_to_str(remote), strerror(err));
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int upf_gtpu_echo_read_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
{
|
||||
struct upf_gtp_dev *dev = ofd->data;
|
||||
|
||||
ssize_t sz;
|
||||
uint8_t buf[4096];
|
||||
struct osmo_sockaddr remote;
|
||||
socklen_t remote_len = sizeof(remote);
|
||||
const struct gtp1u_hdr *h;
|
||||
uint16_t h_length;
|
||||
|
||||
if ((sz = recvfrom(dev->gtpv1.ofd.fd, buf, sizeof(buf), 0, &remote.u.sa, &remote_len)) < 0) {
|
||||
LOG_GTP_DEV(dev, LOGL_ERROR, "recvfrom() failed: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (sz == 0) {
|
||||
LOG_GTP_DEV(dev, LOGL_ERROR, "recvfrom() yields zero bytes\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* A GTPv1-U header of size 8 is valid, but this code expects to handle only ECHO REQUEST messages. These are
|
||||
* required to have a sequence number, hence this check here consciously uses the full sizeof(*h) == 12. */
|
||||
if (sz < sizeof(*h)) {
|
||||
LOG_GTP_DEV(dev, LOGL_ERROR, "rx GTP packet smaller than the GTPv1-U header + sequence nr: %zd < %zu\n",
|
||||
sz, sizeof(*h));
|
||||
return -1;
|
||||
}
|
||||
|
||||
h = (const struct gtp1u_hdr *)buf;
|
||||
if (h->version != 1) {
|
||||
LOG_GTP_DEV(dev, LOGL_ERROR, "rx GTP v%u: only GTP version 1 supported\n", h->version);
|
||||
return -1;
|
||||
}
|
||||
|
||||
h_length = osmo_load16be(&h->length);
|
||||
if (offsetof(struct gtp1u_hdr, data1) + h_length > sz) {
|
||||
LOG_GTP_DEV(dev, LOGL_ERROR, "rx GTP: header + h.length = %zu > received bytes = %zd\n",
|
||||
offsetof(struct gtp1u_hdr, data1) + h_length, sz);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (h->msg_type) {
|
||||
case GTP1U_MSGTYPE_ECHO_REQ:
|
||||
return rx_echo_req(dev, &remote, h);
|
||||
default:
|
||||
LOG_GTP_DEV(dev, LOGL_ERROR, "rx: GTPv1-U message type %u not supported\n", h->msg_type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int upf_gtpu_echo_setup(struct upf_gtp_dev *dev)
|
||||
{
|
||||
if (dev->gtpv1.ofd.fd == -1) {
|
||||
LOGP(DGTP, LOGL_ERROR, "Cannot setup GTP-U ECHO: GTP-v1 socket not initialized\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* the caller should already have osmo_fd_register()ed when setting up the socket. */
|
||||
OSMO_ASSERT(osmo_fd_is_registered(&dev->gtpv1.ofd));
|
||||
/* make sure there is no cb yet that this would be replacing. */
|
||||
OSMO_ASSERT(dev->gtpv1.ofd.cb == NULL);
|
||||
|
||||
dev->gtpv1.ofd.cb = upf_gtpu_echo_read_cb;
|
||||
dev->gtpv1.ofd.data = dev;
|
||||
return 0;
|
||||
}
|
||||
506
src/osmo-upf/upf_nft.c
Normal file
506
src/osmo-upf/upf_nft.c
Normal file
@@ -0,0 +1,506 @@
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <nftables/libnftables.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
|
||||
#include <osmocom/upf/upf.h>
|
||||
#include <osmocom/upf/upf_nft.h>
|
||||
|
||||
static char *upf_nft_ruleset_table_create(void *ctx, const char *table_name)
|
||||
{
|
||||
return talloc_asprintf(ctx, "add table inet %s { flags owner; };\n", table_name);
|
||||
}
|
||||
|
||||
static char *upf_nft_ruleset_vmap_init(void *ctx, const char *table_name, int priority_pre, int priority_post)
|
||||
{
|
||||
/* add chain inet osmo-upf pre { type filter hook prerouting priority -300; policy accept; }
|
||||
* add chain inet osmo-upf post { type filter hook postrouting priority 400; policy accept; }
|
||||
* add map inet osmo-upf tunmap-pre { typeof ip daddr . @ih,32,32 : verdict; }
|
||||
* add map inet osmo-upf tunmap-post { typeof meta mark : verdict; }
|
||||
* add rule inet osmo-upf pre udp dport 2152 ip daddr . @ih,32,32 vmap @tunmap-pre
|
||||
* add rule inet osmo-upf post meta mark vmap @tunmap-post
|
||||
*/
|
||||
return talloc_asprintf(ctx,
|
||||
"add chain inet %s pre { type filter hook prerouting priority %d; policy accept; };\n"
|
||||
"add chain inet %s post { type filter hook postrouting priority %d; policy accept; };\n"
|
||||
"add map inet %s tunmap-pre { typeof ip daddr . @ih,32,32 : verdict; };\n"
|
||||
"add map inet %s tunmap-post { typeof meta mark : verdict; };\n"
|
||||
"add rule inet %s pre udp dport %u ip daddr . @ih,32,32 vmap @tunmap-pre;\n"
|
||||
"add rule inet %s post meta mark vmap @tunmap-post;\n",
|
||||
table_name, priority_pre,
|
||||
table_name, priority_post,
|
||||
table_name,
|
||||
table_name,
|
||||
table_name, PORT_GTP1_U,
|
||||
table_name);
|
||||
}
|
||||
|
||||
static int upf_nft_run_now(const char *ruleset)
|
||||
{
|
||||
int rc;
|
||||
const int logmax = 256;
|
||||
|
||||
if (g_upf->tunmap.mockup) {
|
||||
LOGP(DNFT, LOGL_NOTICE, "tunmap/mockup active: not running nft ruleset: '%s'\n", ruleset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!g_upf->tunmap.nft_ctx) {
|
||||
rc = upf_nft_init();
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = nft_run_cmd_from_buffer(g_upf->tunmap.nft_ctx, ruleset);
|
||||
if (rc < 0) {
|
||||
LOGP(DNFT, LOGL_ERROR, "error running nft ruleset: rc=%d ruleset=%s\n",
|
||||
rc, osmo_quote_str_c(OTC_SELECT, ruleset, -1));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (log_check_level(DNFT, LOGL_DEBUG)) {
|
||||
size_t l = strlen(ruleset);
|
||||
LOGP(DNFT, LOGL_DEBUG, "ran nft ruleset, %zu chars: \"%s%s\"\n",
|
||||
l,
|
||||
osmo_escape_cstr_c(OTC_SELECT, ruleset, OSMO_MIN(logmax, l)),
|
||||
l > logmax ? "..." : "");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct nft_queue {
|
||||
struct osmo_tdef *flush_time_tdef;
|
||||
struct osmo_tdef *ruleset_max_tdef;
|
||||
struct osmo_strbuf sb;
|
||||
/* 128 NFT rulesets amount to about 110 kb of char */
|
||||
char buf[1<<17];
|
||||
unsigned int ruleset_count;
|
||||
struct osmo_timer_list timer;
|
||||
};
|
||||
|
||||
static void nft_queue_clear_buf(struct nft_queue *q)
|
||||
{
|
||||
q->sb = (struct osmo_strbuf){ .buf = q->buf, .len = sizeof(q->buf) };
|
||||
q->buf[0] = '\0';
|
||||
}
|
||||
|
||||
static void nft_queue_init(void *ctx, struct nft_queue *q,
|
||||
struct osmo_tdef *flush_time_tdef,
|
||||
struct osmo_tdef *ruleset_max_tdef)
|
||||
{
|
||||
*q = (struct nft_queue){
|
||||
.flush_time_tdef = flush_time_tdef,
|
||||
.ruleset_max_tdef = ruleset_max_tdef,
|
||||
};
|
||||
nft_queue_clear_buf(q);
|
||||
}
|
||||
|
||||
static void nft_queue_flush(struct nft_queue *q, const char *reason)
|
||||
{
|
||||
static unsigned int flush_count = 0;
|
||||
static unsigned int ruleset_count = 0;
|
||||
|
||||
/* We will now flush the queue empty. A timer needs to run only when the next pending entry is added. */
|
||||
osmo_timer_del(&q->timer);
|
||||
|
||||
/* Nothing to send? */
|
||||
if (!q->sb.chars_needed)
|
||||
return;
|
||||
|
||||
flush_count++;
|
||||
ruleset_count += q->ruleset_count;
|
||||
LOGP(DNFT, LOGL_INFO, "Flushing NFT ruleset queue: %s: n:%u strlen:%zu (flush count: %u avg rules per flush: %s)\n",
|
||||
reason,
|
||||
q->ruleset_count, q->sb.chars_needed,
|
||||
flush_count, osmo_int_to_float_str_c(OTC_SELECT, 10 * ruleset_count / flush_count, 1));
|
||||
|
||||
q->ruleset_count = 0;
|
||||
|
||||
upf_nft_run_now(q->sb.buf);
|
||||
|
||||
nft_queue_clear_buf(q);
|
||||
}
|
||||
|
||||
static void nft_queue_flush_cb(void *q)
|
||||
{
|
||||
nft_queue_flush(q, "timeout");
|
||||
}
|
||||
|
||||
static int nft_enqueue(struct nft_queue *q,
|
||||
int (*tunmap_to_str_buf)(char *buf, size_t len, struct upf_tunmap *tunmap),
|
||||
struct upf_tunmap *tunmap)
|
||||
{
|
||||
int ruleset_max;
|
||||
struct osmo_strbuf q_sb_was = q->sb;
|
||||
|
||||
OSMO_STRBUF_APPEND(q->sb, tunmap_to_str_buf, tunmap);
|
||||
|
||||
/* is that being cut off? then revert the addition. This should never happen in practice. */
|
||||
if (q->sb.chars_needed >= q->sb.len) {
|
||||
q->sb = q_sb_was;
|
||||
if (q->sb.pos)
|
||||
*q->sb.pos = '\0';
|
||||
nft_queue_flush(q, "reached max nr of chars");
|
||||
OSMO_STRBUF_APPEND(q->sb, tunmap_to_str_buf, tunmap);
|
||||
}
|
||||
|
||||
/* Append separator -- no problem if that gets cut off. */
|
||||
OSMO_STRBUF_PRINTF(q->sb, "\n");
|
||||
|
||||
q->ruleset_count++;
|
||||
|
||||
LOGP(DNFT, LOGL_INFO, "Added NFT ruleset to queue: n:%u strlen:%zu\n",
|
||||
q->ruleset_count, q->sb.chars_needed);
|
||||
|
||||
/* Added a rule, see if it has reached ruleset_max. */
|
||||
ruleset_max = osmo_tdef_get(q->ruleset_max_tdef, q->ruleset_max_tdef->T, OSMO_TDEF_CUSTOM, 128);
|
||||
if (q->ruleset_count >= ruleset_max) {
|
||||
nft_queue_flush(q, "reached max nr of rules");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Item added. If the timer is not running yet, schedule a flush in given timeout */
|
||||
if (!osmo_timer_pending(&q->timer)) {
|
||||
struct osmo_tdef *t;
|
||||
unsigned long us;
|
||||
osmo_timer_setup(&q->timer, nft_queue_flush_cb, q);
|
||||
t = q->flush_time_tdef;
|
||||
us = osmo_tdef_get(t, t->T, OSMO_TDEF_US, 100000);
|
||||
osmo_timer_schedule(&q->timer, us / 1000000, us % 1000000);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nft_queue_free(struct nft_queue *q)
|
||||
{
|
||||
osmo_timer_del(&q->timer);
|
||||
}
|
||||
|
||||
static struct nft_queue g_nft_queue = {};
|
||||
|
||||
int upf_nft_init()
|
||||
{
|
||||
int rc;
|
||||
|
||||
nft_queue_init(g_upf, &g_nft_queue,
|
||||
osmo_tdef_get_entry(g_upf_nft_tdefs, -32),
|
||||
osmo_tdef_get_entry(g_upf_nft_tdefs, -33));
|
||||
|
||||
/* Always set up the default settings, also in mockup mode, so that the VTY reflects sane values */
|
||||
if (!g_upf->tunmap.table_name)
|
||||
g_upf->tunmap.table_name = talloc_strdup(g_upf, "osmo-upf");
|
||||
|
||||
/* When in mockup mode, do not set up nft_ctx and netfilter table */
|
||||
if (g_upf->tunmap.mockup) {
|
||||
LOGP(DNFT, LOGL_NOTICE,
|
||||
"tunmap/mockup active: not allocating libnftables nft_ctx. FOR TESTING PURPOSES ONLY.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
g_upf->tunmap.nft_ctx = nft_ctx_new(NFT_CTX_DEFAULT);
|
||||
if (!g_upf->tunmap.nft_ctx) {
|
||||
LOGP(DNFT, LOGL_ERROR, "cannot allocate libnftables nft_ctx\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
rc = upf_nft_run_now(upf_nft_tunmap_get_table_init_str(OTC_SELECT));
|
||||
if (rc) {
|
||||
LOGP(DNFT, LOGL_ERROR, "Failed to create nft table %s\n",
|
||||
osmo_quote_str_c(OTC_SELECT, g_upf->tunmap.table_name, -1));
|
||||
return rc;
|
||||
}
|
||||
LOGP(DNFT, LOGL_NOTICE, "Created nft table %s\n", osmo_quote_str_c(OTC_SELECT, g_upf->tunmap.table_name, -1));
|
||||
|
||||
rc = upf_nft_run_now(upf_nft_tunmap_get_vmap_init_str(OTC_SELECT));
|
||||
if (rc) {
|
||||
LOGP(DNFT, LOGL_ERROR, "Failed to initialize nft verdict map in table %s\n", g_upf->tunmap.table_name);
|
||||
return rc;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int upf_nft_free()
|
||||
{
|
||||
nft_queue_free(&g_nft_queue);
|
||||
if (!g_upf->tunmap.nft_ctx)
|
||||
return 0;
|
||||
nft_ctx_free(g_upf->tunmap.nft_ctx);
|
||||
g_upf->tunmap.nft_ctx = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct upf_nft_args_peer {
|
||||
/* The source IP address in packets received from this peer */
|
||||
const struct osmo_sockaddr *addr_remote;
|
||||
/* The TEID that we send to the peer in GTP packets. */
|
||||
uint32_t teid_remote;
|
||||
/* The local destination IP address in packets received from this peer */
|
||||
const struct osmo_sockaddr *addr_local;
|
||||
/* The TEID that the peer sends to us in GTP packets. */
|
||||
uint32_t teid_local;
|
||||
/* The nft chain id that forwards packets received on addr_local,teid_local. Also used for the 'mark' id in
|
||||
* the verdict map ruleset. */
|
||||
uint32_t chain_id;
|
||||
};
|
||||
|
||||
struct upf_nft_args {
|
||||
/* global table name */
|
||||
const char *table_name;
|
||||
|
||||
struct upf_nft_args_peer peer_a;
|
||||
struct upf_nft_args_peer peer_b;
|
||||
};
|
||||
|
||||
static int tunmap_add_single_direction(char *buf, size_t buflen,
|
||||
const struct upf_nft_args *args,
|
||||
bool dir_a2b)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
const struct upf_nft_args_peer *from_peer;
|
||||
const struct upf_nft_args_peer *to_peer;
|
||||
|
||||
if (dir_a2b) {
|
||||
from_peer = &args->peer_a;
|
||||
to_peer = &args->peer_b;
|
||||
} else {
|
||||
from_peer = &args->peer_b;
|
||||
to_peer = &args->peer_a;
|
||||
}
|
||||
|
||||
/* # add chain for verdict map in prerouting
|
||||
* add chain inet osmo-upf tunmap-pre-123
|
||||
* # mangle destination address at prerouting
|
||||
* add rule inet osmo-upf tunmap-pre-123 ip daddr set 1.1.1.1 meta mark set 123 counter accept
|
||||
*
|
||||
* # add chain for verdict map in postrouting
|
||||
* add chain inet osmo-upf tunmap-post-123
|
||||
* # mangle source address and GTP TID at postrouting
|
||||
* add rule inet osmo-upf tunmap-post-123 ip saddr set 2.2.2.1 udp sport set 2152 @ih,32,32 set 0x00000102 counter accept
|
||||
*
|
||||
* # add elements to verdict map, jump to chain
|
||||
* add element inet osmo-upf tunmap-pre { 2.2.2.3 . 0x00000203 : jump tunmap-pre-123 }
|
||||
* add element inet osmo-upf tunmap-post { 123 : jump tunmap-post-123 }
|
||||
*/
|
||||
|
||||
OSMO_STRBUF_PRINTF(sb, "add chain inet %s tunmap-pre-%u;\n",
|
||||
args->table_name, from_peer->chain_id);
|
||||
|
||||
OSMO_STRBUF_PRINTF(sb, "add rule inet %s tunmap-pre-%u",
|
||||
args->table_name, from_peer->chain_id);
|
||||
OSMO_STRBUF_PRINTF(sb, " ip daddr set ");
|
||||
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, to_peer->addr_remote);
|
||||
OSMO_STRBUF_PRINTF(sb, " meta mark set %u counter accept;\n", from_peer->chain_id);
|
||||
|
||||
OSMO_STRBUF_PRINTF(sb, "add chain inet %s tunmap-post-%u;\n",
|
||||
args->table_name, from_peer->chain_id);
|
||||
|
||||
OSMO_STRBUF_PRINTF(sb, "add rule inet %s tunmap-post-%u",
|
||||
args->table_name, from_peer->chain_id);
|
||||
OSMO_STRBUF_PRINTF(sb, " ip saddr set ");
|
||||
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, to_peer->addr_local);
|
||||
OSMO_STRBUF_PRINTF(sb, " udp sport set 2152");
|
||||
OSMO_STRBUF_PRINTF(sb, " @ih,32,32 set 0x%x", to_peer->teid_remote);
|
||||
OSMO_STRBUF_PRINTF(sb, " counter accept;\n");
|
||||
|
||||
OSMO_STRBUF_PRINTF(sb, "add element inet %s tunmap-pre { ",
|
||||
args->table_name);
|
||||
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, from_peer->addr_local);
|
||||
OSMO_STRBUF_PRINTF(sb, " . 0x%x : jump tunmap-pre-%u };\n",
|
||||
from_peer->teid_local, from_peer->chain_id);
|
||||
|
||||
OSMO_STRBUF_PRINTF(sb, "add element inet %s tunmap-post { %u : jump tunmap-post-%u };\n",
|
||||
args->table_name, from_peer->chain_id, from_peer->chain_id);
|
||||
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
static int tunmap_del_single_direction(char *buf, size_t buflen,
|
||||
const struct upf_nft_args *args,
|
||||
bool dir_a2b)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
const struct upf_nft_args_peer *from_peer;
|
||||
|
||||
if (dir_a2b)
|
||||
from_peer = &args->peer_a;
|
||||
else
|
||||
from_peer = &args->peer_b;
|
||||
|
||||
/* delete element inet osmo-upf tunmap-pre { 2.2.2.3 . 0x203 }
|
||||
* delete element inet osmo-upf tunmap-post { 123 }
|
||||
* delete chain inet osmo-upf tunmap-pre-123
|
||||
* delete chain inet osmo-upf tunmap-post-123
|
||||
*/
|
||||
|
||||
OSMO_STRBUF_PRINTF(sb, "delete element inet %s tunmap-pre { ",
|
||||
args->table_name);
|
||||
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, from_peer->addr_local);
|
||||
OSMO_STRBUF_PRINTF(sb, " . 0x%x };\n", from_peer->teid_local);
|
||||
|
||||
OSMO_STRBUF_PRINTF(sb, "delete element inet %s tunmap-post { %u };\n",
|
||||
args->table_name, from_peer->chain_id);
|
||||
|
||||
OSMO_STRBUF_PRINTF(sb, "delete chain inet %s tunmap-pre-%u;\n",
|
||||
args->table_name, from_peer->chain_id);
|
||||
|
||||
OSMO_STRBUF_PRINTF(sb, "delete chain inet %s tunmap-post-%u;\n",
|
||||
args->table_name, from_peer->chain_id);
|
||||
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
static int upf_nft_ruleset_tunmap_create_buf(char *buf, size_t buflen, const struct upf_nft_args *args)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
|
||||
/* Forwarding from peer_a to peer_b */
|
||||
OSMO_STRBUF_APPEND(sb, tunmap_add_single_direction, args, true);
|
||||
/* And from peer_b to peer_a */
|
||||
OSMO_STRBUF_APPEND(sb, tunmap_add_single_direction, args, false);
|
||||
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
static int upf_nft_ruleset_tunmap_delete_buf(char *buf, size_t buflen, const struct upf_nft_args *args)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
|
||||
/* Forwarding from peer_a to peer_b */
|
||||
OSMO_STRBUF_APPEND(sb, tunmap_del_single_direction, args, true);
|
||||
/* And from peer_b to peer_a */
|
||||
OSMO_STRBUF_APPEND(sb, tunmap_del_single_direction, args, false);
|
||||
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
int upf_nft_tunmap_to_str_buf(char *buf, size_t buflen, const struct upf_tunmap *tunmap)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
|
||||
/* ACCESS 1.1.1.2:0x102 <---> 2.2.2.1:0x201 UPF 2.2.2.3:0x203 <---> 3.3.3.2:0x302 CORE */
|
||||
OSMO_STRBUF_PRINTF(sb, "ACCESS ");
|
||||
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &tunmap->access.tun.remote.addr);
|
||||
OSMO_STRBUF_PRINTF(sb, ":0x%x <---> ", tunmap->access.tun.remote.teid);
|
||||
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &tunmap->access.tun.local.addr);
|
||||
OSMO_STRBUF_PRINTF(sb, ":0x%x UPF ", tunmap->access.tun.local.teid);
|
||||
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &tunmap->core.tun.local.addr);
|
||||
OSMO_STRBUF_PRINTF(sb, ":0x%x <---> ", tunmap->core.tun.local.teid);
|
||||
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &tunmap->core.tun.remote.addr);
|
||||
OSMO_STRBUF_PRINTF(sb, ":0x%x CORE", tunmap->core.tun.remote.teid);
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
char *upf_nft_tunmap_to_str_c(void *ctx, const struct upf_tunmap *tunmap)
|
||||
{
|
||||
OSMO_NAME_C_IMPL(ctx, 128, "ERROR", upf_nft_tunmap_to_str_buf, tunmap)
|
||||
}
|
||||
|
||||
static void upf_nft_args_from_tunmap(struct upf_nft_args *args, const struct upf_tunmap *tunmap)
|
||||
{
|
||||
OSMO_ASSERT(osmo_sockaddr_port(&tunmap->access.tun.remote.addr.u.sa) == 0);
|
||||
OSMO_ASSERT(osmo_sockaddr_port(&tunmap->access.tun.local.addr.u.sa) == 0);
|
||||
OSMO_ASSERT(osmo_sockaddr_port(&tunmap->core.tun.remote.addr.u.sa) == 0);
|
||||
OSMO_ASSERT(osmo_sockaddr_port(&tunmap->core.tun.local.addr.u.sa) == 0);
|
||||
|
||||
*args = (struct upf_nft_args){
|
||||
.table_name = g_upf->tunmap.table_name,
|
||||
.peer_a = {
|
||||
.addr_remote = &tunmap->access.tun.remote.addr,
|
||||
.teid_remote = tunmap->access.tun.remote.teid,
|
||||
.addr_local = &tunmap->access.tun.local.addr,
|
||||
.teid_local = tunmap->access.tun.local.teid,
|
||||
.chain_id = tunmap->access.chain_id,
|
||||
},
|
||||
.peer_b = {
|
||||
.addr_remote = &tunmap->core.tun.remote.addr,
|
||||
.teid_remote = tunmap->core.tun.remote.teid,
|
||||
.addr_local = &tunmap->core.tun.local.addr,
|
||||
.teid_local = tunmap->core.tun.local.teid,
|
||||
.chain_id = tunmap->core.chain_id,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
char *upf_nft_tunmap_get_table_init_str(void *ctx)
|
||||
{
|
||||
return upf_nft_ruleset_table_create(ctx, g_upf->tunmap.table_name);
|
||||
}
|
||||
|
||||
char *upf_nft_tunmap_get_vmap_init_str(void *ctx)
|
||||
{
|
||||
return upf_nft_ruleset_vmap_init(ctx, g_upf->tunmap.table_name, g_upf->tunmap.priority_pre,
|
||||
g_upf->tunmap.priority_post);
|
||||
}
|
||||
|
||||
int upf_nft_tunmap_get_ruleset_str_buf(char *buf, size_t len, struct upf_tunmap *tunmap)
|
||||
{
|
||||
struct upf_nft_args args;
|
||||
upf_nft_args_from_tunmap(&args, tunmap);
|
||||
return upf_nft_ruleset_tunmap_create_buf(buf, len, &args);
|
||||
}
|
||||
|
||||
char *upf_nft_tunmap_get_ruleset_str(void *ctx, struct upf_tunmap *tunmap)
|
||||
{
|
||||
OSMO_NAME_C_IMPL(ctx, 1024, "ERROR", upf_nft_tunmap_get_ruleset_str_buf, tunmap)
|
||||
}
|
||||
|
||||
int upf_nft_tunmap_get_ruleset_del_str_buf(char *buf, size_t len, struct upf_tunmap *tunmap)
|
||||
{
|
||||
struct upf_nft_args args;
|
||||
upf_nft_args_from_tunmap(&args, tunmap);
|
||||
return upf_nft_ruleset_tunmap_delete_buf(buf, len, &args);
|
||||
}
|
||||
|
||||
char *upf_nft_tunmap_get_ruleset_del_str(void *ctx, struct upf_tunmap *tunmap)
|
||||
{
|
||||
OSMO_NAME_C_IMPL(ctx, 1024, "ERROR", upf_nft_tunmap_get_ruleset_del_str_buf, tunmap)
|
||||
}
|
||||
|
||||
static int upf_nft_tunmap_ensure_chain_id(struct upf_nft_tun *tun)
|
||||
{
|
||||
if (tun->chain_id)
|
||||
return 0;
|
||||
tun->chain_id = upf_next_chain_id();
|
||||
if (!tun->chain_id)
|
||||
return -ENOSPC;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int upf_nft_tunmap_create(struct upf_tunmap *tunmap)
|
||||
{
|
||||
if (upf_nft_tunmap_ensure_chain_id(&tunmap->access)
|
||||
|| upf_nft_tunmap_ensure_chain_id(&tunmap->core))
|
||||
return -ENOSPC;
|
||||
return nft_enqueue(&g_nft_queue, upf_nft_tunmap_get_ruleset_str_buf, tunmap);
|
||||
}
|
||||
|
||||
int upf_nft_tunmap_delete(struct upf_tunmap *tunmap)
|
||||
{
|
||||
return nft_enqueue(&g_nft_queue, upf_nft_tunmap_get_ruleset_del_str_buf, tunmap);
|
||||
}
|
||||
532
src/osmo-upf/upf_vty.c
Normal file
532
src/osmo-upf/upf_vty.c
Normal file
@@ -0,0 +1,532 @@
|
||||
/* OsmoUpf interface to quagga VTY */
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/core/sockaddr_str.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
|
||||
#include <osmocom/pfcp/pfcp_endpoint.h>
|
||||
#include <osmocom/pfcp/pfcp_msg.h>
|
||||
|
||||
#include <osmocom/vty/vty.h>
|
||||
#include <osmocom/vty/command.h>
|
||||
|
||||
#include <osmocom/upf/upf.h>
|
||||
#include <osmocom/upf/upf_gtp.h>
|
||||
#include <osmocom/upf/up_endpoint.h>
|
||||
#include <osmocom/upf/up_peer.h>
|
||||
#include <osmocom/upf/up_session.h>
|
||||
#include <osmocom/upf/up_gtp_action.h>
|
||||
#include <osmocom/upf/netinst.h>
|
||||
|
||||
enum upf_vty_node {
|
||||
PFCP_NODE = _LAST_OSMOVTY_NODE + 1,
|
||||
TUNEND_NODE,
|
||||
TUNMAP_NODE,
|
||||
NETINST_NODE,
|
||||
};
|
||||
|
||||
static struct cmd_node cfg_pfcp_node = {
|
||||
PFCP_NODE,
|
||||
"%s(config-pfcp)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
#define pfcp_vty (g_upf->pfcp.vty_cfg)
|
||||
#define tunend_vty (g_upf->tunend.vty_cfg)
|
||||
|
||||
DEFUN(cfg_pfcp, cfg_pfcp_cmd,
|
||||
"pfcp",
|
||||
"Enter the PFCP configuration node\n")
|
||||
{
|
||||
vty->node = PFCP_NODE;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static int config_write_pfcp(struct vty *vty)
|
||||
{
|
||||
vty_out(vty, "pfcp%s", VTY_NEWLINE);
|
||||
if (strcmp(UPF_PFCP_LISTEN_DEFAULT, pfcp_vty.local_addr))
|
||||
vty_out(vty, " local-addr %s%s", pfcp_vty.local_addr, VTY_NEWLINE);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_pfcp_local_addr, cfg_pfcp_local_addr_cmd,
|
||||
"local-addr IP_ADDR",
|
||||
"Set the local IP address to bind on for PFCP\n"
|
||||
"IP address\n")
|
||||
{
|
||||
osmo_talloc_replace_string(g_upf, &pfcp_vty.local_addr, argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static struct cmd_node cfg_tunend_node = {
|
||||
TUNEND_NODE,
|
||||
"%s(config-tunend)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
#define TUNEND_NODE_STR "Enter the 'tunend' node to configure Linux GTP kernel module usage\n"
|
||||
|
||||
DEFUN(cfg_tunend, cfg_tunend_cmd, "tunend", TUNEND_NODE_STR)
|
||||
{
|
||||
vty->node = TUNEND_NODE;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/* legacy compat: "tunend" was originally named "gtp" */
|
||||
DEFUN_CMD_ELEMENT(cfg_tunend, cfg_gtp_cmd, "gtp", TUNEND_NODE_STR, CMD_ATTR_HIDDEN, 0);
|
||||
|
||||
static int config_write_tunend(struct vty *vty)
|
||||
{
|
||||
struct tunend_vty_cfg_dev *d;
|
||||
vty_out(vty, "tunend%s", VTY_NEWLINE);
|
||||
|
||||
if (g_upf->tunend.mockup)
|
||||
vty_out(vty, " mockup%s", VTY_NEWLINE);
|
||||
|
||||
llist_for_each_entry(d, &tunend_vty.devs, entry) {
|
||||
if (d->create) {
|
||||
vty_out(vty, " dev create %s", d->dev_name);
|
||||
if (d->local_addr)
|
||||
vty_out(vty, " %s", d->local_addr);
|
||||
vty_out(vty, "%s", VTY_NEWLINE);
|
||||
} else {
|
||||
vty_out(vty, " dev use %s%s", d->dev_name, VTY_NEWLINE);
|
||||
}
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define DEV_STR "Configure the GTP device to use for encaps/decaps.\n"
|
||||
|
||||
DEFUN(cfg_tunend_mockup, cfg_tunend_mockup_cmd,
|
||||
"mockup",
|
||||
"don't actually send commands to the GTP kernel module, just return success\n")
|
||||
{
|
||||
g_upf->tunend.mockup = true;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_tunend_no_mockup, cfg_tunend_no_mockup_cmd,
|
||||
"no mockup",
|
||||
NO_STR
|
||||
"operate GTP kernel module normally\n")
|
||||
{
|
||||
g_upf->tunend.mockup = false;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static struct tunend_vty_cfg_dev *tunend_dev_add(int argc, const char **argv, bool create)
|
||||
{
|
||||
struct tunend_vty_cfg_dev *d = talloc_zero(g_upf, struct tunend_vty_cfg_dev);
|
||||
d->create = create;
|
||||
d->dev_name = talloc_strdup(d, argv[0]);
|
||||
if (argc > 1)
|
||||
d->local_addr = talloc_strdup(d, argv[1]);
|
||||
llist_add(&d->entry, &tunend_vty.devs);
|
||||
return d;
|
||||
}
|
||||
|
||||
DEFUN(cfg_tunend_dev_create, cfg_tunend_dev_create_cmd,
|
||||
"dev create DEVNAME [LISTEN_ADDR]",
|
||||
DEV_STR
|
||||
"Add GTP device, creating a new Linux kernel GTP device. Will listen on GTPv1 port "
|
||||
OSMO_STRINGIFY_VAL(PORT_GTP1_U)
|
||||
" and GTPv0 port " OSMO_STRINGIFY_VAL(PORT_GTP0_U) " on the specified LISTEN_ADDR\n"
|
||||
"device name, e.g. 'apn0'\n"
|
||||
"IPv4 or IPv6 address to listen on, omit for ANY. LISTEN_ADDR is used to pick a GTP device matching the local"
|
||||
" address for a PFCP Network Instance, which are configured in the 'netinst' node.\n")
|
||||
{
|
||||
struct tunend_vty_cfg_dev *d = tunend_dev_add(argc, argv, true);
|
||||
vty_out(vty, "Added GTP device %s on %s (create new)%s", d->dev_name, d->local_addr ? : "0.0.0.0", VTY_NEWLINE);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_tunend_dev_use, cfg_tunend_dev_use_cmd,
|
||||
"dev use DEVNAME [LOCAL_ADDR]",
|
||||
DEV_STR
|
||||
"Add GTP device, using an existing Linux kernel GTP device, e.g. created by 'gtp-link'\n"
|
||||
"device name, e.g. 'apn0'\n"
|
||||
"The local GTP address this device listens on. It is assumed to be ANY when omitted."
|
||||
" LOCAL_ADDR is used to pick a GTP device matching the local address for a PFCP Network Instance,"
|
||||
" which are configured in the 'netinst' node.\n")
|
||||
{
|
||||
struct tunend_vty_cfg_dev *d = tunend_dev_add(argc, argv, false);
|
||||
vty_out(vty, "Added GTP device %s on %s (use existing)%s", d->dev_name, d->local_addr ? : "0.0.0.0",
|
||||
VTY_NEWLINE);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_tunend_dev_del, cfg_tunend_dev_del_cmd,
|
||||
"dev delete DEVNAME",
|
||||
DEV_STR
|
||||
"Remove a GTP device from the configuration, and delete the Linux kernel GTP device if it was created here.\n"
|
||||
"device name, e.g. 'apn0'\n")
|
||||
{
|
||||
const char *dev_name = argv[0];
|
||||
struct tunend_vty_cfg_dev *d;
|
||||
struct upf_gtp_dev *dev;
|
||||
|
||||
/* remove from VTY cfg */
|
||||
llist_for_each_entry(d, &tunend_vty.devs, entry) {
|
||||
if (strcmp(d->dev_name, dev_name))
|
||||
continue;
|
||||
llist_del(&d->entry);
|
||||
break;
|
||||
}
|
||||
|
||||
/* close device (and possibly delete from system, via talloc destructor) */
|
||||
dev = upf_gtp_dev_find_by_name(dev_name);
|
||||
if (dev)
|
||||
talloc_free(dev);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static struct cmd_node cfg_tunmap_node = {
|
||||
TUNMAP_NODE,
|
||||
"%s(config-tunmap)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
#define TUNMAP_NODE_STR "Enter the 'tunmap' node to configure nftables usage\n"
|
||||
|
||||
DEFUN(cfg_tunmap, cfg_tunmap_cmd, "tunmap", TUNMAP_NODE_STR)
|
||||
{
|
||||
vty->node = TUNMAP_NODE;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/* legacy compat: "tunmap" was originally named "nft" */
|
||||
DEFUN_CMD_ELEMENT(cfg_tunmap, cfg_nft_cmd, "nft", TUNMAP_NODE_STR, CMD_ATTR_HIDDEN, 0);
|
||||
|
||||
static int config_write_tunmap(struct vty *vty)
|
||||
{
|
||||
vty_out(vty, "tunmap%s", VTY_NEWLINE);
|
||||
|
||||
if (g_upf->tunmap.mockup)
|
||||
vty_out(vty, " mockup%s", VTY_NEWLINE);
|
||||
|
||||
if (g_upf->tunmap.table_name && strcmp(g_upf->tunmap.table_name, "osmo-upf"))
|
||||
vty_out(vty, " table-name %s%s", g_upf->tunmap.table_name, VTY_NEWLINE);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_tunmap_mockup, cfg_tunmap_mockup_cmd,
|
||||
"mockup",
|
||||
"don't actually send rulesets to nftables, just return success\n")
|
||||
{
|
||||
g_upf->tunmap.mockup = true;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_tunmap_no_mockup, cfg_tunmap_no_mockup_cmd,
|
||||
"no mockup",
|
||||
NO_STR
|
||||
"operate nftables rulesets normally\n")
|
||||
{
|
||||
g_upf->tunmap.mockup = false;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_tunmap_table_name, cfg_tunmap_table_name_cmd,
|
||||
"table-name TABLE_NAME",
|
||||
"Set the nft inet table name to create and place GTP tunnel forwarding chains in"
|
||||
" (as in 'nft add table inet foo'). If multiple instances of osmo-upf are running on the same system, each"
|
||||
" osmo-upf must have its own table name. Otherwise the names of created forwarding chains will collide."
|
||||
" The default table name is \"osmo-upf\".\n"
|
||||
"nft inet table name\n")
|
||||
{
|
||||
osmo_talloc_replace_string(g_upf, &g_upf->tunmap.table_name, argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define NFT_RULE_STR "nftables rule specifics\n"
|
||||
#define TUNMAP_STR "GTP tunmap use case (a.k.a. forwarding between two GTP tunnels)\n"
|
||||
#define TUNMAP_APPEND_STR "'tunmap append' feature is no longer available.\n"
|
||||
|
||||
DEFUN_DEPRECATED(cfg_tunmap_nft_rule_append, cfg_tunmap_nft_rule_append_cmd,
|
||||
"nft-rule tunmap append .NFT_RULE",
|
||||
NFT_RULE_STR TUNMAP_STR TUNMAP_APPEND_STR TUNMAP_APPEND_STR)
|
||||
{
|
||||
vty_out(vty, "%% deprecated config option: 'nft-rule tunmap append'%s", VTY_NEWLINE);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN_DEPRECATED(cfg_tunmap_no_nft_rule_append, cfg_tunmap_no_nft_rule_append_cmd,
|
||||
"no nft-rule tunmap append",
|
||||
NO_STR NFT_RULE_STR TUNMAP_STR TUNMAP_APPEND_STR TUNMAP_APPEND_STR)
|
||||
{
|
||||
vty_out(vty, "%% deprecated config option: 'no nft-rule tunmap append'%s", VTY_NEWLINE);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN_DEPRECATED(show_nft_rule_append, show_nft_rule_append_cmd,
|
||||
"show nft-rule tunmap append",
|
||||
SHOW_STR NFT_RULE_STR TUNMAP_STR TUNMAP_APPEND_STR)
|
||||
{
|
||||
vty_out(vty, "%% deprecated config option: 'show nft-rule tunmap append'%s", VTY_NEWLINE);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(show_nft_rule_tunmap_example, show_nft_rule_tunmap_example_cmd,
|
||||
"show nft-rule tunmap example",
|
||||
SHOW_STR NFT_RULE_STR TUNMAP_STR
|
||||
"Print a complete nftables ruleset for a tunmap filled with example IP addresses and TEIDs\n")
|
||||
{
|
||||
struct osmo_sockaddr_str str = {};
|
||||
struct upf_tunmap tunmap = {
|
||||
.access = {
|
||||
.tun = {
|
||||
.local.teid = 0x201,
|
||||
.remote.teid = 0x102,
|
||||
},
|
||||
.chain_id = 123,
|
||||
},
|
||||
.core = {
|
||||
.tun = {
|
||||
.local.teid = 0x203,
|
||||
.remote.teid = 0x302,
|
||||
},
|
||||
.chain_id = 321,
|
||||
},
|
||||
};
|
||||
|
||||
osmo_sockaddr_str_from_str2(&str, "1.1.1.1");
|
||||
osmo_sockaddr_str_to_sockaddr(&str, &tunmap.access.tun.remote.addr.u.sas);
|
||||
|
||||
osmo_sockaddr_str_from_str2(&str, "2.2.2.1");
|
||||
osmo_sockaddr_str_to_sockaddr(&str, &tunmap.access.tun.local.addr.u.sas);
|
||||
|
||||
osmo_sockaddr_str_from_str2(&str, "2.2.2.3");
|
||||
osmo_sockaddr_str_to_sockaddr(&str, &tunmap.core.tun.local.addr.u.sas);
|
||||
|
||||
osmo_sockaddr_str_from_str2(&str, "3.3.3.3");
|
||||
osmo_sockaddr_str_to_sockaddr(&str, &tunmap.core.tun.remote.addr.u.sas);
|
||||
|
||||
vty_out(vty, "%% init verdict map:%s", VTY_NEWLINE);
|
||||
vty_out(vty, "%s%s", upf_nft_tunmap_get_table_init_str(OTC_SELECT), VTY_NEWLINE);
|
||||
vty_out(vty, "%s%s", upf_nft_tunmap_get_vmap_init_str(OTC_SELECT), VTY_NEWLINE);
|
||||
vty_out(vty, "%% add tunmap:%s", VTY_NEWLINE);
|
||||
vty_out(vty, "%% %s%s", upf_nft_tunmap_to_str_c(OTC_SELECT, &tunmap), VTY_NEWLINE);
|
||||
vty_out(vty, "%s%s", upf_nft_tunmap_get_ruleset_str(OTC_SELECT, &tunmap), VTY_NEWLINE);
|
||||
vty_out(vty, "%% delete tunmap:%s", VTY_NEWLINE);
|
||||
vty_out(vty, "%s%s", upf_nft_tunmap_get_ruleset_del_str(OTC_SELECT, &tunmap), VTY_NEWLINE);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static struct cmd_node cfg_netinst_node = {
|
||||
NETINST_NODE,
|
||||
"%s(config-netinst)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
DEFUN(cfg_netinst, cfg_netinst_cmd,
|
||||
"netinst",
|
||||
"Enter the Network Instance configuration node\n")
|
||||
{
|
||||
vty->node = NETINST_NODE;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static int config_write_netinst(struct vty *vty)
|
||||
{
|
||||
vty_out(vty, "netinst%s", VTY_NEWLINE);
|
||||
netinst_vty_write(vty, &g_upf->netinst, " ", NULL);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_netinst_add, cfg_netinst_add_cmd,
|
||||
"add NAME ADDR",
|
||||
"add Network Instance: associate a PFCP Network Instance name with a local IP address\n"
|
||||
"Network Instance name as received in PFCP Network Instance IE\n"
|
||||
"IP address of a local interface\n")
|
||||
{
|
||||
const char *errmsg;
|
||||
if (!netinst_add(g_upf, &g_upf->netinst, argv[0], argv[1], &errmsg)) {
|
||||
vty_out(vty, "%% Error: netinst: cannot add %s %s: %s%s", argv[0], argv[1],
|
||||
errmsg ? : "(unknown error)", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(show_netinst, show_netinst_cmd,
|
||||
"show netinst [NAME]",
|
||||
SHOW_STR "List configured Network Instance entries\n"
|
||||
"Show the Network Instance with this name (show all when omitted)\n")
|
||||
{
|
||||
const char *name_or_null = argc > 0 ? argv[0] : NULL;
|
||||
|
||||
if (!netinst_vty_write(vty, &g_upf->netinst, " ", name_or_null)) {
|
||||
if (name_or_null)
|
||||
vty_out(vty, "%% No such Network Instance entry%s", VTY_NEWLINE);
|
||||
else
|
||||
vty_out(vty, "%% No Network Instance entries configured%s", VTY_NEWLINE);
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_netinst_clear, cfg_netinst_clear_cmd,
|
||||
"clear",
|
||||
"Remove all Network Instance entries\n")
|
||||
{
|
||||
int count = netinst_clear(&g_upf->netinst);
|
||||
vty_out(vty, "netinst entries removed: %d%s", count, VTY_NEWLINE);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(show_pdr, show_pdr_cmd,
|
||||
"show pdr",
|
||||
SHOW_STR
|
||||
"List all sessions' PDR and FAR status\n")
|
||||
{
|
||||
struct up_peer *peer;
|
||||
int active_count = 0;
|
||||
int inactive_count = 0;
|
||||
llist_for_each_entry(peer, &g_upf->pfcp.ep->peers, entry) {
|
||||
struct up_session *session;
|
||||
int bkt;
|
||||
hash_for_each(peer->sessions_by_up_seid, bkt, session, node_by_up_seid) {
|
||||
struct pdr *pdr;
|
||||
llist_for_each_entry(pdr, &session->pdrs, entry) {
|
||||
if (!pdr->active) {
|
||||
vty_out(vty, "%s: inactive: %s%s%s%s",
|
||||
session->fi->id, pdr_to_str_c(OTC_SELECT, pdr),
|
||||
pdr->inactive_reason ? ": " : "",
|
||||
pdr->inactive_reason ? : "",
|
||||
VTY_NEWLINE);
|
||||
inactive_count++;
|
||||
} else {
|
||||
vty_out(vty, "%s: active: %s%s",
|
||||
session->fi->id, pdr_to_str_c(OTC_SELECT, pdr),
|
||||
VTY_NEWLINE);
|
||||
active_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
vty_out(vty, "(%d of %d active)%s", active_count, active_count + inactive_count, VTY_NEWLINE);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(show_gtp, show_gtp_cmd,
|
||||
"show gtp",
|
||||
SHOW_STR
|
||||
"Active GTP tunnels, both tunend and tunmap\n")
|
||||
{
|
||||
struct up_peer *peer;
|
||||
int count = 0;
|
||||
|
||||
llist_for_each_entry(peer, &g_upf->pfcp.ep->peers, entry) {
|
||||
struct up_session *session;
|
||||
int bkt;
|
||||
hash_for_each(peer->sessions_by_up_seid, bkt, session, node_by_up_seid) {
|
||||
struct up_gtp_action *a;
|
||||
llist_for_each_entry(a, &session->active_gtp_actions, entry) {
|
||||
vty_out(vty, "%s%s", up_gtp_action_to_str_c(OTC_SELECT, a), VTY_NEWLINE);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
vty_out(vty, "(%d active)%s", count, VTY_NEWLINE);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(show_session, show_session_cmd,
|
||||
"show session",
|
||||
SHOW_STR
|
||||
"PFCP Session status\n")
|
||||
{
|
||||
struct up_peer *peer;
|
||||
int inactive_count = 0;
|
||||
int active_count = 0;
|
||||
int fully_active_count = 0;
|
||||
|
||||
llist_for_each_entry(peer, &g_upf->pfcp.ep->peers, entry) {
|
||||
struct up_session *session;
|
||||
int bkt;
|
||||
hash_for_each(peer->sessions_by_up_seid, bkt, session, node_by_up_seid) {
|
||||
vty_out(vty, "%s %s%s",
|
||||
up_session_to_str_c(OTC_SELECT, session),
|
||||
up_session_gtp_status(session), VTY_NEWLINE);
|
||||
if (up_session_is_active(session)) {
|
||||
if (up_session_is_fully_active(session, NULL, NULL))
|
||||
fully_active_count++;
|
||||
else
|
||||
active_count++;
|
||||
} else {
|
||||
inactive_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
vty_out(vty, "(%d fully-active + %d active with some PDR/FAR ignored + %d inactive)%s",
|
||||
fully_active_count, active_count, inactive_count, VTY_NEWLINE);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
void upf_vty_init()
|
||||
{
|
||||
OSMO_ASSERT(g_upf != NULL);
|
||||
|
||||
install_element_ve(&show_pdr_cmd);
|
||||
install_element_ve(&show_gtp_cmd);
|
||||
install_element_ve(&show_session_cmd);
|
||||
install_element_ve(&show_netinst_cmd);
|
||||
install_element_ve(&show_nft_rule_append_cmd);
|
||||
|
||||
install_node(&cfg_pfcp_node, config_write_pfcp);
|
||||
install_element(CONFIG_NODE, &cfg_pfcp_cmd);
|
||||
|
||||
install_element(PFCP_NODE, &cfg_pfcp_local_addr_cmd);
|
||||
|
||||
install_node(&cfg_tunend_node, config_write_tunend);
|
||||
install_element(CONFIG_NODE, &cfg_tunend_cmd);
|
||||
install_element(CONFIG_NODE, &cfg_gtp_cmd);
|
||||
|
||||
install_element(TUNEND_NODE, &cfg_tunend_mockup_cmd);
|
||||
install_element(TUNEND_NODE, &cfg_tunend_no_mockup_cmd);
|
||||
install_element(TUNEND_NODE, &cfg_tunend_dev_create_cmd);
|
||||
install_element(TUNEND_NODE, &cfg_tunend_dev_use_cmd);
|
||||
install_element(TUNEND_NODE, &cfg_tunend_dev_del_cmd);
|
||||
|
||||
install_node(&cfg_tunmap_node, config_write_tunmap);
|
||||
install_element(CONFIG_NODE, &cfg_tunmap_cmd);
|
||||
install_element(CONFIG_NODE, &cfg_nft_cmd);
|
||||
|
||||
install_element(TUNMAP_NODE, &cfg_tunmap_mockup_cmd);
|
||||
install_element(TUNMAP_NODE, &cfg_tunmap_no_mockup_cmd);
|
||||
install_element(TUNMAP_NODE, &cfg_tunmap_table_name_cmd);
|
||||
install_element(TUNMAP_NODE, &cfg_tunmap_nft_rule_append_cmd);
|
||||
install_element(TUNMAP_NODE, &cfg_tunmap_no_nft_rule_append_cmd);
|
||||
install_element(TUNMAP_NODE, &show_nft_rule_append_cmd);
|
||||
install_element(TUNMAP_NODE, &show_nft_rule_tunmap_example_cmd);
|
||||
|
||||
install_node(&cfg_netinst_node, config_write_netinst);
|
||||
install_element(CONFIG_NODE, &cfg_netinst_cmd);
|
||||
|
||||
install_element(NETINST_NODE, &cfg_netinst_clear_cmd);
|
||||
install_element(NETINST_NODE, &cfg_netinst_add_cmd);
|
||||
install_element(NETINST_NODE, &show_netinst_cmd);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
SUBDIRS = \
|
||||
libosmo-gtlv \
|
||||
unique_ids \
|
||||
$(NULL)
|
||||
|
||||
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
|
||||
@@ -52,7 +52,7 @@ VTY_TEST ?= *.vty
|
||||
vty-test:
|
||||
osmo_verify_transcript_vty.py -v \
|
||||
-n OsmoUPF -p 4275 \
|
||||
-r "$(top_builddir)/src/osmo-upf/osmo-upf -c $(top_srcdir)/doc/examples/osmo-upf/osmo-upf.cfg" \
|
||||
-r "$(top_builddir)/src/osmo-upf/osmo-upf -c $(top_srcdir)/doc/examples/osmo-upf/osmo-upf-mockup.cfg" \
|
||||
$(U) $(srcdir)/$(VTY_TEST)
|
||||
|
||||
check-local: atconfig $(TESTSUITE)
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
SUBDIRS = \
|
||||
test_gtlv_gen \
|
||||
test_tliv \
|
||||
$(NULL)
|
||||
|
||||
AM_CPPFLAGS = \
|
||||
$(all_includes) \
|
||||
-I$(top_srcdir)/include \
|
||||
$(NULL)
|
||||
|
||||
AM_CFLAGS = \
|
||||
-Wall \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
gtlv_test \
|
||||
gtlv_dec_enc_test \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_DIST = \
|
||||
gtlv_test.ok \
|
||||
gtlv_dec_enc_test.ok \
|
||||
$(NULL)
|
||||
|
||||
gtlv_test_SOURCES = \
|
||||
gtlv_test.c \
|
||||
$(NULL)
|
||||
|
||||
gtlv_test_LDADD = \
|
||||
$(top_builddir)/src/libosmo-gtlv/libosmo-gtlv.a \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
gtlv_dec_enc_test_SOURCES = \
|
||||
gtlv_dec_enc_test.c \
|
||||
$(NULL)
|
||||
|
||||
gtlv_dec_enc_test_LDADD = \
|
||||
$(top_builddir)/src/libosmo-gtlv/libosmo-gtlv.a \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
.PHONY: update_exp
|
||||
update_exp:
|
||||
$(builddir)/gtlv_test >$(srcdir)/gtlv_test.ok
|
||||
$(builddir)/gtlv_dec_enc_test >$(srcdir)/gtlv_dec_enc_test.ok
|
||||
$(MAKE) -C test_gtlv_gen update_exp
|
||||
$(MAKE) -C test_tliv update_exp
|
||||
@@ -1,422 +0,0 @@
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
|
||||
#include <osmocom/gtlv/gtlv_dec_enc.h>
|
||||
|
||||
void *ctx;
|
||||
|
||||
enum tags {
|
||||
TAG_FOO = 1,
|
||||
TAG_BAR,
|
||||
TAG_BAZ,
|
||||
TAG_REPEAT_INT,
|
||||
TAG_REPEAT_STRUCT,
|
||||
TAG_NEST,
|
||||
};
|
||||
|
||||
const struct value_string tag_names[] = {
|
||||
{ TAG_FOO, "FOO" },
|
||||
{ TAG_BAR, "BAR" },
|
||||
{ TAG_BAZ, "BAZ" },
|
||||
{ TAG_REPEAT_INT, "REPEAT_INT" },
|
||||
{ TAG_REPEAT_STRUCT, "REPEAT_STRUCT" },
|
||||
{ TAG_NEST, "NEST" },
|
||||
{}
|
||||
};
|
||||
|
||||
struct bar {
|
||||
char str[23];
|
||||
};
|
||||
|
||||
struct baz {
|
||||
int v_int;
|
||||
bool v_bool;
|
||||
};
|
||||
|
||||
enum repeat_enum {
|
||||
R_A,
|
||||
R_B,
|
||||
R_C,
|
||||
};
|
||||
|
||||
const struct value_string repeat_enum_names[] = {
|
||||
OSMO_VALUE_STRING(R_A),
|
||||
OSMO_VALUE_STRING(R_B),
|
||||
OSMO_VALUE_STRING(R_C),
|
||||
{}
|
||||
};
|
||||
|
||||
struct repeat {
|
||||
int v_int;
|
||||
bool v_bool;
|
||||
enum repeat_enum v_enum;
|
||||
};
|
||||
|
||||
struct nested_inner_msg {
|
||||
int foo;
|
||||
struct bar bar;
|
||||
struct baz baz;
|
||||
};
|
||||
|
||||
struct decoded_msg {
|
||||
int foo;
|
||||
struct bar bar;
|
||||
|
||||
bool baz_present;
|
||||
struct baz baz;
|
||||
|
||||
unsigned int repeat_int_count;
|
||||
int repeat_int[32];
|
||||
|
||||
unsigned int repeat_struct_count;
|
||||
struct repeat repeat_struct[32];
|
||||
|
||||
bool nest_present;
|
||||
struct nested_inner_msg nest;
|
||||
};
|
||||
|
||||
int dec_u16(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *gtlv)
|
||||
{
|
||||
int *foo = decode_to;
|
||||
if (gtlv->len != 2)
|
||||
return -EINVAL;
|
||||
*foo = osmo_load16be(gtlv->val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int enc_u16(struct osmo_gtlv_put *gtlv, const void *decoded_struct, const void *encode_from)
|
||||
{
|
||||
const int *foo = encode_from;
|
||||
if (*foo > INT16_MAX)
|
||||
return -EINVAL;
|
||||
msgb_put_u16(gtlv->dst, *foo);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int enc_to_str_u16(char *buf, size_t buflen, const void *encode_from)
|
||||
{
|
||||
const int *foo = encode_from;
|
||||
return snprintf(buf, buflen, "%d", *foo);
|
||||
}
|
||||
|
||||
int dec_bar(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *gtlv)
|
||||
{
|
||||
struct bar *bar = decode_to;
|
||||
if (gtlv->len > sizeof(bar->str) - 1)
|
||||
return -EINVAL;
|
||||
osmo_strlcpy(bar->str, (const char *)gtlv->val, OSMO_MIN(gtlv->len + 1, sizeof(bar->str)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int enc_bar(struct osmo_gtlv_put *gtlv, const void *decoded_struct, const void *encode_from)
|
||||
{
|
||||
const struct bar *bar = encode_from;
|
||||
int len = strnlen(bar->str, sizeof(bar->str));
|
||||
memcpy(msgb_put(gtlv->dst, len), bar, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int enc_to_str_bar(char *buf, size_t buflen, const void *encode_from)
|
||||
{
|
||||
const struct bar *bar = encode_from;
|
||||
return osmo_quote_str_buf3(buf, buflen, bar->str, -1);
|
||||
}
|
||||
|
||||
int dec_baz(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *gtlv)
|
||||
{
|
||||
struct baz *baz = decode_to;
|
||||
uint16_t l;
|
||||
if (gtlv->len != 2)
|
||||
return -EINVAL;
|
||||
l = osmo_load16be(gtlv->val);
|
||||
baz->v_int = l & 0x7fff;
|
||||
baz->v_bool = (l & 0x8000) ? true : false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int enc_baz(struct osmo_gtlv_put *gtlv, const void *decoded_struct, const void *encode_from)
|
||||
{
|
||||
const struct baz *baz = encode_from;
|
||||
if (baz->v_int > 0x7fff)
|
||||
return -EINVAL;
|
||||
msgb_put_u16(gtlv->dst, (baz->v_bool ? 0x8000 : 0) + (baz->v_int & 0x7fff));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int enc_to_str_baz(char *buf, size_t buflen, const void *encode_from)
|
||||
{
|
||||
const struct baz *baz = encode_from;
|
||||
return snprintf(buf, buflen, "{%d,%s}", baz->v_int, baz->v_bool ? "true" : "false");
|
||||
}
|
||||
|
||||
int dec_repeat_struct(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *gtlv)
|
||||
{
|
||||
struct repeat *repeat_struct = decode_to;
|
||||
if (gtlv->len != 3)
|
||||
return -EINVAL;
|
||||
repeat_struct->v_int = osmo_load16be(gtlv->val);
|
||||
repeat_struct->v_bool = gtlv->val[2] & 0x80;
|
||||
repeat_struct->v_enum = gtlv->val[2] & 0x7f;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int enc_repeat_struct(struct osmo_gtlv_put *gtlv, const void *decoded_struct, const void *encode_from)
|
||||
{
|
||||
const struct repeat *repeat_struct = encode_from;
|
||||
msgb_put_u16(gtlv->dst, repeat_struct->v_int);
|
||||
msgb_put_u8(gtlv->dst, (repeat_struct->v_bool ? 0x80 : 0) + (repeat_struct->v_enum & 0x7f));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int enc_to_str_repeat_struct(char *buf, size_t buflen, const void *encode_from)
|
||||
{
|
||||
const struct repeat *repeat_struct = encode_from;
|
||||
return snprintf(buf, buflen, "{%d,%s,%s}", repeat_struct->v_int, repeat_struct->v_bool ? "true" : "false",
|
||||
get_value_string(repeat_enum_names, repeat_struct->v_enum));
|
||||
}
|
||||
|
||||
struct osmo_gtlv_coding nested_inner_msg_ies[] = {
|
||||
{
|
||||
.ti = { TAG_FOO },
|
||||
.dec_func = dec_u16,
|
||||
.enc_func = enc_u16,
|
||||
.enc_to_str_func = enc_to_str_u16,
|
||||
.memb_ofs = offsetof(struct nested_inner_msg, foo),
|
||||
},
|
||||
{
|
||||
.ti = { TAG_BAR },
|
||||
.dec_func = dec_bar,
|
||||
.enc_func = enc_bar,
|
||||
.enc_to_str_func = enc_to_str_bar,
|
||||
.memb_ofs = offsetof(struct nested_inner_msg, bar),
|
||||
},
|
||||
{
|
||||
.ti = { TAG_BAZ },
|
||||
.dec_func = dec_baz,
|
||||
.enc_func = enc_baz,
|
||||
.enc_to_str_func = enc_to_str_baz,
|
||||
.memb_ofs = offsetof(struct nested_inner_msg, baz),
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
struct osmo_gtlv_coding msg_ie_coding[] = {
|
||||
{
|
||||
.ti = { TAG_FOO },
|
||||
.dec_func = dec_u16,
|
||||
.enc_func = enc_u16,
|
||||
.enc_to_str_func = enc_to_str_u16,
|
||||
.memb_ofs = offsetof(struct decoded_msg, foo),
|
||||
},
|
||||
{
|
||||
.ti = { TAG_BAR },
|
||||
.dec_func = dec_bar,
|
||||
.enc_func = enc_bar,
|
||||
.enc_to_str_func = enc_to_str_bar,
|
||||
.memb_ofs = offsetof(struct decoded_msg, bar),
|
||||
},
|
||||
{
|
||||
.ti = { TAG_BAZ },
|
||||
.dec_func = dec_baz,
|
||||
.enc_func = enc_baz,
|
||||
.enc_to_str_func = enc_to_str_baz,
|
||||
.memb_ofs = offsetof(struct decoded_msg, baz),
|
||||
.has_presence_flag = true,
|
||||
.presence_flag_ofs = offsetof(struct decoded_msg, baz_present),
|
||||
},
|
||||
{
|
||||
.ti = { TAG_REPEAT_INT },
|
||||
.dec_func = dec_u16,
|
||||
.enc_func = enc_u16,
|
||||
.enc_to_str_func = enc_to_str_u16,
|
||||
.memb_ofs = offsetof(struct decoded_msg, repeat_int),
|
||||
.memb_array_pitch = OSMO_MEMB_ARRAY_PITCH(struct decoded_msg, repeat_int),
|
||||
.has_count = true,
|
||||
.count_ofs = offsetof(struct decoded_msg, repeat_int_count),
|
||||
.count_max = ARRAY_SIZE(((struct decoded_msg *)0)->repeat_int),
|
||||
},
|
||||
{
|
||||
.ti = { TAG_REPEAT_STRUCT },
|
||||
.dec_func = dec_repeat_struct,
|
||||
.enc_func = enc_repeat_struct,
|
||||
.enc_to_str_func = enc_to_str_repeat_struct,
|
||||
.memb_ofs = offsetof(struct decoded_msg, repeat_struct),
|
||||
.memb_array_pitch = OSMO_MEMB_ARRAY_PITCH(struct decoded_msg, repeat_struct),
|
||||
.has_count = true,
|
||||
.count_ofs = offsetof(struct decoded_msg, repeat_struct_count),
|
||||
.count_max = ARRAY_SIZE(((struct decoded_msg *)0)->repeat_struct),
|
||||
},
|
||||
{
|
||||
.ti = { TAG_NEST },
|
||||
.memb_ofs = offsetof(struct decoded_msg, nest),
|
||||
.nested_ies = nested_inner_msg_ies,
|
||||
.has_presence_flag = true,
|
||||
.presence_flag_ofs = offsetof(struct decoded_msg, nest_present),
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
char *decoded_msg_to_str(const struct decoded_msg *m)
|
||||
{
|
||||
return osmo_gtlvs_encode_to_str_c(ctx, m, 0, msg_ie_coding, tag_names);
|
||||
}
|
||||
|
||||
|
||||
const struct decoded_msg enc_dec_tests[] = {
|
||||
{
|
||||
.foo = 23,
|
||||
.bar = { "twentythree" },
|
||||
},
|
||||
{
|
||||
.foo = 23,
|
||||
.bar = { "twentythree" },
|
||||
|
||||
.baz_present = true,
|
||||
.baz = {
|
||||
.v_int = 2323,
|
||||
.v_bool = true,
|
||||
},
|
||||
},
|
||||
{
|
||||
.foo = 23,
|
||||
.bar = { "twentythree" },
|
||||
|
||||
.baz_present = true,
|
||||
.baz = {
|
||||
.v_int = 2323,
|
||||
.v_bool = true,
|
||||
},
|
||||
|
||||
.repeat_int_count = 3,
|
||||
.repeat_int = { 1, 2, 0x7fff },
|
||||
},
|
||||
{
|
||||
.foo = 23,
|
||||
.bar = { "twentythree" },
|
||||
|
||||
.baz_present = true,
|
||||
.baz = {
|
||||
.v_int = 2323,
|
||||
.v_bool = true,
|
||||
},
|
||||
|
||||
.repeat_int_count = 3,
|
||||
.repeat_int = { 1, 2, 0x7fff },
|
||||
|
||||
.repeat_struct_count = 2,
|
||||
.repeat_struct = {
|
||||
{
|
||||
.v_int = 1001,
|
||||
.v_bool = true,
|
||||
.v_enum = R_A,
|
||||
},
|
||||
{
|
||||
.v_int = 1002,
|
||||
.v_bool = false,
|
||||
.v_enum = R_B,
|
||||
},
|
||||
},
|
||||
|
||||
.nest_present = true,
|
||||
.nest = {
|
||||
.foo = 42,
|
||||
.bar = { "fortytwo" },
|
||||
.baz = {
|
||||
.v_int = 4242,
|
||||
.v_bool = false,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static int verify_err_cb_data;
|
||||
|
||||
void err_cb(void *data, void *decoded_struct, const char *file, int line, const char *fmt, ...)
|
||||
{
|
||||
assert(data == &verify_err_cb_data);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
//printf("ERR: %s:%d ", file, line);
|
||||
printf("ERR: ");
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void test_enc_dec(const char *label, const struct osmo_gtlv_cfg *cfg, bool ordered)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(enc_dec_tests); i++) {
|
||||
int rc;
|
||||
const struct decoded_msg *orig = &enc_dec_tests[i];
|
||||
struct decoded_msg parsed = {};
|
||||
struct osmo_gtlv_load load;
|
||||
struct osmo_gtlv_put put;
|
||||
|
||||
printf("\n=== start %s %s[%d]\n", label, __func__, i);
|
||||
printf("encoded: %s\n", decoded_msg_to_str(orig));
|
||||
|
||||
put = (struct osmo_gtlv_put){
|
||||
.cfg = cfg,
|
||||
.dst = msgb_alloc(1024, __func__),
|
||||
};
|
||||
rc = osmo_gtlvs_encode(&put, (void *)orig, 0, msg_ie_coding, err_cb, &verify_err_cb_data, tag_names);
|
||||
printf("osmo_gtlvs_encode() rc = %d\n", rc);
|
||||
printf("%s.\n", osmo_hexdump(put.dst->data, put.dst->len));
|
||||
|
||||
load = (struct osmo_gtlv_load){
|
||||
.cfg = cfg,
|
||||
.src = { put.dst->data, put.dst->len },
|
||||
};
|
||||
rc = osmo_gtlvs_decode(&parsed, 0, &load, ordered, msg_ie_coding, err_cb, &verify_err_cb_data, tag_names);
|
||||
printf("osmo_gtlvs_decode() rc = %d\n", rc);
|
||||
printf("decoded: %s\n", decoded_msg_to_str(&parsed));
|
||||
if (strcmp(decoded_msg_to_str(orig), decoded_msg_to_str(&parsed))) {
|
||||
printf(" ERROR: parsed != orig\n");
|
||||
exit(1);
|
||||
}
|
||||
printf("=== end %s %s[%d]\n", label, __func__, i);
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
ctx = talloc_named_const(NULL, 0, "gtlv_test");
|
||||
msgb_talloc_ctx_init(ctx, 0);
|
||||
|
||||
test_enc_dec("t8l8v ordered", &osmo_t8l8v_cfg, true);
|
||||
test_enc_dec("t8l8v unordered", &osmo_t8l8v_cfg, false);
|
||||
|
||||
test_enc_dec("t16l16v ordered", &osmo_t16l16v_cfg, true);
|
||||
test_enc_dec("t16l16v unordered", &osmo_t16l16v_cfg, false);
|
||||
|
||||
talloc_free(ctx);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
|
||||
=== start t8l8v ordered test_enc_dec[0]
|
||||
encoded: 'FOO'=23 'BAR'="twentythree"
|
||||
osmo_gtlvs_encode() rc = 0
|
||||
01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 .
|
||||
osmo_gtlvs_decode() rc = 0
|
||||
decoded: 'FOO'=23 'BAR'="twentythree"
|
||||
=== end t8l8v ordered test_enc_dec[0]
|
||||
|
||||
=== start t8l8v ordered test_enc_dec[1]
|
||||
encoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true}
|
||||
osmo_gtlvs_encode() rc = 0
|
||||
01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 03 02 89 13 .
|
||||
osmo_gtlvs_decode() rc = 0
|
||||
decoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true}
|
||||
=== end t8l8v ordered test_enc_dec[1]
|
||||
|
||||
=== start t8l8v ordered test_enc_dec[2]
|
||||
encoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 }
|
||||
osmo_gtlvs_encode() rc = 0
|
||||
01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 03 02 89 13 04 02 00 01 04 02 00 02 04 02 7f ff .
|
||||
osmo_gtlvs_decode() rc = 0
|
||||
decoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 }
|
||||
=== end t8l8v ordered test_enc_dec[2]
|
||||
|
||||
=== start t8l8v ordered test_enc_dec[3]
|
||||
encoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } 'REPEAT_STRUCT'={ {1001,true,R_A}, {1002,false,R_B} } 'NEST'={ 'FOO'=42 'BAR'="fortytwo" 'BAZ'={4242,false} }
|
||||
osmo_gtlvs_encode() rc = 0
|
||||
01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 03 02 89 13 04 02 00 01 04 02 00 02 04 02 7f ff 05 03 03 e9 80 05 03 03 ea 01 06 12 01 02 00 2a 02 08 66 6f 72 74 79 74 77 6f 03 02 10 92 .
|
||||
osmo_gtlvs_decode() rc = 0
|
||||
decoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } 'REPEAT_STRUCT'={ {1001,true,R_A}, {1002,false,R_B} } 'NEST'={ 'FOO'=42 'BAR'="fortytwo" 'BAZ'={4242,false} }
|
||||
=== end t8l8v ordered test_enc_dec[3]
|
||||
|
||||
=== start t8l8v unordered test_enc_dec[0]
|
||||
encoded: 'FOO'=23 'BAR'="twentythree"
|
||||
osmo_gtlvs_encode() rc = 0
|
||||
01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 .
|
||||
osmo_gtlvs_decode() rc = 0
|
||||
decoded: 'FOO'=23 'BAR'="twentythree"
|
||||
=== end t8l8v unordered test_enc_dec[0]
|
||||
|
||||
=== start t8l8v unordered test_enc_dec[1]
|
||||
encoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true}
|
||||
osmo_gtlvs_encode() rc = 0
|
||||
01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 03 02 89 13 .
|
||||
osmo_gtlvs_decode() rc = 0
|
||||
decoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true}
|
||||
=== end t8l8v unordered test_enc_dec[1]
|
||||
|
||||
=== start t8l8v unordered test_enc_dec[2]
|
||||
encoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 }
|
||||
osmo_gtlvs_encode() rc = 0
|
||||
01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 03 02 89 13 04 02 00 01 04 02 00 02 04 02 7f ff .
|
||||
osmo_gtlvs_decode() rc = 0
|
||||
decoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 }
|
||||
=== end t8l8v unordered test_enc_dec[2]
|
||||
|
||||
=== start t8l8v unordered test_enc_dec[3]
|
||||
encoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } 'REPEAT_STRUCT'={ {1001,true,R_A}, {1002,false,R_B} } 'NEST'={ 'FOO'=42 'BAR'="fortytwo" 'BAZ'={4242,false} }
|
||||
osmo_gtlvs_encode() rc = 0
|
||||
01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 03 02 89 13 04 02 00 01 04 02 00 02 04 02 7f ff 05 03 03 e9 80 05 03 03 ea 01 06 12 01 02 00 2a 02 08 66 6f 72 74 79 74 77 6f 03 02 10 92 .
|
||||
osmo_gtlvs_decode() rc = 0
|
||||
decoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } 'REPEAT_STRUCT'={ {1001,true,R_A}, {1002,false,R_B} } 'NEST'={ 'FOO'=42 'BAR'="fortytwo" 'BAZ'={4242,false} }
|
||||
=== end t8l8v unordered test_enc_dec[3]
|
||||
|
||||
=== start t16l16v ordered test_enc_dec[0]
|
||||
encoded: 'FOO'=23 'BAR'="twentythree"
|
||||
osmo_gtlvs_encode() rc = 0
|
||||
00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 .
|
||||
osmo_gtlvs_decode() rc = 0
|
||||
decoded: 'FOO'=23 'BAR'="twentythree"
|
||||
=== end t16l16v ordered test_enc_dec[0]
|
||||
|
||||
=== start t16l16v ordered test_enc_dec[1]
|
||||
encoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true}
|
||||
osmo_gtlvs_encode() rc = 0
|
||||
00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 00 03 00 02 89 13 .
|
||||
osmo_gtlvs_decode() rc = 0
|
||||
decoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true}
|
||||
=== end t16l16v ordered test_enc_dec[1]
|
||||
|
||||
=== start t16l16v ordered test_enc_dec[2]
|
||||
encoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 }
|
||||
osmo_gtlvs_encode() rc = 0
|
||||
00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 00 03 00 02 89 13 00 04 00 02 00 01 00 04 00 02 00 02 00 04 00 02 7f ff .
|
||||
osmo_gtlvs_decode() rc = 0
|
||||
decoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 }
|
||||
=== end t16l16v ordered test_enc_dec[2]
|
||||
|
||||
=== start t16l16v ordered test_enc_dec[3]
|
||||
encoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } 'REPEAT_STRUCT'={ {1001,true,R_A}, {1002,false,R_B} } 'NEST'={ 'FOO'=42 'BAR'="fortytwo" 'BAZ'={4242,false} }
|
||||
osmo_gtlvs_encode() rc = 0
|
||||
00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 00 03 00 02 89 13 00 04 00 02 00 01 00 04 00 02 00 02 00 04 00 02 7f ff 00 05 00 03 03 e9 80 00 05 00 03 03 ea 01 00 06 00 18 00 01 00 02 00 2a 00 02 00 08 66 6f 72 74 79 74 77 6f 00 03 00 02 10 92 .
|
||||
osmo_gtlvs_decode() rc = 0
|
||||
decoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } 'REPEAT_STRUCT'={ {1001,true,R_A}, {1002,false,R_B} } 'NEST'={ 'FOO'=42 'BAR'="fortytwo" 'BAZ'={4242,false} }
|
||||
=== end t16l16v ordered test_enc_dec[3]
|
||||
|
||||
=== start t16l16v unordered test_enc_dec[0]
|
||||
encoded: 'FOO'=23 'BAR'="twentythree"
|
||||
osmo_gtlvs_encode() rc = 0
|
||||
00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 .
|
||||
osmo_gtlvs_decode() rc = 0
|
||||
decoded: 'FOO'=23 'BAR'="twentythree"
|
||||
=== end t16l16v unordered test_enc_dec[0]
|
||||
|
||||
=== start t16l16v unordered test_enc_dec[1]
|
||||
encoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true}
|
||||
osmo_gtlvs_encode() rc = 0
|
||||
00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 00 03 00 02 89 13 .
|
||||
osmo_gtlvs_decode() rc = 0
|
||||
decoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true}
|
||||
=== end t16l16v unordered test_enc_dec[1]
|
||||
|
||||
=== start t16l16v unordered test_enc_dec[2]
|
||||
encoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 }
|
||||
osmo_gtlvs_encode() rc = 0
|
||||
00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 00 03 00 02 89 13 00 04 00 02 00 01 00 04 00 02 00 02 00 04 00 02 7f ff .
|
||||
osmo_gtlvs_decode() rc = 0
|
||||
decoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 }
|
||||
=== end t16l16v unordered test_enc_dec[2]
|
||||
|
||||
=== start t16l16v unordered test_enc_dec[3]
|
||||
encoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } 'REPEAT_STRUCT'={ {1001,true,R_A}, {1002,false,R_B} } 'NEST'={ 'FOO'=42 'BAR'="fortytwo" 'BAZ'={4242,false} }
|
||||
osmo_gtlvs_encode() rc = 0
|
||||
00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 00 03 00 02 89 13 00 04 00 02 00 01 00 04 00 02 00 02 00 04 00 02 7f ff 00 05 00 03 03 e9 80 00 05 00 03 03 ea 01 00 06 00 18 00 01 00 02 00 2a 00 02 00 08 66 6f 72 74 79 74 77 6f 00 03 00 02 10 92 .
|
||||
osmo_gtlvs_decode() rc = 0
|
||||
decoded: 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } 'REPEAT_STRUCT'={ {1001,true,R_A}, {1002,false,R_B} } 'NEST'={ 'FOO'=42 'BAR'="fortytwo" 'BAZ'={4242,false} }
|
||||
=== end t16l16v unordered test_enc_dec[3]
|
||||
@@ -1,629 +0,0 @@
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
|
||||
#include <osmocom/gtlv/gtlv.h>
|
||||
|
||||
void *ctx;
|
||||
|
||||
struct ie {
|
||||
struct osmo_gtlv_tag_inst ti;
|
||||
const char *val;
|
||||
};
|
||||
|
||||
/* write all IEs to a msgb */
|
||||
struct msgb *test_tlv_enc(const struct osmo_gtlv_cfg *cfg, const struct ie *ies)
|
||||
{
|
||||
const struct ie *ie;
|
||||
struct osmo_gtlv_put gtlv = {
|
||||
.cfg = cfg,
|
||||
.dst = msgb_alloc(1024, __func__),
|
||||
};
|
||||
|
||||
for (ie = ies; ie->val; ie++) {
|
||||
/* put header without knowing length yet */
|
||||
OSMO_ASSERT(osmo_gtlv_put_tli(>lv, &ie->ti, 0) == 0);
|
||||
/* put value data, as much as desired */
|
||||
msgb_put(gtlv.dst, osmo_hexparse(ie->val, gtlv.dst->tail, msgb_tailroom(gtlv.dst)));
|
||||
/* update header len from amount of written data */
|
||||
OSMO_ASSERT(osmo_gtlv_put_update_tl(>lv) == 0);
|
||||
}
|
||||
|
||||
printf("- encoded: %s.\n", osmo_hexdump(gtlv.dst->data, gtlv.dst->len));
|
||||
return gtlv.dst;
|
||||
}
|
||||
|
||||
/* read all IEs from the msgb, and verify that it matches the given list of IEs */
|
||||
void test_tlv_dec(const struct osmo_gtlv_cfg *cfg, const struct ie *ies, struct msgb *msg)
|
||||
{
|
||||
const struct ie *ie;
|
||||
struct osmo_gtlv_load gtlv = {
|
||||
.cfg = cfg,
|
||||
.src = { msg->data, msg->len },
|
||||
};
|
||||
|
||||
printf("- decoding:\n");
|
||||
osmo_gtlv_load_start(>lv);
|
||||
|
||||
for (ie = ies; ie->val; ie++) {
|
||||
int rc = osmo_gtlv_load_next(>lv);
|
||||
if (rc) {
|
||||
printf(" ERROR loading TLV structure: osmo_gtlv_load_next() rc = %d\n", rc);
|
||||
exit(1);
|
||||
}
|
||||
/* end of TLV structure? */
|
||||
if (!gtlv.val)
|
||||
break;
|
||||
printf(" T=%s L=%zu", osmo_gtlv_tag_inst_to_str_c(ctx, >lv.ti, NULL), gtlv.len);
|
||||
printf(" v=%s\n", osmo_hexdump_nospc(gtlv.val, gtlv.len));
|
||||
if (gtlv.ti.tag != ie->ti.tag) {
|
||||
printf(" ERROR loading TLV structure: expected tag %u, got tag %u\n", ie->ti.tag, gtlv.ti.tag);
|
||||
exit(1);
|
||||
}
|
||||
if (strcmp(ie->val, osmo_hexdump_nospc(gtlv.val, gtlv.len))) {
|
||||
printf(" ERROR loading TLV structure: expected val %s, got val %s\n", ie->val,
|
||||
osmo_hexdump_nospc(gtlv.val, gtlv.len));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void test_tlv_peek(const struct osmo_gtlv_cfg *cfg, const struct ie *ies, struct msgb *msg)
|
||||
{
|
||||
const struct ie *ie;
|
||||
struct osmo_gtlv_load gtlv = {
|
||||
.cfg = cfg,
|
||||
.src = { msg->data, msg->len },
|
||||
};
|
||||
|
||||
printf("- peeking:\n");
|
||||
osmo_gtlv_load_start(>lv);
|
||||
|
||||
ie = ies;
|
||||
while (1) {
|
||||
int rc;
|
||||
struct osmo_gtlv_tag_inst next_tag;
|
||||
rc = osmo_gtlv_load_peek_tag(>lv, &next_tag);
|
||||
if (rc == -ENOENT) {
|
||||
printf(" peek rc=-ENOENT\n");
|
||||
} else {
|
||||
printf(" peek T=%s", osmo_gtlv_tag_inst_to_str_c(ctx, &next_tag, NULL));
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if (ie->val && osmo_gtlv_tag_inst_cmp(&next_tag, &ie->ti)) {
|
||||
printf(" ERROR peeking tag: expected tag %s, got tag %s\n",
|
||||
osmo_gtlv_tag_inst_to_str_c(ctx, &ie->ti, NULL),
|
||||
osmo_gtlv_tag_inst_to_str_c(ctx, &next_tag, NULL));
|
||||
exit(1);
|
||||
}
|
||||
if (!ie->val && rc != -ENOENT) {
|
||||
printf(" ERROR peeking tag: expected -ENOENT, got rc=%d, tag %s\n", rc,
|
||||
osmo_gtlv_tag_inst_to_str_c(ctx, &next_tag, NULL));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (rc == -ENOENT)
|
||||
break;
|
||||
|
||||
/* go to the next TLV */
|
||||
rc = osmo_gtlv_load_next(>lv);
|
||||
if (rc) {
|
||||
printf(" ERROR loading TLV structure: osmo_gtlv_load_next() rc = %d\n", rc);
|
||||
exit(1);
|
||||
}
|
||||
if (ie->val)
|
||||
ie++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Decode TLV in random order, each time searching for a tag in the raw data */
|
||||
void test_tlv_dec_by_tag(const struct osmo_gtlv_cfg *cfg, const struct ie *ies, struct msgb *msg)
|
||||
{
|
||||
const struct ie *last_ie;
|
||||
const struct ie *ie;
|
||||
int rc;
|
||||
struct osmo_gtlv_load gtlv = {
|
||||
.cfg = cfg,
|
||||
.src = { msg->data, msg->len },
|
||||
};
|
||||
|
||||
printf("- decoding in reverse order:\n");
|
||||
|
||||
last_ie = ies;
|
||||
while (last_ie->val) last_ie++;
|
||||
last_ie--;
|
||||
|
||||
for (ie = last_ie; ie >= ies; ie--) {
|
||||
/* each time, look from the beginning */
|
||||
osmo_gtlv_load_start(>lv);
|
||||
rc = osmo_gtlv_load_next_by_tag_inst(>lv, &ie->ti);
|
||||
if (rc) {
|
||||
printf(" ERROR loading TLV structure: osmo_gtlv_load_next_by_tag_inst(%s) rc = %d\n",
|
||||
osmo_gtlv_tag_inst_to_str_c(ctx, &ie->ti, NULL), rc);
|
||||
exit(1);
|
||||
}
|
||||
if (!gtlv.val) {
|
||||
printf(" ERROR loading TLV structure: osmo_gtlv_load_next_by_tag_inst(%s) returned NULL val\n",
|
||||
osmo_gtlv_tag_inst_to_str_c(ctx, &ie->ti, NULL));
|
||||
exit(1);
|
||||
}
|
||||
if (osmo_gtlv_tag_inst_cmp(>lv.ti, &ie->ti)) {
|
||||
printf(" ERROR loading TLV structure: expected tag %s, got tag %s\n",
|
||||
osmo_gtlv_tag_inst_to_str_c(ctx, &ie->ti, NULL),
|
||||
osmo_gtlv_tag_inst_to_str_c(ctx, >lv.ti, NULL));
|
||||
exit(1);
|
||||
}
|
||||
if (strcmp(ie->val, osmo_hexdump_nospc(gtlv.val, gtlv.len))) {
|
||||
while (1) {
|
||||
printf(" (mismatch: T=%s L=%zu v=%s, checking for another occurrence of T=%s)\n",
|
||||
osmo_gtlv_tag_inst_to_str_c(ctx, >lv.ti, NULL),
|
||||
gtlv.len,
|
||||
osmo_hexdump_nospc(gtlv.val, gtlv.len),
|
||||
osmo_gtlv_tag_inst_to_str_c(ctx, >lv.ti, NULL));
|
||||
|
||||
rc = osmo_gtlv_load_next_by_tag_inst(>lv, &ie->ti);
|
||||
if (rc || !gtlv.val) {
|
||||
printf(" ERROR val not found\n");
|
||||
exit(1);
|
||||
}
|
||||
if (strcmp(ie->val, osmo_hexdump_nospc(gtlv.val, gtlv.len)) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
printf(" T=%s L=%zu v=%s\n",
|
||||
osmo_gtlv_tag_inst_to_str_c(ctx, >lv.ti, NULL),
|
||||
gtlv.len, osmo_hexdump_nospc(gtlv.val, gtlv.len));
|
||||
}
|
||||
|
||||
printf("- decoding every second tag:\n");
|
||||
|
||||
osmo_gtlv_load_start(>lv);
|
||||
for (ie = ies; ie->val; ie++) {
|
||||
/* skip one tag */
|
||||
ie++;
|
||||
if (!ie->val)
|
||||
break;
|
||||
|
||||
rc = osmo_gtlv_load_next_by_tag_inst(>lv, &ie->ti);
|
||||
if (rc) {
|
||||
printf(" ERROR loading TLV structure: osmo_gtlv_load_next_by_tag_inst(%s) rc = %d\n",
|
||||
osmo_gtlv_tag_inst_to_str_c(ctx, &ie->ti, NULL), rc);
|
||||
exit(1);
|
||||
}
|
||||
if (!gtlv.val) {
|
||||
printf(" ERROR loading TLV structure: osmo_gtlv_load_next_by_tag_inst(%s) returned NULL val\n",
|
||||
osmo_gtlv_tag_inst_to_str_c(ctx, &ie->ti, NULL));
|
||||
exit(1);
|
||||
}
|
||||
if (osmo_gtlv_tag_inst_cmp(>lv.ti, &ie->ti)) {
|
||||
printf(" ERROR loading TLV structure: expected tag %s, got tag %s\n",
|
||||
osmo_gtlv_tag_inst_to_str_c(ctx, &ie->ti, NULL),
|
||||
osmo_gtlv_tag_inst_to_str_c(ctx, >lv.ti, NULL));
|
||||
exit(1);
|
||||
}
|
||||
if (strcmp(ie->val, osmo_hexdump_nospc(gtlv.val, gtlv.len))) {
|
||||
while (1) {
|
||||
printf(" (mismatch: T=%s L=%zu v=%s, checking for another occurrence of T=%s)\n",
|
||||
osmo_gtlv_tag_inst_to_str_c(ctx, >lv.ti, NULL),
|
||||
gtlv.len,
|
||||
osmo_hexdump_nospc(gtlv.val, gtlv.len),
|
||||
osmo_gtlv_tag_inst_to_str_c(ctx, >lv.ti, NULL));
|
||||
|
||||
rc = osmo_gtlv_load_next_by_tag_inst(>lv, &ie->ti);
|
||||
if (rc || !gtlv.val) {
|
||||
printf(" ERROR val not found\n");
|
||||
exit(1);
|
||||
}
|
||||
if (strcmp(ie->val, osmo_hexdump_nospc(gtlv.val, gtlv.len)) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
printf(" T=%s L=%zu v=%s\n",
|
||||
osmo_gtlv_tag_inst_to_str_c(ctx, >lv.ti, NULL),
|
||||
gtlv.len, osmo_hexdump_nospc(gtlv.val, gtlv.len));
|
||||
}
|
||||
|
||||
printf("- enforcing order: without restart, a past tag is not parsed again:\n");
|
||||
/* Try to read the first tag, expect that it isn't found because we're already halfway in the message data */
|
||||
ie = ies;
|
||||
rc = osmo_gtlv_load_next_by_tag_inst(>lv, &ie->ti);
|
||||
printf(" osmo_gtlv_load_next_by_tag_inst(%s) rc=", osmo_gtlv_tag_inst_to_str_c(ctx, &ie->ti, NULL));
|
||||
if (rc == -ENOENT) {
|
||||
printf("-ENOENT\n");
|
||||
} else {
|
||||
printf("%d\n", rc);
|
||||
printf(" ERROR: expected -ENOENT\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void test_tlv(const char *label, struct ie *tests[], size_t tests_len, const struct osmo_gtlv_cfg *cfg)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < tests_len; i++) {
|
||||
const struct ie *ies = tests[i];
|
||||
struct msgb *msg;
|
||||
printf("\n=== start: %s[%d]\n", label, i);
|
||||
|
||||
msg = test_tlv_enc(cfg, ies);
|
||||
test_tlv_dec(cfg, ies, msg);
|
||||
test_tlv_peek(cfg, ies, msg);
|
||||
test_tlv_dec_by_tag(cfg, ies, msg);
|
||||
|
||||
msgb_free(msg);
|
||||
|
||||
printf("=== end: %s[%d]\n", label, i);
|
||||
}
|
||||
}
|
||||
|
||||
struct ie t8l8v_test1[] = {
|
||||
/* smallest T */
|
||||
{ {}, "2342" },
|
||||
/* largest T */
|
||||
{ {255}, "2342" },
|
||||
|
||||
/* smallest V (no V data) */
|
||||
{ {1}, "" },
|
||||
/* largest V, 255 bytes is the largest that an 8bit size length can express. */
|
||||
{ {123}, "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||
},
|
||||
|
||||
/* arbitrary test data */
|
||||
{ {101}, "11" },
|
||||
{ {102}, "2222" },
|
||||
{ {103}, "333333" },
|
||||
{}
|
||||
};
|
||||
|
||||
struct ie t8l8v_test_multi[] = {
|
||||
{ {42}, "42" },
|
||||
{ {2}, "0101" },
|
||||
{ {2}, "2222" },
|
||||
{ {3}, "11" },
|
||||
{ {3}, "2222" },
|
||||
{ {3}, "333333" },
|
||||
{ {23}, "23" },
|
||||
{ {42}, "666f72747974776f" },
|
||||
{ {23}, "7477656e74797468726565" },
|
||||
{}
|
||||
};
|
||||
|
||||
struct ie *t8l8v_tests[] = {
|
||||
t8l8v_test1,
|
||||
t8l8v_test_multi,
|
||||
};
|
||||
|
||||
void test_t8l8v()
|
||||
{
|
||||
test_tlv(__func__, t8l8v_tests, ARRAY_SIZE(t8l8v_tests), &osmo_t8l8v_cfg);
|
||||
}
|
||||
|
||||
struct ie t16l16v_test1[] = {
|
||||
/* smallest T */
|
||||
{ {}, "2342" },
|
||||
/* largest T */
|
||||
{ {65535}, "2342" },
|
||||
|
||||
/* smallest V (no V data) */
|
||||
{ {1}, "" },
|
||||
/* 256 bytes is one more than an 8bit size length can express. */
|
||||
{ {123}, "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
},
|
||||
|
||||
/* arbitrary test data */
|
||||
{ {1001}, "11" },
|
||||
{ {1002}, "2222" },
|
||||
{ {1003}, "333333" },
|
||||
{}
|
||||
};
|
||||
|
||||
struct ie t16l16v_test_multi[] = {
|
||||
{ {1042}, "42" },
|
||||
{ {102}, "0101" },
|
||||
{ {102}, "2222" },
|
||||
{ {103}, "11" },
|
||||
{ {103}, "2222" },
|
||||
{ {103}, "333333" },
|
||||
{ {1023}, "23" },
|
||||
{ {1042}, "666f72747974776f" },
|
||||
{ {1023}, "7477656e74797468726565" },
|
||||
{}
|
||||
};
|
||||
|
||||
struct ie *t16l16v_tests[] = {
|
||||
t16l16v_test1,
|
||||
t16l16v_test_multi,
|
||||
};
|
||||
|
||||
void test_t16l16v()
|
||||
{
|
||||
test_tlv(__func__, t16l16v_tests, ARRAY_SIZE(t16l16v_tests), &osmo_t16l16v_cfg);
|
||||
}
|
||||
|
||||
struct ie txlxv_test1[] = {
|
||||
/* smallest T */
|
||||
{ {}, "2342" },
|
||||
/* largest T that still fits in one encoded octet (highest bit serves as flag) */
|
||||
{ {0x7f}, "2342" },
|
||||
/* smallest T that needs two octets to be encoded (first octet = 0x80 flag + 0, second octet = 0x1) */
|
||||
{ {0x80}, "2342" },
|
||||
/* largest T that can be encoded in 16bit - one flag bit. */
|
||||
{ {0x7fff}, "2342" },
|
||||
|
||||
/* smallest V (no V data) */
|
||||
{ {1}, "" },
|
||||
/* 256 bytes is one more than an 8bit size length can express. */
|
||||
{ {123}, "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
},
|
||||
|
||||
/* arbitrary test data */
|
||||
{ {1002}, "2222" },
|
||||
{ {1003}, "333333" },
|
||||
{}
|
||||
};
|
||||
|
||||
struct ie txlxv_test_multi[] = {
|
||||
{ {1042}, "42" },
|
||||
{ {1002}, "0101" },
|
||||
{ {1002}, "2222" },
|
||||
{ {103}, "11" },
|
||||
{ {103}, "2222" },
|
||||
{ {103}, "333333" },
|
||||
{ {1023}, "23" },
|
||||
{ {1042}, "666f72747974776f" },
|
||||
{ {1023}, "7477656e74797468726565" },
|
||||
{}
|
||||
};
|
||||
|
||||
struct ie *txlxv_tests[] = {
|
||||
txlxv_test1,
|
||||
txlxv_test_multi,
|
||||
};
|
||||
|
||||
/* Example of defining a variable TL, where size of T and L depend on the actual tag and length values: load. */
|
||||
int txlxv_load_tl(struct osmo_gtlv_load *gtlv, const uint8_t *src_data, size_t src_data_len)
|
||||
{
|
||||
const uint8_t *pos = src_data;
|
||||
const uint8_t *end = src_data + src_data_len;
|
||||
if (pos[0] & 0x80) {
|
||||
if (pos + 2 > end)
|
||||
return -EINVAL;
|
||||
gtlv->ti.tag = (((int)pos[1]) << 7) + (pos[0] & 0x7f);
|
||||
pos += 2;
|
||||
} else {
|
||||
gtlv->ti.tag = pos[0];
|
||||
pos++;
|
||||
}
|
||||
|
||||
switch (gtlv->ti.tag) {
|
||||
case 1002:
|
||||
/* fixed-length IE */
|
||||
gtlv->len = 2;
|
||||
break;
|
||||
case 123:
|
||||
/* 16bit length IE */
|
||||
if (pos + 2 > end)
|
||||
return -EINVAL;
|
||||
gtlv->len = osmo_load16be(pos);
|
||||
pos += 2;
|
||||
break;
|
||||
default:
|
||||
/* 8bit length IE */
|
||||
if (pos + 1 > end)
|
||||
return -EINVAL;
|
||||
gtlv->len = *pos;
|
||||
pos++;
|
||||
break;
|
||||
}
|
||||
gtlv->val = pos;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Example of defining a variable TL, where size of T and L depend on the actual tag and length values: store. */
|
||||
int txlxv_store_tl(uint8_t *dst_data, size_t dst_data_avail, const struct osmo_gtlv_tag_inst *ti, size_t len,
|
||||
struct osmo_gtlv_put *gtlv)
|
||||
{
|
||||
uint8_t *pos = dst_data;
|
||||
uint8_t *end = dst_data + dst_data_avail;
|
||||
unsigned int tag = ti->tag;
|
||||
if (tag < 0x80) {
|
||||
if (pos + 1 > end)
|
||||
return -ENOSPC;
|
||||
pos[0] = tag;
|
||||
pos++;
|
||||
} else {
|
||||
if (pos + 2 > end)
|
||||
return -ENOSPC;
|
||||
pos[0] = 0x80 + (tag & 0x7f);
|
||||
pos[1] = tag >> 7;
|
||||
pos += 2;
|
||||
}
|
||||
|
||||
switch (tag) {
|
||||
case 1002:
|
||||
/* fixed-length IE, write no len */
|
||||
break;
|
||||
case 123:
|
||||
/* 16bit length IE */
|
||||
if (len > UINT16_MAX)
|
||||
return -ERANGE;
|
||||
if (pos + 2 > end)
|
||||
return -ENOSPC;
|
||||
osmo_store16be(len, pos);
|
||||
pos += 2;
|
||||
break;
|
||||
default:
|
||||
/* 8bit length IE */
|
||||
if (len > UINT8_MAX)
|
||||
return -ERANGE;
|
||||
if (pos + 1 > end)
|
||||
return -ENOSPC;
|
||||
pos[0] = len;
|
||||
pos++;
|
||||
break;
|
||||
}
|
||||
return pos - dst_data;
|
||||
}
|
||||
|
||||
const struct osmo_gtlv_cfg txlxv_cfg = {
|
||||
.tl_min_size = 1,
|
||||
.load_tl = txlxv_load_tl,
|
||||
.store_tl = txlxv_store_tl,
|
||||
};
|
||||
|
||||
void test_txlxv()
|
||||
{
|
||||
test_tlv(__func__, txlxv_tests, ARRAY_SIZE(txlxv_tests), &txlxv_cfg);
|
||||
}
|
||||
|
||||
/* Example of defining a TLI, with an instance indicator */
|
||||
static int tliv_load_tl(struct osmo_gtlv_load *gtlv, const uint8_t *src_data, size_t src_data_len)
|
||||
{
|
||||
/* already validated in next_tl_valid(): src_data_len >= cfg->tl_min_size == 2. */
|
||||
gtlv->ti.tag = src_data[0];
|
||||
gtlv->len = src_data[1];
|
||||
|
||||
switch (gtlv->ti.tag) {
|
||||
/* All tags that are TLIV go here */
|
||||
case 5:
|
||||
case 7:
|
||||
case 9:
|
||||
if (src_data_len < 3)
|
||||
return -ENOSPC;
|
||||
gtlv->ti.instance_present = true;
|
||||
gtlv->ti.instance = src_data[2];
|
||||
gtlv->val = src_data + 3;
|
||||
return 0;
|
||||
default:
|
||||
gtlv->val = src_data + 2;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int tliv_store_tl(uint8_t *dst_data, size_t dst_data_avail, const struct osmo_gtlv_tag_inst *ti, size_t len,
|
||||
struct osmo_gtlv_put *gtlv)
|
||||
{
|
||||
if (ti->tag > UINT8_MAX)
|
||||
return -EINVAL;
|
||||
if (len > UINT8_MAX)
|
||||
return -EMSGSIZE;
|
||||
if (dst_data_avail < 2)
|
||||
return -ENOSPC;
|
||||
|
||||
dst_data[0] = ti->tag;
|
||||
dst_data[1] = len;
|
||||
|
||||
switch (ti->tag) {
|
||||
/* All tags that are TLIV go here */
|
||||
case 5:
|
||||
case 7:
|
||||
case 9:
|
||||
if (dst_data_avail < 3)
|
||||
return -ENOSPC;
|
||||
if (!ti->instance_present)
|
||||
return -EINVAL;
|
||||
if (ti->instance > UINT8_MAX)
|
||||
return -EINVAL;
|
||||
dst_data[2] = ti->instance;
|
||||
return 3;
|
||||
default:
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
const struct osmo_gtlv_cfg osmo_tliv_cfg = {
|
||||
.tl_min_size = 2,
|
||||
.load_tl = tliv_load_tl,
|
||||
.store_tl = tliv_store_tl,
|
||||
};
|
||||
|
||||
struct ie tliv_test1[] = {
|
||||
/* TLV */
|
||||
{ {1}, "0002" },
|
||||
/* TLIV */
|
||||
{ {5, true, 1}, "0017" },
|
||||
/* TLIV */
|
||||
{ {5, true, 2}, "0018" },
|
||||
/* TLIV */
|
||||
{ {5, true, 3}, "0019" },
|
||||
/* TLV */
|
||||
{ {6}, "001a" },
|
||||
/* TLIV */
|
||||
{ {7, true, 1}, "001b" },
|
||||
/* TLIV */
|
||||
{ {9, true, 1}, "001c" },
|
||||
{}
|
||||
};
|
||||
|
||||
struct ie *tliv_tests[] = {
|
||||
tliv_test1,
|
||||
};
|
||||
|
||||
void test_tliv()
|
||||
{
|
||||
test_tlv(__func__, tliv_tests, ARRAY_SIZE(tliv_tests), &osmo_tliv_cfg);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
ctx = talloc_named_const(NULL, 0, "gtlv_test");
|
||||
msgb_talloc_ctx_init(ctx, 0);
|
||||
|
||||
test_t8l8v();
|
||||
test_t16l16v();
|
||||
test_txlxv();
|
||||
test_tliv();
|
||||
|
||||
talloc_free(ctx);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,291 +0,0 @@
|
||||
|
||||
=== start: test_t8l8v[0]
|
||||
- encoded: 00 02 23 42 ff 02 23 42 01 00 7b ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 65 01 11 66 02 22 22 67 03 33 33 33 .
|
||||
- decoding:
|
||||
T=0 L=2 v=2342
|
||||
T=255 L=2 v=2342
|
||||
T=1 L=0 v=
|
||||
T=123 L=255 v=ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
T=101 L=1 v=11
|
||||
T=102 L=2 v=2222
|
||||
T=103 L=3 v=333333
|
||||
- peeking:
|
||||
peek T=0
|
||||
peek T=255
|
||||
peek T=1
|
||||
peek T=123
|
||||
peek T=101
|
||||
peek T=102
|
||||
peek T=103
|
||||
peek rc=-ENOENT
|
||||
- decoding in reverse order:
|
||||
T=103 L=3 v=333333
|
||||
T=102 L=2 v=2222
|
||||
T=101 L=1 v=11
|
||||
T=123 L=255 v=ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
T=1 L=0 v=
|
||||
T=255 L=2 v=2342
|
||||
T=0 L=2 v=2342
|
||||
- decoding every second tag:
|
||||
T=255 L=2 v=2342
|
||||
T=123 L=255 v=ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
T=102 L=2 v=2222
|
||||
- enforcing order: without restart, a past tag is not parsed again:
|
||||
osmo_gtlv_load_next_by_tag_inst(0) rc=-ENOENT
|
||||
=== end: test_t8l8v[0]
|
||||
|
||||
=== start: test_t8l8v[1]
|
||||
- encoded: 2a 01 42 02 02 01 01 02 02 22 22 03 01 11 03 02 22 22 03 03 33 33 33 17 01 23 2a 08 66 6f 72 74 79 74 77 6f 17 0b 74 77 65 6e 74 79 74 68 72 65 65 .
|
||||
- decoding:
|
||||
T=42 L=1 v=42
|
||||
T=2 L=2 v=0101
|
||||
T=2 L=2 v=2222
|
||||
T=3 L=1 v=11
|
||||
T=3 L=2 v=2222
|
||||
T=3 L=3 v=333333
|
||||
T=23 L=1 v=23
|
||||
T=42 L=8 v=666f72747974776f
|
||||
T=23 L=11 v=7477656e74797468726565
|
||||
- peeking:
|
||||
peek T=42
|
||||
peek T=2
|
||||
peek T=2
|
||||
peek T=3
|
||||
peek T=3
|
||||
peek T=3
|
||||
peek T=23
|
||||
peek T=42
|
||||
peek T=23
|
||||
peek rc=-ENOENT
|
||||
- decoding in reverse order:
|
||||
(mismatch: T=23 L=1 v=23, checking for another occurrence of T=23)
|
||||
T=23 L=11 v=7477656e74797468726565
|
||||
(mismatch: T=42 L=1 v=42, checking for another occurrence of T=42)
|
||||
T=42 L=8 v=666f72747974776f
|
||||
T=23 L=1 v=23
|
||||
(mismatch: T=3 L=1 v=11, checking for another occurrence of T=3)
|
||||
(mismatch: T=3 L=2 v=2222, checking for another occurrence of T=3)
|
||||
T=3 L=3 v=333333
|
||||
(mismatch: T=3 L=1 v=11, checking for another occurrence of T=3)
|
||||
T=3 L=2 v=2222
|
||||
T=3 L=1 v=11
|
||||
(mismatch: T=2 L=2 v=0101, checking for another occurrence of T=2)
|
||||
T=2 L=2 v=2222
|
||||
T=2 L=2 v=0101
|
||||
T=42 L=1 v=42
|
||||
- decoding every second tag:
|
||||
T=2 L=2 v=0101
|
||||
T=3 L=1 v=11
|
||||
(mismatch: T=3 L=2 v=2222, checking for another occurrence of T=3)
|
||||
T=3 L=3 v=333333
|
||||
T=42 L=8 v=666f72747974776f
|
||||
- enforcing order: without restart, a past tag is not parsed again:
|
||||
osmo_gtlv_load_next_by_tag_inst(42) rc=-ENOENT
|
||||
=== end: test_t8l8v[1]
|
||||
|
||||
=== start: test_t16l16v[0]
|
||||
- encoded: 00 00 00 02 23 42 ff ff 00 02 23 42 00 01 00 00 00 7b 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 e9 00 01 11 03 ea 00 02 22 22 03 eb 00 03 33 33 33 .
|
||||
- decoding:
|
||||
T=0 L=2 v=2342
|
||||
T=65535 L=2 v=2342
|
||||
T=1 L=0 v=
|
||||
T=123 L=256 v=00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
T=1001 L=1 v=11
|
||||
T=1002 L=2 v=2222
|
||||
T=1003 L=3 v=333333
|
||||
- peeking:
|
||||
peek T=0
|
||||
peek T=65535
|
||||
peek T=1
|
||||
peek T=123
|
||||
peek T=1001
|
||||
peek T=1002
|
||||
peek T=1003
|
||||
peek rc=-ENOENT
|
||||
- decoding in reverse order:
|
||||
T=1003 L=3 v=333333
|
||||
T=1002 L=2 v=2222
|
||||
T=1001 L=1 v=11
|
||||
T=123 L=256 v=00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
T=1 L=0 v=
|
||||
T=65535 L=2 v=2342
|
||||
T=0 L=2 v=2342
|
||||
- decoding every second tag:
|
||||
T=65535 L=2 v=2342
|
||||
T=123 L=256 v=00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
T=1002 L=2 v=2222
|
||||
- enforcing order: without restart, a past tag is not parsed again:
|
||||
osmo_gtlv_load_next_by_tag_inst(0) rc=-ENOENT
|
||||
=== end: test_t16l16v[0]
|
||||
|
||||
=== start: test_t16l16v[1]
|
||||
- encoded: 04 12 00 01 42 00 66 00 02 01 01 00 66 00 02 22 22 00 67 00 01 11 00 67 00 02 22 22 00 67 00 03 33 33 33 03 ff 00 01 23 04 12 00 08 66 6f 72 74 79 74 77 6f 03 ff 00 0b 74 77 65 6e 74 79 74 68 72 65 65 .
|
||||
- decoding:
|
||||
T=1042 L=1 v=42
|
||||
T=102 L=2 v=0101
|
||||
T=102 L=2 v=2222
|
||||
T=103 L=1 v=11
|
||||
T=103 L=2 v=2222
|
||||
T=103 L=3 v=333333
|
||||
T=1023 L=1 v=23
|
||||
T=1042 L=8 v=666f72747974776f
|
||||
T=1023 L=11 v=7477656e74797468726565
|
||||
- peeking:
|
||||
peek T=1042
|
||||
peek T=102
|
||||
peek T=102
|
||||
peek T=103
|
||||
peek T=103
|
||||
peek T=103
|
||||
peek T=1023
|
||||
peek T=1042
|
||||
peek T=1023
|
||||
peek rc=-ENOENT
|
||||
- decoding in reverse order:
|
||||
(mismatch: T=1023 L=1 v=23, checking for another occurrence of T=1023)
|
||||
T=1023 L=11 v=7477656e74797468726565
|
||||
(mismatch: T=1042 L=1 v=42, checking for another occurrence of T=1042)
|
||||
T=1042 L=8 v=666f72747974776f
|
||||
T=1023 L=1 v=23
|
||||
(mismatch: T=103 L=1 v=11, checking for another occurrence of T=103)
|
||||
(mismatch: T=103 L=2 v=2222, checking for another occurrence of T=103)
|
||||
T=103 L=3 v=333333
|
||||
(mismatch: T=103 L=1 v=11, checking for another occurrence of T=103)
|
||||
T=103 L=2 v=2222
|
||||
T=103 L=1 v=11
|
||||
(mismatch: T=102 L=2 v=0101, checking for another occurrence of T=102)
|
||||
T=102 L=2 v=2222
|
||||
T=102 L=2 v=0101
|
||||
T=1042 L=1 v=42
|
||||
- decoding every second tag:
|
||||
T=102 L=2 v=0101
|
||||
T=103 L=1 v=11
|
||||
(mismatch: T=103 L=2 v=2222, checking for another occurrence of T=103)
|
||||
T=103 L=3 v=333333
|
||||
T=1042 L=8 v=666f72747974776f
|
||||
- enforcing order: without restart, a past tag is not parsed again:
|
||||
osmo_gtlv_load_next_by_tag_inst(1042) rc=-ENOENT
|
||||
=== end: test_t16l16v[1]
|
||||
|
||||
=== start: test_txlxv[0]
|
||||
- encoded: 00 02 23 42 7f 02 23 42 80 01 02 23 42 ff ff 02 23 42 01 00 7b 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ea 07 22 22 eb 07 03 33 33 33 .
|
||||
- decoding:
|
||||
T=0 L=2 v=2342
|
||||
T=127 L=2 v=2342
|
||||
T=128 L=2 v=2342
|
||||
T=32767 L=2 v=2342
|
||||
T=1 L=0 v=
|
||||
T=123 L=256 v=00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
T=1002 L=2 v=2222
|
||||
T=1003 L=3 v=333333
|
||||
- peeking:
|
||||
peek T=0
|
||||
peek T=127
|
||||
peek T=128
|
||||
peek T=32767
|
||||
peek T=1
|
||||
peek T=123
|
||||
peek T=1002
|
||||
peek T=1003
|
||||
peek rc=-ENOENT
|
||||
- decoding in reverse order:
|
||||
T=1003 L=3 v=333333
|
||||
T=1002 L=2 v=2222
|
||||
T=123 L=256 v=00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
T=1 L=0 v=
|
||||
T=32767 L=2 v=2342
|
||||
T=128 L=2 v=2342
|
||||
T=127 L=2 v=2342
|
||||
T=0 L=2 v=2342
|
||||
- decoding every second tag:
|
||||
T=127 L=2 v=2342
|
||||
T=32767 L=2 v=2342
|
||||
T=123 L=256 v=00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
T=1003 L=3 v=333333
|
||||
- enforcing order: without restart, a past tag is not parsed again:
|
||||
osmo_gtlv_load_next_by_tag_inst(0) rc=-ENOENT
|
||||
=== end: test_txlxv[0]
|
||||
|
||||
=== start: test_txlxv[1]
|
||||
- encoded: 92 08 01 42 ea 07 01 01 ea 07 22 22 67 01 11 67 02 22 22 67 03 33 33 33 ff 07 01 23 92 08 08 66 6f 72 74 79 74 77 6f ff 07 0b 74 77 65 6e 74 79 74 68 72 65 65 .
|
||||
- decoding:
|
||||
T=1042 L=1 v=42
|
||||
T=1002 L=2 v=0101
|
||||
T=1002 L=2 v=2222
|
||||
T=103 L=1 v=11
|
||||
T=103 L=2 v=2222
|
||||
T=103 L=3 v=333333
|
||||
T=1023 L=1 v=23
|
||||
T=1042 L=8 v=666f72747974776f
|
||||
T=1023 L=11 v=7477656e74797468726565
|
||||
- peeking:
|
||||
peek T=1042
|
||||
peek T=1002
|
||||
peek T=1002
|
||||
peek T=103
|
||||
peek T=103
|
||||
peek T=103
|
||||
peek T=1023
|
||||
peek T=1042
|
||||
peek T=1023
|
||||
peek rc=-ENOENT
|
||||
- decoding in reverse order:
|
||||
(mismatch: T=1023 L=1 v=23, checking for another occurrence of T=1023)
|
||||
T=1023 L=11 v=7477656e74797468726565
|
||||
(mismatch: T=1042 L=1 v=42, checking for another occurrence of T=1042)
|
||||
T=1042 L=8 v=666f72747974776f
|
||||
T=1023 L=1 v=23
|
||||
(mismatch: T=103 L=1 v=11, checking for another occurrence of T=103)
|
||||
(mismatch: T=103 L=2 v=2222, checking for another occurrence of T=103)
|
||||
T=103 L=3 v=333333
|
||||
(mismatch: T=103 L=1 v=11, checking for another occurrence of T=103)
|
||||
T=103 L=2 v=2222
|
||||
T=103 L=1 v=11
|
||||
(mismatch: T=1002 L=2 v=0101, checking for another occurrence of T=1002)
|
||||
T=1002 L=2 v=2222
|
||||
T=1002 L=2 v=0101
|
||||
T=1042 L=1 v=42
|
||||
- decoding every second tag:
|
||||
T=1002 L=2 v=0101
|
||||
T=103 L=1 v=11
|
||||
(mismatch: T=103 L=2 v=2222, checking for another occurrence of T=103)
|
||||
T=103 L=3 v=333333
|
||||
T=1042 L=8 v=666f72747974776f
|
||||
- enforcing order: without restart, a past tag is not parsed again:
|
||||
osmo_gtlv_load_next_by_tag_inst(1042) rc=-ENOENT
|
||||
=== end: test_txlxv[1]
|
||||
|
||||
=== start: test_tliv[0]
|
||||
- encoded: 01 02 00 02 05 02 01 00 17 05 02 02 00 18 05 02 03 00 19 06 02 00 1a 07 02 01 00 1b 09 02 01 00 1c .
|
||||
- decoding:
|
||||
T=1 L=2 v=0002
|
||||
T=5[1] L=2 v=0017
|
||||
T=5[2] L=2 v=0018
|
||||
T=5[3] L=2 v=0019
|
||||
T=6 L=2 v=001a
|
||||
T=7[1] L=2 v=001b
|
||||
T=9[1] L=2 v=001c
|
||||
- peeking:
|
||||
peek T=1
|
||||
peek T=5[1]
|
||||
peek T=5[2]
|
||||
peek T=5[3]
|
||||
peek T=6
|
||||
peek T=7[1]
|
||||
peek T=9[1]
|
||||
peek rc=-ENOENT
|
||||
- decoding in reverse order:
|
||||
T=9[1] L=2 v=001c
|
||||
T=7[1] L=2 v=001b
|
||||
T=6 L=2 v=001a
|
||||
T=5[3] L=2 v=0019
|
||||
T=5[2] L=2 v=0018
|
||||
T=5[1] L=2 v=0017
|
||||
T=1 L=2 v=0002
|
||||
- decoding every second tag:
|
||||
T=5[1] L=2 v=0017
|
||||
T=5[3] L=2 v=0019
|
||||
T=7[1] L=2 v=001b
|
||||
- enforcing order: without restart, a past tag is not parsed again:
|
||||
osmo_gtlv_load_next_by_tag_inst(1) rc=-ENOENT
|
||||
=== end: test_tliv[0]
|
||||
@@ -1,60 +0,0 @@
|
||||
AM_CPPFLAGS = \
|
||||
$(all_includes) \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(bulddir) \
|
||||
$(NULL)
|
||||
|
||||
AM_CFLAGS = \
|
||||
-Wall \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
gen__myproto_ies_auto \
|
||||
gtlv_gen_test \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_DIST = \
|
||||
myproto_ies_custom.h \
|
||||
gtlv_gen_test.ok \
|
||||
$(NULL)
|
||||
|
||||
BUILT_SOURCES = \
|
||||
myproto_ies_auto.h \
|
||||
myproto_ies_auto.c \
|
||||
$(NULL)
|
||||
|
||||
CLEANFILES = \
|
||||
myproto_ies_auto.h \
|
||||
myproto_ies_auto.c \
|
||||
$(NULL)
|
||||
|
||||
gen__myproto_ies_auto_SOURCES = \
|
||||
gen__myproto_ies_auto.c \
|
||||
myproto_ies_custom.c \
|
||||
$(NULL)
|
||||
|
||||
gen__myproto_ies_auto_LDADD = \
|
||||
$(top_builddir)/src/libosmo-gtlv/libosmo-gtlv.a \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
myproto_ies_auto.h: $(builddir)/gen__myproto_ies_auto
|
||||
$(builddir)/gen__myproto_ies_auto h > $(builddir)/myproto_ies_auto.h
|
||||
myproto_ies_auto.c: $(builddir)/gen__myproto_ies_auto
|
||||
$(builddir)/gen__myproto_ies_auto c > $(builddir)/myproto_ies_auto.c
|
||||
|
||||
gtlv_gen_test_SOURCES = \
|
||||
gtlv_gen_test.c \
|
||||
myproto_ies_custom.c \
|
||||
myproto_ies_auto.c \
|
||||
$(NULL)
|
||||
|
||||
gtlv_gen_test_LDADD = \
|
||||
$(top_builddir)/src/libosmo-gtlv/libosmo-gtlv.a \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
.PHONY: update_exp
|
||||
update_exp:
|
||||
$(builddir)/gtlv_gen_test >$(srcdir)/gtlv_gen_test.ok
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/gtlv/gtlv_gen.h>
|
||||
|
||||
#define O OSMO_GTLV_GEN_O
|
||||
#define M OSMO_GTLV_GEN_M
|
||||
#define O_MULTI OSMO_GTLV_GEN_O_MULTI
|
||||
#define M_MULTI OSMO_GTLV_GEN_M_MULTI
|
||||
|
||||
#define ALL_FROM_NAME osmo_gtlv_gen_ie_auto
|
||||
|
||||
/* An IE where the type is not a 'struct myproto_ie_${name}'. */
|
||||
static const struct osmo_gtlv_gen_ie number = {
|
||||
.decoded_type = "int", /* add 'int foo;' to the struct */
|
||||
.dec_enc = "u16", /* use myproto_dec_u16() and myproto_enc_u16() for the TLV value part */
|
||||
.spec_ref = "an int coded as uint16_t",
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie_o ies_in_moo_nest[] = {
|
||||
/* Mandatory member xxx.foo of the type defined in 'number' above. */
|
||||
M(number, "foo"),
|
||||
/* Mandatory member xxx.bar of type 'struct myproto_ie_bar', using myproto_ie_dec_bar(), myproto_ie_enc_bar(),
|
||||
* myproto_ie_enc_to_str_bar(), all defined in myproto_ies_custom.h/c. */
|
||||
M(ALL_FROM_NAME, "bar"),
|
||||
M(ALL_FROM_NAME, "baz"),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie huge_number = {
|
||||
.decoded_type = "uint64_t",
|
||||
.dec_enc = "u64",
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie moo_nest = {
|
||||
.tag_name = "moo_nest",
|
||||
.nested_ies = ies_in_moo_nest,
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie_o ies_in_goo_nest[] = {
|
||||
O(huge_number, "val"),
|
||||
M(moo_nest, "nest"),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie goo_nest = {
|
||||
.tag_name = "goo_nest",
|
||||
.nested_ies = ies_in_goo_nest,
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie_o ies_in_moo_msg[] = {
|
||||
M(number, "foo"),
|
||||
M(ALL_FROM_NAME, "bar"),
|
||||
O(ALL_FROM_NAME, "baz"),
|
||||
O_MULTI(32, number, "repeat_int"),
|
||||
O_MULTI(32, ALL_FROM_NAME, "repeat_struct"),
|
||||
O(moo_nest, "nest"),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_ie_o ies_in_goo_msg[] = {
|
||||
M(number, "foo"),
|
||||
O(ALL_FROM_NAME, "bar"),
|
||||
O_MULTI(8, goo_nest, "nest"),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct osmo_gtlv_gen_msg msg_defs[] = {
|
||||
{ "moo", ies_in_moo_msg },
|
||||
{ "goo", ies_in_goo_msg },
|
||||
{}
|
||||
};
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
struct osmo_gtlv_gen_cfg cfg = {
|
||||
.proto_name = "myproto",
|
||||
.message_type_enum = "enum myproto_msg_type",
|
||||
.message_type_prefix = "MYPROTO_MSGT_",
|
||||
.tag_enum = "enum myproto_iei",
|
||||
.tag_prefix = "MYPROTO_IEI_",
|
||||
.decoded_type_prefix = "struct myproto_ie_",
|
||||
.h_header = "#include \"myproto_ies_custom.h\"",
|
||||
.c_header = "#include <myproto_ies_auto.h>",
|
||||
.msg_defs = msg_defs,
|
||||
.add_enc_to_str = true,
|
||||
};
|
||||
return osmo_gtlv_gen_main(&cfg, argc, argv);
|
||||
}
|
||||
@@ -1,261 +0,0 @@
|
||||
/*
|
||||
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
|
||||
#include <osmocom/gtlv/gtlv.h>
|
||||
|
||||
#include <myproto_ies_auto.h>
|
||||
|
||||
struct myproto_msg {
|
||||
enum myproto_msg_type type;
|
||||
union myproto_ies ies;
|
||||
};
|
||||
|
||||
static void err_cb(void *data, void *decoded_struct, const char *file, int line, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
//printf("ERR: %s:%d ", file, line);
|
||||
printf("ERR: ");
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static int myproto_msg_enc(struct msgb *dst, const struct myproto_msg *msg, const struct osmo_gtlv_cfg *cfg)
|
||||
{
|
||||
struct osmo_gtlv_put gtlv = {
|
||||
.cfg = cfg,
|
||||
.dst = dst,
|
||||
};
|
||||
|
||||
msgb_put_u8(gtlv.dst, msg->type);
|
||||
return myproto_ies_encode(>lv, (void *)&msg->ies, msg->type, err_cb, NULL, myproto_iei_names);
|
||||
}
|
||||
|
||||
static int myproto_msg_dec(struct myproto_msg *msg, const uint8_t *data, size_t data_len,
|
||||
const struct osmo_gtlv_cfg *cfg, bool ordered)
|
||||
{
|
||||
struct osmo_gtlv_load gtlv;
|
||||
if (data_len < 1)
|
||||
return -EINVAL;
|
||||
msg->type = data[0];
|
||||
gtlv = (struct osmo_gtlv_load){
|
||||
.cfg = cfg,
|
||||
.src = { data + 1, data_len - 1 },
|
||||
};
|
||||
return myproto_ies_decode(&msg->ies, >lv, ordered, msg->type, err_cb, NULL, myproto_iei_names);
|
||||
}
|
||||
|
||||
void *ctx;
|
||||
|
||||
struct myproto_msg tests[] = {
|
||||
{
|
||||
MYPROTO_MSGT_MOO,
|
||||
{
|
||||
.moo = {
|
||||
.foo = 23,
|
||||
.bar = { "twentythree" },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
MYPROTO_MSGT_MOO,
|
||||
{
|
||||
.moo = {
|
||||
.foo = 23,
|
||||
.bar = { "twentythree" },
|
||||
|
||||
.baz_present = true,
|
||||
.baz = {
|
||||
.v_int = 2323,
|
||||
.v_bool = true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
MYPROTO_MSGT_MOO,
|
||||
{
|
||||
.moo = {
|
||||
.foo = 23,
|
||||
.bar = { "twentythree" },
|
||||
|
||||
.baz_present = true,
|
||||
.baz = {
|
||||
.v_int = 2323,
|
||||
.v_bool = true,
|
||||
},
|
||||
|
||||
.repeat_int_count = 3,
|
||||
.repeat_int = { 1, 2, 0x7fff },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
MYPROTO_MSGT_MOO,
|
||||
{
|
||||
.moo = {
|
||||
.foo = 23,
|
||||
.bar = { "twentythree" },
|
||||
|
||||
.baz_present = true,
|
||||
.baz = {
|
||||
.v_int = 2323,
|
||||
.v_bool = true,
|
||||
},
|
||||
|
||||
.repeat_int_count = 3,
|
||||
.repeat_int = { 1, 2, 0x7fff },
|
||||
|
||||
.repeat_struct_count = 2,
|
||||
.repeat_struct = {
|
||||
{
|
||||
.v_int = 1001,
|
||||
.v_bool = true,
|
||||
.v_enum = R_A,
|
||||
},
|
||||
{
|
||||
.v_int = 1002,
|
||||
.v_bool = false,
|
||||
.v_enum = R_B,
|
||||
},
|
||||
},
|
||||
|
||||
.nest_present = true,
|
||||
.nest = {
|
||||
.foo = 42,
|
||||
.bar = { "fortytwo" },
|
||||
.baz = {
|
||||
.v_int = 4242,
|
||||
.v_bool = false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
MYPROTO_MSGT_GOO,
|
||||
{
|
||||
.goo = {
|
||||
.foo = 17,
|
||||
|
||||
.bar_present = true,
|
||||
.bar = { "gooei" },
|
||||
|
||||
.nest_count = 2,
|
||||
.nest = {
|
||||
{
|
||||
.val_present = true,
|
||||
.val = 0x0123456789abcdef,
|
||||
.nest = {
|
||||
.foo = 11,
|
||||
.bar = { "eleven" },
|
||||
.baz = {
|
||||
.v_int = 1111,
|
||||
.v_bool = true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.val_present = false,
|
||||
.nest = {
|
||||
.foo = 12,
|
||||
.bar = { "twelve" },
|
||||
.baz = {
|
||||
.v_int = 1212,
|
||||
.v_bool = false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
int myproto_msg_to_str_buf(char *buf, size_t buflen, const struct myproto_msg *m)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
OSMO_STRBUF_PRINTF(sb, "%s={", get_value_string(myproto_msg_type_names, m->type));
|
||||
OSMO_STRBUF_APPEND(sb, osmo_gtlvs_encode_to_str_buf, &m->ies, 0, myproto_get_msg_coding(m->type),
|
||||
myproto_iei_names);
|
||||
OSMO_STRBUF_PRINTF(sb, " }");
|
||||
return sb.chars_needed;
|
||||
|
||||
}
|
||||
|
||||
char *myproto_msg_to_str(const struct myproto_msg *m)
|
||||
{
|
||||
OSMO_NAME_C_IMPL(ctx, 256, "ERROR", myproto_msg_to_str_buf, m)
|
||||
}
|
||||
|
||||
void test_enc_dec(const char *label, const struct osmo_gtlv_cfg *cfg, bool ordered)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(tests); i++) {
|
||||
int rc;
|
||||
const struct myproto_msg *orig = &tests[i];
|
||||
struct myproto_msg parsed = {};
|
||||
struct msgb *msg;
|
||||
|
||||
printf("\n=== start %s %s[%d]\n", label, __func__, i);
|
||||
printf("encoded: %s\n", myproto_msg_to_str(orig));
|
||||
|
||||
msg = msgb_alloc(1024, __func__);
|
||||
rc = myproto_msg_enc(msg, orig, cfg);
|
||||
printf("myproto_msg_enc() rc = %d\n", rc);
|
||||
printf("%s.\n", osmo_hexdump(msg->data, msg->len));
|
||||
|
||||
rc = myproto_msg_dec(&parsed, msg->data, msg->len, cfg, ordered);
|
||||
printf("myproto_msg_dec() rc = %d\n", rc);
|
||||
printf("decoded: %s\n", myproto_msg_to_str(&parsed));
|
||||
if (strcmp(myproto_msg_to_str(orig), myproto_msg_to_str(&parsed))) {
|
||||
printf(" ERROR: parsed != orig\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
msgb_free(msg);
|
||||
printf("=== end %s %s[%d]\n", label, __func__, i);
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
ctx = talloc_named_const(NULL, 0, "test_gen_tlv");
|
||||
msgb_talloc_ctx_init(ctx, 0);
|
||||
|
||||
test_enc_dec("t8l8v ordered", &osmo_t8l8v_cfg, true);
|
||||
test_enc_dec("t8l8v unordered", &osmo_t8l8v_cfg, false);
|
||||
|
||||
test_enc_dec("t16l16v ordered", &osmo_t16l16v_cfg, true);
|
||||
test_enc_dec("t16l16v unordered", &osmo_t16l16v_cfg, false);
|
||||
|
||||
talloc_free(ctx);
|
||||
return 0;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user