mirror of https://gitea.it/1414codeforge/ubgpsuite
[*] Initial commit
commit
b0ef4dd774
@ -0,0 +1 @@
|
||||
Checks: 'clang-*,-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling'
|
@ -0,0 +1,51 @@
|
||||
# meson build directory
|
||||
build/
|
||||
|
||||
# exclude test data folders
|
||||
test/*/
|
||||
|
||||
# external projects, if any
|
||||
subprojects/*/
|
||||
|
||||
# hidden files
|
||||
.*
|
||||
!.clang-format
|
||||
!.clang-tidy
|
||||
!.gitignore
|
||||
!.gitmodules
|
||||
|
||||
# KDevelop projects
|
||||
*.kdev4
|
||||
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Fortran module files
|
||||
*.mod
|
||||
*.smod
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
@ -0,0 +1,675 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is 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. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
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.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
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 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. Use with the GNU Affero General Public License.
|
||||
|
||||
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 Affero 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 special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU 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 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 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 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 General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
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 GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
|
@ -0,0 +1,166 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser 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
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
|
@ -0,0 +1,60 @@
|
||||
µbgpsuite -- Micro BGP Suite and Utility library
|
||||
================================================
|
||||
|
||||
# Introduction
|
||||
|
||||
`µbgpsuite`, the Micro BGP Suite and Utility library, is a project
|
||||
aiming to provide a low level library and utilities for
|
||||
high-performance and flexible network analysis, with special
|
||||
focus on the Border Gateway Protocol (BGP).
|
||||
The purpose of this suite is establishing a friendly environment
|
||||
for research and experimentation of useful data study
|
||||
techniques to improve network health.
|
||||
|
||||
# lonetix -- Low-overhead Networking programming Interface
|
||||
|
||||
The project is centered around `lonetix`, a low overhead and
|
||||
low level networking library written in C.
|
||||
It provides a set of general functionality to implement the suite utilities.
|
||||
`lonetix` principles are:
|
||||
- efficiency: `lonetix` has to be fast and versatile;
|
||||
- predictability: data structures and functions should be predictable
|
||||
and reflect the actual protocol, abstraction should not degenerate
|
||||
into alienation;
|
||||
- zero copy and zero overhead: be friendly to your target CPU and cache,
|
||||
you never know just how fast or poweful the target platform will be,
|
||||
ideally `lonetix` should be capable of performing useful work on embedded
|
||||
systems as well as full fledged power systems alike;
|
||||
- lean: try to be self-contained and only introduce dependencies when strictly
|
||||
necessary.
|
||||
|
||||
Extensive documentation of `lonetix` and its API is available.
|
||||
|
||||
# Utilities
|
||||
|
||||
`lonetix` is the building block of `bgpgrep`, this far our single
|
||||
utility -- but more of them are coming, right?
|
||||
|
||||
`bgpgrep` performs fast and reliable analysis of MRT dumps
|
||||
collected by most Route Collecting projects. It takes a different
|
||||
turn compared to most similar tools, in that it provides extensive
|
||||
filtering utilities, in order to extrapolate only relevant data
|
||||
out of each MRT dump (and incidentally save quite some time).
|
||||
In-depth documentation of `bgpgrep` is available in its man page.
|
||||
|
||||
# License
|
||||
|
||||
The Micro BGP Suite is free software.
|
||||
You can redistribute the `lonetix` library and/or modify it under the terms of the
|
||||
GNU Lesser General Public License.
|
||||
You can redistribute any utility and/or modify it under the terms of the
|
||||
GNU General Public License.
|
||||
|
||||
The Micro BGP Suite is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the license terms for
|
||||
more details.
|
||||
|
||||
See `COPYING.LESSER` for the GNU Lesser General Public License terms,
|
||||
and `COPYING.GPL` for the GNU General Public License terms.
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,55 @@
|
||||
Micro BGP Suite history
|
||||
=======================
|
||||
|
||||
This document summarizes a bit of history of the project, it tells you anything
|
||||
you didn't want to know about `ubgpsuite` and never cared to ask.
|
||||
|
||||
`ubgpsuite` was created as an evolution over `bgpscanner` and its companion
|
||||
library, `isocore`. `bgpscanner` was originally developed by me starting in 2017
|
||||
as part of the Isolario Project, under the Institute of Informatics and
|
||||
Telematics of the Italian National Research Council
|
||||
[IIT-CNR](https://www.iit.cnr.it/).
|
||||
`bgpscanner` was covered by the MIT license terms and is still available
|
||||
(as of May 2021) at Isolario's
|
||||
[website](https://isolario.it/web_content/php/site_content/tools.php).
|
||||
Despite the fact that `bgpscanner` code was developed mostly by me, the
|
||||
help of the Isolario team, their patience, and their knowledge about BGP's
|
||||
nuts and bolts was invaluable to get it rolling.
|
||||
|
||||
By mid 2019, my collaboration with IIT-CNR has ceased.
|
||||
In an attempt to further the concepts behind `bgpscanner` and keep experimenting
|
||||
with them, I started the `ubgpsuite` project. At this time I was involved with
|
||||
[Alpha Cogs](https://www.alphacogs.com), a company I co-founded, so I undertook
|
||||
`ubgpsuite` development and got it moving forward with its financial support.
|
||||
|
||||
`ubgpsuite` was born by a partial rewrite of `bgpscanner`, taking advantage of
|
||||
the fact that there was no more interoperability constrain with a larger
|
||||
project -- `isocore` was originally intended to be used by other
|
||||
components inside the Isolario project as well, but that wasn't the
|
||||
case anymore. This allowed some minor improvement to the codebase, but the
|
||||
overall software architecture was unchanged. Though `ubgpsuite` was relicensed
|
||||
under the terms of LGPLv3+ for library code and GPLv3+ for utilities and tools.
|
||||
The choice was motivated by my intention to keep the project free
|
||||
(as in freedom) forever, considering that the only reason I was able to continue
|
||||
developing the code I authored at IIT-CNR and keep the project alive was
|
||||
its original open source license.
|
||||
|
||||
Unfortunately a chronical lack of time, due to more pressing company priorities,
|
||||
has hit the fan during that year and most of 2020. But finally,
|
||||
by the end of 2020, I got the chance to dedicate some time to the project.
|
||||
As a matter of fact, I left Alpha Cogs and devoted a bit of myself to
|
||||
move `ubgpsuite` and other projects I cared about out of stagnation.
|
||||
`ubgpsuite` was then outside Alpha Cogs and became the first of
|
||||
DoubleFourteen Code Forge projects -- 1414° for short, an initiative I
|
||||
founded to research and develop free software, in the hope of improving
|
||||
the software ecosystem.
|
||||
|
||||
On 2021, a total rewrite of `ubgpsuite` was complete and ready for release,
|
||||
with this document included. History of the project will thereafter remain
|
||||
unread.
|
||||
|
||||
Keep developing the code you love and enjoy,
|
||||
|
||||
---
|
||||
|
||||
Lorenzo Cogotti
|
@ -0,0 +1,3 @@
|
||||
:root {
|
||||
--note-color-darker: #8e7618;
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
/**
|
||||
|
||||
Doxygen Awesome
|
||||
https://github.com/jothepro/doxygen-awesome-css
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 jothepro
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
:root {
|
||||
/* side nav width. MUST be = `TREEVIEW_WIDTH`.
|
||||
* Make sure it is wide enought to contain the page title (logo + title + version)
|
||||
*/
|
||||
--side-nav-fixed-width: 350px;
|
||||
--menu-display: none;
|
||||
|
||||
--top-height: 120px;
|
||||
}
|
||||
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
:root {
|
||||
--searchbar-background: var(--page-background-color);
|
||||
}
|
||||
|
||||
#side-nav {
|
||||
min-width: var(--side-nav-fixed-width);
|
||||
max-width: var(--side-nav-fixed-width);
|
||||
top: var(--top-height);
|
||||
}
|
||||
|
||||
#nav-tree, #side-nav {
|
||||
height: calc(100vh - var(--top-height)) !important;
|
||||
}
|
||||
|
||||
#nav-tree {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#top {
|
||||
display: block;
|
||||
border-bottom: none;
|
||||
height: var(--top-height);
|
||||
margin-bottom: calc(0px - var(--top-height));
|
||||
max-width: var(--side-nav-fixed-width);
|
||||
background: var(--side-nav-background);
|
||||
}
|
||||
#main-nav {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.ui-resizable-handle {
|
||||
cursor: default;
|
||||
width: 1px !important;
|
||||
box-shadow: 0 calc(-2 * var(--top-height)) 0 0 var(--separator-color);
|
||||
}
|
||||
|
||||
#nav-path {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
left: var(--side-nav-fixed-width);
|
||||
bottom: 0;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
#doc-content {
|
||||
height: calc(100vh - 31px) !important;
|
||||
padding-bottom: calc(3 * var(--spacing-large));
|
||||
padding-top: calc(var(--top-height) - 80px);
|
||||
box-sizing: border-box;
|
||||
margin-left: var(--side-nav-fixed-width) !important;
|
||||
}
|
||||
|
||||
#MSearchBox {
|
||||
width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)));
|
||||
}
|
||||
|
||||
#MSearchField {
|
||||
width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - 65px);
|
||||
}
|
||||
|
||||
#MSearchResultsWindow {
|
||||
left: var(--spacing-medium) !important;
|
||||
right: auto;
|
||||
}
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,21 @@
|
||||
cdata = configuration_data({
|
||||
'TOP_SRCDIR': meson.source_root(),
|
||||
'TOP_BUILDDIR': meson.build_root(),
|
||||
'OUTPUT_DIR': meson.build_root() / 'doc',
|
||||
'VERSION': meson.project_version(),
|
||||
'PROJECT_NAME': meson.project_name(),
|
||||
})
|
||||
|
||||
doxyfile = configure_file(input : 'Doxyfile.in',
|
||||
output : 'Doxyfile',
|
||||
configuration : cdata,
|
||||
install : false)
|
||||
|
||||
doc_target = custom_target('doc',
|
||||
build_by_default : false,
|
||||
build_always_stale : true,
|
||||
console : true,
|
||||
command : [ doxygen, doxyfile ],
|
||||
output : [ 'doc' ])
|
||||
|
||||
alias_target('doc', doc_target)
|
@ -0,0 +1,170 @@
|
||||
Low-overhead Networking library Interface
|
||||
=============================================
|
||||
|
||||
# Introdution and philosophy
|
||||
|
||||
`lonetix` is a general, performance oriented, C networking library.
|
||||
Its field of application leans towards high-performance analysis of
|
||||
Border Gateway Protocol (BGP) data.
|
||||
|
||||
`lonetix` is somewhat opinionated, its principles are:
|
||||
- efficiency: `lonetix` has to be fast and versatile;
|
||||
- predictability: data structures and functions should be predictable
|
||||
and reflect the actual protocol, abstraction should not degenerate
|
||||
into alienation;
|
||||
- zero copy and zero overhead: be friendly to your target CPU and cache,
|
||||
you never know just how fast or poweful the target platform will be,
|
||||
ideally `lonetix` should be capable of performing useful work on embedded
|
||||
systems as well as full fledged power systems alike;
|
||||
- lean: try to be self-contained and only introduce dependencies when
|
||||
necessary.
|
||||
|
||||
Following sections further elaborate on these points.
|
||||
|
||||
## Efficiency
|
||||
|
||||
Network analysis is usually thought as a computationally intensive task,
|
||||
involving powerful machines capable of crunching large datasets.
|
||||
Fast prototyping is tipically preferred over carefully planned, optimized code
|
||||
and tools. Our belief is that careful optimizations, good algorithms and general
|
||||
libraries may empower scientific research to productively elaborate data faster.
|
||||
|
||||
Efficient algorithms and tools make previously prohibitive tasks plausible.
|
||||
|
||||
Some researchers have no access to powerful workstations, though devices
|
||||
commonly available to the general public are capable enough to perform
|
||||
interesting network analysis tasks.
|
||||
|
||||
Good tools should not restrict research, they should encourage it.
|
||||
|
||||
Efficiency should be a guiding principle behind `lonetix`, and the main
|
||||
reason for choosing C as a language.
|
||||
|
||||
## Predictability
|
||||
|
||||
`lonetix` is a relatively low-level C library. As such it deals with
|
||||
common software engineering problems. In contrast with common opinion, C has
|
||||
sufficient means to define a decent level of abstraction.
|
||||
Powerful abstractions have to be formalized, documented, explained and learned.
|
||||
Once this process is complete, powerful abstractions need to be used correctly.
|
||||
Therefore, powerful abstractions need expensive engineering and comprehensive
|
||||
documentation, they imply a learning curve and a period of practice.
|
||||
|
||||
Abstraction is a key software engineering concept only valuable if worthwhile.
|
||||
|
||||
Excessive abstraction may distract too much from the intent of a programming
|
||||
interface, making it more obfuscated, less obvious, thus less predictable.
|
||||
Additionally, it makes it harder for a programmer using them to guess or
|
||||
estimate their performance penalty -- a particularly undesirable feature
|
||||
in a scenario where such estimate could be crucial.
|
||||
Whenever possible an interface should be transparent to the programmer,
|
||||
essential, immediate in conveying its purpose and model, keep its field of
|
||||
application clear and confined.
|
||||
|
||||
`lonetix` builds complex abstraction only in face of a sufficient gain.
|
||||
Simplicity is a virtue, and obvious solutions need less explaination.
|
||||
Oftentimes, solving a large problem with clear straight to the point code
|
||||
is testament of a solid approach.
|
||||
|
||||
## Zero copy
|
||||
|
||||
`lonetix` deals, for the most part, with BGP messages.
|
||||
Decoding them, ensuring their integrity and accessing their fields
|
||||
conveniently and efficiently is central to the usefulness of the library.
|
||||
|
||||
Most libraries that read messages encoded in a particular protocol,
|
||||
take the common approach of introducing a decode phase upfront,
|
||||
with the intent of transforming its raw data into a more palatable
|
||||
representation for the library.
|
||||
The obvious advantages of this approach are:
|
||||
- an efficient resulting data structure that makes it easy to access every
|
||||
message field when needed;
|
||||
- any data integrity error is detected upfront, during the transformation.
|
||||
|
||||
Situation is specular for message writing.
|
||||
|
||||
This approach comes with its own set of disadvantages, though:
|
||||
- an initial decoding phase implies the whole message is scanned at least
|
||||
once to organize it in the new data structure, even when only a single field of
|
||||
the entire message would be relevant to the user;
|
||||
- CPU architectures greatly benefit from cache reuse, introducing a decode
|
||||
phase upfront that moves data around from a plain byte buffer to a complex
|
||||
data structure is usually bad news to the CPU cache;
|
||||
- more data structures generally imply more memory allocations;
|
||||
- translating raw data to the target data structure and back may
|
||||
require more complex API and implementation than providing equivalent
|
||||
facilities to access raw data directly.
|
||||
|
||||
These reasons motivated `lonetix` to explore a more trivial zero-copy approach:
|
||||
whenever possible `lonetix` should work with raw BGP messages and require no
|
||||
unnecessary data copy.
|
||||
|
||||
Do note that this approach is not perfect either. We simply believe that the
|
||||
tradeoff is to `lonetix` advantage, and a zero copy approach fits better in
|
||||
a performance-oriented and predictable library.
|
||||
|
||||
Same considerations apply to any portions of the library facing similar
|
||||
situations (MRT data, other network protocols, etc...).
|
||||
|
||||
## Zero overhead
|
||||
|
||||
`lonetix` provides comprehensive facilities for network analysis and additional
|
||||
utility functions for a wide variety of common tasks
|
||||
(including string utilities, text parsing, etc...).
|
||||
Library users should not be burdened with overhead for functionality they don't
|
||||
need.
|
||||
|
||||
By design `lonetix` should be modular and require no runtime overhead
|
||||
(such as background threads, `atexit()` hooks, or static initialization)
|
||||
unless deemed as positively and unmistakably unavoidable.
|
||||
|
||||
`lonetix` is a **static library**, making it possible for the compiler to
|
||||
strip any unused code from the resulting binary.
|
||||
|
||||
Careful coding should always allow to compile the library with
|
||||
full optimizations on, including Link Time Optimization
|
||||
(whether the binary should be optimized for space or
|
||||
speed should be configurable by the user).
|
||||
|
||||
## Lean
|
||||
|
||||
No external dependency should be introduced unless strictly necessary.
|
||||
This helps improving portability and makes `lonetix` usable even under
|
||||
constrained environments.
|
||||
|
||||
`lonetix` **does not pursue strict ABI or API stability**.
|
||||
|
||||
Given that `lonetix` is a static library, keeping ABI stability is unnecessary.
|
||||
Strict API stability tends to clutter libraries with large amounts of
|
||||
legacy code, `lonetix` strives for incremental code improvement.
|
||||
This sometimes calls for changes to the API and minor interface variations.
|
||||
Users wishing for specific features from older versions that have been
|
||||
evicted or changed on current ones, may fetch the older versions and link to
|
||||
them.
|
||||
Though API stability is not guaranteed, it should not be broken deliberately,
|
||||
viable code migration paths should be offered when possible, for sensible use
|
||||
cases.
|
||||
|
||||
# Documentation and examples
|
||||
|
||||
Complete project documentation is currently work in progress.
|
||||
|
||||
Extensive `Doxygen` API documentation is available for most of the library.
|
||||
We also believe code should be clear, understandable and idiomatic, so
|
||||
you can check out the code of any utility using `lonetix`
|
||||
(for example `bgpgrep`) as a reference of how to take advantage of the library.
|
||||
|
||||
# License
|
||||
|
||||
`lonetix` is free software. You can redistribute it and/or modify it under
|
||||
the terms of the GNU Lesser General Public License version 3 as published
|
||||
by the Free Software Foundation, or, at your choice, any subsequent version
|
||||
of the same license. `lonetix` is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the license terms for
|
||||
more details.
|
||||
|
||||
See `COPYING.LESSER` under the Micro BGP Suite root project directory for the
|
||||
GNU Lesser General Public License terms, or see the
|
||||
[Free Software Foundation website](https://www.gnu.org/licenses/lgpl-3.0.txt).
|
||||
|
@ -0,0 +1,349 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file argv.c
|
||||
*
|
||||
* Portable command line argument parsing implementation.
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*/
|
||||
|
||||
#include "argv.h"
|
||||
|
||||
#include "sys/con.h"
|
||||
#include "utf/utf.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h> // for getenv() on __GNUC__
|
||||
#include <string.h>
|
||||
|
||||
const char *com_progName = NULL;
|
||||
const char *com_synopsis = NULL;
|
||||
const char *com_shortDescr = NULL;
|
||||
const char *com_longDescr = NULL;
|
||||
|
||||
#define OPT_ISLONLY(flag) ((flag)->opt == '-')
|
||||
#define OPT_HASLONGNAM(flag) ((flag)->longopt != NULL)
|
||||
#define OPT_ARGNAME(flag) (((flag)->argName) ? (flag)->argName : "arg")
|
||||
|
||||
static void PrintHelpMessage(const char *prog, const Optflag *options)
|
||||
{
|
||||
if (!prog)
|
||||
return; // quiet parsing
|
||||
|
||||
Sys_Printf(STDOUT, "Usage: %s", prog);
|
||||
if (com_synopsis)
|
||||
Sys_Printf(STDOUT, " %s", com_synopsis);
|
||||
|
||||
Sys_Print(STDOUT, "\n");
|
||||
if (com_shortDescr)
|
||||
Sys_Printf(STDOUT, "%s\n", com_shortDescr);
|
||||
|
||||
if (com_longDescr)
|
||||
Sys_Printf(STDOUT, "\n%s\n\n", com_longDescr);
|
||||
|
||||
char buf[MAXUTF + 1];
|
||||
size_t n;
|
||||
|
||||
for (const Optflag *flag = options; flag->opt != '\0'; flag++) {
|
||||
Sys_Print(STDOUT, " ");
|
||||
|
||||
// Single char option name
|
||||
if (OPT_ISLONLY(flag)) {
|
||||
// Long option only, leave 2 chars for alignment purposes
|
||||
assert(flag->longopt);
|
||||
Sys_Print(STDOUT, " ");
|
||||
|
||||
} else {
|
||||
// Write down single-char option first
|
||||
n = runetochar(buf, flag->opt);
|
||||
buf[n] = '\0';
|
||||
|
||||
Sys_Printf(STDOUT, "-%s", buf);
|
||||
}
|
||||
|
||||
// Long name and (optional) argument
|
||||
if (flag->longopt) {
|
||||
// Write comma if necessary, or leave 2 spaces for alignment
|
||||
Sys_Print(STDOUT, OPT_ISLONLY(flag) ? " " : ", ");
|
||||
Sys_Printf(STDOUT, "--%s", flag->longopt);
|
||||
|
||||
// Append argument with a leading = sign
|
||||
switch (flag->hasArg) {
|
||||
default: assert(FALSE); break;
|
||||
case ARG_NONE: break;
|
||||
case ARG_OPT: Sys_Printf(STDOUT, "[=%s]", OPT_ARGNAME(flag)); break;
|
||||
case ARG_REQ: Sys_Printf(STDOUT, "=%s", OPT_ARGNAME(flag)); break;
|
||||
}
|
||||
} else {
|
||||
// Output argument, short options have no leading =
|
||||
switch (flag->hasArg) {
|
||||
default: assert(FALSE); break;
|
||||
case ARG_NONE: break;
|
||||
case ARG_OPT: Sys_Printf(STDOUT, " [%s]", OPT_ARGNAME(flag)); break;
|
||||
case ARG_REQ: Sys_Printf(STDOUT, " <%s>", OPT_ARGNAME(flag)); break;
|
||||
}
|
||||
}
|
||||
if (flag->descr)
|
||||
Sys_Printf(STDOUT, "\t%s", flag->descr);
|
||||
|
||||
Sys_Print(STDOUT, "\n");
|
||||
}
|
||||
|
||||
// Append help options
|
||||
Sys_Print(STDOUT, " -h, --help\tPrint this help message\n");
|
||||
Sys_Print(STDOUT, " -?\tEquivalent to -h\n");
|
||||
}
|
||||
|
||||
// If `prog` is not NULL, output an excess argument error (only called for long options).
|
||||
static void PrintExcessArgError(const char *prog,
|
||||
const Optflag *flag,
|
||||
const char *arg)
|
||||
{
|
||||
if (!prog)
|
||||
return; // quiet parse
|
||||
|
||||
assert(flag->longopt);
|
||||
|
||||
Sys_Printf(STDERR, "%s: Option --%s takes no argument: --%s=%s", prog, flag->longopt, flag->longopt, arg);
|
||||
}
|
||||
|
||||
static void PrintMissingArgError(const char *prog, const Optflag *flag, Boolean longName)
|
||||
{
|
||||
if (!prog)
|
||||
return; // quiet parse
|
||||
|
||||
char buf[MAXUTF+1];
|
||||
const char *nam = NULL;
|
||||
const char *pfx = (longName) ? "--" : "-";
|
||||
|
||||
if (longName)
|
||||
nam = flag->longopt;
|
||||
|
||||
else {
|
||||
size_t n = runetochar(buf, flag->opt);
|
||||
buf[n] = '\0';
|
||||
|
||||
nam = buf;
|
||||
}
|
||||
|
||||
Sys_Printf(STDERR, "%s: Option", prog);
|
||||
if (nam)
|
||||
Sys_Printf(STDERR, " %s%s", pfx, nam);
|
||||
|
||||
Sys_Print(STDERR, " requires mandatory argument");
|
||||
if (flag->argName)
|
||||
Sys_Printf(STDERR, " <%s>", flag->argName);
|
||||
|
||||
Sys_Printf(STDERR, ": %s%s\n", pfx, nam);
|
||||
}
|
||||
|
||||
CHECK_PRINTF(2, 0) static void PrintBadCmdLineError(const char *prog,
|
||||
const char *fmt,
|
||||
...)
|
||||
{
|
||||
if (!prog)
|
||||
return; // quiet parsing
|
||||
|
||||
va_list va;
|
||||
|
||||
va_start(va, fmt);
|
||||
Sys_Printf(STDERR, "%s: ", prog);
|
||||
Sys_VPrintf(STDERR, fmt, va);
|
||||
Sys_Print(STDERR, "\n");
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
static Optflag *FindLongFlag(const char *p, char **optarg, const char *prog, Optflag *options)
|
||||
{
|
||||
char *arg = strchr(p, '=');
|
||||
|
||||
*optarg = arg;
|
||||
|
||||
char *name;
|
||||
if (arg) {
|
||||
size_t n = arg - p;
|
||||
name = (char *) alloca(n + 1);
|
||||
|
||||
memcpy(name, p, n);
|
||||
name[n] = '\0';
|
||||
} else
|
||||
name = (char *) p; // safe
|
||||
|
||||
for (Optflag *flag = options; flag->opt != '\0'; flag++) {
|
||||
if (OPT_HASLONGNAM(flag) && strcmp(flag->longopt, name) == 0) {
|
||||
flag->flagged = TRUE;
|
||||
return flag;
|
||||
}
|
||||
}
|
||||
|
||||
if (prog)
|
||||
Sys_Printf(STDERR, "%s: Unrecognized option: --%s\n", prog, name);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static Optflag *FindFlag(Rune r, const char *prog, Optflag *flags)
|
||||
{
|
||||
for (Optflag *flag = flags; flag->opt != '\0'; flag++) {
|
||||
// NOTE: options with long names are skipped (-- is end of argument list)
|
||||
if (flag->opt == r) {
|
||||
flag->flagged = TRUE;
|
||||
return flag;
|
||||
}
|
||||
}
|
||||
|
||||
if (prog) {
|
||||
char buf[MAXUTF + 1];
|
||||
|
||||
size_t n = runetochar(buf, r);
|
||||
buf[n] = '\0';
|
||||
|
||||
Sys_Printf(STDERR, "%s: Unrecognized option: -%s\n", prog, buf);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
static void ReorderArgv(int argc, char **argv, const Optflag *options)
|
||||
{
|
||||
if (getenv("POSIXLY_CORRECT"))
|
||||
return; // don't mess with argv is POSIX behavior is requested
|
||||
|
||||
USED(argc); USED(argv); USED(options);
|
||||
// TODO
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int Com_ArgParse(int argc, char **argv, Optflag *options, unsigned flags)
|
||||
{
|
||||
Optflag *flag;
|
||||
|
||||
char *p, *optarg;
|
||||
|
||||
// Initial setup
|
||||
char *prog = NULL; // FindFlag*() functions won't log on NULL `prog`
|
||||
if ((flags & ARG_QUIET) == 0) {
|
||||
// Extract program name, so parsing outputs meaningful messages
|
||||
prog = (char *) com_progName;
|
||||
if (!prog) {
|
||||
// Generate from argv[0]
|
||||
prog = argv[0]; // TODO: basename(argv[0]);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
/* GNU allows program options in any order with respect to program
|
||||
* arguments, for example:
|
||||
* ```c
|
||||
* program file -cv
|
||||
* ```
|
||||
* According to POSIX both `file` and `-cv` are program arguments.
|
||||
* According to GNU `-cv` are options, `file` is a program argument.
|
||||
*
|
||||
* To do this GNU getopt() implicitly reorders `argv`.
|
||||
*/
|
||||
if ((flags & ARG_NOREORD) == 0)
|
||||
ReorderArgv(argc, argv, options);
|
||||
#endif
|
||||
|
||||
// Parse argument list
|
||||
int optind;
|
||||
for (optind = 1; optind < argc; optind++) {
|
||||
p = argv[optind];
|
||||
if (strcmp(p, "--") == 0) {
|
||||
optind++;
|
||||
break; // explicit end of command list
|
||||
}
|
||||
|
||||
if (p[0] == '-' && p[1] == '-') {
|
||||
// GNU style long option
|
||||
p += 2;
|
||||
if (strcmp(p, "help") == 0) {
|
||||
PrintHelpMessage(prog, options);
|
||||
return OPT_HELP;
|
||||
}
|
||||
|
||||
flag = FindLongFlag(p, &optarg, prog, options);
|
||||
if (!flag)
|
||||
return OPT_UNKNOWN;
|
||||
|
||||
if (!optarg && flag->hasArg)
|
||||
optarg = argv[++optind]; // fetch argument from `argv`
|
||||
|
||||
if (optarg && flag->hasArg == ARG_NONE) {
|
||||
PrintExcessArgError(prog, flag, optarg);
|
||||
return OPT_EXCESSARG;
|
||||
}
|
||||
if (!optarg && flag->hasArg == ARG_REQ) {
|
||||
PrintMissingArgError(prog, flag, /*longName=*/TRUE);
|
||||
return OPT_ARGMISS;
|
||||
}
|
||||
|
||||
flag->optarg = optarg;
|
||||
|
||||
} else if (p[0] == '-' && p[1] != '\0') {
|
||||
// Unix-style single char option
|
||||
p++;
|
||||
|
||||
do {
|
||||
Rune r;
|
||||
p += chartorune(&r, p);
|
||||
if (r == '-') {
|
||||
/* This can happen in events like: -a-c
|
||||
* where a->hasArg == ARG_NONE
|
||||
*
|
||||
* There is no way to fix this ambiguity safely:
|
||||
* * if -c is an argument to -a then it is OPT_EXCESSARG error
|
||||
* * if -a -- -c problematic because it's counterintuitive
|
||||
* for the user
|
||||
* (one could get surprising -c program arguments)
|
||||
* * if we take it as -a --(force as option) -c it would be
|
||||
* dangerous (sometimes -- is an option, sometimes an
|
||||
* end of option list)
|
||||
*
|
||||
* NOTE: getopt() appears to do the last, and it's
|
||||
* horrific
|
||||
*/
|
||||
PrintBadCmdLineError(prog, "Ambiguous '-' in short options list: %s", argv[optind]);
|
||||
return OPT_BADARGV;
|
||||
}
|
||||
|
||||
// Handle single char help requests
|
||||
if (r == '?' || r == 'h') {
|
||||
PrintHelpMessage(prog, options);
|
||||
return OPT_HELP;
|
||||
}
|
||||
|
||||
flag = FindFlag(r, prog, options);
|
||||
if (!flag)
|
||||
return OPT_UNKNOWN;
|
||||
|
||||
optarg = NULL;
|
||||
if (flag->hasArg)
|
||||
optarg = (*p != '\0') ? p : argv[++optind];
|
||||
|
||||
if (!optarg && flag->hasArg == ARG_REQ) {
|
||||
PrintMissingArgError(prog, flag, /*longName=*/FALSE);
|
||||
return OPT_ARGMISS;
|
||||
}
|
||||
|
||||
flag->optarg = optarg;
|
||||
|
||||
} while (*p != '\0');
|
||||
}
|
||||
#if 0 && defined(_WIN32) // TODO
|
||||
else if (p[0] == '/' && p[1] != '\0') {
|
||||
// DOS style option
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
// End of argument list, break the loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return optind;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,611 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file bgp/bgp.c
|
||||
*
|
||||
* BGP message decoding.
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*/
|
||||
|
||||
#include "bgp/bgp_local.h"
|
||||
#include "sys/sys_local.h"
|
||||
#include "sys/dbg.h"
|
||||
#include "sys/endian.h"
|
||||
#include "sys/ip.h"
|
||||
#include "sys/con.h"
|
||||
#include "argv.h"
|
||||
#include "numlib.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static const Uint8 bgp_marker[] = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
|
||||
STATIC_ASSERT(sizeof(bgp_marker) == BGP_MARKER_LEN, "Malformed 'bgp_marker'");
|
||||
|
||||
static THREAD_LOCAL BgpErrStat bgp_errs;
|
||||
|
||||
const char *Bgp_ErrorString(BgpRet code)
|
||||
{
|
||||
switch (code) {
|
||||
case BGPEBADVM: return "Attempting operation on BGP VM under error state";
|
||||
case BGPEVMNOPROG: return "Attempt to execute BGP VM with empty bytecode";
|
||||
case BGPEVMBADCOMTCH: return "Bad COMMUNITY match expression";
|
||||
case BGPEVMASMTCHESIZE: return "BGP VM AS_PATH match expression too complex";
|
||||
case BGPEVMASNGRPLIM: return "BGP VM AS_PATH match expression group limit hit";
|
||||
case BGPEVMBADASMTCH: return "Bad AS_PATH match expression";
|
||||
case BGPEVMBADJMP: return "BGP VM jump instruction target is out of bounds";
|
||||
case BGPEVMILL: return "Illegal instruction in BGP VM bytecode";
|
||||
case BGPEVMOOM: return "BGP VM heap memory exhausted";
|
||||
case BGPEVMBADENDBLK: return "Encountered ENDBLK with no corresponding BLK";
|
||||
case BGPEVMUFLOW: return "BGP VM stack underflow";
|
||||
case BGPEVMOFLOW: return "BGP VM stack overflow";
|
||||
case BGPEVMBADFN: return "CALL instruction index is out of bounds";
|
||||
case BGPEVMBADK: return "LOADK instruction index is out of bounds";
|
||||
case BGPEVMMSGERR: return "Error encountered during BGP message access";
|
||||
case BGPEVMBADOP: return "BGP VM instruction has invalid operand";
|
||||
case BGPENOERR: return "No error";
|
||||
case BGPEIO: return "Input/Output error";
|
||||
case BGPEBADTYPE: return "Provided BGP message has inconsistent type";
|
||||
case BGPENOADDPATH: return "Provided BGP message contains no ADD_PATH information";
|
||||
case BGPEBADATTRTYPE: return "Provided BGP attribute has inconsistent type";
|
||||
case BGPEBADMARKER: return "BGP message marker mismatch";
|
||||
case BGPENOMEM: return "Memory allocation failure";
|
||||
case BGPETRUNCMSG: return "Truncated BGP message";
|
||||
case BGPEOVRSIZ: return "Oversized BGP message";
|
||||
case BGPEBADOPENLEN: return "BGP OPEN message has inconsistent length";
|
||||
case BGPEDUPNLRIATTR: return "Duplicate NLRI attribute detected inside UPDATE message";
|
||||
case BGPEBADPFXWIDTH: return "Bad prefix width";
|
||||
case BGPETRUNCPFX: return "Truncated prefix";
|
||||
case BGPETRUNCATTR: return "Truncated BGP attribute";
|
||||
case BGPEBADAGGR: return "Malformed AGGREGATOR attribute";
|
||||
case BGPEBADAGGR4: return "Malformed AS4_AGGREGATOR attribute";
|
||||
case BGPEAFIUNSUP: return "Unsupported Address Family Identifier";
|
||||
case BGPESAFIUNSUP: return "Unsupported Subsequent Address Family Identifier";
|
||||
case BGPEBADMRTTYPE: return "Provided MRT record has inconsistent type";
|
||||
case BGPETRUNCMRT: return "Truncated MRT record";
|
||||
case BGPEBADPEERIDXCNT: return "TABLE_DUMPV2 Peer Index Table record has incoherent peer entry count";
|
||||
case BGPETRUNCPEERV2: return "Truncated peer entry in TABLE_DUMPV2 Peer Index Table record";
|
||||
case BGPEBADRIBV2CNT: return "TABLE_DUMPV2 RIB record has incoherent RIB entry count";
|
||||
case BGPETRUNCRIBV2: return "Truncated entry in TABLE_DUMPV2 RIB record";
|
||||
case BGPEBADRIBV2MPREACH: return "TABLE_DUMPV2 RIB record contains an illegal MP_REACH attribute";
|
||||
case BGPERIBNOMPREACH: return "IPv6 RIB entry lacks MP_REACH_NLRI attribute";
|
||||
case BGPEBADPEERIDX: return "Peer entry index is out of bounds";
|
||||
default: return "Unknown BGP error";
|
||||
}
|
||||
}
|
||||
|
||||
static NOINLINE NORETURN void Bgp_Quit(BgpRet code, Srcloc *loc, void *obj)
|
||||
{
|
||||
USED(obj);
|
||||
|
||||
loc->call_depth++; // include Bgp_Quit() itself
|
||||
|
||||
if (com_progName) {
|
||||
Sys_Print(STDERR, com_progName);
|
||||
Sys_Print(STDERR, ": ");
|
||||
}
|
||||
|
||||
Sys_Print(STDERR, "Terminate called in response to an unrecoverable BGP error.\n\t");
|
||||
|
||||
if (loc) {
|
||||
#ifndef NDEBUG
|
||||
if (loc->filename && loc->line > 0) {
|
||||
char buf[64];
|
||||
|
||||
Utoa(loc->line, buf);
|
||||
Sys_Print(STDERR, "Error occurred in file ");
|
||||
Sys_Print(STDERR, loc->filename);
|
||||
Sys_Print(STDERR, ":");
|
||||
Sys_Print(STDERR, buf);
|
||||
Sys_Print(STDERR, "\n\t");
|
||||
}
|
||||
#endif
|
||||
if (loc->func) {
|
||||
Sys_Print(STDERR, "[");
|
||||
Sys_Print(STDERR, loc->func);
|
||||
Sys_Print(STDERR, "()]: ");
|
||||
}
|
||||
}
|
||||
|
||||
Sys_Print(STDERR, Bgp_ErrorString(code));
|
||||
Sys_Print(STDERR, ".\n");
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
Judgement _Bgp_SetErrStat(BgpRet code,
|
||||
const char *filename,
|
||||
const char *func,
|
||||
unsigned long long line,
|
||||
unsigned depth)
|
||||
{
|
||||
// Don't clobber error code on BGP message access error inside filtering VM
|
||||
if (code != BGPEVMMSGERR)
|
||||
bgp_errs.code = code;
|
||||
if (code == BGPENOERR)
|
||||
return OK; // usual case
|
||||
|
||||
void (*err_func)(BgpRet, Srcloc *, void *);
|
||||
Srcloc loc;
|
||||
|
||||
err_func = bgp_errs.func;
|
||||
if (err_func) {
|
||||
loc.filename = filename;
|
||||
loc.func = func;
|
||||
loc.line = line;
|
||||
loc.call_depth = depth + 1;
|
||||
|
||||
if (err_func == BGP_ERR_QUIT)
|
||||
err_func = Bgp_Quit;
|
||||
|
||||
err_func(code, &loc, bgp_errs.obj);
|
||||
}
|
||||
|
||||
return NG;
|
||||
}
|
||||
|
||||
void Bgp_SetErrFunc(void (*func)(BgpRet, Srcloc *, void *),
|
||||
void *arg)
|
||||
{
|
||||
bgp_errs.func = func;
|
||||
bgp_errs.obj = arg;
|
||||
}
|
||||
|
||||
BgpRet Bgp_GetErrStat(BgpErrStat *stat)
|
||||
{
|
||||
if (stat)
|
||||
*stat = bgp_errs;
|
||||
|
||||
return bgp_errs.code;
|
||||
}
|
||||
|
||||
Uint16 Bgp_CheckMsgHdr(const void *data,
|
||||
size_t nbytes,
|
||||
Boolean allowExtendedSize)
|
||||
{
|
||||
if (nbytes < BGP_HDRSIZ) {
|
||||
Bgp_SetErrStat(BGPETRUNCMSG);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const Bgphdr *hdr = (const Bgphdr *) data;
|
||||
if (memcmp(hdr->marker, bgp_marker, BGP_MARKER_LEN) != 0) {
|
||||
Bgp_SetErrStat(BGPEBADMARKER);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Uint16 len = beswap16(hdr->len);
|
||||
if (len < BGP_HDRSIZ) {
|
||||
Bgp_SetErrStat(BGPETRUNCMSG);
|
||||
return 0;
|
||||
}
|
||||
if (len > BGP_MSGSIZ && !allowExtendedSize) {
|
||||
Bgp_SetErrStat(BGPEOVRSIZ);
|
||||
return 0;
|
||||
}
|
||||
if (len > nbytes) {
|
||||
Bgp_SetErrStat(BGPETRUNCMSG);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
Judgement Bgp_MsgFromBuf(Bgpmsg *msg,
|
||||
const void *data,
|
||||
size_t nbytes,
|
||||
unsigned flags)
|
||||
{
|
||||
// Immediately initialize flags (mask away superflous ones)
|
||||
msg->flags = flags & (BGPF_ADDPATH|BGPF_ASN32BIT|BGPF_EXMSG|BGPF_UNOWNED);
|
||||
|
||||
// Check header data for correctness
|
||||
Uint16 len = Bgp_CheckMsgHdr(data, nbytes, BGP_ISEXMSG(msg->flags));
|
||||
if (len == 0)
|
||||
return NG; // error already set by Bgp_CheckHdr()
|
||||
|
||||
if (BGP_ISUNOWNED(msg->flags))
|
||||
msg->buf = (Uint8 *) data; // don't copy data over
|
||||
|
||||
else {
|
||||
// Copy over relevant data
|
||||
const MemOps *memOps = BGP_MEMOPS(msg);
|
||||
|
||||
msg->buf = (Uint8 *) memOps->Alloc(msg->allocp, len, NULL);
|
||||
if (!msg->buf)
|
||||
return Bgp_SetErrStat(BGPENOMEM);
|
||||
|
||||
memcpy(msg->buf, data, len);
|
||||
}
|
||||
|
||||
BGP_CLRATTRTAB(msg->table);
|
||||
return Bgp_SetErrStat(BGPENOERR);
|
||||
}
|
||||
|
||||
Judgement Bgp_ReadMsg(Bgpmsg *msg,
|
||||
void *streamp,
|
||||
const StmOps *ops,
|
||||
unsigned flags)
|
||||
{
|
||||
// Immediately initialize flags (mask away superflous ones)
|
||||
msg->flags = flags & (BGPF_ADDPATH|BGPF_ASN32BIT|BGPF_EXMSG|BGPF_UNOWNED);
|
||||
|
||||
Uint8 buf[BGP_HDRSIZ];
|
||||
Sint64 n = ops->Read(streamp, buf, BGP_HDRSIZ);
|
||||
if (n == 0) {
|
||||
// precisely at end of stream, no error, just no more BGP messages
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return NG;
|
||||
}
|
||||
if (n < 0)
|
||||
return Bgp_SetErrStat(BGPEIO);
|
||||
if ((size_t) n != BGP_HDRSIZ)
|
||||
return Bgp_SetErrStat(BGPEIO);
|
||||
|
||||
// Retrieve memory allocator
|
||||
const MemOps *memOps = BGP_MEMOPS(msg);
|
||||
|
||||
// Check header and allocate message
|
||||
Uint16 len = Bgp_CheckMsgHdr(buf, BGP_HDRSIZ, BGP_ISEXMSG(msg->flags));
|
||||
if (len == 0)
|
||||
return NG;
|
||||
|
||||
msg->buf = (Uint8 *) memOps->Alloc(msg->allocp, len, NULL);
|
||||
if (!msg->buf)
|
||||
return Bgp_SetErrStat(BGPENOMEM);
|
||||
|
||||
// Copy over the message
|
||||
memcpy(msg->buf, buf, BGP_HDRSIZ);
|
||||
len -= BGP_HDRSIZ;
|
||||
|
||||
n = ops->Read(streamp, msg->buf + BGP_HDRSIZ, len);
|
||||
if (n < 0) {
|
||||
Bgp_SetErrStat(BGPEIO);
|
||||
goto fail;
|
||||
}
|
||||
if ((size_t) n != len) {
|
||||
Bgp_SetErrStat(BGPEIO);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
BGP_CLRATTRTAB(msg->table);
|
||||
return Bgp_SetErrStat(BGPENOERR);
|
||||
|
||||
fail:
|
||||
memOps->Free(msg->allocp, msg->buf);
|
||||
return NG;
|
||||
}
|
||||
|
||||
#define BGP_OPEN_MINSIZ (BGP_HDRSIZ + 1uLL + 2uLL + 2uLL + 4uLL + 1uLL)
|
||||
|
||||
Bgpopen *Bgp_GetMsgOpen(Bgpmsg *msg)
|
||||
{
|
||||
Bgphdr *hdr = BGP_HDR(msg);
|
||||
if (hdr->type != BGP_OPEN) {
|
||||
Bgp_SetErrStat(BGPEBADTYPE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t len = beswap16(hdr->len);
|
||||
if (len < BGP_OPEN_MINSIZ) {
|
||||
Bgp_SetErrStat(BGPETRUNCMSG);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgpopen *open = (Bgpopen *) hdr;
|
||||
size_t nbytes = BGP_OPEN_MINSIZ + open->parmsLen;
|
||||
if (nbytes != len) {
|
||||
Bgp_SetErrStat((nbytes > len) ? BGPETRUNCMSG : BGPEBADOPENLEN);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return open;
|
||||
}
|
||||
|
||||
#define BGP_UPDATE_MINSIZ (BGP_HDRSIZ + 2uLL + 2uLL)
|
||||
|
||||
Bgpupdate *Bgp_GetMsgUpdate(Bgpmsg *msg)
|
||||
{
|
||||
Bgphdr *hdr = BGP_HDR(msg);
|
||||
if (hdr->type != BGP_UPDATE) {
|
||||
Bgp_SetErrStat(BGPEBADTYPE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Uint16 len = beswap16(hdr->len);
|
||||
if (len < BGP_UPDATE_MINSIZ) {
|
||||
Bgp_SetErrStat(BGPETRUNCMSG);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return (Bgpupdate *) hdr;
|
||||
}
|
||||
|
||||
static Boolean Bgp_SwitchMpIterator(Bgpmpiter *it)
|
||||
{
|
||||
if (!it->nextAttr)
|
||||
return FALSE; // no additional attribute to iterate, we're done
|
||||
|
||||
// Switch iterator
|
||||
const Bgpmpfam *family = Bgp_GetMpFamily(it->nextAttr); // sets error
|
||||
if (!family)
|
||||
return FALSE; // corrupted attribute
|
||||
|
||||
size_t nbytes;
|
||||
void *routes = Bgp_GetMpRoutes(it->nextAttr, &nbytes);
|
||||
if (!routes)
|
||||
return FALSE; // corrupted message
|
||||
|
||||
// Begin subsequent iteration
|
||||
Afi afi = family->afi;
|
||||
Safi safi = family->safi;
|
||||
if (Bgp_StartPrefixes(&it->rng, afi, safi, routes, nbytes, it->rng.isAddPath) != OK)
|
||||
return FALSE; // shouldn't happen
|
||||
|
||||
// Switch complete, clear attribute and move on
|
||||
it->nextAttr = NULL;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Judgement Bgp_StartMsgWithdrawn(Prefixiter *it, Bgpmsg *msg)
|
||||
{
|
||||
Bgpupdate *update = Bgp_GetMsgUpdate(msg);
|
||||
if (!update)
|
||||
return NG;
|
||||
|
||||
Bgpwithdrawnseg *withdrawn = Bgp_GetUpdateWithdrawn(update);
|
||||
if (!withdrawn)
|
||||
return NG;
|
||||
|
||||
return Bgp_StartPrefixes(it, AFI_IP, SAFI_UNICAST,
|
||||
withdrawn->nlri, beswap16(withdrawn->len),
|
||||
BGP_ISADDPATH(msg->flags));
|
||||
}
|
||||
|
||||
Judgement Bgp_StartAllMsgWithdrawn(Bgpmpiter *it, Bgpmsg *msg)
|
||||
{
|
||||
it->nextAttr = Bgp_GetMsgAttribute(msg, BGP_ATTR_MP_UNREACH_NLRI);
|
||||
if (!it->nextAttr && Bgp_GetErrStat(NULL))
|
||||
return NG;
|
||||
if (Bgp_StartMsgWithdrawn(&it->rng, msg) != OK)
|
||||
return NG;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
void Bgp_InitMpWithdrawn(Bgpmpiter *it,
|
||||
const Bgpwithdrawnseg *withdrawn,
|
||||
const Bgpattr *mpUnreach,
|
||||
Boolean isAddPath)
|
||||
{
|
||||
it->nextAttr = (Bgpattr *) mpUnreach;
|
||||
Bgp_StartPrefixes(&it->rng,
|
||||
AFI_IP, SAFI_UNICAST,
|
||||
withdrawn->nlri, beswap16(withdrawn->len),
|
||||
isAddPath);
|
||||
}
|
||||
|
||||
void Bgp_InitMpNlri(Bgpmpiter *it,
|
||||
const void *nlri,
|
||||
size_t nbytes,
|
||||
const Bgpattr *mpReach,
|
||||
Boolean isAddPath)
|
||||
{
|
||||
it->nextAttr = (Bgpattr *) mpReach;
|
||||
Bgp_StartPrefixes(&it->rng, AFI_IP, SAFI_UNICAST, nlri, nbytes, isAddPath);
|
||||
}
|
||||
|
||||
Judgement Bgp_StartMsgNlri(Prefixiter *it, Bgpmsg *msg)
|
||||
{
|
||||
Bgpupdate *update = Bgp_GetMsgUpdate(msg);
|
||||
if (!update)
|
||||
return NG;
|
||||
|
||||
size_t nbytes;
|
||||
void *nlri = Bgp_GetUpdateNlri(update, &nbytes);
|
||||
if (!nlri)
|
||||
return NG;
|
||||
|
||||
return Bgp_StartPrefixes(it, AFI_IP, SAFI_UNICAST,
|
||||
nlri, nbytes,
|
||||
BGP_ISADDPATH(msg->flags));
|
||||
}
|
||||
|
||||
Judgement Bgp_StartAllMsgNlri(Bgpmpiter *it, Bgpmsg *msg)
|
||||
{
|
||||
it->nextAttr = Bgp_GetMsgAttribute(msg, BGP_ATTR_MP_REACH_NLRI);
|
||||
if (!it->nextAttr && Bgp_GetErrStat(NULL))
|
||||
return NG;
|
||||
if (Bgp_StartMsgNlri(&it->rng, msg) != OK)
|
||||
return NG;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Prefix *Bgp_NextMpPrefix(Bgpmpiter *it)
|
||||
{
|
||||
void *rawPfx;
|
||||
do {
|
||||
rawPfx = Bgp_NextPrefix(&it->rng); // sets error
|
||||
if (!rawPfx) {
|
||||
// Swap iterator if necessary and try again
|
||||
if (Bgp_GetErrStat(NULL))
|
||||
return NULL;
|
||||
if (!Bgp_SwitchMpIterator(it))
|
||||
return NULL;
|
||||
}
|
||||
} while (!rawPfx);
|
||||
|
||||
// Extended prefix info
|
||||
Prefix *cur = &it->pfx;
|
||||
cur->afi = it->rng.afi;
|
||||
cur->safi = it->rng.safi;
|
||||
if (it->rng.isAddPath) {
|
||||
// ADD-PATH enabled prefix
|
||||
const ApRawPrefix *pfx = (const ApRawPrefix *) rawPfx;
|
||||
|
||||
cur->isAddPath = TRUE;
|
||||
cur->pathId = pfx->pathId;
|
||||
cur->width = pfx->width;
|
||||
memcpy(cur->bytes, pfx->bytes, PFXLEN(pfx->width));
|
||||
} else {
|
||||
// Regular prefix
|
||||
const RawPrefix *pfx = (const RawPrefix *) rawPfx;
|
||||
|
||||
cur->isAddPath = FALSE;
|
||||
cur->pathId = 0;
|
||||
cur->width = pfx->width;
|
||||
memcpy(cur->bytes, pfx->bytes, PFXLEN(pfx->width));
|
||||
}
|
||||
|
||||
return cur;
|
||||
}
|
||||
|
||||
#define BGP_NOTIFICATION_MINSIZ (BGP_HDRSIZ + 1uLL + 1uLL)
|
||||
|
||||
Bgpnotification *Bgp_GetNotification(Bgpmsg *msg)
|
||||
{
|
||||
Bgphdr *hdr = BGP_HDR(msg);
|
||||
if (hdr->type != BGP_NOTIFICATION) {
|
||||
Bgp_SetErrStat(BGPEBADTYPE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Uint16 len = beswap16(hdr->len);
|
||||
if (len < BGP_NOTIFICATION_MINSIZ) {
|
||||
Bgp_SetErrStat(BGPETRUNCMSG);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return (Bgpnotification *) hdr;
|
||||
}
|
||||
|
||||
Bgpparmseg *Bgp_GetParmsFromMemory(const void *data, size_t size)
|
||||
{
|
||||
const size_t BGP_OPEN_PARMSOFF = BGP_OPEN_MINSIZ - BGP_HDRSIZ;
|
||||
|
||||
if (size < BGP_OPEN_PARMSOFF) {
|
||||
Bgp_SetErrStat(BGPETRUNCMSG);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgpparmseg *parms = (Bgpparmseg *) ((Uint8 *) data + BGP_OPEN_PARMSOFF - 1);
|
||||
size_t nbytes = BGP_OPEN_PARMSOFF + parms->len;
|
||||
if (nbytes != size) {
|
||||
Bgp_SetErrStat((nbytes > size) ? BGPETRUNCMSG : BGPEBADOPENLEN);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return parms;
|
||||
}
|
||||
|
||||
Bgpparmseg *Bgp_GetOpenParms(const Bgpopen *open)
|
||||
{
|
||||
assert(open->hdr.type == BGP_OPEN);
|
||||
|
||||
size_t len = beswap16(open->hdr.len) - BGP_HDRSIZ;
|
||||
|
||||
return Bgp_GetParmsFromMemory(&open->version, len);
|
||||
}
|
||||
|
||||
Bgpwithdrawnseg *Bgp_GetWithdrawnFromMemory(const void *data, size_t size)
|
||||
{
|
||||
if (size < 2uLL) {
|
||||
Bgp_SetErrStat(BGPETRUNCMSG);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgpwithdrawnseg *withdrawn = (Bgpwithdrawnseg *) data;
|
||||
size -= 2;
|
||||
|
||||
if (size < beswap16(withdrawn->len) + 2uLL) { // also accounts for TPA length
|
||||
Bgp_SetErrStat(BGPETRUNCMSG);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return withdrawn;
|
||||
}
|
||||
|
||||
Bgpwithdrawnseg *Bgp_GetUpdateWithdrawn(const Bgpupdate *msg)
|
||||
{
|
||||
assert(msg->hdr.type == BGP_UPDATE);
|
||||
|
||||
size_t len = beswap16(msg->hdr.len);
|
||||
|
||||
return Bgp_GetWithdrawnFromMemory(msg->data, len - BGP_HDRSIZ);
|
||||
}
|
||||
|
||||
Bgpattrseg *Bgp_GetAttributesFromMemory(const void *data, size_t size)
|
||||
{
|
||||
Bgpwithdrawnseg *withdrawn = Bgp_GetWithdrawnFromMemory(data, size);
|
||||
if (!withdrawn)
|
||||
return NULL; // sets error
|
||||
|
||||
size_t withdrawnLen = beswap16(withdrawn->len);
|
||||
|
||||
Bgpattrseg *tpa = (Bgpattrseg *) &withdrawn->nlri[withdrawnLen];
|
||||
size_t tpaLen = beswap16(tpa->len);
|
||||
if (size < 2uLL + withdrawnLen + 2uLL + tpaLen) {
|
||||
Bgp_SetErrStat(BGPETRUNCMSG);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return tpa; // error already cleared
|
||||
}
|
||||
|
||||
Bgpattrseg *Bgp_GetUpdateAttributes(const Bgpupdate *msg)
|
||||
{
|
||||
assert(msg->hdr.type == BGP_UPDATE);
|
||||
|
||||
size_t len = beswap16(msg->hdr.len);
|
||||
|
||||
return Bgp_GetAttributesFromMemory(msg->data, len - BGP_HDRSIZ);
|
||||
}
|
||||
|
||||
void *Bgp_GetNlriFromMemory(const void *nlri, size_t size, size_t *nbytes)
|
||||
{
|
||||
Bgpattrseg *tpa = Bgp_GetAttributesFromMemory(nlri, size);
|
||||
if (!tpa)
|
||||
return NULL; // error already set
|
||||
|
||||
size_t tpaLen = beswap16(tpa->len);
|
||||
if (nbytes) {
|
||||
size_t offset = &tpa->attrs[tpaLen] - (Uint8 *) nlri;
|
||||
|
||||
*nbytes = size - offset;
|
||||
}
|
||||
|
||||
return &tpa->attrs[tpaLen];
|
||||
}
|
||||
|
||||
void *Bgp_GetUpdateNlri(const Bgpupdate *msg, size_t *nbytes)
|
||||
{
|
||||
assert(msg->hdr.type == BGP_UPDATE);
|
||||
|
||||
size_t len = beswap16(msg->hdr.len);
|
||||
|
||||
return Bgp_GetNlriFromMemory(msg->data, len - BGP_HDRSIZ, nbytes);
|
||||
}
|
||||
|
||||
void Bgp_ClearMsg(Bgpmsg *msg)
|
||||
{
|
||||
if (!BGP_ISUNOWNED(msg->flags))
|
||||
BGP_MEMOPS(msg)->Free(msg->allocp, msg->buf);
|
||||
|
||||
msg->flags = 0;
|
||||
msg->buf = NULL;
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file bgp/bgp_local.h
|
||||
*
|
||||
* Private BGP library header.
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*/
|
||||
|
||||
#ifndef DF_BGP_LOCAL_H_
|
||||
#define DF_BGP_LOCAL_H_
|
||||
|
||||
#include "bgp/mrt.h"
|
||||
|
||||
// Low level prefix operations
|
||||
|
||||
void Bgp_InitMpWithdrawn(Bgpmpiter *it, const Bgpwithdrawnseg *withdrawn, const Bgpattr *mpUnreach, Boolean isAddPath);
|
||||
void Bgp_InitMpNlri(Bgpmpiter *it, const void *data, size_t nbytes, const Bgpattr *mpReach, Boolean isAddPath);
|
||||
|
||||
// Low level BGP operations
|
||||
|
||||
Uint16 Bgp_CheckMsgHdr(const void *data, size_t nbytes, Boolean allowExtendedSize);
|
||||
|
||||
Bgpparmseg *Bgp_GetParmsFromMemory(const void *data, size_t size);
|
||||
Bgpwithdrawnseg *Bgp_GetWithdrawnFromMemory(const void *data, size_t size);
|
||||
Bgpattrseg *Bgp_GetAttributesFromMemory(const void *data, size_t size);
|
||||
void *Bgp_GetNlriFromMemory(const void *nlri, size_t size, size_t *nbytes);
|
||||
|
||||
// Extension in attribute.c special iteration on attributes
|
||||
|
||||
/// Non-caching variant of `Bgp_NextAttribute()`, doesn't update `it->table`.
|
||||
Bgpattr *Bgp_NcNextAttribute(Bgpattriter *it);
|
||||
|
||||
#define Bgp_SetErrStat(code) \
|
||||
_Bgp_SetErrStat(code, __FILE__, __func__, __LINE__, 0)
|
||||
|
||||
NOINLINE Judgement _Bgp_SetErrStat(BgpRet code,
|
||||
const char *filename,
|
||||
const char *func,
|
||||
unsigned long long line,
|
||||
unsigned depth);
|
||||
|
||||
#endif
|
@ -0,0 +1,177 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file bgp/bytebuf.c
|
||||
*
|
||||
* Trivial BGP memory allocator for basic BGP workloads.
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*/
|
||||
|
||||
#include "bgp/bytebuf.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
STATIC_ASSERT(BGP_MEMBUF_ALIGN >= 4, "bytebuf.c assumes Uint32 header");
|
||||
|
||||
#define USEDBIT BIT(0)
|
||||
#define MAXBUFCHUNKSIZ 0xffffffffuLL
|
||||
|
||||
#define BLKSIZ(ptr) ((*(Uint32 *) (ptr)) & ~USEDBIT)
|
||||
#define ISUSED(ptr) (((*(Uint32 *) (ptr)) & USEDBIT) != 0)
|
||||
#define SETUSED(ptr) ((void) ((*(Uint32 *) (ptr)) |= USEDBIT))
|
||||
#define CLRUSED(ptr) ((void) ((*(Uint32 *) (ptr)) &= ~USEDBIT))
|
||||
|
||||
static Boolean Mem_IsInBuffer(Bgpbytebuf *buf, void *ptr)
|
||||
{
|
||||
return (Uint8 *) ptr >= buf->base &&
|
||||
(Uint8 *) ptr < buf->base + buf->size;
|
||||
}
|
||||
|
||||
static Uint8 *Mem_FindPrevChunk(Bgpbytebuf *buf, void *chunk)
|
||||
{
|
||||
assert(Mem_IsInBuffer(buf, chunk));
|
||||
|
||||
Uint8 *p = buf->base;
|
||||
while (p < (Uint8 *) chunk) {
|
||||
size_t siz = BLKSIZ(p);
|
||||
|
||||
if (p + siz == (Uint8 *) chunk)
|
||||
return p;
|
||||
|
||||
p += siz;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void Mem_BgpFree(void *allocator, void *ptr)
|
||||
{
|
||||
Bgpbytebuf *buf = (Bgpbytebuf *) allocator;
|
||||
|
||||
// Regular free() for out of buffer allocations
|
||||
if (!Mem_IsInBuffer(buf, ptr)) {
|
||||
free(ptr);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get pointer to chunk
|
||||
Uint8 *p = (Uint8 *) ptr - 4;
|
||||
assert(ISUSED(p));
|
||||
// Get buffer limit
|
||||
Uint8 *lim = buf->base + buf->pos;
|
||||
|
||||
Uint32 siz = BLKSIZ(p);
|
||||
CLRUSED(p); // toggle off USEDBIT
|
||||
|
||||
// Find successor if any
|
||||
Uint8 *next = p + siz;
|
||||
if (next < lim && !ISUSED(next)) {
|
||||
// Merge forward
|
||||
siz += BLKSIZ(next);
|
||||
|
||||
*(Uint32 *) p = siz;
|
||||
}
|
||||
|
||||
// Find predecessor, if any
|
||||
Uint8 *prev = Mem_FindPrevChunk(buf, p);
|
||||
if (prev && !ISUSED(prev)) {
|
||||
// Merge backwards
|
||||
siz += BLKSIZ(prev);
|
||||
p = prev;
|
||||
|
||||
*(Uint32 *) p = siz;
|
||||
}
|
||||
|
||||
// Move position backwards when freeing last block
|
||||
if (p + siz == lim)
|
||||
buf->pos -= siz;
|
||||
}
|
||||
|
||||
static void *Mem_BgpDoRealloc(Bgpbytebuf *buf, void *oldp, size_t nbytes)
|
||||
{
|
||||
// Use plain realloc() if we're not managing a buffered pointer
|
||||
if (!Mem_IsInBuffer(buf, oldp))
|
||||
return realloc(oldp, nbytes);
|
||||
|
||||
assert(IS_PTR_ALIGNED(oldp, BGP_MEMBUF_ALIGN));
|
||||
|
||||
Uint8 *ptr = (Uint8 *) oldp - 4;
|
||||
assert(ISUSED(ptr));
|
||||
|
||||
Uint32 oldSiz = BLKSIZ(ptr);
|
||||
assert(buf->pos >= oldSiz);
|
||||
|
||||
size_t siz = 4 + ALIGN(nbytes, BGP_MEMBUF_ALIGN);
|
||||
if (oldSiz >= siz) {
|
||||
// Shrink operation, free up the trailing part if we're the last chunk
|
||||
if (ptr + oldSiz == buf->base + buf->pos) {
|
||||
*(Uint32 *) ptr = siz | USEDBIT;
|
||||
buf->pos -= (oldSiz - siz);
|
||||
}
|
||||
|
||||
return oldp;
|
||||
}
|
||||
|
||||
// May only grow a chunk if this is the last one and we don't overflow
|
||||
if (ptr + oldSiz != buf->base + buf->pos)
|
||||
return NULL;
|
||||
|
||||
size_t newPos = buf->pos + (siz - oldSiz);
|
||||
if (newPos > buf->size)
|
||||
return NULL;
|
||||
|
||||
// Ok to grow the chunk
|
||||
*(Uint32 *) ptr = siz | USEDBIT;
|
||||
buf->pos = newPos;
|
||||
return oldp;
|
||||
}
|
||||
|
||||
static void *Mem_BgpDoAlloc(Bgpbytebuf *buf, size_t nbytes)
|
||||
{
|
||||
// Use plain malloc() for large allocations or when out of buffer space
|
||||
size_t siz = 4 + ALIGN(nbytes, BGP_MEMBUF_ALIGN);
|
||||
if (buf->pos + siz > buf->size || siz > MAXBUFCHUNKSIZ)
|
||||
return malloc(nbytes);
|
||||
|
||||
// Return the next chunk available
|
||||
Uint32 *ptr = (Uint32 *) (buf->base + buf->pos);
|
||||
buf->pos += siz;
|
||||
assert((siz & USEDBIT) == 0);
|
||||
|
||||
*ptr++ = siz | USEDBIT;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void *Mem_BgpAlloc(void *allocator, size_t nbytes, void *oldp)
|
||||
{
|
||||
Bgpbytebuf *buf = (Bgpbytebuf *) allocator;
|
||||
|
||||
// Handle common allocations with no `oldp`
|
||||
if (!oldp)
|
||||
return Mem_BgpDoAlloc(buf, nbytes);
|
||||
|
||||
// Attempt memory reuse
|
||||
void *ptr = Mem_BgpDoRealloc(buf, oldp, nbytes);
|
||||
if (ptr)
|
||||
return ptr;
|
||||
|
||||
// Fallback to simple allocation+memcpy()
|
||||
ptr = Mem_BgpDoAlloc(buf, nbytes);
|
||||
if (ptr) {
|
||||
memcpy(ptr, oldp, nbytes);
|
||||
Mem_BgpFree(buf, oldp);
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static const MemOps mem_bgpBufTable = {
|
||||
Mem_BgpAlloc,
|
||||
Mem_BgpFree
|
||||
};
|
||||
|
||||
const MemOps *const Mem_BgpBufOps = &mem_bgpBufTable;
|
@ -0,0 +1,82 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file bgp/dump.c
|
||||
*
|
||||
* General BGP dump functions wrappers.
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*/
|
||||
|
||||
#include "bgp/bgp_local.h"
|
||||
#include "bgp/dump.h"
|
||||
#include "sys/endian.h"
|
||||
|
||||
#define CALLFMT(fn, ...) \
|
||||
((fn) ? (fn(__VA_ARGS__)) : ((Sint64) Bgp_SetErrStat(BGPENOERR)))
|
||||
|
||||
Sint64 Bgp_DumpMrtUpdate(const Mrthdr *hdr,
|
||||
void *streamp, const StmOps *ops,
|
||||
const BgpDumpfmt *fmt)
|
||||
{
|
||||
Bgpattrtab table;
|
||||
|
||||
if (!ops->Write) {
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
BGP_CLRATTRTAB(table);
|
||||
if (MRT_ISBGP4MP(hdr->type)) {
|
||||
return CALLFMT(fmt->DumpBgp4mp, hdr, streamp, ops, table);
|
||||
|
||||
} else if (hdr->type == MRT_BGP) {
|
||||
return CALLFMT(fmt->DumpZebra, hdr, streamp, ops, table);
|
||||
|
||||
} else {
|
||||
Bgp_SetErrStat(BGPEBADMRTTYPE);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
Sint64 Bgp_DumpMrtRibv2(const Mrthdr *hdr,
|
||||
const Mrtpeerentv2 *peer, const Mrtribentv2 *ent,
|
||||
void *streamp, const StmOps *ops,
|
||||
const BgpDumpfmt *fmt)
|
||||
{
|
||||
Bgpattrtab table;
|
||||
|
||||
if (!ops->Write) {
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return 0;
|
||||
}
|
||||
if (hdr->type != MRT_TABLE_DUMPV2 || !TABLE_DUMPV2_ISRIB(hdr->subtype)) {
|
||||
Bgp_SetErrStat(BGPEBADMRTTYPE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
BGP_CLRATTRTAB(table);
|
||||
return CALLFMT(fmt->DumpRibv2, hdr, peer, ent, streamp, ops, table);
|
||||
}
|
||||
|
||||
Sint64 Bgp_DumpMrtRib(const Mrthdr *hdr,
|
||||
const Mrtribent *ent,
|
||||
void *streamp, const StmOps *ops,
|
||||
const BgpDumpfmt *fmt)
|
||||
{
|
||||
Bgpattrtab table;
|
||||
|
||||
if (!ops->Write) {
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return 0;
|
||||
}
|
||||
if (hdr->type != MRT_TABLE_DUMP) {
|
||||
Bgp_SetErrStat(BGPEBADMRTTYPE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
BGP_CLRATTRTAB(table);
|
||||
return CALLFMT(fmt->DumpRib, hdr, ent, streamp, ops, table);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,751 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file bgp/mrt.c
|
||||
*
|
||||
* Deals with Multi-Threaded Routing Toolkit (MRT) format.
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*/
|
||||
|
||||
#include "bgp/bgp_local.h"
|
||||
#include "sys/endian.h"
|
||||
#include "sys/interlocked.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
static void *MRT_DATAPTR(const Mrtrecord *rec)
|
||||
{
|
||||
return rec->buf + MRT_HDRSIZ + (MRT_ISEXHDRTYPE(MRT_HDR(rec)->type) << 2);
|
||||
}
|
||||
|
||||
Judgement Bgp_MrtFromBuf(Mrtrecord *rec, const void *buf, size_t nbytes)
|
||||
{
|
||||
if (nbytes < MRT_HDRSIZ)
|
||||
return Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
|
||||
const Mrthdr *hdr = (const Mrthdr *) buf;
|
||||
|
||||
size_t left = beswap32(hdr->len);
|
||||
if (MRT_ISEXHDRTYPE(hdr->type))
|
||||
left += 4; // account for extended timestamp
|
||||
|
||||
size_t siz = sizeof(*hdr) + left;
|
||||
if (siz > nbytes)
|
||||
return Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
|
||||
const MemOps *memOps = MRT_MEMOPS(rec);
|
||||
|
||||
rec->buf = (Uint8 *) memOps->Alloc(rec->allocp, siz, NULL);
|
||||
if (!rec->buf)
|
||||
return Bgp_SetErrStat(BGPENOMEM);
|
||||
|
||||
rec->peerOffTab = NULL;
|
||||
memcpy(rec->buf, buf, siz);
|
||||
return Bgp_SetErrStat(BGPENOERR);
|
||||
}
|
||||
|
||||
Judgement Bgp_ReadMrt(Mrtrecord *rec, void *streamp, const StmOps *ops)
|
||||
{
|
||||
Mrthdr hdr;
|
||||
|
||||
// Read header
|
||||
Sint64 n = ops->Read(streamp, &hdr, sizeof(hdr));
|
||||
if (n == 0) {
|
||||
// Precisely at end of file, no error, just no more records
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return NG;
|
||||
}
|
||||
if (n < 0)
|
||||
return Bgp_SetErrStat(BGPEIO);
|
||||
if ((size_t) n != sizeof(hdr))
|
||||
return Bgp_SetErrStat(BGPEIO);
|
||||
|
||||
size_t left = beswap32(hdr.len);
|
||||
if (MRT_ISEXHDRTYPE(hdr.type))
|
||||
left += 4; // account for extended timestamp
|
||||
|
||||
// Allocate buffer
|
||||
// NOTE: MRT header length doesn't account for header size itself
|
||||
size_t siz = sizeof(hdr) + left;
|
||||
const MemOps *memOps = MRT_MEMOPS(rec);
|
||||
|
||||
rec->buf = (Uint8 *) memOps->Alloc(rec->allocp, siz, NULL);
|
||||
if (!rec->buf)
|
||||
return Bgp_SetErrStat(BGPENOMEM);
|
||||
|
||||
// Populate buffer
|
||||
memcpy(rec->buf, &hdr, sizeof(hdr));
|
||||
n = ops->Read(streamp, rec->buf + sizeof(hdr), left);
|
||||
if (n < 0) {
|
||||
Bgp_SetErrStat(BGPEIO);
|
||||
goto fail;
|
||||
}
|
||||
if ((size_t) n != left) {
|
||||
Bgp_SetErrStat(BGPEIO);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rec->peerOffTab = NULL;
|
||||
return Bgp_SetErrStat(BGPENOERR);
|
||||
|
||||
fail:
|
||||
memOps->Free(rec->allocp, rec->buf);
|
||||
return NG;
|
||||
}
|
||||
|
||||
#define MRT_PEERIDX_MINSIZ (4 + 2 + 2)
|
||||
|
||||
Mrtpeeridx *Bgp_GetMrtPeerIndex(Mrtrecord *rec)
|
||||
{
|
||||
const Mrthdr *hdr = MRT_HDR(rec);
|
||||
if (hdr->type != MRT_TABLE_DUMPV2 || hdr->subtype != TABLE_DUMPV2_PEER_INDEX_TABLE) {
|
||||
Bgp_SetErrStat(BGPEBADMRTTYPE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Basic size check (only for fixed size portion)
|
||||
size_t len = beswap32(hdr->len);
|
||||
size_t siz = MRT_PEERIDX_MINSIZ;
|
||||
if (len < siz) {
|
||||
Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// NOTE: PEER_INDEX_TABLE cannot have extended timestamp
|
||||
assert(!MRT_ISEXHDRTYPE(hdr->subtype));
|
||||
Mrtpeeridx *peerIdx = (Mrtpeeridx *) (hdr + 1);
|
||||
|
||||
// View Name field size check
|
||||
siz += beswap16(peerIdx->viewNameLen);
|
||||
if (len < siz) {
|
||||
Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return peerIdx;
|
||||
}
|
||||
|
||||
void *Bgp_GetMrtPeerIndexPeers(Mrtrecord *rec, size_t *peersCount, size_t *nbytes)
|
||||
{
|
||||
const Mrthdr *hdr = MRT_HDR(rec);
|
||||
if (hdr->type != MRT_TABLE_DUMPV2 || hdr->subtype != TABLE_DUMPV2_PEER_INDEX_TABLE) {
|
||||
Bgp_SetErrStat(BGPEBADMRTTYPE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Basic size check
|
||||
size_t len = beswap32(hdr->len);
|
||||
size_t off = 4; // BGP Identifier
|
||||
if (len < off + 2) { // view name length
|
||||
Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// NOTE: PEER_INDEX_TABLE cannot have extended timestamp
|
||||
assert(!MRT_ISEXHDRTYPE(hdr->subtype));
|
||||
Mrtpeeridx *peerIdx = (Mrtpeeridx *) (hdr + 1);
|
||||
|
||||
// View Name size check
|
||||
off += 2 + beswap16(peerIdx->viewNameLen); // skip view name
|
||||
if (len < off + 2) { // entry count
|
||||
Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
|
||||
// Calculate relevant sizes and return peers chunk
|
||||
if (peersCount) {
|
||||
Uint16 count;
|
||||
|
||||
memcpy(&count, (Uint8 *) peerIdx + off, sizeof(count));
|
||||
*peersCount = beswap16(count);
|
||||
}
|
||||
|
||||
off += 2; // skip peers count
|
||||
|
||||
if (nbytes)
|
||||
*nbytes = len - off;
|
||||
|
||||
return (Uint8 *) peerIdx + off;
|
||||
}
|
||||
|
||||
static size_t MRT_PEERENTSIZ(const Mrtpeerentv2 *ent)
|
||||
{
|
||||
size_t len = 1 + 4;
|
||||
len += MRT_ISPEERASN32BIT(ent->type) ? 4 : 2;
|
||||
len += MRT_ISPEERIPV6(ent->type) ? IPV6_SIZE : IPV4_SIZE;
|
||||
return len;
|
||||
}
|
||||
|
||||
static Mrtpeertabv2 *Bgp_GetPeerOffsetTable(Mrtrecord *rec)
|
||||
{
|
||||
Mrtpeertabv2 *tab;
|
||||
while (TRUE) {
|
||||
tab = (Mrtpeertabv2 *) Smp_AtomicLoadPtrAcq(&rec->peerOffTab);
|
||||
if (tab)
|
||||
break; // already allocated
|
||||
|
||||
// Must allocate the table anew
|
||||
size_t peerCount;
|
||||
if (!Bgp_GetMrtPeerIndexPeers(rec, &peerCount, /*nbytes=*/NULL))
|
||||
return NULL; // bad record type or corrupted PEER_INDEX_TABLE
|
||||
|
||||
const MemOps *memOps = MRT_MEMOPS(rec);
|
||||
|
||||
tab = (Mrtpeertabv2 *) memOps->Alloc(rec->allocp, offsetof(Mrtpeertabv2, offsets[peerCount]), NULL);
|
||||
if (!tab) {
|
||||
Bgp_SetErrStat(BGPENOMEM);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Smp_AtomicStore16Rx(&tab->validCount, 0);
|
||||
Smp_AtomicStore16Rx(&tab->peerCount, peerCount);
|
||||
if (Smp_AtomicCasPtrRel(&rec->peerOffTab, NULL, tab))
|
||||
break; // all good
|
||||
|
||||
memOps->Free(rec->allocp, tab); // ...somebody just allocated the table for us
|
||||
}
|
||||
|
||||
return tab;
|
||||
}
|
||||
|
||||
Mrtpeerentv2 *Bgp_GetMrtPeerByIndex(Mrtrecord *rec, Uint16 idx)
|
||||
{
|
||||
// NOTE: no extended timestamp TABLE_DUMPV2 exists, so we can simplify
|
||||
// record access
|
||||
|
||||
Mrtpeertabv2 *tab = Bgp_GetPeerOffsetTable(rec);
|
||||
if (!tab)
|
||||
return NULL;
|
||||
|
||||
// If we have a `Mrtpeertabv2` we're positively sure that `Mrtpeeridx`
|
||||
// is well formed.
|
||||
const Mrthdr *hdr = MRT_HDR(rec);
|
||||
const Mrtpeeridx *peerIdx = (const Mrtpeeridx *) (hdr + 1);
|
||||
|
||||
size_t baseOff = MRT_HDRSIZ + MRT_PEERIDX_MINSIZ + beswap16(peerIdx->viewNameLen);
|
||||
|
||||
// IMPORTANT INVARIANT: `tab->validCount` may change, but will only ever be
|
||||
// incremented.
|
||||
|
||||
Uint16 validCount = Smp_AtomicLoad16Acq(&tab->validCount);
|
||||
if (idx < validCount) {
|
||||
// FAST PATH: Offset was cached
|
||||
Uint32 off = Smp_AtomicLoad32Rx(&tab->offsets[idx]);
|
||||
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return (Mrtpeerentv2 *) (rec->buf + baseOff + off);
|
||||
}
|
||||
|
||||
// SLOW PATH: Must scan PEER_INDEX_TABLE and update offsets
|
||||
|
||||
// Check that a valid peer was actually requested
|
||||
Uint16 peerCount = Smp_AtomicLoad16Rx(&tab->peerCount);
|
||||
if (idx >= peerCount) {
|
||||
Bgp_SetErrStat(BGPEBADPEERIDX);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* NOTE: We cheat a bit:
|
||||
* - if we have a `peerOffTab`, then we know PEER_INDEX_TABLE header is
|
||||
* well formed, so we can build a `Mrtpeeriterv2` confidently
|
||||
* without checking data integrity;
|
||||
* - we know the data was well formed at least up to the last valid
|
||||
* peer entry, so we can resume iteration there;
|
||||
*/
|
||||
|
||||
Mrtpeeriterv2 it;
|
||||
|
||||
Mrtpeerentv2 *ent;
|
||||
Uint32 lastOff;
|
||||
|
||||
// Initialize iterator to last known offset
|
||||
if (validCount == 0)
|
||||
lastOff = 0;
|
||||
|
||||
else {
|
||||
lastOff = Smp_AtomicLoad32Rx(&tab->offsets[validCount - 1]);
|
||||
ent = (Mrtpeerentv2 *) (rec->buf + baseOff + lastOff);
|
||||
|
||||
lastOff += MRT_PEERENTSIZ(ent);
|
||||
}
|
||||
|
||||
it.base = rec->buf + baseOff;
|
||||
it.lim = rec->buf + MRT_HDRSIZ + beswap32(hdr->len);
|
||||
it.ptr = it.base + lastOff;
|
||||
|
||||
it.peerCount = peerCount;
|
||||
it.nextIdx = validCount;
|
||||
|
||||
// Keep iterating to find the new entry, update table in the process
|
||||
|
||||
/* NOTE: We don't care if we concurrently write offsets to the table
|
||||
* while some other thread also updates that, we know we'll be writing
|
||||
* the same offsets in the same slots there.
|
||||
*/
|
||||
|
||||
Uint16 newValidCount = validCount;
|
||||
do {
|
||||
ent = Bgp_NextMrtPeerv2(&it);
|
||||
if (!ent)
|
||||
return NULL; // error status already set by iterator
|
||||
|
||||
Uint32 off = (Uint8 *) ent - it.base;
|
||||
Smp_AtomicStore32Rx(&tab->offsets[newValidCount], off);
|
||||
|
||||
newValidCount++;
|
||||
} while (idx >= newValidCount);
|
||||
|
||||
// Signal what we've done to the world, don't update anything
|
||||
// if somebody else changed the table under our feet.
|
||||
Smp_AtomicCas16Rel(&tab->validCount, validCount, newValidCount);
|
||||
|
||||
return ent; // success status already set by iterator
|
||||
}
|
||||
|
||||
Judgement Bgp_StartMrtPeersv2(Mrtpeeriterv2 *it, Mrtrecord *rec)
|
||||
{
|
||||
size_t peerCount, nbytes;
|
||||
void *peers = (Uint8 *) Bgp_GetMrtPeerIndexPeers(rec, &peerCount, &nbytes);
|
||||
if (!peers)
|
||||
return NG;
|
||||
|
||||
it->base = (Uint8 *) peers;
|
||||
it->lim = it->base + nbytes;
|
||||
it->ptr = it->base;
|
||||
|
||||
it->peerCount = peerCount;
|
||||
it->nextIdx = 0;
|
||||
return OK; // success already set
|
||||
}
|
||||
|
||||
Mrtpeerentv2 *Bgp_NextMrtPeerv2(Mrtpeeriterv2 *it)
|
||||
{
|
||||
if (it->ptr >= it->lim) {
|
||||
// End of iteration, check for correct peer count
|
||||
if (it->nextIdx == it->peerCount)
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
else
|
||||
Bgp_SetErrStat(BGPEBADPEERIDXCNT);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t left = it->lim - it->ptr;
|
||||
assert(left > 0);
|
||||
|
||||
Mrtpeerentv2 *ent = (Mrtpeerentv2 *) it->ptr;
|
||||
|
||||
size_t len = MRT_PEERENTSIZ(ent);
|
||||
if (left < len) {
|
||||
Bgp_SetErrStat(BGPETRUNCPEERV2);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
it->ptr += len;
|
||||
it->nextIdx++;
|
||||
return ent;
|
||||
}
|
||||
|
||||
Mrtribhdrv2 *Bgp_GetMrtRibHdrv2(Mrtrecord *rec, size_t *nbytes)
|
||||
{
|
||||
const Mrthdr *hdr = MRT_HDR(rec);
|
||||
if (hdr->type != MRT_TABLE_DUMPV2) {
|
||||
Bgp_SetErrStat(BGPEBADMRTTYPE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Mrtribhdrv2 *rib = RIBV2_HDR(hdr);
|
||||
size_t len = beswap32(hdr->len);
|
||||
size_t offset = 0;
|
||||
|
||||
offset += 4; // sequence number
|
||||
|
||||
size_t maxPfxWidth;
|
||||
if (TABLE_DUMPV2_ISGENERICRIB(hdr->subtype)) {
|
||||
offset += 2 + 1; // AFI, SAFI
|
||||
if (len < offset) {
|
||||
Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (rib->gen.afi) {
|
||||
case AFI_IP: maxPfxWidth = IPV4_WIDTH; break;
|
||||
case AFI_IP6: maxPfxWidth = IPV6_WIDTH; break;
|
||||
default:
|
||||
Bgp_SetErrStat(BGPEAFIUNSUP);
|
||||
return NULL;
|
||||
}
|
||||
if (rib->gen.safi != SAFI_UNICAST && rib->gen.safi != SAFI_MULTICAST) {
|
||||
Bgp_SetErrStat(BGPESAFIUNSUP);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
} else if (TABLE_DUMPV2_ISIPV4RIB(hdr->subtype)) {
|
||||
maxPfxWidth = IPV4_WIDTH;
|
||||
} else if (TABLE_DUMPV2_ISIPV6RIB(hdr->subtype)) {
|
||||
maxPfxWidth = IPV6_WIDTH;
|
||||
} else {
|
||||
Bgp_SetErrStat(BGPEBADMRTTYPE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const RawPrefix *pfx = (const RawPrefix *) ((Uint8 *) rib + offset);
|
||||
|
||||
offset++; // prefix width
|
||||
if (len < offset) {
|
||||
Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
return NULL;
|
||||
}
|
||||
if (pfx->width > maxPfxWidth) {
|
||||
Bgp_SetErrStat(BGPEBADPFXWIDTH);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
offset += PFXLEN(pfx->width);
|
||||
if (len < offset) {
|
||||
Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
if (nbytes)
|
||||
*nbytes = offset;
|
||||
|
||||
return rib;
|
||||
}
|
||||
|
||||
Mrtribentriesv2 *Bgp_GetMrtRibEntriesv2(Mrtrecord *rec, size_t *nbytes)
|
||||
{
|
||||
const Mrthdr *hdr = MRT_HDR(rec);
|
||||
if (hdr->type != MRT_TABLE_DUMPV2) {
|
||||
Bgp_SetErrStat(BGPEBADMRTTYPE);
|
||||
return NULL;
|
||||
}
|
||||
if (!TABLE_DUMPV2_ISRIB(hdr->subtype)) {
|
||||
Bgp_SetErrStat(BGPEBADMRTTYPE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t len = beswap32(hdr->len);
|
||||
|
||||
size_t offset;
|
||||
Mrtribhdrv2 *rib = Bgp_GetMrtRibHdrv2(rec, &offset);
|
||||
if (!rib)
|
||||
return NULL; // error already set
|
||||
|
||||
Mrtribentriesv2 *ents = (Mrtribentriesv2 *) ((Uint8 *) rib + offset);
|
||||
|
||||
offset += 2; // entries count
|
||||
if (len < offset) {
|
||||
Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (nbytes)
|
||||
*nbytes = len - offset;
|
||||
|
||||
return ents;
|
||||
}
|
||||
|
||||
Judgement Bgp_StartMrtRibEntriesv2(Mrtribiterv2 *it, Mrtrecord *rec)
|
||||
{
|
||||
size_t nbytes;
|
||||
Mrtribentriesv2 *ents = Bgp_GetMrtRibEntriesv2(rec, &nbytes);
|
||||
if (!ents)
|
||||
return NG;
|
||||
|
||||
it->base = ents->entries;
|
||||
it->lim = it->base + nbytes;
|
||||
it->ptr = it->base;
|
||||
|
||||
it->isAddPath = TABLE_DUMPV2_ISADDPATHRIB(MRT_HDR(rec)->subtype);
|
||||
it->entryCount = beswap16(ents->entryCount);
|
||||
it->nextIdx = 0;
|
||||
return OK;
|
||||
}
|
||||
|
||||
Mrtribentv2 *Bgp_NextRibEntryv2(Mrtribiterv2 *it)
|
||||
{
|
||||
if (it->ptr >= it->lim) {
|
||||
if (it->nextIdx == it->entryCount)
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
else
|
||||
Bgp_SetErrStat(BGPEBADRIBV2CNT);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t left = it->lim - it->ptr;
|
||||
assert(left > 0);
|
||||
|
||||
Mrtribentv2 *ent = (Mrtribentv2 *) it->ptr;
|
||||
|
||||
size_t offset = 2 + 4; // peer index, originated time
|
||||
if (it->isAddPath)
|
||||
offset += 4; // path id
|
||||
|
||||
if (left < offset + 2) { // attributes length
|
||||
Bgp_SetErrStat(BGPETRUNCRIBV2);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgpattrseg *tpa = (Bgpattrseg *) ((Uint8 *) ent + offset);
|
||||
offset += 2 + beswap16(tpa->len);
|
||||
if (left < offset) {
|
||||
Bgp_SetErrStat(BGPETRUNCRIBV2);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
it->ptr += offset;
|
||||
it->nextIdx++;
|
||||
return ent;
|
||||
}
|
||||
|
||||
Bgp4mphdr *Bgp_GetBgp4mpHdr(Mrtrecord *rec, size_t *nbytes)
|
||||
{
|
||||
const Mrthdr *hdr = MRT_HDR(rec);
|
||||
if (!MRT_ISBGP4MP(hdr->type)) {
|
||||
Bgp_SetErrStat(BGPEBADMRTTYPE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t len = beswap32(hdr->len);
|
||||
size_t offset = 0;
|
||||
Afi afi;
|
||||
|
||||
Bgp4mphdr *bgp4mp = (Bgp4mphdr *) MRT_DATAPTR(rec);
|
||||
if (BGP4MP_ISASN32BIT(hdr->subtype)) {
|
||||
offset += 2 * 4;
|
||||
offset += 2 + 2;
|
||||
if (len < offset) {
|
||||
Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
afi = bgp4mp->a32.afi;
|
||||
} else {
|
||||
offset += 2 * 2;
|
||||
offset += 2 + 2;
|
||||
if (len < offset) {
|
||||
Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
afi = bgp4mp->a16.afi;
|
||||
}
|
||||
|
||||
switch (afi) {
|
||||
case AFI_IP: offset += 2 * IPV4_SIZE; break;
|
||||
case AFI_IP6: offset += 2 * IPV6_SIZE; break;
|
||||
default:
|
||||
Bgp_SetErrStat(BGPEAFIUNSUP);
|
||||
return NULL;
|
||||
}
|
||||
if (BGP4MP_ISSTATECHANGE(hdr->subtype))
|
||||
offset += 2 * 2;
|
||||
|
||||
if (len < offset) {
|
||||
Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
if (nbytes)
|
||||
*nbytes = offset;
|
||||
|
||||
return bgp4mp;
|
||||
}
|
||||
|
||||
Judgement Bgp_UnwrapBgp4mp(Mrtrecord *rec, Bgpmsg *dest, unsigned flags)
|
||||
{
|
||||
const Mrthdr *hdr = MRT_HDR(rec);
|
||||
if (!MRT_ISBGP4MP(hdr->type))
|
||||
return Bgp_SetErrStat(BGPEBADMRTTYPE);
|
||||
if (!BGP4MP_ISMESSAGE(hdr->subtype))
|
||||
return Bgp_SetErrStat(BGPEBADMRTTYPE);
|
||||
|
||||
Uint32 len = beswap32(hdr->len);
|
||||
Uint8 *base = (Uint8 *) MRT_DATAPTR(rec);
|
||||
|
||||
// Skip header
|
||||
size_t siz = BGP4MP_ISASN32BIT(hdr->subtype) ? 2*4 : 2*2; // skip ASN
|
||||
siz += 2; // skip interface index
|
||||
|
||||
Afi afi;
|
||||
if (len < siz + sizeof(afi))
|
||||
return Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
|
||||
// Skip AFI and addresses
|
||||
memcpy(&afi, base + siz, sizeof(afi));
|
||||
siz += sizeof(afi);
|
||||
|
||||
switch (afi) {
|
||||
case AFI_IP:
|
||||
siz += 2 * sizeof(Ipv4adr);
|
||||
break;
|
||||
|
||||
case AFI_IP6:
|
||||
siz += 2 * sizeof(Ipv6adr);
|
||||
break;
|
||||
|
||||
default:
|
||||
return Bgp_SetErrStat(BGPEAFIUNSUP);
|
||||
}
|
||||
if (len < siz)
|
||||
return Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
|
||||
size_t msgsiz = len - siz;
|
||||
void *buf = base + siz;
|
||||
|
||||
// Mask away ignored flags
|
||||
flags &= ~(BGPF_ADDPATH|BGPF_ASN32BIT);
|
||||
|
||||
// ...and automatically reset them as defined by BGP4MP subtype
|
||||
if (BGP4MP_ISASN32BIT(hdr->subtype))
|
||||
flags |= BGPF_ASN32BIT;
|
||||
if (BGP4MP_ISADDPATH(hdr->subtype))
|
||||
flags |= BGPF_ADDPATH;
|
||||
|
||||
// Unwrap BGP message
|
||||
return Bgp_MsgFromBuf(dest, buf, msgsiz, flags);
|
||||
}
|
||||
|
||||
#define TABLE_DUMP_MINSIZ (2 + 2 /*+ PFX*/ + 1 + 1 + 4 /*+ IP*/ + 2 + 2)
|
||||
|
||||
Mrtribent *Bgp_GetMrtRibHdr(Mrtrecord *rec)
|
||||
{
|
||||
Mrthdr *hdr = MRT_HDR(rec);
|
||||
if (hdr->type != MRT_TABLE_DUMP) {
|
||||
Bgp_SetErrStat(BGPEBADMRTTYPE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t len = beswap32(hdr->len);
|
||||
size_t siz = TABLE_DUMP_MINSIZ - 2; // do not include attribute length
|
||||
switch (hdr->subtype) {
|
||||
case AFI_IP: siz += 2 * IPV4_SIZE; break;
|
||||
case AFI_IP6: siz += 2 * IPV6_SIZE; break;
|
||||
|
||||
default:
|
||||
Bgp_SetErrStat(BGPEAFIUNSUP);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (len < siz + 2) { // include attribute length in size check
|
||||
Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// NOTE: TABLE_DUMP has no extended timestamp variant
|
||||
Mrtribent *ent = (Mrtribent *) (hdr + 1);
|
||||
|
||||
Uint16 attrLen;
|
||||
memcpy(&attrLen, (Uint8 *) ent + siz, sizeof(attrLen));
|
||||
siz += 2; // now include offset
|
||||
|
||||
siz += beswap16(attrLen);
|
||||
if (len < siz) {
|
||||
Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return (Mrtribent *) (hdr + 1);
|
||||
}
|
||||
|
||||
Zebrahdr *Bgp_GetZebraHdr(Mrtrecord *rec, size_t *nbytes)
|
||||
{
|
||||
Mrthdr *hdr = MRT_HDR(rec);
|
||||
if (hdr->type != MRT_BGP) {
|
||||
Bgp_SetErrStat(BGPEBADMRTTYPE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t len = beswap32(hdr->len);
|
||||
size_t offset = 2 + IPV4_SIZE;
|
||||
if (ZEBRA_ISMESSAGE(hdr->subtype))
|
||||
offset += 2 + IPV4_SIZE;
|
||||
else if (hdr->subtype == ZEBRA_STATE_CHANGE)
|
||||
offset += 2 * 2;
|
||||
|
||||
if (len < offset) {
|
||||
Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
if (nbytes)
|
||||
*nbytes = offset;
|
||||
|
||||
// NOTE: Legacy ZEBRA type doesn't have extended timestamp variants
|
||||
return (Zebrahdr *) (hdr + 1);
|
||||
}
|
||||
|
||||
#define ZEBRA_MSGSIZ (2uLL + IPV4_SIZE + 2uLL + IPV4_SIZE)
|
||||
|
||||
Judgement Bgp_UnwrapZebra(Mrtrecord *rec, Bgpmsg *dest, unsigned flags)
|
||||
{
|
||||
Mrthdr *hdr = MRT_HDR(rec);
|
||||
if (hdr->type != MRT_BGP)
|
||||
return Bgp_SetErrStat(BGPEBADMRTTYPE);
|
||||
|
||||
// Resolve BGP type from ZEBRA subtype
|
||||
BgpType type;
|
||||
switch (hdr->subtype) {
|
||||
case ZEBRA_UPDATE: type = BGP_UPDATE; break;
|
||||
case ZEBRA_OPEN: type = BGP_OPEN; break;
|
||||
case ZEBRA_KEEPALIVE: type = BGP_KEEPALIVE; break;
|
||||
case ZEBRA_NOTIFY: type = BGP_NOTIFICATION; break;
|
||||
|
||||
default: return Bgp_SetErrStat(BGPEBADMRTTYPE);
|
||||
}
|
||||
|
||||
Zebramsghdr *zebra = (Zebramsghdr *) (hdr + 1); // NOTE: ZEBRA doesn't have extended timestamp variants
|
||||
size_t len = beswap32(hdr->len);
|
||||
if (len < ZEBRA_MSGSIZ)
|
||||
return Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
|
||||
// ZEBRA dumps don't include BGP message header
|
||||
size_t zebralen = len - ZEBRA_MSGSIZ;
|
||||
size_t msglen = BGP_HDRSIZ + zebralen;
|
||||
|
||||
// Validate message size
|
||||
if (msglen > BGP_EXMSGSIZ || (msglen > BGP_MSGSIZ && !BGP_ISEXMSG(flags)))
|
||||
return Bgp_SetErrStat(BGPEOVRSIZ);
|
||||
|
||||
// Allocate new message
|
||||
const MemOps *ops = BGP_MEMOPS(dest);
|
||||
Bgphdr *msg = (Bgphdr *) ops->Alloc(dest->allocp, msglen, NULL);
|
||||
if (!msg)
|
||||
return Bgp_SetErrStat(BGPENOMEM);
|
||||
|
||||
// Build a valid BGP header
|
||||
memset(msg->marker, 0xff, sizeof(msg->marker));
|
||||
msg->len = beswap16(msglen);
|
||||
msg->type = type;
|
||||
memcpy(msg + 1, zebra->msg, zebralen);
|
||||
|
||||
// Populate `dest`
|
||||
dest->buf = (Uint8 *) msg;
|
||||
dest->flags = flags & BGPF_EXMSG; // only acceptable flag
|
||||
BGP_CLRATTRTAB(dest->table);
|
||||
return Bgp_SetErrStat(BGPENOERR);
|
||||
}
|
||||
|
||||
void Bgp_ClearMrt(Mrtrecord *rec)
|
||||
{
|
||||
const MemOps *memOps = MRT_MEMOPS(rec);
|
||||
memOps->Free(rec->allocp, rec->buf);
|
||||
memOps->Free(rec->allocp, rec->peerOffTab);
|
||||
|
||||
rec->buf = NULL;
|
||||
rec->peerOffTab = NULL;
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file bgp/parameters.c
|
||||
*
|
||||
* Deals with BGP OPEN parameters.
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*/
|
||||
|
||||
#include "bgp/bgp_local.h"
|
||||
|
||||
Judgement Bgp_StartMsgParms(Bgpparmiter *it, Bgpmsg *msg)
|
||||
{
|
||||
const Bgpopen *open = Bgp_GetMsgOpen(msg);
|
||||
if (!open)
|
||||
return NG; // error already set
|
||||
|
||||
Bgp_StartParms(it, BGP_OPENPARMS(open));
|
||||
return OK; // error already cleared
|
||||
}
|
||||
|
||||
void Bgp_StartParms(Bgpparmiter *it, const Bgpparmseg *p)
|
||||
{
|
||||
it->ptr = (Uint8 *) p->parms;
|
||||
it->base = it->ptr;
|
||||
it->lim = it->base + p->len;
|
||||
}
|
||||
|
||||
Bgpparm *Bgp_NextParm(Bgpparmiter *it)
|
||||
{
|
||||
if (it->ptr >= it->lim) {
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgpparm *p = (Bgpparm *) it->ptr;
|
||||
size_t left = it->lim - it->ptr;
|
||||
if (left < 2uLL || left < 2uLL + p->len) {
|
||||
Bgp_SetErrStat(BGPETRUNCMSG);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
it->ptr += 2 + p->len;
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return p;
|
||||
}
|
||||
|
||||
Judgement Bgp_StartMsgCaps(Bgpcapiter *it, Bgpmsg *msg)
|
||||
{
|
||||
if (Bgp_StartMsgParms(&it->pi, msg) != OK)
|
||||
return NG; // error already set
|
||||
|
||||
// Set starting point so Bgp_NextCap() scans for next parameter
|
||||
it->base = it->lim = it->ptr = it->pi.ptr;
|
||||
return OK;
|
||||
}
|
||||
|
||||
void Bgp_StartCaps(Bgpcapiter *it, const Bgpparmseg *parms)
|
||||
{
|
||||
Bgp_StartParms(&it->pi, parms);
|
||||
// Set starting point so Bgp_NextCap() scans for next parameter
|
||||
it->base = it->lim = it->ptr = it->pi.ptr;
|
||||
}
|
||||
|
||||
Bgpcap *Bgp_NextCap(Bgpcapiter *it)
|
||||
{
|
||||
if (it->ptr >= it->lim) {
|
||||
// Try to find another CAPABILITY parameter
|
||||
Bgpparm *p;
|
||||
|
||||
while ((p = Bgp_NextParm(&it->pi)) != NULL) {
|
||||
if (p->code == BGP_PARM_CAPABILITY)
|
||||
break;
|
||||
}
|
||||
if (!p) {
|
||||
// Scanned all parameters, nothing found
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Setup for reading new capabilities from `p`
|
||||
it->base = (Uint8 *) p + 2;
|
||||
it->lim = it->base + p->len;
|
||||
it->ptr = it->base;
|
||||
}
|
||||
|
||||
// Return current capability and move over
|
||||
size_t left = it->lim - it->ptr;
|
||||
Bgpcap *cap = (Bgpcap *) it->ptr;
|
||||
if (left < 2uLL || left < 2uLL + cap->len) {
|
||||
Bgp_SetErrStat(BGPETRUNCMSG);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
it->ptr += 2 + cap->len;
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return cap;
|
||||
}
|
@ -0,0 +1,518 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file bgp/patricia.c
|
||||
*
|
||||
* Implements PATRICIA trie utilities.
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*/
|
||||
|
||||
#include "bgp/patricia.h"
|
||||
#include "sys/sys.h" // for Sys_OutOfMemory()
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define PAT_BLOCK_ALIGN_SHIFT 2
|
||||
#define PAT_BLOCK_ALIGN (1uLL << PAT_BLOCK_ALIGN_SHIFT)
|
||||
#define PAT_BLOCK_SIZE 2048
|
||||
|
||||
struct Patblock {
|
||||
Patblock *nextBlock;
|
||||
Uint32 freeOfs;
|
||||
ALIGNED(PAT_BLOCK_ALIGN, Uint8 buf[PAT_BLOCK_SIZE]);
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Trie node, contains node and prefix info.
|
||||
*
|
||||
* A node may be either:
|
||||
* - Used - node is in use and part of the trie.
|
||||
* - Unused - node is inside free node list and may be reclaimed on further
|
||||
* node insertion.
|
||||
*/
|
||||
union Patnode {
|
||||
// Following struct is significant when node is used.
|
||||
struct {
|
||||
Patnode *parent; // NOTE: LSB used to mark glue nodes
|
||||
Patnode *children[2];
|
||||
|
||||
// Prefix part follows...
|
||||
Uint8 width;
|
||||
Uint8 bytes[FLEX_ARRAY];
|
||||
};
|
||||
// Following field is significant when node is unused.
|
||||
Patnode *nextFree;
|
||||
};
|
||||
|
||||
static Patnode *Pat_NodeForPrefix(const RawPrefix *pfx)
|
||||
{
|
||||
return (Patnode *) ((Uint8 *) pfx - offsetof(Patnode, width));
|
||||
}
|
||||
|
||||
static Boolean Pat_IsNodeGlue(const Patnode *n)
|
||||
{
|
||||
return (((Uintptr) n->parent) & 1uLL) != 0;
|
||||
}
|
||||
|
||||
static Patnode *Pat_GetNodeParent(const Patnode *n)
|
||||
{
|
||||
return (Patnode *) ((Uintptr) n->parent & ~1uLL);
|
||||
}
|
||||
|
||||
static void Pat_SetNodeParent(Patnode *n, Patnode *parent)
|
||||
{
|
||||
n->parent = (Patnode *) ((Uintptr) parent | (((Uintptr) n->parent) & 1uLL));
|
||||
}
|
||||
|
||||
static void Pat_SetNodeGlue(Patnode *n)
|
||||
{
|
||||
n->parent = (Patnode *) ((Uintptr) n->parent | 1uLL);
|
||||
}
|
||||
|
||||
static void Pat_ResetNodeGlue(Patnode *n)
|
||||
{
|
||||
n->parent = (Patnode *) ((Uintptr) n->parent & ~1uLL);
|
||||
}
|
||||
|
||||
static Patnode *Pat_AllocNode(Patricia *trie, Uint8 width)
|
||||
{
|
||||
Patnode *n;
|
||||
Patblock *block;
|
||||
size_t siz, len;
|
||||
unsigned idx;
|
||||
|
||||
// Calculate block size and lookup inside free cache
|
||||
len = PFXLEN(width);
|
||||
assert(len <= IPV6_SIZE);
|
||||
|
||||
idx = len >> PAT_BLOCK_ALIGN_SHIFT;
|
||||
siz = ALIGN(offsetof(Patnode, bytes[len]), PAT_BLOCK_ALIGN);
|
||||
n = trie->freeBins[idx];
|
||||
if (n) {
|
||||
// ...free list cache hit
|
||||
trie->freeBins[idx] = n->nextFree;
|
||||
|
||||
goto return_node;
|
||||
}
|
||||
|
||||
// Need to allocate a new node
|
||||
block = trie->blocks;
|
||||
if (!block || block->freeOfs + siz > PAT_BLOCK_SIZE) {
|
||||
// Must allocate a new block altoghether
|
||||
block = (Patblock *) malloc(sizeof(*block));
|
||||
if (!block) {
|
||||
Sys_OutOfMemory();
|
||||
return NULL; // too bad...
|
||||
}
|
||||
|
||||
block->freeOfs = 0;
|
||||
block->nextBlock = trie->blocks;
|
||||
trie->blocks = block;
|
||||
}
|
||||
|
||||
n = (Patnode *) (block->buf + block->freeOfs);
|
||||
block->freeOfs += siz;
|
||||
|
||||
return_node:
|
||||
n->parent = NULL;
|
||||
n->children[0] = n->children[1] = NULL;
|
||||
|
||||
n->width = width;
|
||||
return n;
|
||||
}
|
||||
|
||||
static void Pat_FreeNode(Patricia *trie, Patnode *n)
|
||||
{
|
||||
// Place inside free cache bins
|
||||
unsigned idx = PFXLEN(n->width) >> PAT_BLOCK_ALIGN_SHIFT;
|
||||
|
||||
n->nextFree = trie->freeBins[idx];
|
||||
trie->freeBins[idx] = n;
|
||||
}
|
||||
|
||||
RawPrefix *Pat_Insert(Patricia *trie, const RawPrefix *pfx)
|
||||
{
|
||||
Patnode *n;
|
||||
unsigned maxWidth;
|
||||
|
||||
assert(trie->afi == AFI_IP || trie->afi == AFI_IP6);
|
||||
maxWidth = (trie->afi == AFI_IP6) ? IPV6_WIDTH : IPV4_WIDTH;
|
||||
|
||||
assert(pfx->width <= maxWidth);
|
||||
|
||||
n = trie->head;
|
||||
if (!n) {
|
||||
// First node ever, create trie head node
|
||||
n = Pat_AllocNode(trie, pfx->width);
|
||||
if (!n)
|
||||
return NULL;
|
||||
|
||||
memcpy(n->bytes, pfx->bytes, PFXLEN(pfx->width));
|
||||
|
||||
// Place it in `trie`
|
||||
trie->head = n;
|
||||
trie->nprefixes++;
|
||||
return PLAINPFX(n);
|
||||
}
|
||||
|
||||
while (n->width < pfx->width || Pat_IsNodeGlue(n)) {
|
||||
int bit = (n->width < maxWidth) &&
|
||||
(pfx->bytes[n->width >> 3] & (0x80 >> (n->width & 0x07)));
|
||||
|
||||
if (!n->children[bit])
|
||||
break;
|
||||
|
||||
n = n->children[bit];
|
||||
}
|
||||
|
||||
unsigned checkBit = MIN(n->width, pfx->width);
|
||||
unsigned differBit = 0;
|
||||
|
||||
#if 1
|
||||
// unoptimized version
|
||||
unsigned r;
|
||||
for (unsigned i = 0, z = 0; z < checkBit; i++, z += 8) {
|
||||
r = (pfx->bytes[i] ^ n->bytes[i]);
|
||||
if (r == 0) {
|
||||
differBit = z + 8;
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned j;
|
||||
for (j = 0; j < 8; j++)
|
||||
if (r & (0x80 >> j))
|
||||
break;
|
||||
|
||||
differBit = z + j;
|
||||
break;
|
||||
}
|
||||
#else
|
||||
/* TODO possible optimization:
|
||||
* example 32 bit portion with different endianness:
|
||||
|
||||
LSB (visit using LSB->MSB) MSB (LE)
|
||||
01000000 00000000 00000100 00000000
|
||||
MSB (visit using MSB->LSB) LSB (BE)
|
||||
|
||||
leftmost bit is:
|
||||
-> 32 - bsr32() (BE)
|
||||
-> bsf32() - 1 (LE)
|
||||
*/
|
||||
|
||||
for (unsigned i = 0, z = 0; z < checkBit; i++, z += 32) {
|
||||
Uint32 r = (pfx->u32[i] ^ n->u32[i]);
|
||||
|
||||
if (r == 0) {
|
||||
differBit = z + 32;
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned j = (EDN_NATIVE == EDN_BIG) ? 32 - bsr32(r) : bsf32(r) - 1; // clz(beswap32(r));
|
||||
|
||||
differBit = z + j;
|
||||
break;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (differBit > checkBit)
|
||||
differBit = checkBit;
|
||||
|
||||
Patnode *parent = Pat_GetNodeParent(n);
|
||||
while (parent && parent->width >= differBit) {
|
||||
n = parent;
|
||||
parent = Pat_GetNodeParent(n);
|
||||
}
|
||||
|
||||
if (differBit == pfx->width && n->width == pfx->width) {
|
||||
if (Pat_IsNodeGlue(n)) {
|
||||
// Replace glue node
|
||||
Pat_ResetNodeGlue(n);
|
||||
memcpy(n->bytes, pfx->bytes, PFXLEN(pfx->width));
|
||||
}
|
||||
|
||||
trie->nprefixes++;
|
||||
return PLAINPFX(n);
|
||||
}
|
||||
|
||||
// Must allocate new node
|
||||
Patnode *newNode = Pat_AllocNode(trie, pfx->width);
|
||||
if (!newNode)
|
||||
return NULL; // out of memory
|
||||
|
||||
memcpy(newNode->bytes, pfx->bytes, PFXLEN(pfx->width));
|
||||
trie->nprefixes++;
|
||||
|
||||
if (n->width == differBit) {
|
||||
Pat_SetNodeParent(newNode, n);
|
||||
|
||||
int bit = (n->width < maxWidth) &&
|
||||
(pfx->bytes[n->width >> 3] & (0x80 >> (n->width & 0x07)));
|
||||
|
||||
n->children[bit] = newNode;
|
||||
return PLAINPFX(newNode);
|
||||
}
|
||||
|
||||
if (pfx->width == differBit) {
|
||||
int bit = (pfx->width < maxWidth) &&
|
||||
(n->bytes[pfx->width >> 3] & (0x80 >> (pfx->width & 0x07)));
|
||||
|
||||
newNode->children[bit] = n;
|
||||
Pat_SetNodeParent(newNode, n);
|
||||
|
||||
parent = Pat_GetNodeParent(n);
|
||||
if (!parent)
|
||||
trie->head = newNode;
|
||||
|
||||
else if (parent->children[1] == n) {
|
||||
int bit = (parent->children[1] == n);
|
||||
parent->children[bit] = n;
|
||||
}
|
||||
|
||||
Pat_SetNodeParent(n, newNode);
|
||||
} else {
|
||||
Patnode *glue = Pat_AllocNode(trie, differBit);
|
||||
if (!glue)
|
||||
return NULL;
|
||||
|
||||
parent = Pat_GetNodeParent(n);
|
||||
|
||||
glue->parent = parent;
|
||||
Pat_SetNodeGlue(glue);
|
||||
|
||||
int bit = (differBit < maxWidth) &&
|
||||
(pfx->bytes[differBit >> 3] & (0x80 >> (differBit & 0x07)));
|
||||
|
||||
glue->children[bit] = newNode;
|
||||
glue->children[!bit] = n;
|
||||
|
||||
newNode->parent = glue;
|
||||
if (!parent)
|
||||
trie->head = glue;
|
||||
|
||||
else {
|
||||
int bit = (parent->children[1] == n);
|
||||
parent->children[bit] = glue;
|
||||
}
|
||||
|
||||
Pat_SetNodeParent(n, glue);
|
||||
}
|
||||
|
||||
return PLAINPFX(newNode);
|
||||
}
|
||||
|
||||
static Boolean Ip_CompWithMask(const Uint8 *a, const Uint8 *b, Uint8 mask)
|
||||
{
|
||||
unsigned n = mask / 8;
|
||||
|
||||
if (memcmp(a, b, n) == 0) {
|
||||
unsigned m = ~0u << (8 - (mask % 8));
|
||||
|
||||
if ((mask & 0x7) == 0 || (a[n] & m) == (b[n] & m))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
RawPrefix *Pat_SearchExact(const Patricia *trie, const RawPrefix *pfx)
|
||||
{
|
||||
const Patnode *n = trie->head;
|
||||
if (!n)
|
||||
return NULL;
|
||||
|
||||
while (n->width < pfx->width) {
|
||||
int bit = (pfx->bytes[n->width >> 3] & (0x80 >> (n->width & 0x07))) != 0;
|
||||
|
||||
n = n->children[bit];
|
||||
if (!n)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (n->width > pfx->width || Pat_IsNodeGlue(n))
|
||||
return NULL;
|
||||
|
||||
if (Ip_CompWithMask(n->bytes, pfx->bytes, pfx->width))
|
||||
return PLAINPFX(n);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Boolean Pat_IsSubnetOf(const Patricia *trie, const RawPrefix *pfx)
|
||||
{
|
||||
const Patnode *n = trie->head;
|
||||
|
||||
while (n && n->width < pfx->width) {
|
||||
if (!Pat_IsNodeGlue(n))
|
||||
return Ip_CompWithMask(n->bytes, pfx->bytes, n->width);
|
||||
|
||||
int bit = (pfx->bytes[n->width >> 3] & (0x80 >> (n->width & 0x07))) != 0;
|
||||
|
||||
n = n->children[bit];
|
||||
}
|
||||
|
||||
return n && !Pat_IsNodeGlue(n) &&
|
||||
n->width <= pfx->width &&
|
||||
Ip_CompWithMask(n->bytes, pfx->bytes, pfx->width);
|
||||
}
|
||||
|
||||
Boolean Pat_IsSupernetOf(const Patricia *trie, const RawPrefix *pfx)
|
||||
{
|
||||
Patnode *start = trie->head;
|
||||
while (start && start->width < pfx->width) {
|
||||
int bit = (pfx->bytes[start->width >> 3] & (0x80 >> (start->width & 0x07))) != 0;
|
||||
|
||||
start = start->children[bit];
|
||||
}
|
||||
|
||||
Patnode *node;
|
||||
|
||||
Patnode *stack[128+1];
|
||||
Patnode **sp = stack;
|
||||
Patnode *next = start;
|
||||
while ((node = next) != NULL) {
|
||||
if (!Pat_IsNodeGlue(node)) {
|
||||
if (Ip_CompWithMask(node->bytes, pfx->bytes, pfx->width))
|
||||
return TRUE;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (next->children[0]) {
|
||||
if (next->children[1])
|
||||
*sp++ = next->children[1];
|
||||
|
||||
next = next->children[0];
|
||||
} else if (next->children[1]) {
|
||||
next = next->children[1];
|
||||
} else if (sp != stack) {
|
||||
next = *(sp--);
|
||||
} else {
|
||||
next = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
Boolean Pat_IsRelatedOf(const Patricia *trie, const RawPrefix *pfx)
|
||||
{
|
||||
Patnode *start = trie->head;
|
||||
while (start && start->width < pfx->width) {
|
||||
if (!Pat_IsNodeGlue(start) && Ip_CompWithMask(start->bytes, pfx->bytes, start->width))
|
||||
return TRUE;
|
||||
|
||||
int bit = (pfx->bytes[start->width >> 3] & (0x80 >> (start->width & 0x07))) != 0;
|
||||
|
||||
start = start->children[bit];
|
||||
}
|
||||
|
||||
Patnode *node;
|
||||
|
||||
Patnode *stack[128+1];
|
||||
Patnode **sp = stack;
|
||||
Patnode *next = start;
|
||||
while ((node = next) != NULL) {
|
||||
if (!Pat_IsNodeGlue(node) && Ip_CompWithMask(node->bytes, pfx->bytes, pfx->width))
|
||||
return TRUE;
|
||||
|
||||
if (next->children[0]) {
|
||||
if (next->children[1])
|
||||
*sp++ = next->children[1];
|
||||
|
||||
next = next->children[0];
|
||||
} else if (next->children[1]) {
|
||||
next = next->children[1];
|
||||
} else if (sp != stack) {
|
||||
next = *(sp--);
|
||||
} else {
|
||||
next = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
Boolean Pat_Remove(Patricia *trie, const RawPrefix *pfx)
|
||||
{
|
||||
RawPrefix *res = Pat_SearchExact(trie, pfx);
|
||||
if (!res)
|
||||
return FALSE;
|
||||
|
||||
Patnode *n = Pat_NodeForPrefix(res);
|
||||
if (!n)
|
||||
return FALSE;
|
||||
|
||||
trie->nprefixes--;
|
||||
if (n->children[0] && n->children[1]) {
|
||||
Pat_SetNodeGlue(n);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Patnode *parent, *pparent;
|
||||
Patnode *child;
|
||||
int bit;
|
||||
|
||||
parent = Pat_GetNodeParent(n);
|
||||
if (!n->children[0] && !n->children[1]) {
|
||||
Pat_FreeNode(trie, n);
|
||||
if (!parent) {
|
||||
trie->head = NULL;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bit = (parent->children[1] == n);
|
||||
parent->children[bit] = NULL;
|
||||
child = parent->children[!bit];
|
||||
|
||||
if (!Pat_IsNodeGlue(parent))
|
||||
return TRUE;
|
||||
|
||||
// If here, then parent is glue, we need to remove them both
|
||||
pparent = Pat_GetNodeParent(parent);
|
||||
if (!pparent) {
|
||||
trie->head = child;
|
||||
|
||||
} else {
|
||||
bit = (pparent->children[1] == parent);
|
||||
pparent->children[bit] = child;
|
||||
}
|
||||
|
||||
Pat_SetNodeParent(child, pparent);
|
||||
Pat_FreeNode(trie, parent);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bit = (n->children[1] != NULL);
|
||||
child = n->children[bit];
|
||||
|
||||
Pat_SetNodeParent(child, parent);
|
||||
Pat_FreeNode(trie, n);
|
||||
if (!parent) {
|
||||
trie->head = child;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bit = (parent->children[1] == n);
|
||||
parent->children[bit] = child;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void Pat_Clear(Patricia *trie)
|
||||
{
|
||||
while (trie->blocks) {
|
||||
Patblock *t = trie->blocks;
|
||||
|
||||
trie->blocks = t->nextBlock;
|
||||
free(t);
|
||||
}
|
||||
|
||||
trie->afi = 0;
|
||||
trie->nprefixes = 0;
|
||||
trie->head = NULL;
|
||||
memset(trie->freeBins, 0, sizeof(trie->freeBins));
|
||||
}
|
@ -0,0 +1,191 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file bgp/prefix.c
|
||||
*
|
||||
* Deal with network prefixes.
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*/
|
||||
|
||||
#include "bgp/bgp_local.h"
|
||||
#include "sys/endian.h"
|
||||
#include "sys/ip.h"
|
||||
#include "numlib.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
// ===========================================================================
|
||||
// Some performance oriented macros to avoid branching during prefix iteration
|
||||
|
||||
/// Calculate the minimum size of a possibly ADD_PATH enabled prefix.
|
||||
#define MINPFXSIZ(isAddPath) \
|
||||
((((isAddPath) != 0) << 2) + 1)
|
||||
|
||||
/// Extract the prefix portion out of a possibly ADD_PATH enabled prefix pointer.
|
||||
#define RAWPFXPTR(base, isAddPath) \
|
||||
((RawPrefix *) ((Uint8 *) (base) + (((isAddPath) != 0) << 2)))
|
||||
|
||||
/// Calculate maximum prefix width in bits given an address family
|
||||
#define MAXPFXWIDTH(family) (((family) == AFI_IP6) ? IPV6_WIDTH : IPV4_WIDTH) // simple CMOV
|
||||
|
||||
// ===========================================================================
|
||||
|
||||
char *Bgp_PrefixToString(Afi afi, const RawPrefix *prefix, char *dest)
|
||||
{
|
||||
Ipv4adr adr;
|
||||
Ipv6adr adr6;
|
||||
|
||||
switch (afi) {
|
||||
case AFI_IP:
|
||||
memset(&adr, 0, sizeof(adr));
|
||||
memcpy(&adr, prefix->bytes, PFXLEN(prefix->width));
|
||||
dest = Ipv4_AdrToString(&adr, dest);
|
||||
break;
|
||||
|
||||
case AFI_IP6:
|
||||
memset(&adr6, 0, sizeof(adr6));
|
||||
memcpy(&adr6, prefix->bytes, PFXLEN(prefix->width));
|
||||
dest = Ipv6_AdrToString(&adr6, dest);
|
||||
break;
|
||||
|
||||
default:
|
||||
return NULL; // invalid argument
|
||||
}
|
||||
|
||||
*dest++ = '/';
|
||||
dest = Utoa(prefix->width, dest);
|
||||
return dest;
|
||||
}
|
||||
|
||||
char *Bgp_ApPrefixToString(Afi afi, const ApRawPrefix *prefix, char *dest)
|
||||
{
|
||||
// NOTE: Test early to avoid polluting `dest` in case of invalid argument;
|
||||
// hopefully compilers will flatten this function to
|
||||
// eliminate duplicate test inside switch
|
||||
if (afi != AFI_IP && afi != AFI_IP6)
|
||||
return NULL; // invalid argument
|
||||
|
||||
dest = Utoa(beswap32(prefix->pathId), dest);
|
||||
*dest++ = ' ';
|
||||
return Bgp_PrefixToString(afi, PLAINPFX(prefix), dest);
|
||||
}
|
||||
|
||||
Judgement Bgp_StringToPrefix(const char *s, Prefix *dest)
|
||||
{
|
||||
Ipadr adr;
|
||||
unsigned width;
|
||||
NumConvRet res;
|
||||
|
||||
const char *ptr = s;
|
||||
while (*ptr != '/' && *ptr != '\0') ptr++;
|
||||
|
||||
size_t len = ptr - s;
|
||||
char *buf = (char *) alloca(len + 1);
|
||||
|
||||
memcpy(buf, s, len);
|
||||
buf[len] = '\0';
|
||||
|
||||
if (Ip_StringToAdr(buf, &adr) != OK)
|
||||
return NG; // Bad IP string
|
||||
|
||||
if (*ptr == '/') {
|
||||
ptr++; // skip '/' separator
|
||||
|
||||
char *eptr;
|
||||
width = Atou(ptr, &eptr, 10, &res);
|
||||
|
||||
if (res != NCVENOERR || *eptr != '\0')
|
||||
return NG;
|
||||
} else
|
||||
width = (adr.family == IP6) ? IPV6_WIDTH : IPV4_WIDTH; // implicit full prefix
|
||||
|
||||
switch (adr.family) {
|
||||
case IP4:
|
||||
if (width > IPV4_WIDTH) return NG; // illegal prefix length
|
||||
|
||||
dest->afi = AFI_IP;
|
||||
break;
|
||||
|
||||
case IP6:
|
||||
if (width > IPV6_WIDTH) return NG; // illegal prefix length
|
||||
|
||||
dest->afi = AFI_IP6;
|
||||
break;
|
||||
|
||||
default:
|
||||
UNREACHABLE;
|
||||
return NG;
|
||||
}
|
||||
|
||||
dest->isAddPath = FALSE;
|
||||
dest->width = width;
|
||||
memcpy(dest->bytes, adr.bytes, PFXLEN(width));
|
||||
return OK;
|
||||
}
|
||||
|
||||
Judgement Bgp_StartPrefixes(Prefixiter *it,
|
||||
Afi afi,
|
||||
Safi safi,
|
||||
const void *data,
|
||||
size_t nbytes,
|
||||
Boolean isAddPath)
|
||||
{
|
||||
if (afi != AFI_IP && afi != AFI_IP6) {
|
||||
Bgp_SetErrStat(BGPEAFIUNSUP);
|
||||
return NG;
|
||||
}
|
||||
if (safi != SAFI_UNICAST && safi != SAFI_MULTICAST) {
|
||||
Bgp_SetErrStat(BGPESAFIUNSUP);
|
||||
return NG;
|
||||
}
|
||||
|
||||
it->afi = afi;
|
||||
it->safi = safi;
|
||||
it->isAddPath = isAddPath;
|
||||
it->base = (Uint8 *) data;
|
||||
it->lim = it->base + nbytes;
|
||||
it->ptr = it->base;
|
||||
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return OK;
|
||||
}
|
||||
|
||||
void *Bgp_NextPrefix(Prefixiter *it)
|
||||
{
|
||||
if (it->ptr >= it->lim) {
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return NULL; // end of iteration
|
||||
}
|
||||
|
||||
// Basic check for prefix initial bytes
|
||||
size_t left = it->lim - it->ptr;
|
||||
size_t siz = MINPFXSIZ(it->isAddPath);
|
||||
if (left < siz) {
|
||||
Bgp_SetErrStat(BGPETRUNCPFX);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Adjust a pointer to skip Path identifier info if necessary
|
||||
const RawPrefix *rawPfx = RAWPFXPTR(it->ptr, it->isAddPath);
|
||||
if (rawPfx->width > MAXPFXWIDTH(it->afi)) {
|
||||
Bgp_SetErrStat(BGPEBADPFXWIDTH);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Ensure all the necessary prefix bytes are present
|
||||
siz += PFXLEN(rawPfx->width);
|
||||
if (left < siz) {
|
||||
Bgp_SetErrStat(BGPETRUNCPFX);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// All good, advance and return
|
||||
void *pfx = it->ptr;
|
||||
it->ptr += siz;
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
|
||||
return pfx;
|
||||
}
|
@ -0,0 +1,838 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file bgp/vm.c
|
||||
*
|
||||
* BGP VM initialization and execution loop.
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*/
|
||||
|
||||
#include "bgp/bgp_local.h"
|
||||
#include "bgp/patricia.h"
|
||||
#include "bgp/vmintrin.h"
|
||||
#include "sys/endian.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(__GNUC__) && !defined(DF_BGP_VM_NO_COMPUTED_GOTO)
|
||||
#define DF_BGP_VM_USES_COMPUTED_GOTO
|
||||
#include "bgp/vm_gccdef.h"
|
||||
#else
|
||||
#include "bgp/vm_cdef.h"
|
||||
#endif
|
||||
|
||||
#define BGP_VM_MINHEAPSIZ (4 * 1024)
|
||||
#define BGP_VM_STKSIZ (4 * 1024)
|
||||
|
||||
#define BGP_VM_GROWPROGN 128
|
||||
|
||||
/* During VM execution instructions update the current BGP message
|
||||
* match. But sometimes the match is irrelevant (think about something
|
||||
* like:
|
||||
*
|
||||
* ```
|
||||
* LOADU 1
|
||||
* NOT
|
||||
* CPASS
|
||||
* ```
|
||||
*
|
||||
* This bytecode doesn't examine any actual BGP message segment,
|
||||
* to simplify instructions, whenever an irrelevant match is being produced,
|
||||
* the following static variable is referenced by `vm->curMatch`,
|
||||
* to provide a dummy match that can be updated at will
|
||||
* by any VM and is always discarded by `Bgp_VmStoreMatch()`.
|
||||
*/
|
||||
static Bgpvmmatch discardMatch;
|
||||
|
||||
Judgement Bgp_InitVm(Bgpvm *vm, size_t heapSiz)
|
||||
{
|
||||
size_t siz = BGP_VM_STKSIZ + MAX(heapSiz, BGP_VM_MINHEAPSIZ);
|
||||
siz = ALIGN(siz, ALIGNMENT);
|
||||
|
||||
assert(siz <= 0xffffffffuLL);
|
||||
|
||||
void *heap = malloc(siz);
|
||||
if (!heap)
|
||||
return Bgp_SetErrStat(BGPENOMEM);
|
||||
|
||||
memset(vm, 0, sizeof(*vm));
|
||||
|
||||
vm->heap = heap;
|
||||
vm->hMemSiz = siz;
|
||||
vm->hHighMark = siz;
|
||||
return Bgp_SetErrStat(BGPENOERR);
|
||||
}
|
||||
|
||||
Judgement Bgp_VmEmit(Bgpvm *vm, Bgpvmbytec bytec)
|
||||
{
|
||||
assert(!vm->isRunning);
|
||||
|
||||
BGP_VMCLRERR(vm);
|
||||
|
||||
if (BGP_VMOPC(bytec) == BGP_VMOP_END)
|
||||
return Bgp_SetErrStat(BGPENOERR); // ignore useless emit
|
||||
|
||||
if (vm->progLen + 1 >= vm->progCap) {
|
||||
// Grow the VM program segment
|
||||
size_t newSiz = vm->progCap + BGP_VM_GROWPROGN;
|
||||
Bgpvmbytec *newProg = (Bgpvmbytec *) realloc(vm->prog, newSiz * sizeof(*newProg));
|
||||
if (!newProg) {
|
||||
// Flag the VM as bad
|
||||
vm->setupFailed = TRUE;
|
||||
vm->errCode = BGPENOMEM;
|
||||
|
||||
return Bgp_SetErrStat(BGPENOMEM);
|
||||
}
|
||||
|
||||
vm->prog = newProg;
|
||||
vm->progCap = newSiz;
|
||||
}
|
||||
|
||||
// Append instruction and follow it with BGP_VMOP_END
|
||||
vm->prog[vm->progLen++] = bytec;
|
||||
vm->prog[vm->progLen] = BGP_VMOP_END;
|
||||
return Bgp_SetErrStat(BGPENOERR);
|
||||
}
|
||||
|
||||
void *Bgp_VmPermAlloc(Bgpvm *vm, size_t size)
|
||||
{
|
||||
assert(!vm->isRunning);
|
||||
|
||||
BGP_VMCLRERR(vm);
|
||||
|
||||
size = ALIGN(size, ALIGNMENT);
|
||||
|
||||
if (vm->hLowMark + size > vm->hMemSiz) {
|
||||
// Flag the VM as bad
|
||||
vm->setupFailed = TRUE;
|
||||
vm->errCode = BGPEVMOOM;
|
||||
|
||||
Bgp_SetErrStat(BGPEVMOOM);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *ptr = (Uint8 *) vm->heap + vm->hLowMark;
|
||||
vm->hLowMark += size;
|
||||
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void *Bgp_VmTempAlloc(Bgpvm *vm, size_t size)
|
||||
{
|
||||
assert(vm->isRunning);
|
||||
|
||||
size = ALIGN(size, ALIGNMENT);
|
||||
|
||||
size_t stksiz = vm->si * sizeof(Bgpvmval);
|
||||
if (vm->hLowMark + stksiz + size > vm->hHighMark) UNLIKELY {
|
||||
// NOTE: VM is being executed, don't set BGP error state
|
||||
// it will be updated by Bgp_VmExec() as needed
|
||||
vm->errCode = BGPEVMOOM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
assert(vm->hHighMark >= size);
|
||||
|
||||
vm->hHighMark -= size;
|
||||
return (Uint8 *) vm->heap + vm->hHighMark;
|
||||
}
|
||||
|
||||
void Bgp_VmTempFree(Bgpvm *vm, size_t size)
|
||||
{
|
||||
assert(vm->isRunning);
|
||||
|
||||
size = ALIGN(size, ALIGNMENT);
|
||||
|
||||
assert(size + vm->hHighMark <= vm->hMemSiz);
|
||||
vm->hHighMark += size;
|
||||
}
|
||||
|
||||
Boolean Bgp_VmExec(Bgpvm *vm, Bgpmsg *msg)
|
||||
{
|
||||
// Fundamental sanity checks
|
||||
assert(!vm->isRunning);
|
||||
|
||||
if (vm->setupFailed) UNLIKELY {
|
||||
vm->errCode = BGPEBADVM;
|
||||
goto cant_run;
|
||||
}
|
||||
if (!vm->prog) UNLIKELY {
|
||||
vm->errCode = BGPEVMNOPROG;
|
||||
goto cant_run;
|
||||
}
|
||||
|
||||
// Setup initial VM state
|
||||
Boolean result = TRUE; // assume PASS unless CFAIL says otherwise
|
||||
|
||||
vm->pc = 0;
|
||||
vm->si = 0;
|
||||
vm->nblk = 0;
|
||||
vm->nmatches = 0;
|
||||
vm->hHighMark = vm->hMemSiz;
|
||||
vm->msg = msg;
|
||||
vm->curMatch = &discardMatch;
|
||||
vm->matches = NULL;
|
||||
vm->errCode = BGPENOERR;
|
||||
|
||||
// Populate computed goto table if necessary
|
||||
#ifdef DF_BGP_VM_USES_COMPUTED_GOTO
|
||||
#include "bgp/vm_optab.h"
|
||||
#endif
|
||||
|
||||
// Execute bytecode according to the #included vm_<impl>def.h
|
||||
Bgpvmbytec ir; // Instruction Register
|
||||
|
||||
vm->isRunning = TRUE;
|
||||
while (TRUE) {
|
||||
// FETCH stage
|
||||
FETCH(ir, vm);
|
||||
|
||||
// DECODE-DISPATCH stage
|
||||
DISPATCH(BGP_VMOPC(ir)) {
|
||||
// EXECUTE stage
|
||||
EXECUTE(NOP): UNLIKELY;
|
||||
break;
|
||||
EXECUTE(LOAD):
|
||||
Bgp_VmDoLoad(vm, (Sint8) BGP_VMOPARG(ir));
|
||||
break;
|
||||
EXECUTE(LOADU):
|
||||
Bgp_VmDoLoadu(vm, BGP_VMOPARG(ir));
|
||||
break;
|
||||
EXECUTE(LOADN):
|
||||
Bgp_VmDoLoadn(vm);
|
||||
break;
|
||||
EXECUTE(LOADK):
|
||||
Bgp_VmDoLoadk(vm, BGP_VMOPARG(ir));
|
||||
break;
|
||||
EXECUTE(CALL):
|
||||
Bgp_VmDoCall(vm, BGP_VMOPARG(ir));
|
||||
break;
|
||||
EXECUTE(BLK):
|
||||
vm->nblk++;
|
||||
break;
|
||||
EXECUTE(ENDBLK):
|
||||
if (vm->nblk == 0) UNLIKELY {
|
||||
vm->errCode = BGPEVMBADENDBLK;
|
||||
goto terminate;
|
||||
}
|
||||
|
||||
vm->nblk--;
|
||||
break;
|
||||
EXECUTE(TAG):
|
||||
Bgp_VmDoTag(vm, BGP_VMOPARG(ir));
|
||||
break;
|
||||
EXECUTE(NOT):
|
||||
Bgp_VmDoNot(vm);
|
||||
|
||||
EXPECT(CFAIL, ir, vm);
|
||||
EXPECT(CPASS, ir, vm);
|
||||
break;
|
||||
EXECUTE(CFAIL):
|
||||
if (Bgp_VmDoCfail(vm)) {
|
||||
result = FALSE; // immediate terminate on FAIL
|
||||
goto terminate;
|
||||
}
|
||||
|
||||
break;
|
||||
EXECUTE(CPASS):
|
||||
if (Bgp_VmDoCpass(vm))
|
||||
goto terminate; // immediate PASS
|
||||
|
||||
break;
|
||||
EXECUTE(JZ):
|
||||
if (!BGP_VMCHKSTKSIZ(vm, 1)) UNLIKELY
|
||||
break;
|
||||
|
||||
if (!BGP_VMPEEK(vm, -1)) {
|
||||
// Zero, do jump
|
||||
vm->pc += BGP_VMOPARG(ir);
|
||||
if (vm->pc > vm->progLen) UNLIKELY
|
||||
vm->errCode = BGPEVMBADJMP; // jump target out of bounds
|
||||
|
||||
} else
|
||||
BGP_VMPOP(vm); // no jump, pop the stack and move on
|
||||
|
||||
break;
|
||||
EXECUTE(JNZ):
|
||||
if (!BGP_VMCHKSTKSIZ(vm, 1)) UNLIKELY
|
||||
break;
|
||||
|
||||
if (BGP_VMPEEK(vm, -1)) {
|
||||
// Non-Zero, do jump
|
||||
vm->pc += BGP_VMOPARG(ir);
|
||||
if (vm->pc > vm->progLen) UNLIKELY
|
||||
vm->errCode = BGPEVMBADJMP; // jump target out of bounds
|
||||
|
||||
} else
|
||||
BGP_VMPOP(vm); // no jump, pop the stack and move on
|
||||
|
||||
break;
|
||||
EXECUTE(CHKT):
|
||||
Bgp_VmDoChkt(vm, (BgpType) BGP_VMOPARG(ir));
|
||||
break;
|
||||
EXECUTE(CHKA):
|
||||
Bgp_VmDoChka(vm, (BgpAttrCode) BGP_VMOPARG(ir));
|
||||
break;
|
||||
EXECUTE(EXCT):
|
||||
Bgp_VmDoExct(vm, BGP_VMOPARG(ir));
|
||||
break;
|
||||
EXECUTE(SUPN):
|
||||
Bgp_VmDoSupn(vm, BGP_VMOPARG(ir));
|
||||
break;
|
||||
EXECUTE(SUBN):
|
||||
Bgp_VmDoSubn(vm, BGP_VMOPARG(ir));
|
||||
break;
|
||||
EXECUTE(RELT):
|
||||
Bgp_VmDoRelt(vm, BGP_VMOPARG(ir));
|
||||
break;
|
||||
EXECUTE(ASMTCH):
|
||||
Bgp_VmDoAsmtch(vm);
|
||||
break;
|
||||
EXECUTE(FASMTC):
|
||||
Bgp_VmDoFasmtc(vm);
|
||||
break;
|
||||
EXECUTE(COMTCH):
|
||||
Bgp_VmDoComtch(vm);
|
||||
break;
|
||||
EXECUTE(ACOMTC):
|
||||
Bgp_VmDoAcomtc(vm);
|
||||
break;
|
||||
EXECUTE(END): UNLIKELY;
|
||||
// Implicitly PASS current match
|
||||
vm->curMatch->isPassing = TRUE;
|
||||
goto terminate;
|
||||
EXECUTE_SIGILL: UNLIKELY;
|
||||
vm->errCode = BGPEVMILL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (vm->errCode) UNLIKELY
|
||||
goto terminate; // error encountered, abort execution
|
||||
}
|
||||
|
||||
terminate:
|
||||
vm->curMatch = NULL; // prevent accidental access outside Bgp_VmExec()
|
||||
vm->isRunning = FALSE;
|
||||
|
||||
if (Bgp_SetErrStat(vm->errCode) != OK) UNLIKELY
|
||||
result = FALSE;
|
||||
|
||||
return result;
|
||||
|
||||
cant_run:
|
||||
Bgp_SetErrStat(vm->errCode);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
Judgement Bgp_VmStoreMsgTypeMatch(Bgpvm *vm, Boolean isMatching)
|
||||
{
|
||||
vm->curMatch = (Bgpvmmatch *) Bgp_VmTempAlloc(vm, sizeof(*vm->curMatch));
|
||||
if (!vm->curMatch) UNLIKELY
|
||||
return NG;
|
||||
|
||||
Bgphdr *hdr = BGP_HDR(vm->msg);
|
||||
|
||||
vm->curMatch->pc = BGP_VMCURPC(vm);
|
||||
vm->curMatch->tag = 0;
|
||||
vm->curMatch->base = (Uint8 *) hdr;
|
||||
vm->curMatch->lim = (Uint8 *) (hdr + 1);
|
||||
vm->curMatch->pos = &hdr->type;
|
||||
vm->curMatch->isMatching = isMatching;
|
||||
|
||||
Bgp_VmStoreMatch(vm);
|
||||
return OK;
|
||||
}
|
||||
|
||||
void Bgp_VmStoreMatch(Bgpvm *vm)
|
||||
{
|
||||
assert(vm->isRunning);
|
||||
|
||||
if (vm->curMatch == &discardMatch)
|
||||
return; // discard store request
|
||||
|
||||
// Prepend match to matches list, still keep the `curMatch` pointer
|
||||
// around in case result is updated by following instructions (e.g. NOT)
|
||||
vm->curMatch->nextMatch = vm->matches;
|
||||
vm->matches = vm->curMatch;
|
||||
vm->nmatches++;
|
||||
}
|
||||
|
||||
Boolean Bgp_VmDoCpass(Bgpvm *vm)
|
||||
{
|
||||
/* POPS:
|
||||
* -1: Last operation Boolean result (as a Sint64, 0 for FALSE)
|
||||
*
|
||||
* PUSHES:
|
||||
* * On PASS result:
|
||||
* - TRUE
|
||||
* * Otherwise:
|
||||
* - Nothing.
|
||||
*
|
||||
* SIDE-EFFECTS:
|
||||
* - Breaks current BLK on PASS
|
||||
* - Updates `curMatch->isPassing` flag accordingly
|
||||
*/
|
||||
|
||||
if (!BGP_VMCHKSTKSIZ(vm, 1)) UNLIKELY
|
||||
return FALSE; // error, let vm->errCode handle this
|
||||
|
||||
Boolean shouldTerm = FALSE; // unless proven otherwise
|
||||
|
||||
// If stack top is non-zero we FAIL
|
||||
if (BGP_VMPEEK(vm, -1)) {
|
||||
// Leave TRUE on stack and break current BLK, this is a PASS
|
||||
vm->curMatch->isPassing = TRUE;
|
||||
|
||||
if (vm->nblk > 0)
|
||||
Bgp_VmDoBreak(vm);
|
||||
else
|
||||
shouldTerm = TRUE; // no more BLK
|
||||
|
||||
} else {
|
||||
// Pop the stack and move on, no PASS
|
||||
vm->curMatch->isPassing = FALSE;
|
||||
BGP_VMPOP(vm);
|
||||
}
|
||||
|
||||
// Current match information has been collected,
|
||||
// discard anything up to the next relevant operation
|
||||
vm->curMatch = &discardMatch;
|
||||
|
||||
return shouldTerm;
|
||||
}
|
||||
|
||||
Boolean Bgp_VmDoCfail(Bgpvm *vm)
|
||||
{
|
||||
/* POPS:
|
||||
* -1: Last operation Boolean result (as a Sint64, 0 for FALSE)
|
||||
*
|
||||
* PUSHES:
|
||||
* * On FAIL result:
|
||||
* - FALSE
|
||||
* * Otherwise:
|
||||
* - Nothing.
|
||||
*
|
||||
* SIDE-EFFECTS:
|
||||
* - Breaks current BLK on FAIL
|
||||
* - Updates `curMatch->isPassing` flag accordingly
|
||||
*/
|
||||
|
||||
if (!BGP_VMCHKSTKSIZ(vm, 1)) UNLIKELY
|
||||
return FALSE; // error, let vm->errCode handle this
|
||||
|
||||
Boolean shouldTerm = FALSE; // unless proven otherwise
|
||||
|
||||
// If stack top is non-zero we FAIL
|
||||
Bgpvmval *v = BGP_VMSTKGET(vm, -1);
|
||||
if (v->val) {
|
||||
// Push FALSE and break current BLK, this is a FAIL
|
||||
vm->curMatch->isPassing = FALSE;
|
||||
|
||||
v->val = FALSE;
|
||||
if (vm->nblk > 0)
|
||||
Bgp_VmDoBreak(vm);
|
||||
else
|
||||
shouldTerm = TRUE; // no more BLK
|
||||
|
||||
} else {
|
||||
// Pop the stack and move on, no FAIL
|
||||
vm->curMatch->isPassing = TRUE;
|
||||
BGP_VMPOP(vm);
|
||||
}
|
||||
|
||||
// Current match information has been collected,
|
||||
// discard anything up to the next relevant operation
|
||||
vm->curMatch = &discardMatch;
|
||||
|
||||
return shouldTerm;
|
||||
}
|
||||
|
||||
void Bgp_VmDoChkt(Bgpvm *vm, BgpType type)
|
||||
{
|
||||
/* PUSHES:
|
||||
* TRUE if type matches, FALSE otherwise
|
||||
*/
|
||||
|
||||
if (!BGP_VMCHKSTK(vm, 1)) UNLIKELY
|
||||
return;
|
||||
|
||||
Boolean isMatching = (BGP_VMCHKMSGTYPE(vm, type) != NULL);
|
||||
|
||||
BGP_VMPUSH(vm, isMatching);
|
||||
Bgp_VmStoreMsgTypeMatch(vm, isMatching);
|
||||
}
|
||||
|
||||
void Bgp_VmDoChka(Bgpvm *vm, BgpAttrCode code)
|
||||
{
|
||||
/* PUSHES:
|
||||
* TRUE if attribute exists inside UPDATE message, FALSE otherwise
|
||||
*/
|
||||
|
||||
if (!BGP_VMCHKSTK(vm, 1)) UNLIKELY
|
||||
return;
|
||||
|
||||
Bgpupdate *update = (Bgpupdate *) BGP_VMCHKMSGTYPE(vm, BGP_UPDATE);
|
||||
if (!update) {
|
||||
Bgp_VmStoreMsgTypeMatch(vm, /*isMatching=*/FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Attribute lookup
|
||||
Bgpattrseg *tpa = Bgp_GetUpdateAttributes(update);
|
||||
if (!tpa) {
|
||||
vm->errCode = BGPEVMMSGERR;
|
||||
return;
|
||||
}
|
||||
|
||||
Bgpattr *attr = Bgp_GetUpdateAttribute(tpa, code, vm->msg->table);
|
||||
if (Bgp_GetErrStat(NULL)) {
|
||||
vm->errCode = BGPEVMMSGERR;
|
||||
return;
|
||||
}
|
||||
|
||||
Boolean isMatching = (attr != NULL);
|
||||
BGP_VMPUSH(vm, isMatching);
|
||||
|
||||
// Create a new match
|
||||
vm->curMatch = (Bgpvmmatch *) Bgp_VmTempAlloc(vm, sizeof(*vm->curMatch));
|
||||
if (!vm->curMatch) UNLIKELY
|
||||
return;
|
||||
|
||||
vm->curMatch->pc = BGP_VMCURPC(vm);
|
||||
vm->curMatch->tag = 0;
|
||||
vm->curMatch->base = tpa->attrs;
|
||||
vm->curMatch->lim = &tpa->attrs[beswap16(tpa->len)];
|
||||
vm->curMatch->pos = attr;
|
||||
vm->curMatch->isMatching = isMatching;
|
||||
|
||||
Bgp_VmStoreMatch(vm);
|
||||
}
|
||||
|
||||
static Judgement Bgp_VmStartNets(Bgpvm *vm, Bgpmpiter *it, Uint8 mode)
|
||||
{
|
||||
if (!BGP_VMCHKMSGTYPE(vm, BGP_UPDATE)) {
|
||||
Bgp_VmStoreMsgTypeMatch(vm, /*isMatching=*/FALSE);
|
||||
return NG;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case BGP_VMOPA_NLRI:
|
||||
Bgp_StartMsgNlri(&it->rng, vm->msg);
|
||||
it->nextAttr = NULL;
|
||||
break;
|
||||
case BGP_VMOPA_WITHDRAWN:
|
||||
Bgp_StartMsgWithdrawn(&it->rng, vm->msg);
|
||||
it->nextAttr = NULL;
|
||||
break;
|
||||
case BGP_VMOPA_ALL_NLRI:
|
||||
Bgp_StartAllMsgNlri(it, vm->msg);
|
||||
break;
|
||||
case BGP_VMOPA_ALL_WITHDRAWN:
|
||||
Bgp_StartAllMsgWithdrawn(it, vm->msg);
|
||||
break;
|
||||
|
||||
default: UNLIKELY;
|
||||
vm->errCode = BGPEVMBADOP;
|
||||
return NG;
|
||||
}
|
||||
if (Bgp_GetErrStat(NULL)) UNLIKELY {
|
||||
vm->errCode = BGPEVMMSGERR;
|
||||
return NG;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static void Bgp_VmCollectNetMatch(Bgpvm *vm, Bgpmpiter *it)
|
||||
{
|
||||
// Push on stack first --
|
||||
// we know we have at least one available spot on the stack for
|
||||
// any network operation (we POP at least one Patricia address from it).
|
||||
// Perform the temporary allocation afterwards.
|
||||
// This saves one check on stack space
|
||||
BGP_VMPUSH(vm, TRUE);
|
||||
|
||||
vm->curMatch = (Bgpvmmatch *) Bgp_VmTempAlloc(vm, sizeof(*vm->curMatch));
|
||||
if (!vm->curMatch) UNLIKELY
|
||||
return;
|
||||
|
||||
vm->curMatch->pc = BGP_VMCURPC(vm);
|
||||
vm->curMatch->tag = 0;
|
||||
vm->curMatch->base = BGP_CURMPBASE(it);
|
||||
vm->curMatch->lim = BGP_CURMPLIM(it);
|
||||
vm->curMatch->pos = BGP_CURMPPFX(it);
|
||||
vm->curMatch->isMatching = TRUE;
|
||||
|
||||
Bgp_VmStoreMatch(vm);
|
||||
}
|
||||
|
||||
static void Bgp_VmNetMatchFailed(Bgpvm *vm)
|
||||
{
|
||||
BGP_VMPUSH(vm, FALSE); // NOTE: See `Bgp_VmCollectNetMatch()`
|
||||
|
||||
vm->curMatch = (Bgpvmmatch *) Bgp_VmTempAlloc(vm, sizeof(*vm->curMatch));
|
||||
if (!vm->curMatch) UNLIKELY
|
||||
return;
|
||||
|
||||
vm->curMatch->pc = BGP_VMCURPC(vm);
|
||||
vm->curMatch->tag = 0;
|
||||
vm->curMatch->base = NULL;
|
||||
vm->curMatch->lim = NULL;
|
||||
vm->curMatch->pos = NULL;
|
||||
vm->curMatch->isMatching = FALSE;
|
||||
|
||||
Bgp_VmStoreMatch(vm);
|
||||
}
|
||||
|
||||
static const Patricia emptyTrie4 = { AFI_IP };
|
||||
static const Patricia emptyTrie6 = { AFI_IP6 };
|
||||
|
||||
void Bgp_VmDoExct(Bgpvm *vm, Uint8 arg)
|
||||
{
|
||||
// POPS:
|
||||
// -1: AFI_IP6 Patricia (may be NULL)
|
||||
// -2: AFI_IP Patricia (may be NULL)
|
||||
//
|
||||
// PUSHES:
|
||||
// TRUE on EXACT match of any network prefix in message, FALSE otherwise
|
||||
|
||||
if (!BGP_VMCHKSTKSIZ(vm, 2)) UNLIKELY
|
||||
return;
|
||||
|
||||
Bgpmpiter it;
|
||||
const Patricia *trie6 = (Patricia *) BGP_VMPOPA(vm);
|
||||
const Patricia *trie = (Patricia *) BGP_VMPOPA(vm);
|
||||
if (!trie6) trie6 = &emptyTrie6;
|
||||
if (!trie) trie = &emptyTrie4;
|
||||
|
||||
if (trie->afi != AFI_IP || trie6->afi != AFI_IP6) UNLIKELY {
|
||||
vm->errCode = BGPEVMBADOP;
|
||||
return;
|
||||
}
|
||||
if (Bgp_VmStartNets(vm, &it, arg) != OK) UNLIKELY
|
||||
return; // error already set
|
||||
|
||||
Prefix *pfx;
|
||||
while ((pfx = Bgp_NextMpPrefix(&it)) != NULL) {
|
||||
RawPrefix *match = NULL;
|
||||
|
||||
// Select appropriate PATRICIA
|
||||
switch (pfx->afi) {
|
||||
case AFI_IP:
|
||||
match = Pat_SearchExact(trie, PLAINPFX(pfx));
|
||||
break;
|
||||
case AFI_IP6:
|
||||
match = Pat_SearchExact(trie6, PLAINPFX(pfx));
|
||||
break;
|
||||
|
||||
default: UNREACHABLE; return;
|
||||
}
|
||||
|
||||
if (match) {
|
||||
Bgp_VmCollectNetMatch(vm, &it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (Bgp_GetErrStat(NULL)) {
|
||||
vm->errCode = BGPEVMMSGERR;
|
||||
return;
|
||||
}
|
||||
|
||||
Bgp_VmNetMatchFailed(vm);
|
||||
}
|
||||
|
||||
void Bgp_VmDoSubn(Bgpvm *vm, Uint8 arg)
|
||||
{
|
||||
// POPS:
|
||||
// -1: AFI_IP6 Patricia (may be NULL)
|
||||
// -2: AFI_IP Patricia (may be NULL)
|
||||
//
|
||||
// PUSHES:
|
||||
// TRUE on SUBN match of any network prefix in message, FALSE otherwise
|
||||
|
||||
if (!BGP_VMCHKSTKSIZ(vm, 2)) UNLIKELY
|
||||
return;
|
||||
|
||||
Bgpmpiter it;
|
||||
const Patricia *trie6 = (Patricia *) BGP_VMPOPA(vm);
|
||||
const Patricia *trie = (Patricia *) BGP_VMPOPA(vm);
|
||||
if (!trie6) trie6 = &emptyTrie6;
|
||||
if (!trie) trie = &emptyTrie4;
|
||||
|
||||
if (trie->afi != AFI_IP || trie6->afi != AFI_IP6 ) UNLIKELY {
|
||||
vm->errCode = BGPEVMBADOP;
|
||||
return;
|
||||
}
|
||||
if (Bgp_VmStartNets(vm, &it, arg) != OK) UNLIKELY
|
||||
return; // error already set
|
||||
|
||||
Prefix *pfx;
|
||||
while ((pfx = Bgp_NextMpPrefix(&it)) != NULL) {
|
||||
Boolean isMatching = FALSE;
|
||||
|
||||
// Select appropriate PATRICIA
|
||||
switch (pfx->afi) {
|
||||
case AFI_IP:
|
||||
isMatching = Pat_IsSubnetOf(trie, PLAINPFX(pfx));
|
||||
break;
|
||||
case AFI_IP6:
|
||||
isMatching = Pat_IsSubnetOf(trie6, PLAINPFX(pfx));
|
||||
break;
|
||||
|
||||
default: UNREACHABLE; return;
|
||||
}
|
||||
|
||||
if (isMatching) {
|
||||
Bgp_VmCollectNetMatch(vm, &it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (Bgp_GetErrStat(NULL)) {
|
||||
vm->errCode = BGPEVMMSGERR;
|
||||
return;
|
||||
}
|
||||
|
||||
Bgp_VmNetMatchFailed(vm);
|
||||
}
|
||||
|
||||
void Bgp_VmDoSupn(Bgpvm *vm, Uint8 arg)
|
||||
{
|
||||
// POPS:
|
||||
// -1: AFI_IP6 Patricia (may be NULL)
|
||||
// -2: AFI_IP Patricia (may be NULL)
|
||||
//
|
||||
// PUSHES:
|
||||
// TRUE on SUPN match of any network prefix in message, FALSE otherwise
|
||||
|
||||
if (!BGP_VMCHKSTKSIZ(vm, 2)) UNLIKELY
|
||||
return;
|
||||
|
||||
Bgpmpiter it;
|
||||
const Patricia *trie6 = (Patricia *) BGP_VMPOPA(vm);
|
||||
const Patricia *trie = (Patricia *) BGP_VMPOPA(vm);
|
||||
if (!trie6) trie6 = &emptyTrie6;
|
||||
if (!trie) trie = &emptyTrie4;
|
||||
|
||||
if (trie->afi != AFI_IP || trie6->afi != AFI_IP6) UNLIKELY {
|
||||
vm->errCode = BGPEVMBADOP;
|
||||
return;
|
||||
}
|
||||
if (Bgp_VmStartNets(vm, &it, arg) != OK) UNLIKELY
|
||||
return; // error already set
|
||||
|
||||
Prefix *pfx;
|
||||
while ((pfx = Bgp_NextMpPrefix(&it)) != NULL) {
|
||||
Boolean isMatching = FALSE;
|
||||
|
||||
// Select appropriate PATRICIA
|
||||
switch (pfx->afi) {
|
||||
case AFI_IP:
|
||||
isMatching = Pat_IsSupernetOf(trie, PLAINPFX(pfx));
|
||||
break;
|
||||
|
||||
case AFI_IP6:
|
||||
isMatching = Pat_IsSupernetOf(trie6, PLAINPFX(pfx));
|
||||
break;
|
||||
|
||||
default: UNREACHABLE; return;
|
||||
}
|
||||
|
||||
if (isMatching) {
|
||||
Bgp_VmCollectNetMatch(vm, &it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (Bgp_GetErrStat(NULL)) {
|
||||
vm->errCode = BGPEVMMSGERR;
|
||||
return;
|
||||
}
|
||||
|
||||
Bgp_VmNetMatchFailed(vm);
|
||||
}
|
||||
|
||||
void Bgp_VmDoRelt(Bgpvm *vm, Uint8 arg)
|
||||
{
|
||||
// POPS:
|
||||
// -1: AFI_IP6 Patricia (may be NULL)
|
||||
// -2: AFI_IP Patricia (may be NULL)
|
||||
//
|
||||
// PUSHES:
|
||||
// TRUE on SUPN match of any network prefix in message, FALSE otherwise
|
||||
|
||||
if (!BGP_VMCHKSTKSIZ(vm, 2)) UNLIKELY
|
||||
return;
|
||||
|
||||
Bgpmpiter it;
|
||||
const Patricia *trie6 = (Patricia *) BGP_VMPOPA(vm);
|
||||
const Patricia *trie = (Patricia *) BGP_VMPOPA(vm);
|
||||
if (!trie6) trie6 = &emptyTrie6;
|
||||
if (!trie) trie = &emptyTrie4;
|
||||
|
||||
if (trie->afi != AFI_IP || trie6->afi != AFI_IP6) UNLIKELY {
|
||||
vm->errCode = BGPEVMBADOP;
|
||||
return;
|
||||
}
|
||||
if (Bgp_VmStartNets(vm, &it, arg) != OK) UNLIKELY
|
||||
return; // error already set
|
||||
|
||||
Prefix *pfx;
|
||||
while ((pfx = Bgp_NextMpPrefix(&it)) != NULL) {
|
||||
Boolean isMatching = FALSE;
|
||||
|
||||
// Select appropriate PATRICIA
|
||||
switch (pfx->afi) {
|
||||
case AFI_IP:
|
||||
isMatching = Pat_IsRelatedOf(trie, PLAINPFX(pfx));
|
||||
break;
|
||||
case AFI_IP6:
|
||||
isMatching = Pat_IsRelatedOf(trie6, PLAINPFX(pfx));
|
||||
break;
|
||||
|
||||
default: UNREACHABLE; return;
|
||||
}
|
||||
|
||||
if (isMatching) {
|
||||
Bgp_VmCollectNetMatch(vm, &it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (Bgp_GetErrStat(NULL)) {
|
||||
vm->errCode = BGPEVMMSGERR;
|
||||
return;
|
||||
}
|
||||
|
||||
Bgp_VmNetMatchFailed(vm);
|
||||
}
|
||||
|
||||
void Bgp_ResetVm(Bgpvm *vm)
|
||||
{
|
||||
assert(!vm->isRunning);
|
||||
|
||||
vm->nk = 0;
|
||||
vm->nfuncs = 0;
|
||||
vm->nmatches = 0;
|
||||
vm->progLen = 0;
|
||||
vm->hLowMark = 0;
|
||||
vm->hHighMark = vm->hMemSiz;
|
||||
|
||||
BGP_VMCLRSETUP(vm);
|
||||
BGP_VMCLRERR(vm);
|
||||
|
||||
memset(vm->k, 0, sizeof(vm->k));
|
||||
memset(vm->funcs, 0, sizeof(vm->funcs));
|
||||
|
||||
vm->matches = NULL;
|
||||
}
|
||||
|
||||
void Bgp_ClearVm(Bgpvm *vm)
|
||||
{
|
||||
assert(!vm->isRunning);
|
||||
|
||||
free(vm->heap);
|
||||
free(vm->prog);
|
||||
}
|
@ -0,0 +1,834 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file bgp/vm_asmtch.c
|
||||
*
|
||||
* Implements ASMTCH and FASMTC, and ASN match IR compilation.
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*
|
||||
* This code is a modified version of the Plan9 regexp library matching algorithm.
|
||||
* The Plan9 regexp library is available under the Lucent Public License
|
||||
* at: https://9fans.github.io/plan9port/unix/libregexp9.tgz
|
||||
*
|
||||
* \see [Regular Expression Matching Can Be Simple And Fast](https://swtch.com/~rsc/regexp/regexp1.html)
|
||||
*/
|
||||
|
||||
#include "bgp/vmintrin.h"
|
||||
#include "sys/endian.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <setjmp.h>
|
||||
#include <string.h>
|
||||
|
||||
// Define this to dump the compiled AS MATCH expressions to stderr
|
||||
//#define DF_DEBUG_ASMTCH
|
||||
#ifdef NDEBUG
|
||||
// Force DF_DEBUG_ASMTCH off on release build
|
||||
#undef DF_DEBUG_ASMTCH
|
||||
#endif
|
||||
|
||||
#ifdef DF_DEBUG_ASMTCH
|
||||
#include "sys/con.h"
|
||||
#endif
|
||||
|
||||
#define AS 126
|
||||
#define NAS 127
|
||||
#define START 128 // start, used for marker on stack
|
||||
#define RPAR 129 // right parens, )
|
||||
#define LPAR 130 // left parens, (
|
||||
#define ALT 131 // alternation, |
|
||||
#define CAT 132 // concatentation, implicit operator
|
||||
#define STAR 133 // closure, *
|
||||
#define PLUS 134 // a+ == aa*
|
||||
#define QUEST 135 // a? == a|nothing, i.e. 0 or 1 a's
|
||||
#define ANY 192 // any character except newline, .
|
||||
#define NOP 193 // no operation
|
||||
#define BOL 194 // beginning of line, ^
|
||||
#define EOL 195 // end of line, $
|
||||
#define STOP 255 // terminate: match found
|
||||
|
||||
#define BOTTOM (START - 1)
|
||||
|
||||
#define NSTACK 32
|
||||
#define LISTSIZ 10
|
||||
#define BIGLISTSIZ (32 * LISTSIZ)
|
||||
|
||||
#define ASNBOLFLAG BIT(60) // used to mark the initial ASN, so that BOL operator works
|
||||
|
||||
#define ASN_EOL -1 // equals to -1 returned by Bgp_NextAsPath()
|
||||
|
||||
// Automaton instruction.
|
||||
typedef struct Nfainst Nfainst;
|
||||
struct Nfainst{
|
||||
int type; // instruction type, any of the macros above
|
||||
|
||||
union { // NOTE: keep `next` and `left` in the same union!
|
||||
Nfainst *next; // next instruction in chain
|
||||
Nfainst *left; // left output state, for ALT instructions
|
||||
};
|
||||
union {
|
||||
Uint32 asn; // ASN match, for AS/NAS instructions
|
||||
Uint16 grpid; // match group id, for LPAR/RPAR instructions
|
||||
Nfainst *right; // right output state, for ALT instructions
|
||||
};
|
||||
};
|
||||
|
||||
// A block of instructions (used during NFA construction)
|
||||
typedef struct Nfanode Nfanode;
|
||||
struct Nfanode {
|
||||
Nfainst *first, *last;
|
||||
};
|
||||
|
||||
// Start and end position of a subexpression match
|
||||
typedef struct {
|
||||
Aspathiter spos;
|
||||
Aspathiter epos;
|
||||
} Nfamatch;
|
||||
|
||||
typedef struct Nfastate Nfastate;
|
||||
struct Nfastate {
|
||||
Nfainst *ip;
|
||||
Nfamatch se[MAXBGPVMASNGRP];
|
||||
};
|
||||
|
||||
// State list used during simulation (clist, nlist)
|
||||
typedef struct Nfalist Nfalist;
|
||||
struct Nfalist {
|
||||
unsigned ns, lim;
|
||||
Nfastate list[FLEX_ARRAY];
|
||||
};
|
||||
|
||||
typedef struct Nfacomp Nfacomp;
|
||||
struct Nfacomp {
|
||||
Nfainst *basep; // base instruction pointer
|
||||
Nfainst *freep; // next free instruction pointer
|
||||
Nfanode andstack[NSTACK]; // operands stack
|
||||
Uint16 grpidstack[NSTACK]; // subexpression id stack
|
||||
Uint8 opstack[NSTACK]; // operators stack
|
||||
Boolean8 lastWasAnd; // whether last encountered term was an operand
|
||||
Uint16 nands, nops, ngrpids; // counters inside stacks
|
||||
Uint16 nparens; // currently encountered open parens counter
|
||||
Uint16 curgrpid; // next group id
|
||||
jmp_buf oops; // compilation error jump
|
||||
};
|
||||
|
||||
typedef struct Nfa Nfa;
|
||||
struct Nfa {
|
||||
Aspathiter spos; // Current ASN iterator start position
|
||||
Aspathiter cur; // Current ASN iterator position
|
||||
unsigned nmatches;
|
||||
Nfamatch se[MAXBGPVMASNGRP];
|
||||
};
|
||||
|
||||
static NORETURN void comperr(Nfacomp *nc, BgpvmRet err)
|
||||
{
|
||||
assert(err != BGPENOERR);
|
||||
|
||||
longjmp(nc->oops, err);
|
||||
}
|
||||
|
||||
static Nfainst *newinst(Nfacomp *nc, int t)
|
||||
{
|
||||
Nfainst *i = nc->freep++;
|
||||
|
||||
i->type = t;
|
||||
i->left = i->right = NULL;
|
||||
return i;
|
||||
}
|
||||
|
||||
static void pushator(Nfacomp *nc, Asn t)
|
||||
{
|
||||
if (nc->nops == NSTACK)
|
||||
comperr(nc, BGPEVMBADASMTCH);
|
||||
|
||||
nc->opstack[nc->nops++] = t;
|
||||
nc->grpidstack[nc->ngrpids++] = nc->curgrpid;
|
||||
}
|
||||
|
||||
static Nfanode *pushand(Nfacomp *nc, Nfainst *f, Nfainst *l)
|
||||
{
|
||||
if (nc->nands == NSTACK)
|
||||
comperr(nc, BGPEVMBADASMTCH);
|
||||
|
||||
Nfanode *n = &nc->andstack[nc->nands++];
|
||||
|
||||
n->first = f;
|
||||
n->last = l;
|
||||
return n;
|
||||
}
|
||||
|
||||
static Nfanode *popand(Nfacomp *nc)
|
||||
{
|
||||
if(nc->nands == 0)
|
||||
comperr(nc, BGPEVMBADASMTCH);
|
||||
|
||||
return &nc->andstack[--nc->nands];
|
||||
}
|
||||
|
||||
static Uint8 popator(Nfacomp *nc)
|
||||
{
|
||||
if (nc->nops == 0)
|
||||
comperr(nc, BGPEVMBADASMTCH);
|
||||
|
||||
--nc->ngrpids;
|
||||
return nc->opstack[--nc->nops];
|
||||
}
|
||||
|
||||
static void evaluntil(Nfacomp *nc, int prio)
|
||||
{
|
||||
Nfanode *op1, *op2;
|
||||
Nfainst *inst1, *inst2;
|
||||
|
||||
while (prio == RPAR || nc->opstack[nc->nops-1] >= prio) {
|
||||
switch (popator(nc)) {
|
||||
default: UNREACHABLE;
|
||||
|
||||
case LPAR:
|
||||
op1 = popand(nc);
|
||||
|
||||
inst2 = newinst(nc, RPAR);
|
||||
inst2->grpid = nc->grpidstack[nc->ngrpids-1];
|
||||
op1->last->next = inst2;
|
||||
|
||||
inst1 = newinst(nc, LPAR);
|
||||
inst1->grpid = nc->grpidstack[nc->ngrpids-1];
|
||||
inst1->next = op1->first;
|
||||
|
||||
pushand(nc, inst1, inst2);
|
||||
return;
|
||||
|
||||
case ALT:
|
||||
op2 = popand(nc), op1 = popand(nc);
|
||||
|
||||
inst2 = newinst(nc, NOP);
|
||||
op2->last->next = inst2;
|
||||
op1->last->next = inst2;
|
||||
|
||||
inst1 = newinst(nc, ALT);
|
||||
inst1->right = op1->first;
|
||||
inst1->left = op2->first;
|
||||
|
||||
pushand(nc, inst1, inst2);
|
||||
break;
|
||||
|
||||
case CAT:
|
||||
op2 = popand(nc), op1 = popand(nc);
|
||||
|
||||
op1->last->next = op2->first;
|
||||
|
||||
pushand(nc, op1->first, op2->last);
|
||||
break;
|
||||
|
||||
case STAR:
|
||||
op2 = popand(nc);
|
||||
|
||||
inst1 = newinst(nc, ALT);
|
||||
op2->last->next = inst1;
|
||||
|
||||
inst1->right = op2->first;
|
||||
|
||||
pushand(nc, inst1, inst1);
|
||||
break;
|
||||
|
||||
case PLUS:
|
||||
op2 = popand(nc);
|
||||
|
||||
inst1 = newinst(nc, ALT);
|
||||
op2->last->next = inst1;
|
||||
|
||||
inst1->right = op2->first;
|
||||
|
||||
pushand(nc, op2->first, inst1);
|
||||
break;
|
||||
|
||||
case QUEST:
|
||||
op2 = popand(nc);
|
||||
|
||||
inst1 = newinst(nc, ALT);
|
||||
inst2 = newinst(nc, NOP);
|
||||
inst1->left = inst2;
|
||||
inst1->right = op2->first;
|
||||
|
||||
op2->last->next = inst2;
|
||||
|
||||
pushand(nc, inst1, inst2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void operator(Nfacomp *nc, int op)
|
||||
{
|
||||
if (op == RPAR) {
|
||||
if (nc->nparens == 0)
|
||||
comperr(nc, BGPEVMBADASMTCH);
|
||||
|
||||
nc->nparens--;
|
||||
}
|
||||
if (op == LPAR) {
|
||||
nc->curgrpid++;
|
||||
|
||||
if (nc->curgrpid == MAXBGPVMASNGRP)
|
||||
comperr(nc, BGPEVMBADASMTCH);
|
||||
|
||||
nc->nparens++;
|
||||
if (nc->lastWasAnd)
|
||||
operator(nc, CAT); // add implicit CAT before group
|
||||
} else
|
||||
evaluntil(nc, op);
|
||||
|
||||
if (op != RPAR)
|
||||
pushator(nc, op);
|
||||
|
||||
// Some operators behave like operands
|
||||
nc->lastWasAnd = (op == STAR || op == QUEST || op == PLUS || op == RPAR);
|
||||
}
|
||||
|
||||
static void operand(Nfacomp *nc, int t, Asn32 asn)
|
||||
{
|
||||
if (nc->lastWasAnd)
|
||||
operator(nc, CAT); // add implicit CAT
|
||||
|
||||
Nfainst *i = newinst(nc, t);
|
||||
if (t == AS || t == NAS)
|
||||
i->asn = asn;
|
||||
|
||||
pushand(nc, i, i);
|
||||
nc->lastWasAnd = TRUE;
|
||||
}
|
||||
|
||||
static void compinit(Nfacomp *nc, Nfainst *dest)
|
||||
{
|
||||
nc->nands = nc->nops = nc->ngrpids = 0;
|
||||
|
||||
nc->nparens = 0;
|
||||
nc->curgrpid = 0;
|
||||
nc->lastWasAnd = FALSE;
|
||||
nc->basep = nc->freep = dest;
|
||||
}
|
||||
|
||||
static Nfainst *compile(Nfacomp *nc, const Asn *expression, size_t n)
|
||||
{
|
||||
pushator(nc, BOTTOM);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
Asn asn = expression[i];
|
||||
|
||||
switch (asn) {
|
||||
case ASN_START: operand(nc, BOL, 0); break;
|
||||
case ASN_END: operand(nc, EOL, 0); break;
|
||||
case ASN_ANY: operand(nc, ANY, 0); break;
|
||||
case ASN_STAR: operator(nc, STAR); break;
|
||||
case ASN_QUEST: operator(nc, QUEST); break;
|
||||
case ASN_PLUS: operator(nc, PLUS); break;
|
||||
case ASN_NEWGRP: operator(nc, LPAR); break;
|
||||
case ASN_ALT: operator(nc, ALT); break;
|
||||
case ASN_ENDGRP: operator(nc, RPAR); break;
|
||||
default:
|
||||
if (ISASNNOT(asn)) operand(nc, NAS, ASN(asn));
|
||||
else operand(nc, AS, ASN(asn));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
evaluntil(nc, START);
|
||||
operand(nc, STOP, 0);
|
||||
evaluntil(nc, START);
|
||||
if (nc->nparens != 0)
|
||||
comperr(nc, BGPEVMBADASMTCH);
|
||||
|
||||
return nc->andstack[nc->nands - 1].first;
|
||||
}
|
||||
|
||||
static void optimize(Bgpvm *vm, Nfacomp *nc, size_t bufsiz)
|
||||
{
|
||||
assert(IS_ALIGNED(bufsiz, ALIGNMENT));
|
||||
assert(vm->hLowMark >= bufsiz);
|
||||
|
||||
// Get rid of NOP chains
|
||||
for (Nfainst *i = nc->basep; i->type != STOP; i++) {
|
||||
Nfainst *j = i->next;
|
||||
|
||||
while (j->type == NOP)
|
||||
j = j->next;
|
||||
|
||||
i->next = j;
|
||||
}
|
||||
|
||||
// Initial program allocation is an upperbound, release excess memory
|
||||
size_t siz = (nc->freep - nc->basep) * sizeof(*nc->basep);
|
||||
size_t alsiz = ALIGN(siz, ALIGNMENT);
|
||||
assert(alsiz <= bufsiz);
|
||||
|
||||
vm->hLowMark -= (bufsiz - alsiz);
|
||||
assert(IS_ALIGNED(vm->hLowMark, ALIGNMENT));
|
||||
}
|
||||
|
||||
#ifdef DF_DEBUG_ASMTCH
|
||||
|
||||
static void dumpprog(const Nfainst *prog)
|
||||
{
|
||||
const Nfainst *i = prog;
|
||||
while (TRUE) {
|
||||
Sys_Printf(STDERR, "%d:\t%#2x", (int) (i - prog), (unsigned) i->type);
|
||||
switch (i->type) {
|
||||
case ALT:
|
||||
Sys_Printf(STDERR, "\t%d\t%d", (int) (i->left - prog), (int) (i->right - prog));
|
||||
break;
|
||||
case AS:
|
||||
Sys_Printf(STDERR, "\tASN(%lu)\t%d", (unsigned long) beswap32(i->asn), (int) (i->next - prog));
|
||||
break;
|
||||
case NAS:
|
||||
Sys_Printf(STDERR, "\t!ASN(%lu)\t%d", (unsigned long) beswap32(i->asn), (int) (i->next - prog));
|
||||
break;
|
||||
case LPAR: case RPAR:
|
||||
Sys_Printf(STDERR, "\tGRP(%d)", (int) i->grpid);
|
||||
// FALLTHROUGH
|
||||
default:
|
||||
Sys_Printf(STDERR, "\t%d", (int) (i->next - prog));
|
||||
break;
|
||||
|
||||
case NOP: case STOP:
|
||||
break;
|
||||
}
|
||||
|
||||
Sys_Print(STDERR, "\n");
|
||||
if (i->type == STOP)
|
||||
break;
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// `TRUE` if `pos` comes before `m` starting position
|
||||
static Boolean isbefore(const Aspathiter *pos, const Nfamatch *m)
|
||||
{
|
||||
return BGP_CURASINDEX(pos) < BGP_CURASINDEX(&m->spos);
|
||||
}
|
||||
|
||||
// `TRUE` if `a` starts at the same position as `b`, but terminates after it
|
||||
static Boolean islongermatch(const Nfamatch *a, const Nfamatch *b)
|
||||
{
|
||||
return BGP_CURASINDEX(&a->spos) == BGP_CURASINDEX(&b->spos) &&
|
||||
BGP_CURASINDEX(&a->epos) > BGP_CURASINDEX(&b->epos);
|
||||
}
|
||||
|
||||
/* An invalid AS INDEX, there can't be a 65535 AS index,
|
||||
* Given that an AS is at least 2 bytes wide and a legal TPA segment
|
||||
* is at most of 64K (though in practice its even smaller)
|
||||
*/
|
||||
#define BADASIDX 0xffffu
|
||||
|
||||
static void clearmatch(Nfamatch *m)
|
||||
{
|
||||
m->spos.asIdx = BADASIDX;
|
||||
m->epos.asIdx = 0;
|
||||
}
|
||||
|
||||
static Boolean isnullmatch(const Nfamatch *m)
|
||||
{
|
||||
return BGP_CURASINDEX(&m->spos) == BADASIDX;
|
||||
}
|
||||
|
||||
static void copymatches(Nfamatch *dest, const Nfamatch *src)
|
||||
{
|
||||
do *dest++ = *src; while (!isnullmatch(src++));
|
||||
}
|
||||
|
||||
static Boolean addstartinst(Nfalist *clist, Nfainst *start, const Nfa *nfa)
|
||||
{
|
||||
Nfastate *s;
|
||||
|
||||
// Don't add the instruction twice
|
||||
for (unsigned i = 0; i < clist->ns; i++) {
|
||||
s = &clist->list[i];
|
||||
|
||||
if (s->ip == start) {
|
||||
if (isbefore(&nfa->spos, &s->se[0])) {
|
||||
// Move match position
|
||||
s->se[0].spos = nfa->spos;
|
||||
s->se[0].epos.asIdx = 0; // so any end pos is accepted
|
||||
clearmatch(&s->se[1]);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (clist->ns == clist->lim)
|
||||
return FALSE;
|
||||
|
||||
// Append to list
|
||||
s = &clist->list[clist->ns++];
|
||||
s->ip = start;
|
||||
|
||||
s->se[0].spos = nfa->spos;
|
||||
s->se[0].epos.asIdx = 0; // so any end pos is accepted
|
||||
clearmatch(&s->se[1]);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static Boolean addinst(Nfalist *nlist, Nfainst *in, const Nfamatch *se)
|
||||
{
|
||||
Nfastate *s;
|
||||
|
||||
// Don't add the same instruction twice
|
||||
for (unsigned i = 0; i < nlist->ns; i++) {
|
||||
s = &nlist->list[i];
|
||||
|
||||
if (s->ip == in) {
|
||||
if (isbefore(&se[0].spos, &s->se[0]))
|
||||
copymatches(s->se, se);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (nlist->ns == nlist->lim)
|
||||
return FALSE; // instruction list overflow
|
||||
|
||||
// Append to list
|
||||
s = &nlist->list[nlist->ns++];
|
||||
s->ip = in;
|
||||
copymatches(s->se, se);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void newmatch(Nfastate *s, Nfa *nfa)
|
||||
{
|
||||
// Accept the new match if it is the first one, or it comes before
|
||||
// a previous match, or if it is a longer match than the previous one
|
||||
if (nfa->nmatches == 0 ||
|
||||
isbefore(&s->se[0].spos, &nfa->se[0]) ||
|
||||
islongermatch(&s->se[0], &nfa->se[0])) {
|
||||
|
||||
copymatches(nfa->se, s->se);
|
||||
}
|
||||
|
||||
nfa->nmatches++;
|
||||
}
|
||||
|
||||
static Judgement step(Nfalist *clist, Asn asn, Nfalist *nlist, Nfa *nfa)
|
||||
{
|
||||
nlist->ns = 0;
|
||||
|
||||
for (unsigned i = 0; i < clist->ns; i++) {
|
||||
Nfastate *s = &clist->list[i];
|
||||
Nfainst *ip = s->ip;
|
||||
|
||||
eval_next:
|
||||
switch (ip->type) {
|
||||
default: UNREACHABLE;
|
||||
|
||||
case NOP:
|
||||
ip = ip->next;
|
||||
goto eval_next;
|
||||
case AS:
|
||||
if (ip->asn == ASN(asn) && !addinst(nlist, ip->next, s->se))
|
||||
return NG;
|
||||
|
||||
break;
|
||||
case NAS:
|
||||
if (ip->asn != ASN(asn) && !addinst(nlist, ip->next, s->se))
|
||||
return NG;
|
||||
|
||||
break;
|
||||
case ANY:
|
||||
if (asn != ASN_EOL && !addinst(nlist, ip->next, s->se))
|
||||
return NG;
|
||||
|
||||
break;
|
||||
case BOL:
|
||||
if (asn & ASNBOLFLAG) {
|
||||
ip = ip->next;
|
||||
goto eval_next;
|
||||
}
|
||||
|
||||
break;
|
||||
case EOL:
|
||||
if (asn == ASN_EOL) {
|
||||
ip = ip->next;
|
||||
goto eval_next;
|
||||
}
|
||||
|
||||
break;
|
||||
case LPAR:
|
||||
for (unsigned i = 0; i < ip->grpid; i++)
|
||||
assert(!isnullmatch(&s->se[i]));
|
||||
|
||||
assert(isnullmatch(&s->se[ip->grpid]));
|
||||
|
||||
s->se[ip->grpid].spos = nfa->spos;
|
||||
clearmatch(&s->se[ip->grpid+1]);
|
||||
|
||||
ip = ip->next;
|
||||
goto eval_next;
|
||||
case ALT:
|
||||
// Evaluate right branch later IN THIS LIST
|
||||
if (!addinst(clist, ip->right, s->se))
|
||||
return NG;
|
||||
|
||||
ip = ip->left; // take left branch now
|
||||
goto eval_next;
|
||||
case RPAR:
|
||||
for (unsigned i = 0; i < ip->grpid; i++)
|
||||
assert(!isnullmatch(&s->se[i]));
|
||||
|
||||
assert(!isnullmatch(&s->se[ip->grpid]));
|
||||
assert( isnullmatch(&s->se[ip->grpid+1]));
|
||||
|
||||
s->se[ip->grpid].epos = nfa->cur;
|
||||
ip = ip->next;
|
||||
goto eval_next;
|
||||
case STOP:
|
||||
// *** MATCH ***
|
||||
s->se[0].epos = nfa->cur;
|
||||
newmatch(s, nfa);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return BGPENOERR;
|
||||
}
|
||||
|
||||
static void collect(Bgpvm *vm, const Nfa *nfa)
|
||||
{
|
||||
Boolean isMatching = (nfa->nmatches > 0);
|
||||
BGP_VMPUSH(vm, isMatching);
|
||||
|
||||
vm->curMatch = (Bgpvmmatch *) Bgp_VmTempAlloc(vm, sizeof(*vm->curMatch));
|
||||
if (!vm->curMatch)
|
||||
return; // out of memory
|
||||
|
||||
Bgpattrseg *tpa = Bgp_GetUpdateAttributes(BGP_MSGUPDATE(vm->msg));
|
||||
assert(tpa != NULL);
|
||||
|
||||
// Generate matches list
|
||||
Bgpvmasmatch *matches = NULL;
|
||||
Bgpvmasmatch **pmatches = &matches;
|
||||
for (unsigned i = 0; !isnullmatch(&nfa->se[i]); i++) {
|
||||
const Nfamatch *src = &nfa->se[i];
|
||||
Bgpvmasmatch *dest = (Bgpvmasmatch *) Bgp_VmTempAlloc(vm, sizeof(*dest));
|
||||
if (!dest)
|
||||
return; // out of memory
|
||||
|
||||
dest->next = *pmatches;
|
||||
dest->spos = src->spos;
|
||||
dest->epos = src->epos;
|
||||
*pmatches = dest;
|
||||
|
||||
pmatches = &dest->next;
|
||||
}
|
||||
|
||||
vm->curMatch->pc = BGP_VMCURPC(vm);
|
||||
vm->curMatch->tag = 0;
|
||||
vm->curMatch->base = tpa->attrs;
|
||||
vm->curMatch->lim = &tpa->attrs[beswap16(tpa->len)];
|
||||
vm->curMatch->pos = matches;
|
||||
vm->curMatch->isMatching = isMatching;
|
||||
}
|
||||
|
||||
static BgpvmRet execute(Bgpvm *vm, Nfainst *program, unsigned listlen, Nfa *nfa)
|
||||
{
|
||||
// Prepare AS PATH iterator
|
||||
BgpvmRet err = BGPENOERR; // unless found otherwise
|
||||
if (Bgp_StartMsgRealAsPath(&nfa->cur, vm->msg) != OK) {
|
||||
vm->errCode = BGPEVMMSGERR;
|
||||
return vm->errCode;
|
||||
}
|
||||
|
||||
// Setup state lists
|
||||
Nfalist *clist, *nlist, *t;
|
||||
|
||||
size_t listsiz = offsetof(Nfalist, list[listlen]);
|
||||
if (listlen > LISTSIZ) {
|
||||
// Allocate on temporary memory
|
||||
clist = (Nfalist *) Bgp_VmTempAlloc(vm, listsiz);
|
||||
nlist = (Nfalist *) Bgp_VmTempAlloc(vm, listsiz);
|
||||
} else {
|
||||
// Allocate on stack
|
||||
clist = (Nfalist *) alloca(listsiz);
|
||||
nlist = (Nfalist *) alloca(listsiz);
|
||||
}
|
||||
if (!clist || !nlist)
|
||||
return vm->errCode;
|
||||
|
||||
clist->lim = nlist->lim = listlen;
|
||||
|
||||
// Simulate NFA, execute once per ASN (including ASN_BOL and ASN_EOL)
|
||||
nfa->nmatches = 0; // clear result list
|
||||
clist->ns = 0; // clear current list for the first time
|
||||
clearmatch(&nfa->se[0]); // by default no match
|
||||
|
||||
Asn asn, flag = ASNBOLFLAG; // first ASN is marked as BOL
|
||||
do {
|
||||
// Copy initial position to start
|
||||
nfa->spos = nfa->cur;
|
||||
|
||||
// Always include first instruction if no match took place yet
|
||||
if (nfa->nmatches == 0 && !addstartinst(clist, program, nfa)) {
|
||||
// State list overflow
|
||||
err = BGPEVMASMTCHESIZE;
|
||||
break;
|
||||
}
|
||||
|
||||
// Fetch new ASN
|
||||
asn = Bgp_NextAsPath(&nfa->cur);
|
||||
if (asn == -1 && Bgp_GetErrStat(NULL)) {
|
||||
err = BGPEVMMSGERR;
|
||||
break;
|
||||
}
|
||||
|
||||
// Advance NFA evaluating the current ASN
|
||||
if (step(clist, asn | flag, nlist, nfa) != OK) {
|
||||
// List overflow
|
||||
err = BGPEVMASMTCHESIZE;
|
||||
break;
|
||||
}
|
||||
|
||||
t = clist, clist = nlist, nlist = t; // swap lists
|
||||
|
||||
flag = 0; // no more the first ASN
|
||||
} while (asn != -1);
|
||||
|
||||
if (listlen > LISTSIZ) {
|
||||
Bgp_VmTempFree(vm, listsiz);
|
||||
Bgp_VmTempFree(vm, listsiz);
|
||||
}
|
||||
|
||||
vm->errCode = err;
|
||||
return err;
|
||||
}
|
||||
|
||||
void *Bgp_VmCompileAsMatch(Bgpvm *vm, const Asn *expression, size_t n)
|
||||
{
|
||||
Nfainst *buf, *prog;
|
||||
|
||||
// NOTE: Bgp_VmPermAlloc() already clears VM error and asserts !vm->isRunning
|
||||
|
||||
const size_t maxsiz = ALIGN(6 * n * sizeof(*buf), ALIGNMENT);
|
||||
|
||||
// we request an already aligned chunk
|
||||
// so optimize() can make accurate memory adjustments
|
||||
buf = Bgp_VmPermAlloc(vm, maxsiz);
|
||||
if (!buf)
|
||||
return NULL;
|
||||
|
||||
Nfacomp nc;
|
||||
compinit(&nc, buf);
|
||||
|
||||
int err;
|
||||
if ((err = setjmp(nc.oops)) != 0) {
|
||||
vm->errCode = err;
|
||||
vm->hLowMark -= maxsiz; // release permanent allocation
|
||||
return NULL;
|
||||
}
|
||||
|
||||
prog = compile(&nc, expression, n);
|
||||
optimize(vm, &nc, maxsiz);
|
||||
#ifdef DF_DEBUG_ASMTCH
|
||||
dumpprog(prog);
|
||||
#endif
|
||||
|
||||
// vm->errCode = BGPENOERR; - already set by Bgp_VmPermAlloc()
|
||||
return prog;
|
||||
}
|
||||
|
||||
void Bgp_VmDoAsmtch(Bgpvm *vm)
|
||||
{
|
||||
/* POPS:
|
||||
* -1: Asn match array length
|
||||
* -2: Address to Asn match array
|
||||
*
|
||||
* PUSHES:
|
||||
* TRUE on successful match, FALSE otherwise
|
||||
*/
|
||||
|
||||
Nfacomp nc;
|
||||
Nfa nfa;
|
||||
Nfainst *buf, *prog;
|
||||
|
||||
if (!BGP_VMCHKSTK(vm, 2))
|
||||
return;
|
||||
|
||||
// Pop arguments from stack
|
||||
Sint64 n = BGP_VMPOP(vm);
|
||||
const Asn *match = (const Asn *) BGP_VMPOPA(vm);
|
||||
if (n <= 0 || match == NULL) {
|
||||
vm->errCode = BGPEVMBADASMTCH;
|
||||
return;
|
||||
}
|
||||
if (!BGP_VMCHKMSGTYPE(vm, BGP_UPDATE)) {
|
||||
Bgp_VmStoreMsgTypeMatch(vm, /*isMatching=*/FALSE);
|
||||
BGP_VMPUSH(vm, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Compile on the fly on temporary memory
|
||||
const size_t maxsiz = 6 * n * sizeof(*buf);
|
||||
|
||||
buf = (Nfainst *) Bgp_VmTempAlloc(vm, maxsiz);
|
||||
if (!buf)
|
||||
return;
|
||||
|
||||
compinit(&nc, buf);
|
||||
|
||||
int err; // compilation status
|
||||
if ((err = setjmp(nc.oops)) != 0) {
|
||||
vm->errCode = err;
|
||||
return;
|
||||
}
|
||||
|
||||
prog = compile(&nc, match, n);
|
||||
#ifdef DF_DEBUG_ASMTCH
|
||||
dumpprog(prog);
|
||||
#endif
|
||||
|
||||
BgpvmRet status = execute(vm, prog, LISTSIZ, &nfa);
|
||||
if (status == BGPEVMASMTCHESIZE)
|
||||
status = execute(vm, prog, BIGLISTSIZ, &nfa);
|
||||
|
||||
Bgp_VmTempFree(vm, maxsiz);
|
||||
|
||||
if (status == BGPENOERR)
|
||||
collect(vm, &nfa);
|
||||
}
|
||||
|
||||
void Bgp_VmDoFasmtc(Bgpvm *vm)
|
||||
{
|
||||
/* POPS:
|
||||
* -1: Precompiled NFA instructions
|
||||
*
|
||||
* PUSHES:
|
||||
* TRUE on successful match, FALSE otherwise
|
||||
*/
|
||||
|
||||
Nfa nfa;
|
||||
|
||||
if (!BGP_VMCHKSTK(vm, 1))
|
||||
return;
|
||||
|
||||
Nfainst *prog = (Nfainst *) BGP_VMPOPA(vm);
|
||||
if (!prog) {
|
||||
vm->errCode = BGPEVMBADASMTCH;
|
||||
return;
|
||||
}
|
||||
if (!BGP_VMCHKMSGTYPE(vm, BGP_UPDATE)) {
|
||||
Bgp_VmStoreMsgTypeMatch(vm, /*isMatching=*/FALSE);
|
||||
BGP_VMPUSH(vm, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
BgpvmRet status = execute(vm, prog, LISTSIZ, &nfa);
|
||||
if (status == BGPEVMASMTCHESIZE)
|
||||
status = execute(vm, prog, BIGLISTSIZ, &nfa);
|
||||
|
||||
if (status == BGPENOERR)
|
||||
collect(vm, &nfa);
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file bgp/vm_cdef.c
|
||||
*
|
||||
* Portable implementation for BGP VM execution loop
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*
|
||||
* Plain C switch based FETCH-DECODE-DISPATCH-EXECUTE BGP filtering
|
||||
* engine VM implementation
|
||||
*
|
||||
* \note File should be `#include`d by bgp/vm.c
|
||||
*/
|
||||
|
||||
#ifdef DF_BGP_VMDEF_H_
|
||||
#error "Only one vm_<impl>def.h file may be #include-d"
|
||||
#endif
|
||||
#define DF_BGP_VMDEF_H_
|
||||
|
||||
#define LIKELY
|
||||
#define UNLIKELY
|
||||
|
||||
#define FETCH(ir, vm) (ir = (vm)->prog[(vm)->pc++])
|
||||
|
||||
#define EXPECT(opcode, ir, vm) ((void) 0)
|
||||
|
||||
#define DISPATCH(opcode) switch (opcode)
|
||||
|
||||
#define EXECUTE(opcode) case BGP_VMOP_ ## opcode
|
||||
|
||||
#define EXECUTE_SIGILL default
|
@ -0,0 +1,103 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file bgp/vm_commsort.h
|
||||
*
|
||||
* Generic basic sorting and binary searching over unsigned integer arrays.
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*
|
||||
* The following defines a bunch of static functions to sort
|
||||
* and search basic integer arrays.
|
||||
*
|
||||
* `#define` `UINT_TYPE` with an unsigned <= 4 bytes and `FNSUFFIX`
|
||||
* before inclusion.
|
||||
*
|
||||
* \note No guards, file `#include`d by bgp/vm_communities.c
|
||||
*/
|
||||
|
||||
#define _CAT(X, Y) X ## Y
|
||||
#define _XCAT(X, Y) _CAT(X, Y)
|
||||
|
||||
#define _MANGLE(FN) _XCAT(FN, FNSUFFIX)
|
||||
|
||||
|
||||
static Sint64 _MANGLE(BinarySearch) (const UINT_TYPE *arr,
|
||||
Uint32 n,
|
||||
UINT_TYPE v)
|
||||
{
|
||||
Uint32 len = n;
|
||||
Uint32 mid = n;
|
||||
Sint64 off = 0;
|
||||
while (mid > 0) {
|
||||
mid = len >> 1;
|
||||
if (arr[off+mid] <= v)
|
||||
off += mid;
|
||||
|
||||
len -= mid;
|
||||
}
|
||||
|
||||
return (off < n && arr[off] == v) ? off : -1;
|
||||
}
|
||||
|
||||
static void _MANGLE(Radix) (int off,
|
||||
const UINT_TYPE *src,
|
||||
Uint32 n,
|
||||
UINT_TYPE *dest)
|
||||
{
|
||||
const Uint8 *sortKey;
|
||||
|
||||
Uint32 index[256];
|
||||
Uint32 count[256] = { 0 };
|
||||
|
||||
for (Uint32 i = 0; i < n; i++) {
|
||||
sortKey = ((const Uint8 *) &src[i]) + off;
|
||||
count[*sortKey]++;
|
||||
}
|
||||
|
||||
index[0] = 0;
|
||||
for (Uint32 i = 1; i < 256; i++)
|
||||
index[i] = index[i-1] + count[i-1];
|
||||
|
||||
for (Uint32 i = 0; i < n; i++) {
|
||||
sortKey = ((const Uint8 *) &src[i]) + off;
|
||||
dest[index[*sortKey]++] = src[i];
|
||||
}
|
||||
}
|
||||
|
||||
static void _MANGLE(RadixSort) (UINT_TYPE *arr, Uint32 n)
|
||||
{
|
||||
UINT_TYPE *scratch = (UINT_TYPE *) alloca(n * sizeof(*scratch));
|
||||
|
||||
STATIC_ASSERT(sizeof(UINT_TYPE) % 2 == 0, "?!");
|
||||
|
||||
if (EDN_NATIVE == EDN_LE) {
|
||||
for (unsigned i = 0; i < sizeof(UINT_TYPE); i += 2) {
|
||||
_MANGLE(Radix) (i + 0, arr, n, scratch);
|
||||
_MANGLE(Radix) (i + 1, scratch, n, arr);
|
||||
}
|
||||
} else {
|
||||
for (unsigned i = sizeof(UINT_TYPE); i > 0; i -= 2) {
|
||||
_MANGLE(Radix) (i - 1, arr, n, scratch);
|
||||
_MANGLE(Radix) (i - 2, scratch, n, arr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Uint32 _MANGLE(Uniq) (UINT_TYPE *arr, Uint32 n)
|
||||
{
|
||||
Uint32 i, j;
|
||||
|
||||
if (n == 0) return 0;
|
||||
|
||||
for (i = 0, j = 1; j < n; j++) {
|
||||
if (arr[i] != arr[j])
|
||||
arr[++i] = arr[j];
|
||||
}
|
||||
return ++i;
|
||||
}
|
||||
|
||||
#undef _MANGLE
|
||||
#undef _XCAT
|
||||
#undef _CAT
|
@ -0,0 +1,442 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file bgp/vm_communities.c
|
||||
*
|
||||
* BGP VM COMTCH, ACOMTC instructions and COMMUNITY index.
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*/
|
||||
|
||||
#include "bgp/bgp_local.h"
|
||||
|
||||
#include "bgp/vmintrin.h"
|
||||
#include "sys/endian.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct {
|
||||
BgpVmOpt opt;
|
||||
Uint32 hiOnlyCount;
|
||||
Uint32 loOnlyCount;
|
||||
Uint32 fullCount;
|
||||
Uint32 bitsetWords;
|
||||
Uint32 *bitset;
|
||||
Uint16 *hiOnly; // parital match on community hi
|
||||
Uint16 *loOnly; // partial match on community lo
|
||||
Uint32 full[]; // full matches on whole community codes
|
||||
// Uint32 bitset[]; <- order preserves alignment requirements
|
||||
// Uint16 hi[];
|
||||
// Uint16 lo[];
|
||||
} Bgpcommidx;
|
||||
|
||||
FORCE_INLINE size_t BITSETWIDTH(const Bgpcommidx *idx)
|
||||
{
|
||||
return idx->hiOnlyCount + idx->loOnlyCount + idx->fullCount;
|
||||
}
|
||||
FORCE_INLINE size_t BITSETLEN(size_t width)
|
||||
{
|
||||
return (width >> 5) + ((width & 0x1f) != 0);
|
||||
}
|
||||
FORCE_INLINE size_t FULLBITIDX(const Bgpcommidx *idx, Uint32 i)
|
||||
{
|
||||
USED(idx);
|
||||
return i;
|
||||
}
|
||||
FORCE_INLINE size_t HIBITIDX(const Bgpcommidx *idx, Uint32 i)
|
||||
{
|
||||
return (size_t) idx->fullCount + i;
|
||||
}
|
||||
FORCE_INLINE size_t LOBITIDX(const Bgpcommidx *idx, Uint32 i)
|
||||
{
|
||||
return (size_t) idx->fullCount + idx->hiOnlyCount + i;
|
||||
}
|
||||
|
||||
FORCE_INLINE Boolean ISBITSET(const Uint32 *bitset, size_t idx)
|
||||
{
|
||||
return (bitset[idx >> 5] & (1u << (idx & 0x1f))) != 0;
|
||||
}
|
||||
|
||||
FORCE_INLINE void SETBIT(Uint32 *bitset, size_t idx)
|
||||
{
|
||||
bitset[idx >> 5] |= (1u << (idx & 0x1f));
|
||||
}
|
||||
|
||||
FORCE_INLINE void CLRBITSET(Uint32 *bitset, size_t len)
|
||||
{
|
||||
memset(bitset, 0, len * sizeof(*bitset));
|
||||
}
|
||||
|
||||
|
||||
#ifdef __GNUC__
|
||||
FORCE_INLINE unsigned FindFirstSet(Uint32 x)
|
||||
{
|
||||
STATIC_ASSERT(sizeof(x) == sizeof(int), "__builtin_ffs() operates on int");
|
||||
return __builtin_ffs(x);
|
||||
}
|
||||
#else
|
||||
FORCE_INLINE unsigned FindFirstSet(Uint32 x)
|
||||
{
|
||||
if (x == 0) return 0;
|
||||
|
||||
unsigned n = 0;
|
||||
|
||||
if ((x & 0x0000ffffu) == 0) n += 16, x >>= 16;
|
||||
if ((x & 0x000000ffu) == 0) n += 8, x >>= 8;
|
||||
if ((x & 0x0000000fu) == 0) n += 4, x >>= 4;
|
||||
if ((x & 0x00000003u) == 0) n += 2, x >>= 2;
|
||||
if ((x & 0x00000001u) == 0) n += 1;
|
||||
|
||||
return ++n;
|
||||
}
|
||||
#endif
|
||||
|
||||
static size_t FFZ(const Uint32 *bitset, size_t len)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
assert(len > 0);
|
||||
|
||||
len--;
|
||||
for (i = 0; i < len && bitset[i] == 0xffffffffu; i++);
|
||||
|
||||
size_t n = i << 6;
|
||||
n += FindFirstSet(~bitset[i]) - 1;
|
||||
return n;
|
||||
}
|
||||
|
||||
#define UINT_TYPE Uint16
|
||||
#define FNSUFFIX 16
|
||||
#include "bgp/vm_commsort.h"
|
||||
#undef UINT_TYPE
|
||||
#undef FNSUFFIX
|
||||
|
||||
#define UINT_TYPE Uint32
|
||||
#define FNSUFFIX 32
|
||||
#include "bgp/vm_commsort.h"
|
||||
#undef UINT_TYPE
|
||||
#undef FNSUFFIX
|
||||
|
||||
static Boolean MatchCommunity(const Bgpcomm *c, const Bgpcommidx *idx)
|
||||
{
|
||||
return BinarySearch16(idx->hiOnly, idx->hiOnlyCount, c->hi) >= 0 ||
|
||||
BinarySearch16(idx->loOnly, idx->loOnlyCount, c->lo) >= 0 ||
|
||||
BinarySearch32(idx->full, idx->fullCount, c->code) >= 0;
|
||||
}
|
||||
|
||||
static void OptimizeComtch(Bgpcommidx *idx)
|
||||
{
|
||||
// Remove every full match more specific than an existing partial match.
|
||||
|
||||
// NOTE: Assumes arrays have been sorted and Uniq()d
|
||||
|
||||
Uint32 i, j;
|
||||
Bgpcomm c;
|
||||
|
||||
for (i = 0, j = 0; i < idx->fullCount; i++) {
|
||||
c.code = idx->full[i];
|
||||
if (BinarySearch16(idx->hiOnly, idx->hiOnlyCount, c.hi) >= 0 ||
|
||||
BinarySearch16(idx->loOnly, idx->loOnlyCount, c.lo) >= 0)
|
||||
continue;
|
||||
|
||||
idx->full[j++] = idx->full[i];
|
||||
}
|
||||
|
||||
idx->fullCount = j;
|
||||
}
|
||||
|
||||
static void OptimizeAcomtc(Bgpcommidx *idx)
|
||||
{
|
||||
// Remove every partial match less specific than an existing full match
|
||||
|
||||
// NOTE: Assumes arrays have been sorted and Uniq()d
|
||||
|
||||
Uint32 i, j;
|
||||
|
||||
// Mark redundant entries in bitset
|
||||
CLRBITSET(idx->bitset, idx->bitsetWords);
|
||||
for (i = 0; i < idx->fullCount; i++) {
|
||||
Sint64 pos;
|
||||
Bgpcomm c;
|
||||
|
||||
c.code = idx->full[i];
|
||||
|
||||
pos = BinarySearch16(idx->hiOnly, idx->hiOnlyCount, c.hi);
|
||||
if (pos >= 0)
|
||||
SETBIT(idx->bitset, HIBITIDX(idx, pos));
|
||||
|
||||
pos = BinarySearch16(idx->loOnly, idx->loOnlyCount, c.lo);
|
||||
if (pos >= 0)
|
||||
SETBIT(idx->bitset, LOBITIDX(idx, pos));
|
||||
}
|
||||
|
||||
// Remove redundant entries
|
||||
for (i = 0, j = 0; i < idx->hiOnlyCount; i++) {
|
||||
if (!ISBITSET(idx->bitset, HIBITIDX(idx, i)))
|
||||
idx->hiOnly[j++] = idx->hiOnly[i];
|
||||
}
|
||||
idx->hiOnlyCount = j;
|
||||
|
||||
for (i = 0, j = 0; i < idx->loOnlyCount; i++) {
|
||||
if (!ISBITSET(idx->bitset, LOBITIDX(idx, i)))
|
||||
idx->loOnly[j++] = idx->loOnly[i];
|
||||
}
|
||||
idx->loOnlyCount = j;
|
||||
}
|
||||
|
||||
static void CompactIndex(Bgpvm *vm, Bgpcommidx *idx, size_t idxSize)
|
||||
{
|
||||
size_t offset = offsetof(Bgpcommidx, full[idx->fullCount]);
|
||||
size_t bitsetSiz = idx->bitsetWords * sizeof(*idx->bitset);
|
||||
size_t hiSiz = idx->hiOnlyCount * sizeof(*idx->hiOnly);
|
||||
size_t loSiz = idx->loOnlyCount * sizeof(*idx->loOnly);
|
||||
|
||||
Uint8 *ptr = (Uint8 *) idx + offset;
|
||||
|
||||
idx->bitset = (Uint32 *) memmove(ptr, idx->bitset, bitsetSiz);
|
||||
ptr += bitsetSiz;
|
||||
idx->hiOnly = (Uint16 *) memmove(ptr, idx->hiOnly, hiSiz);
|
||||
ptr += hiSiz;
|
||||
idx->loOnly = (Uint16 *) memmove(ptr, idx->loOnly, loSiz);
|
||||
ptr += loSiz;
|
||||
|
||||
size_t siz = ptr - (Uint8 *) idx;
|
||||
|
||||
siz = ALIGN(siz, ALIGNMENT);
|
||||
offset = idxSize - siz;
|
||||
|
||||
vm->hLowMark -= offset;
|
||||
}
|
||||
|
||||
void *Bgp_VmCompileCommunityMatch(Bgpvm *vm,
|
||||
const Bgpmatchcomm *match,
|
||||
size_t n,
|
||||
BgpVmOpt opt)
|
||||
{
|
||||
// NOTE: Bgp_VmPermAlloc() already clears VM error and asserts !vm->isRunning
|
||||
|
||||
Sint32 nlow = 0, nhigh = 0, nfull = 0, nbitswords = 0;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
const Bgpmatchcomm *m = &match[i];
|
||||
if (m->maskLo && m->maskHi) {
|
||||
vm->errCode = BGPEVMBADCOMTCH;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (m->maskLo)
|
||||
nhigh++;
|
||||
else if (m->maskHi)
|
||||
nlow++;
|
||||
else
|
||||
nfull++;
|
||||
}
|
||||
|
||||
Bgpcommidx *idx;
|
||||
|
||||
size_t offBits = offsetof(Bgpcommidx, full[nfull]);
|
||||
if (opt != BGP_VMOPT_ASSUME_COMTCH)
|
||||
nbitswords = BITSETLEN(nlow + nhigh + nfull); // must allocate bitset
|
||||
|
||||
size_t offHigh = offBits + nbitswords * sizeof(*idx->bitset);
|
||||
size_t offLow = offHigh + nhigh * sizeof(*idx->hiOnly);
|
||||
size_t nbytes = offLow + nlow * sizeof(*idx->loOnly);
|
||||
|
||||
nbytes = ALIGN(nbytes, ALIGNMENT);
|
||||
|
||||
idx = Bgp_VmPermAlloc(vm, nbytes);
|
||||
if (!idx)
|
||||
return NULL;
|
||||
|
||||
idx->bitset = (Uint32 *) ((Uint8 *) idx + offBits);
|
||||
idx->hiOnly = (Uint16 *) ((Uint8 *) idx + offHigh);
|
||||
idx->loOnly = (Uint16 *) ((Uint8 *) idx + offLow);
|
||||
|
||||
idx->opt = opt;
|
||||
idx->bitsetWords = nbitswords;
|
||||
idx->hiOnlyCount = idx->loOnlyCount = idx->fullCount = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
const Bgpmatchcomm *m = &match[i];
|
||||
if (m->maskLo)
|
||||
idx->hiOnly[idx->hiOnlyCount++] = m->c.hi;
|
||||
else if (m->maskHi)
|
||||
idx->loOnly[idx->loOnlyCount++] = m->c.lo;
|
||||
else
|
||||
idx->full[idx->fullCount++] = m->c.code;
|
||||
}
|
||||
|
||||
// Sort lookup arrays
|
||||
RadixSort16(idx->hiOnly, idx->hiOnlyCount);
|
||||
RadixSort16(idx->loOnly, idx->loOnlyCount);
|
||||
RadixSort32(idx->full, idx->fullCount);
|
||||
|
||||
// Optimize tables
|
||||
idx->hiOnlyCount = Uniq16(idx->hiOnly, idx->hiOnlyCount);
|
||||
idx->loOnlyCount = Uniq16(idx->loOnly, idx->loOnlyCount);
|
||||
idx->fullCount = Uniq32(idx->full, idx->fullCount);
|
||||
|
||||
// Discard redundant entries
|
||||
switch (opt) {
|
||||
case BGP_VMOPT_ASSUME_COMTCH: OptimizeComtch(idx); break;
|
||||
case BGP_VMOPT_ASSUME_ACOMTC: OptimizeAcomtc(idx); break;
|
||||
|
||||
default:
|
||||
case BGP_VMOPT_NONE:
|
||||
break;
|
||||
}
|
||||
|
||||
// Free-up excess memory after optimization
|
||||
CompactIndex(vm, idx, nbytes);
|
||||
return idx;
|
||||
}
|
||||
|
||||
static Bgpattr *Bgp_VmDoComSetup(Bgpvm *vm, Bgpcommiter *it, BgpAttrCode code)
|
||||
{
|
||||
Bgpupdate *update = (Bgpupdate *) BGP_VMCHKMSGTYPE(vm, BGP_UPDATE);
|
||||
if (!update) {
|
||||
Bgp_VmStoreMsgTypeMatch(vm, /*isMatching=*/FALSE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgpattrseg *tpa = Bgp_GetUpdateAttributes(update);
|
||||
if (!tpa) {
|
||||
vm->errCode = BGPEVMMSGERR;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgpattr *attr = Bgp_GetUpdateAttribute(tpa, code, vm->msg->table);
|
||||
if (Bgp_GetErrStat(NULL)) {
|
||||
vm->errCode = BGPEVMMSGERR;
|
||||
return NULL;
|
||||
}
|
||||
if (attr)
|
||||
Bgp_StartCommunity(it, attr);
|
||||
|
||||
return attr;
|
||||
}
|
||||
|
||||
void Bgp_VmDoComtch(Bgpvm *vm)
|
||||
{
|
||||
if (!BGP_VMCHKSTKSIZ(vm, 1))
|
||||
return;
|
||||
|
||||
Bgpcommidx *idx = (Bgpcommidx *) BGP_VMPOPA(vm);
|
||||
if (!idx || idx->opt == BGP_VMOPT_ASSUME_ACOMTC) {
|
||||
vm->errCode = BGPEVMBADCOMTCH; // TODO: BGPEVMBADCOMIDX;
|
||||
return;
|
||||
}
|
||||
|
||||
Boolean isMatching = FALSE; // unless found otherwise
|
||||
|
||||
Bgpcommiter it;
|
||||
Bgpattr *attr = Bgp_VmDoComSetup(vm, &it, BGP_ATTR_COMMUNITY);
|
||||
if (vm->errCode)
|
||||
return;
|
||||
if (!attr)
|
||||
goto done;
|
||||
|
||||
Bgpcomm *c;
|
||||
while ((c = Bgp_NextCommunity(&it)) != NULL) {
|
||||
if (MatchCommunity(c, idx)) {
|
||||
isMatching = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (Bgp_GetErrStat(NULL)) {
|
||||
vm->errCode = BGPEVMMSGERR;
|
||||
return;
|
||||
}
|
||||
|
||||
done:
|
||||
BGP_VMPUSH(vm, isMatching);
|
||||
}
|
||||
|
||||
static void ScMatchCommunityAndSetBit(const Bgpcomm *c, Bgpcommidx *idx)
|
||||
{
|
||||
Sint64 pos = BinarySearch16(idx->hiOnly, idx->hiOnlyCount, c->hi);
|
||||
if (pos >= 0) {
|
||||
SETBIT(idx->bitset, HIBITIDX(idx, pos));
|
||||
return;
|
||||
}
|
||||
|
||||
pos = BinarySearch16(idx->loOnly, idx->loOnlyCount, c->lo);
|
||||
if (pos >= 0) {
|
||||
SETBIT(idx->bitset, LOBITIDX(idx, pos));
|
||||
return;
|
||||
}
|
||||
|
||||
pos = BinarySearch32(idx->full, idx->fullCount, c->code);
|
||||
if (pos >= 0)
|
||||
SETBIT(idx->bitset, FULLBITIDX(idx, pos));
|
||||
}
|
||||
|
||||
static void MatchCommunityAndSetBit(const Bgpcomm *c, Bgpcommidx *idx)
|
||||
{
|
||||
Sint64 pos = BinarySearch16(idx->hiOnly, idx->hiOnlyCount, c->hi);
|
||||
if (pos >= 0)
|
||||
SETBIT(idx->bitset, HIBITIDX(idx, pos));
|
||||
|
||||
pos = BinarySearch16(idx->loOnly, idx->loOnlyCount, c->lo);
|
||||
if (pos >= 0)
|
||||
SETBIT(idx->bitset, LOBITIDX(idx, pos));
|
||||
|
||||
pos = BinarySearch32(idx->full, idx->fullCount, c->code);
|
||||
if (pos >= 0)
|
||||
SETBIT(idx->bitset, FULLBITIDX(idx, pos));
|
||||
}
|
||||
|
||||
static Boolean Bgp_VmDoAcomtcFast(Bgpcommidx *idx, Bgpcommiter *it)
|
||||
{
|
||||
Bgpcomm *c;
|
||||
while ((c = Bgp_NextCommunity(it)) != NULL)
|
||||
ScMatchCommunityAndSetBit(c, idx);
|
||||
|
||||
return FFZ(idx->bitset, idx->bitsetWords) == BITSETWIDTH(idx);
|
||||
}
|
||||
|
||||
static Boolean Bgp_VmDoAcomtcSlow(Bgpcommidx *idx, Bgpcommiter *it)
|
||||
{
|
||||
Bgpcomm *c;
|
||||
while ((c = Bgp_NextCommunity(it)) != NULL)
|
||||
MatchCommunityAndSetBit(c, idx);
|
||||
|
||||
return FFZ(idx->bitset, idx->bitsetWords) == BITSETWIDTH(idx);
|
||||
}
|
||||
|
||||
void Bgp_VmDoAcomtc(Bgpvm *vm)
|
||||
{
|
||||
if (!BGP_VMCHKSTKSIZ(vm, 1))
|
||||
return;
|
||||
|
||||
Bgpcommidx *idx = (Bgpcommidx *) BGP_VMPOPA(vm);
|
||||
if (!idx || idx->opt == BGP_VMOPT_ASSUME_COMTCH) {
|
||||
vm->errCode = BGPEVMBADCOMTCH; // TODO: BGPEVMBADCOMIDX;
|
||||
return;
|
||||
}
|
||||
|
||||
Boolean isMatching = FALSE;
|
||||
|
||||
Bgpcommiter it;
|
||||
Bgpattr *attr = Bgp_VmDoComSetup(vm, &it, BGP_ATTR_COMMUNITY);
|
||||
if (vm->errCode)
|
||||
return;
|
||||
if (!attr)
|
||||
goto done;
|
||||
|
||||
CLRBITSET(idx->bitset, idx->bitsetWords);
|
||||
if (idx->opt == BGP_VMOPT_ASSUME_ACOMTC)
|
||||
isMatching = Bgp_VmDoAcomtcFast(idx, &it);
|
||||
else
|
||||
isMatching = Bgp_VmDoAcomtcSlow(idx, &it);
|
||||
|
||||
if (Bgp_GetErrStat(NULL)) {
|
||||
vm->errCode = BGPEVMMSGERR;
|
||||
return;
|
||||
}
|
||||
|
||||
done:
|
||||
BGP_VMPUSH(vm, isMatching);
|
||||
}
|
@ -0,0 +1,303 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file bgp/vm_dump.c
|
||||
*
|
||||
* BGP VM bytecode dump.
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*/
|
||||
|
||||
#include "bgp/vmintrin.h"
|
||||
|
||||
#include "sys/dbg.h"
|
||||
#include "numlib.h"
|
||||
#include "strlib.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#define LINEDIGS 5
|
||||
#define MAXOPCSTRLEN 6
|
||||
#define CODELINELEN 50
|
||||
#define MAXINDENT 8
|
||||
#define INDENTLEN 2
|
||||
#define COMMENTLEN 70
|
||||
|
||||
static const char *OpcString(Bgpvmopc opc)
|
||||
{
|
||||
// NOTE: Invariant: strlen(return) <= MAXOPCSTRLEN
|
||||
switch (opc) {
|
||||
case BGP_VMOP_NOP: return "NOP";
|
||||
case BGP_VMOP_LOAD: return "LOAD";
|
||||
case BGP_VMOP_LOADU: return "LOADU";
|
||||
case BGP_VMOP_LOADN: return "LOADN";
|
||||
case BGP_VMOP_LOADK: return "LOADK";
|
||||
case BGP_VMOP_CALL: return "CALL";
|
||||
case BGP_VMOP_BLK: return "BLK";
|
||||
case BGP_VMOP_ENDBLK: return "ENDBLK";
|
||||
case BGP_VMOP_TAG: return "TAG";
|
||||
case BGP_VMOP_NOT: return "NOT";
|
||||
case BGP_VMOP_CFAIL: return "CFAIL";
|
||||
case BGP_VMOP_CPASS: return "CPASS";
|
||||
case BGP_VMOP_JZ: return "JZ";
|
||||
case BGP_VMOP_JNZ: return "JNZ";
|
||||
case BGP_VMOP_CHKT: return "CHKT";
|
||||
case BGP_VMOP_CHKA: return "CHKA";
|
||||
case BGP_VMOP_EXCT: return "EXCT";
|
||||
case BGP_VMOP_SUPN: return "SUPN";
|
||||
case BGP_VMOP_SUBN: return "SUBN";
|
||||
case BGP_VMOP_RELT: return "RELT";
|
||||
case BGP_VMOP_ASMTCH: return "ASMTCH";
|
||||
case BGP_VMOP_FASMTC: return "FASMTC";
|
||||
case BGP_VMOP_COMTCH: return "COMTCH";
|
||||
case BGP_VMOP_ACOMTC: return "ACOMTC";
|
||||
case BGP_VMOP_END: return "END";
|
||||
default: return "???";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *BgpTypeString(BgpType typ)
|
||||
{
|
||||
switch (typ) {
|
||||
case BGP_OPEN: return "OPEN";
|
||||
case BGP_UPDATE: return "UPDATE";
|
||||
case BGP_NOTIFICATION: return "NOTIFICATION";
|
||||
case BGP_KEEPALIVE: return "KEEPALIVE";
|
||||
case BGP_ROUTE_REFRESH: return "ROUTE_REFRESH";
|
||||
case BGP_CLOSE: return "CLOSE";
|
||||
default: return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *BgpAttrString(BgpAttrCode code)
|
||||
{
|
||||
switch (code) {
|
||||
case BGP_ATTR_ORIGIN: return "ORIGIN";
|
||||
case BGP_ATTR_AS_PATH: return "AS_PATH";
|
||||
case BGP_ATTR_NEXT_HOP: return "NEXT_HOP";
|
||||
case BGP_ATTR_MULTI_EXIT_DISC: return "MULTI_EXIT_DISC";
|
||||
case BGP_ATTR_LOCAL_PREF: return "LOCAL_PREF";
|
||||
case BGP_ATTR_ATOMIC_AGGREGATE: return "ATOMIC_AGGREGATE";
|
||||
case BGP_ATTR_AGGREGATOR: return "AGGREGATOR";
|
||||
case BGP_ATTR_COMMUNITY: return "COMMUNITY";
|
||||
case BGP_ATTR_ORIGINATOR_ID: return "ORIGINATOR_ID";
|
||||
case BGP_ATTR_CLUSTER_LIST: return "CLUSTER_LIST";
|
||||
case BGP_ATTR_DPA: return "DPA";
|
||||
case BGP_ATTR_ADVERTISER: return "ADVERTISER";
|
||||
case BGP_ATTR_RCID_PATH_CLUSTER_ID: return "RCID_PATH_CLUSTER_ID";
|
||||
case BGP_ATTR_MP_REACH_NLRI: return "MP_REACH_NLRI";
|
||||
case BGP_ATTR_MP_UNREACH_NLRI: return "MP_UNREACH_NLRI";
|
||||
case BGP_ATTR_EXTENDED_COMMUNITY: return "EXTENDED_COMMUNITY";
|
||||
case BGP_ATTR_AS4_PATH: return "AS4_PATH";
|
||||
case BGP_ATTR_AS4_AGGREGATOR: return "AS4_AGGREGATOR";
|
||||
case BGP_ATTR_SAFI_SSA: return "SAFI_SSA";
|
||||
case BGP_ATTR_CONNECTOR: return "CONNECTOR";
|
||||
case BGP_ATTR_AS_PATHLIMIT: return "AS_PATHLIMIT";
|
||||
case BGP_ATTR_PMSI_TUNNEL: return "PMSI_TUNNEL";
|
||||
case BGP_ATTR_TUNNEL_ENCAPSULATION: return "TUNNEL_ENCAPSULATION";
|
||||
case BGP_ATTR_TRAFFIC_ENGINEERING: return "TRAFFIC_ENGINEERING";
|
||||
case BGP_ATTR_IPV6_ADDRESS_SPECIFIC_EXTENDED_COMMUNITY: return "IPV6_ADDRESS_SPECIFIC_EXTENDED_COMMUNITY";
|
||||
case BGP_ATTR_AIGP: return "AIGP";
|
||||
case BGP_ATTR_PE_DISTINGUISHER_LABELS: return "PE_DISTINGUISHER_LABELS";
|
||||
case BGP_ATTR_ENTROPY_LEVEL_CAPABILITY: return "ENTROPY_LEVEL_CAPABILITY";
|
||||
case BGP_ATTR_LS: return "LS";
|
||||
case BGP_ATTR_LARGE_COMMUNITY: return "LARGE_COMMUNITY";
|
||||
case BGP_ATTR_BGPSEC_PATH: return "BGPSEC_PATH";
|
||||
case BGP_ATTR_COMMUNITY_CONTAINER: return "COMMUNITY_CONTAINER";
|
||||
case BGP_ATTR_PREFIX_SID: return "PREFIX_SID";
|
||||
case BGP_ATTR_SET: return "SET";
|
||||
default: return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *NetOpArgString(Uint8 opa)
|
||||
{
|
||||
switch (opa) {
|
||||
case BGP_VMOPA_NLRI: return "NLRI";
|
||||
case BGP_VMOPA_MPREACH: return "MP_REACH_NLRI";
|
||||
case BGP_VMOPA_ALL_NLRI: return "ALL_NLRI";
|
||||
case BGP_VMOPA_WITHDRAWN: return "WITHDRAWN";
|
||||
case BGP_VMOPA_MPUNREACH: return "MP_UNREACH_NLRI";
|
||||
case BGP_VMOPA_ALL_WITHDRAWN: return "ALL_WITHDRAWN";
|
||||
default: return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static char *ExplainJump(char *buf, Uint32 ip, Uint8 disp, Uint32 progLen)
|
||||
{
|
||||
char *p = buf;
|
||||
|
||||
strcpy(p, "to line: "); p += 9;
|
||||
|
||||
Uint32 target = ip + 1;
|
||||
target += 1 + disp;
|
||||
|
||||
p = Utoa(target, p);
|
||||
if (target > progLen)
|
||||
strcpy(p, " (JUMP TARGET OUT OF BOUNDS!)");
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static char *Indent(char *p, Bgpvmopc opc, int level)
|
||||
{
|
||||
int n = CLAMP(level, 0, MAXINDENT);
|
||||
int last = n - 1;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
*p++ = (opc == BGP_VMOP_ENDBLK && i == last) ? '+' : '|';
|
||||
for (int j = 1; j < INDENTLEN; j++)
|
||||
*p++ = (opc == BGP_VMOP_ENDBLK && i == last) ? '-' : ' ';
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
static char *CommentCodeLine(char *line, const char *comment)
|
||||
{
|
||||
char *p = line;
|
||||
|
||||
p += Df_strpadr(p, ' ', CODELINELEN);
|
||||
|
||||
*p++ = ' ';
|
||||
*p++ = ';'; *p++ = ' ';
|
||||
|
||||
size_t n = strlen(comment);
|
||||
if (3 + n >= COMMENTLEN) {
|
||||
n = COMMENTLEN - 3 - 3;
|
||||
|
||||
memcpy(p, comment, n);
|
||||
p += n;
|
||||
*p++ = '.'; *p++ = '.'; *p++ = '.';
|
||||
} else {
|
||||
memcpy(p, comment, n);
|
||||
p += n;
|
||||
}
|
||||
|
||||
*p = '\0';
|
||||
return p;
|
||||
}
|
||||
|
||||
void Bgp_VmDumpProgram(Bgpvm *vm, void *streamp, const StmOps *ops)
|
||||
{
|
||||
char explainbuf[64];
|
||||
char buf[256];
|
||||
|
||||
int indent = 0;
|
||||
|
||||
// NOTE: <= so it includes trailing END
|
||||
for (Uint32 ip = 0; ip <= vm->progLen; ip++) {
|
||||
Bgpvmbytec ir = vm->prog[ip];
|
||||
Bgpvmopc opc = BGP_VMOPC(ir);
|
||||
Uint8 opa = BGP_VMOPARG(ir);
|
||||
|
||||
const char *opcnam = OpcString(opc);
|
||||
assert(strlen(opcnam) <= MAXOPCSTRLEN);
|
||||
|
||||
char *p = buf;
|
||||
|
||||
// Line number
|
||||
Utoa(ip+1, p);
|
||||
p += Df_strpadl(p, '0', LINEDIGS);
|
||||
|
||||
*p++ = ':';
|
||||
*p++ = ' ';
|
||||
|
||||
// Instruction hex dump
|
||||
*p++ = '0';
|
||||
*p++ = 'x';
|
||||
Xtoa(ir, p);
|
||||
p += Df_strpadl(p, '0', XDIGS(ir));
|
||||
*p++ = ' ';
|
||||
|
||||
// Code indent
|
||||
p = Indent(p, opc, indent);
|
||||
|
||||
// Opcode
|
||||
strcpy(p, opcnam);
|
||||
p += Df_strpadr(p, ' ', MAXOPCSTRLEN);
|
||||
|
||||
// Instruction argument
|
||||
const char *opastr = NULL;
|
||||
switch (opc) {
|
||||
case BGP_VMOP_LOAD:
|
||||
*p++ = ' ';
|
||||
|
||||
p = Itoa((Sint8) opa, p);
|
||||
|
||||
break;
|
||||
case BGP_VMOP_LOADU:
|
||||
case BGP_VMOP_JZ:
|
||||
case BGP_VMOP_JNZ:
|
||||
*p++ = ' ';
|
||||
|
||||
p = Utoa(opa, p);
|
||||
if (opc == BGP_VMOP_JZ || opc == BGP_VMOP_JNZ)
|
||||
opastr = ExplainJump(explainbuf, ip, opa, vm->progLen);
|
||||
|
||||
break;
|
||||
case BGP_VMOP_TAG:
|
||||
case BGP_VMOP_CHKT:
|
||||
case BGP_VMOP_CHKA:
|
||||
case BGP_VMOP_EXCT:
|
||||
case BGP_VMOP_SUBN:
|
||||
case BGP_VMOP_SUPN:
|
||||
case BGP_VMOP_RELT:
|
||||
*p++ = ' ';
|
||||
*p++ = '0'; *p++ = 'x';
|
||||
|
||||
Xtoa(opa, p);
|
||||
p += Df_strpadl(p, '0', XDIGS(opa));
|
||||
|
||||
if (opc == BGP_VMOP_CHKT)
|
||||
opastr = BgpTypeString(opa);
|
||||
else if (opc == BGP_VMOP_CHKA)
|
||||
opastr = BgpAttrString(opa);
|
||||
else
|
||||
opastr = NetOpArgString(opa);
|
||||
|
||||
break;
|
||||
case BGP_VMOP_LOADK:
|
||||
*p++ = ' ';
|
||||
*p++ = 'K';
|
||||
*p++ = '[';
|
||||
p = Utoa(opa, p);
|
||||
*p++ = ']';
|
||||
*p = '\0';
|
||||
break;
|
||||
case BGP_VMOP_CALL:
|
||||
*p++ = ' ';
|
||||
*p++ = 'F';
|
||||
*p++ = 'N';
|
||||
*p++ = '[';
|
||||
p = Utoa(opa, p);
|
||||
*p++ = ']';
|
||||
*p = '\0';
|
||||
|
||||
if (opa < vm->nfuncs) {
|
||||
Funsym fsym;
|
||||
|
||||
fsym.func = (void (*)(void)) vm->funcs[opa];
|
||||
opastr = Sys_GetSymbolName(fsym.sym);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Optional comment after CODELINELEN columns
|
||||
if (opastr)
|
||||
p = CommentCodeLine(buf, opastr);
|
||||
|
||||
// Flush line (no need for '\0')
|
||||
*p++ = '\n';
|
||||
ops->Write(streamp, buf, p - buf);
|
||||
|
||||
// Update indent
|
||||
if (opc == BGP_VMOP_BLK)
|
||||
indent++;
|
||||
if (opc == BGP_VMOP_ENDBLK)
|
||||
indent--;
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file bgp/vm_gccdef.h
|
||||
*
|
||||
* `#define`s for GNUC optimized BGP VM execution loop.
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*
|
||||
* \note This file should be `#include`d by `bgp/vm.c`
|
||||
*/
|
||||
|
||||
#ifdef DF_BGP_VMDEF_H_
|
||||
#error "Only one vm_<impl>def.h file may be #include-d"
|
||||
#endif
|
||||
#define DF_BGP_VMDEF_H_
|
||||
|
||||
#define _CONCAT(x, y) x ## y
|
||||
#define _XCONCAT(x, y) _CONCAT(x, y)
|
||||
|
||||
#ifdef __clang__
|
||||
// No __attribute__ on labels in CLANG
|
||||
#define LIKELY
|
||||
#else
|
||||
|
||||
#define LIKELY \
|
||||
_XCONCAT(_BRANCH_PREDICT_HINT, __COUNTER__): \
|
||||
__attribute__((__hot__, __unused__))
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __clang__
|
||||
// No __attribute__ on labels in CLANG
|
||||
#define UNLIKELY
|
||||
#else
|
||||
|
||||
#define UNLIKELY \
|
||||
_XCONCAT(_BRANCH_PREDICT_HINT, __COUNTER__): \
|
||||
__attribute__((__cold__, __unused__))
|
||||
|
||||
#endif
|
||||
|
||||
#define FETCH(ir, vm) (ir = (vm)->prog[(vm)->pc++])
|
||||
|
||||
#define EXPECT(opcode, ir, vm) \
|
||||
do { \
|
||||
if (__builtin_expect( \
|
||||
BGP_VMOPC((vm)->prog[(vm)->pc]) == BGP_VMOP_ ## opcode, \
|
||||
1 \
|
||||
)) { \
|
||||
ir = (vm)->prog[(vm)->pc++]; \
|
||||
goto EX_ ## opcode; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define DISPATCH(opcode) \
|
||||
_Pragma("GCC diagnostic push"); \
|
||||
_Pragma("GCC diagnostic ignored \"-Wpedantic\"") \
|
||||
goto *bgp_vmOpTab[opcode]; \
|
||||
_Pragma("GCC diagnostic pop") \
|
||||
switch (opcode) // This keeps consistency with regular vm_cdef.h
|
||||
|
||||
#define EXECUTE(opcode) case BGP_VMOP_ ## opcode: EX_ ## opcode
|
||||
|
||||
#define EXECUTE_SIGILL default: EX_SIGILL
|
@ -0,0 +1,98 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file bgp/vm_optab.h
|
||||
*
|
||||
* Computed goto table for GNUC optimized BGP VM execution loop.
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*
|
||||
* File defines a GNUC-specific computed goto table.
|
||||
*
|
||||
* It can be used as an alternative to a regular switch to
|
||||
* accelerate the BGP filtering engine execution loop (Bgp_VmExec()).
|
||||
*
|
||||
* Constraints:
|
||||
* - Array length: 256 (8-bit OPCODE width)
|
||||
* - Label address MUST follow the convention EX_<OPCODE NAME> (e.g. EX_NOP, EX_EXCT)
|
||||
* - Any unused OPCODE MUST be set to EX_SIGILL
|
||||
*
|
||||
* Operations on the computed goto table are defined in: bgp/vm_gccdef.h
|
||||
*
|
||||
* \see [GCC Documentation](https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html)
|
||||
*
|
||||
* \warning KEEP TABLE IN SYNC WITH OPCODES IN: bgp/vm.h
|
||||
*/
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wpedantic"
|
||||
#pragma GCC diagnostic ignored "-Woverride-init"
|
||||
|
||||
static void *const bgp_vmOpTab[256] = {
|
||||
// Following clears everything else in the array to SIGILL,
|
||||
// 8 instructions per line (256 &&EX_SIGILL)
|
||||
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
|
||||
// Following initializes valid OPCODEs
|
||||
|
||||
[BGP_VMOP_NOP] = &&EX_NOP,
|
||||
[BGP_VMOP_LOAD] = &&EX_LOAD,
|
||||
[BGP_VMOP_LOADU] = &&EX_LOADU,
|
||||
[BGP_VMOP_LOADN] = &&EX_LOADN,
|
||||
[BGP_VMOP_LOADK] = &&EX_LOADK,
|
||||
[BGP_VMOP_CALL] = &&EX_CALL,
|
||||
[BGP_VMOP_BLK] = &&EX_BLK,
|
||||
[BGP_VMOP_ENDBLK] = &&EX_ENDBLK,
|
||||
[BGP_VMOP_TAG] = &&EX_TAG,
|
||||
[BGP_VMOP_NOT] = &&EX_NOT,
|
||||
[BGP_VMOP_CFAIL] = &&EX_CFAIL,
|
||||
[BGP_VMOP_CPASS] = &&EX_CPASS,
|
||||
[BGP_VMOP_JZ] = &&EX_JZ,
|
||||
[BGP_VMOP_JNZ] = &&EX_JNZ,
|
||||
[BGP_VMOP_CHKT] = &&EX_CHKT,
|
||||
[BGP_VMOP_CHKA] = &&EX_CHKA,
|
||||
[BGP_VMOP_EXCT] = &&EX_EXCT,
|
||||
[BGP_VMOP_SUPN] = &&EX_SUPN,
|
||||
[BGP_VMOP_SUBN] = &&EX_SUBN,
|
||||
[BGP_VMOP_RELT] = &&EX_RELT,
|
||||
[BGP_VMOP_ASMTCH] = &&EX_ASMTCH,
|
||||
[BGP_VMOP_FASMTC] = &&EX_FASMTC,
|
||||
[BGP_VMOP_COMTCH] = &&EX_COMTCH,
|
||||
[BGP_VMOP_ACOMTC] = &&EX_ACOMTC,
|
||||
[BGP_VMOP_END] = &&EX_END
|
||||
};
|
||||
|
||||
#pragma GCC diagnostic pop
|
@ -0,0 +1,119 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file bufio.c
|
||||
*
|
||||
* I/O stream buffering utilities implementation.
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*/
|
||||
|
||||
#include "sys/sys_local.h" // for Sys_SetErrStat() - vsnprintf()
|
||||
#include "bufio.h"
|
||||
#include "numlib.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
Sint64 Bufio_Flush(Stmbuf *sb)
|
||||
{
|
||||
assert(sb->ops->Write);
|
||||
|
||||
while (sb->len > 0) {
|
||||
Sint64 n = sb->ops->Write(sb->streamp, sb->buf, sb->len);
|
||||
if (n < 0)
|
||||
return NG;
|
||||
|
||||
memmove(sb->buf, sb->buf + n, sb->len - n);
|
||||
sb->len -= n;
|
||||
sb->total += n;
|
||||
}
|
||||
|
||||
return sb->total;
|
||||
}
|
||||
|
||||
Sint64 _Bufio_Putsn(Stmbuf *sb, const char *s, size_t nbytes)
|
||||
{
|
||||
if (sb->len + nbytes > sizeof(sb->buf) && Bufio_Flush(sb) == -1)
|
||||
return -1;
|
||||
if (nbytes > sizeof(sb->buf))
|
||||
return sb->ops->Write(sb->streamp, sb, nbytes);
|
||||
|
||||
memcpy(sb->buf + sb->len, s, nbytes);
|
||||
sb->len += nbytes;
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
Sint64 Bufio_Putu(Stmbuf *sb, unsigned long long val)
|
||||
{
|
||||
char buf[DIGS(val) + 1];
|
||||
|
||||
char *eptr = Utoa(val, buf);
|
||||
return Bufio_Putsn(sb, buf, eptr - buf);
|
||||
}
|
||||
|
||||
Sint64 Bufio_Putx(Stmbuf *sb, unsigned long long val)
|
||||
{
|
||||
char buf[XDIGS(val) + 1];
|
||||
|
||||
char *eptr = Xtoa(val, buf);
|
||||
return Bufio_Putsn(sb, buf, eptr - buf);
|
||||
}
|
||||
|
||||
Sint64 Bufio_Puti(Stmbuf *sb, long long val)
|
||||
{
|
||||
char buf[1 + DIGS(val) + 1];
|
||||
|
||||
char *eptr = Itoa(val, buf);
|
||||
return Bufio_Putsn(sb, buf, eptr - buf);
|
||||
}
|
||||
|
||||
Sint64 Bufio_Putf(Stmbuf *sb, double val)
|
||||
{
|
||||
char buf[DOUBLE_STRLEN + 1];
|
||||
|
||||
char *eptr = Ftoa(val, buf);
|
||||
return Bufio_Putsn(sb, buf, eptr - buf);
|
||||
}
|
||||
|
||||
Sint64 Bufio_Printf(Stmbuf *sb, const char *fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
Sint64 n;
|
||||
|
||||
va_start(va, fmt);
|
||||
n = Bufio_Vprintf(sb, fmt, va);
|
||||
va_end(va);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
Sint64 Bufio_Vprintf(Stmbuf *sb, const char *fmt, va_list va)
|
||||
{
|
||||
va_list vc;
|
||||
char *buf;
|
||||
int n1, n2;
|
||||
|
||||
va_copy(vc, va);
|
||||
n1 = vsnprintf(NULL, 0, fmt, vc);
|
||||
va_end(vc);
|
||||
if (n1 < 0) {
|
||||
Sys_SetErrStat(errno, "vsnprintf() failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf = (char *) alloca(n1 + 1);
|
||||
n2 = vsnprintf(buf, n1 + 1, fmt, va);
|
||||
if (n2 < 0) {
|
||||
Sys_SetErrStat(errno, "vsnprintf() failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
assert(n1 == n2);
|
||||
|
||||
return Bufio_Putsn(sb, buf, n2);
|
||||
}
|
||||
|
@ -0,0 +1,328 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file cpr/bzip2.c
|
||||
*
|
||||
* Interfaces with `libbzip2` and implements BZ2 compressor/decompressor.
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*/
|
||||
|
||||
#include "cpr/bzip2.h"
|
||||
|
||||
#include <bzlib.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct Bzip2StmObj Bzip2StmObj;
|
||||
struct Bzip2StmObj {
|
||||
bz_stream bz2;
|
||||
void *streamp;
|
||||
const StmOps *ops;
|
||||
unsigned bufsiz;
|
||||
Boolean8 compressing;
|
||||
char buf[FLEX_ARRAY]; // `bufsiz' bytes
|
||||
};
|
||||
|
||||
#define BZIP2_EBADSTREAM 0xffff
|
||||
|
||||
#define BZIP2_BUFSIZ (32 * 1024)
|
||||
|
||||
#define MAKESINT64(lo32, hi32) \
|
||||
((Sint64) ((Uint64) (lo32) | (((Uint64) hi32) << 32)))
|
||||
|
||||
static Sint64 Bzip2_FlushData(Bzip2StmHn hn)
|
||||
{
|
||||
size_t nbytes = hn->bufsiz - hn->bz2.avail_out;
|
||||
|
||||
Sint64 n = hn->ops->Write(hn->streamp, hn->buf, nbytes);
|
||||
if (n <= 0)
|
||||
return -1;
|
||||
|
||||
size_t left = nbytes - n;
|
||||
memmove(hn->buf, hn->buf + n, left);
|
||||
|
||||
hn->bz2.next_out = hn->buf + left;
|
||||
hn->bz2.avail_out = hn->bufsiz - left;
|
||||
return n;
|
||||
}
|
||||
|
||||
static Sint64 Bzip2_StmRead(void *streamp, void *buf, size_t nbytes)
|
||||
{
|
||||
return Bzip2_Read((Bzip2StmHn) streamp, buf, nbytes);
|
||||
}
|
||||
|
||||
static Sint64 Bzip2_StmWrite(void *streamp, const void *buf, size_t nbytes)
|
||||
{
|
||||
return Bzip2_Write((Bzip2StmHn) streamp, buf, nbytes);
|
||||
}
|
||||
|
||||
static Sint64 Bzip2_StmTell(void *streamp)
|
||||
{
|
||||
Bzip2StmHn hn = (Bzip2StmHn) streamp;
|
||||
|
||||
return hn->compressing ?
|
||||
MAKESINT64(hn->bz2.total_out_lo32, hn->bz2.total_out_hi32) :
|
||||
MAKESINT64(hn->bz2.total_in_lo32, hn->bz2.total_in_hi32);
|
||||
}
|
||||
|
||||
static Judgement Bzip2_StmFinish(void *streamp)
|
||||
{
|
||||
return Bzip2_Finish((Bzip2StmHn) streamp);
|
||||
}
|
||||
|
||||
static void Bzip2_StmClose(void *streamp)
|
||||
{
|
||||
Bzip2_Close((Bzip2StmHn) streamp);
|
||||
}
|
||||
|
||||
static const StmOps bzip2_stmOps = {
|
||||
Bzip2_StmRead,
|
||||
Bzip2_StmWrite,
|
||||
NULL,
|
||||
Bzip2_StmTell,
|
||||
Bzip2_StmFinish,
|
||||
Bzip2_StmClose
|
||||
};
|
||||
static const StmOps bzip2_ncStmOps = {
|
||||
Bzip2_StmRead,
|
||||
Bzip2_StmWrite,
|
||||
NULL,
|
||||
Bzip2_StmTell,
|
||||
Bzip2_StmFinish,
|
||||
NULL
|
||||
};
|
||||
|
||||
const StmOps *const Bzip2_StmOps = &bzip2_stmOps;
|
||||
const StmOps *const Bzip2_NcStmOps = &bzip2_ncStmOps;
|
||||
|
||||
static THREAD_LOCAL Bzip2Ret bzip2_errStat = 0;
|
||||
|
||||
static void Bzip2_SetErrStat(Bzip2Ret ret)
|
||||
{
|
||||
bzip2_errStat = ret;
|
||||
}
|
||||
|
||||
Bzip2Ret Bzip2_GetErrStat(void)
|
||||
{
|
||||
return bzip2_errStat;
|
||||
}
|
||||
|
||||
const char *Bzip2_ErrorString(Bzip2Ret ret)
|
||||
{
|
||||
switch (ret) {
|
||||
case BZIP2_EBADSTREAM: return "Bad stream operation";
|
||||
case BZ_OK: return "Success";
|
||||
case BZ_SEQUENCE_ERROR: return "Sequence error";
|
||||
case BZ_PARAM_ERROR: return "Invalid parameter";
|
||||
case BZ_MEM_ERROR: return "Memory allocation failure";
|
||||
case BZ_DATA_ERROR: return "Data integrity error";
|
||||
case BZ_DATA_ERROR_MAGIC: return "Stream magic number mismatch";
|
||||
case BZ_IO_ERROR: return "I/O error";
|
||||
case BZ_UNEXPECTED_EOF: return "Unexpected compressed stream end";
|
||||
case BZ_OUTBUFF_FULL: return "Output buffer full";
|
||||
case BZ_CONFIG_ERROR: return "Bzip2 library configuration error";
|
||||
default: return "Unknown BZ2 error";
|
||||
}
|
||||
}
|
||||
|
||||
Bzip2StmHn Bzip2_OpenCompress(void *streamp,
|
||||
const StmOps *ops,
|
||||
const Bzip2CprOpts *opts)
|
||||
{
|
||||
const Bzip2CprOpts defOpts = { 0, 0, 0, 0 };
|
||||
if (!opts)
|
||||
opts = &defOpts;
|
||||
|
||||
if (!ops->Write) {
|
||||
Bzip2_SetErrStat(BZIP2_EBADSTREAM);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int compression = CLAMP(opts->compression, 0, 9);
|
||||
if (compression == 0)
|
||||
compression = 9; // default value
|
||||
|
||||
int verbosity = CLAMP(opts->verbose, 0, 4);
|
||||
int factor = opts->factor;
|
||||
size_t bufsiz = MIN(opts->bufsiz, INT_MAX);
|
||||
if (bufsiz == 0)
|
||||
bufsiz = BZIP2_BUFSIZ;
|
||||
|
||||
Bzip2StmObj *hn = (Bzip2StmObj *) malloc(offsetof(Bzip2StmObj, buf) + bufsiz);
|
||||
if (!hn) {
|
||||
Bzip2_SetErrStat(BZ_MEM_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(&hn->bz2, 0, sizeof(hn->bz2));
|
||||
hn->streamp = streamp;
|
||||
hn->ops = ops;
|
||||
hn->bufsiz = bufsiz;
|
||||
hn->compressing = TRUE;
|
||||
|
||||
int err = BZ2_bzCompressInit(&hn->bz2, compression, verbosity, factor);
|
||||
if (err != BZ_OK) {
|
||||
Bzip2_SetErrStat(err);
|
||||
free(hn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hn->bz2.next_out = hn->buf;
|
||||
hn->bz2.avail_out = hn->bufsiz;
|
||||
Bzip2_SetErrStat(BZ_OK);
|
||||
return hn;
|
||||
}
|
||||
|
||||
Bzip2StmHn Bzip2_OpenDecompress(void *streamp,
|
||||
const StmOps *ops,
|
||||
const Bzip2DecOpts *opts)
|
||||
{
|
||||
const Bzip2DecOpts defOpts = { 0, 0, FALSE };
|
||||
if (!opts)
|
||||
opts = &defOpts;
|
||||
|
||||
if (!ops->Read) {
|
||||
Bzip2_SetErrStat(BZIP2_EBADSTREAM);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int small = opts->low_mem;
|
||||
int verbosity = CLAMP(opts->verbose, 0, 4);
|
||||
size_t bufsiz = MIN(opts->bufsiz, INT_MAX);
|
||||
if (bufsiz == 0)
|
||||
bufsiz = BZIP2_BUFSIZ;
|
||||
|
||||
Bzip2StmObj *hn = (Bzip2StmObj *) malloc(offsetof(Bzip2StmObj, buf[bufsiz]));
|
||||
if (!hn) {
|
||||
Bzip2_SetErrStat(BZ_MEM_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(&hn->bz2, 0, sizeof(hn->bz2));
|
||||
hn->streamp = streamp;
|
||||
hn->ops = ops;
|
||||
hn->bufsiz = bufsiz;
|
||||
hn->compressing = FALSE;
|
||||
|
||||
int err = BZ2_bzDecompressInit(&hn->bz2, verbosity, small);
|
||||
if (err != BZ_OK) {
|
||||
Bzip2_SetErrStat(err);
|
||||
free(hn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bzip2_SetErrStat(BZ_OK);
|
||||
return hn;
|
||||
}
|
||||
|
||||
Sint64 Bzip2_Read(Bzip2StmHn hn, void *buf, size_t nbytes)
|
||||
{
|
||||
if (hn->compressing) {
|
||||
Bzip2_SetErrStat(BZIP2_EBADSTREAM);
|
||||
return -1;
|
||||
}
|
||||
|
||||
Bzip2Ret ret = BZ_OK;
|
||||
|
||||
hn->bz2.next_out = (char *) buf;
|
||||
hn->bz2.avail_out = nbytes;
|
||||
while (hn->bz2.avail_out > 0) {
|
||||
if (hn->bz2.avail_in == 0) {
|
||||
Sint64 n = hn->ops->Read(hn->streamp, hn->buf, hn->bufsiz);
|
||||
if (n <= 0) {
|
||||
if (n < 0) ret = BZ_IO_ERROR;
|
||||
|
||||
break; // EOF
|
||||
}
|
||||
|
||||
hn->bz2.next_in = hn->buf;
|
||||
hn->bz2.avail_in = n;
|
||||
}
|
||||
|
||||
int err = BZ2_bzDecompress(&hn->bz2);
|
||||
if (err == BZ_STREAM_END)
|
||||
break;
|
||||
if (err != BZ_OK) {
|
||||
ret = err;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Bzip2_SetErrStat(ret);
|
||||
return nbytes - hn->bz2.avail_out;
|
||||
}
|
||||
|
||||
Sint64 Bzip2_Write(Bzip2StmHn hn, const void *buf, size_t nbytes)
|
||||
{
|
||||
if (!hn->compressing) {
|
||||
Bzip2_SetErrStat(BZIP2_EBADSTREAM);
|
||||
return -1;
|
||||
}
|
||||
|
||||
Bzip2Ret ret = BZ_OK;
|
||||
|
||||
hn->bz2.next_in = (char *) buf; // safe
|
||||
hn->bz2.avail_in = nbytes;
|
||||
while (hn->bz2.avail_in > 0) {
|
||||
if (hn->bz2.avail_out == 0) {
|
||||
Sint64 n = Bzip2_FlushData(hn);
|
||||
if (n <= 0) {
|
||||
if (n < 0) ret = BZ_IO_ERROR;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int err = BZ2_bzCompress(&hn->bz2, BZ_RUN);
|
||||
if (err != BZ_RUN_OK) {
|
||||
ret = err;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Bzip2_SetErrStat(ret);
|
||||
return nbytes - hn->bz2.avail_in;
|
||||
}
|
||||
|
||||
Judgement Bzip2_Finish(Bzip2StmHn hn)
|
||||
{
|
||||
if (!hn->compressing) {
|
||||
Bzip2_SetErrStat(BZIP2_EBADSTREAM);
|
||||
return NG;
|
||||
}
|
||||
|
||||
int err;
|
||||
|
||||
do {
|
||||
// Call BZ2_bzCompress() repeatedly with BZ_FINISH to consume all data
|
||||
err = BZ2_bzCompress(&hn->bz2, BZ_FINISH);
|
||||
if (err != BZ_STREAM_END && err != BZ_FINISH_OK) {
|
||||
Bzip2_SetErrStat(err);
|
||||
return NG;
|
||||
}
|
||||
if (Bzip2_FlushData(hn) == -1) {
|
||||
Bzip2_SetErrStat(BZ_IO_ERROR);
|
||||
return NG;
|
||||
}
|
||||
} while (err != BZ_STREAM_END);
|
||||
|
||||
Bzip2_SetErrStat(BZ_OK);
|
||||
return OK;
|
||||
}
|
||||
|
||||
void Bzip2_Close(Bzip2StmHn hn)
|
||||
{
|
||||
if (hn->ops->Close)
|
||||
hn->ops->Close(hn->streamp);
|
||||
|
||||
if (hn->compressing)
|
||||
BZ2_bzCompressEnd(&hn->bz2);
|
||||
else
|
||||
BZ2_bzDecompressEnd(&hn->bz2);
|
||||
|
||||
free(hn);
|
||||
}
|
@ -0,0 +1,387 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file cpr/flate.c
|
||||
*
|
||||
* Interfaces with `zlib` and implements INFLATE/DEFLATE.
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*/
|
||||
|
||||
#include "cpr/flate.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <zlib.h>
|
||||
|
||||
typedef struct ZlibStmObj ZlibStmObj;
|
||||
struct ZlibStmObj {
|
||||
z_stream zs;
|
||||
void *streamp;
|
||||
const StmOps *ops;
|
||||
unsigned bufsiz;
|
||||
Boolean8 deflating;
|
||||
Uint8 buf[FLEX_ARRAY]; // `bufsiz` bytes
|
||||
};
|
||||
|
||||
#define ZSTM_BUFSIZ (32 * 1024)
|
||||
|
||||
#define ZSTM_EIO -1LL
|
||||
#define ZSTM_EBADSTREAM -2LL
|
||||
|
||||
static Sint64 Zlib_FlushData(ZlibStmHn hn)
|
||||
{
|
||||
size_t nbytes = hn->bufsiz - hn->zs.avail_out;
|
||||
|
||||
Sint64 n = hn->ops->Write(hn->streamp, hn->buf, nbytes);
|
||||
if (n == -1)
|
||||
return -1;
|
||||
|
||||
size_t left = nbytes - n;
|
||||
memmove(hn->buf, hn->buf + n, left);
|
||||
|
||||
hn->zs.next_out = hn->buf + left;
|
||||
hn->zs.avail_out = hn->bufsiz - left;
|
||||
return n;
|
||||
}
|
||||
|
||||
static Sint64 Zlib_StmRead(void *streamp, void *buf, size_t nbytes)
|
||||
{
|
||||
return Zlib_Read((ZlibStmHn) streamp, buf, nbytes);
|
||||
}
|
||||
|
||||
static Sint64 Zlib_StmWrite(void *streamp, const void *buf, size_t nbytes)
|
||||
{
|
||||
return Zlib_Write((ZlibStmHn) streamp, buf, nbytes);
|
||||
}
|
||||
|
||||
static Sint64 Zlib_StmTell(void *streamp)
|
||||
{
|
||||
ZlibStmHn hn = (ZlibStmHn) streamp;
|
||||
return hn->deflating ? hn->zs.total_out : hn->zs.total_in;
|
||||
}
|
||||
|
||||
static Judgement Zlib_StmFinish(void *streamp)
|
||||
{
|
||||
return Zlib_Finish((ZlibStmHn) streamp);
|
||||
}
|
||||
|
||||
static void Zlib_StmClose(void *streamp)
|
||||
{
|
||||
Zlib_Close((ZlibStmHn) streamp);
|
||||
}
|
||||
|
||||
static const StmOps zlib_stmOps = {
|
||||
Zlib_StmRead,
|
||||
Zlib_StmWrite,
|
||||
NULL,
|
||||
Zlib_StmTell,
|
||||
Zlib_StmFinish,
|
||||
Zlib_StmClose
|
||||
};
|
||||
static const StmOps zlib_ncStmOps = {
|
||||
Zlib_StmRead,
|
||||
Zlib_StmWrite,
|
||||
NULL,
|
||||
Zlib_StmTell,
|
||||
Zlib_StmFinish,
|
||||
NULL
|
||||
};
|
||||
|
||||
const StmOps *const Zlib_StmOps = &zlib_stmOps;
|
||||
const StmOps *const Zlib_NcStmOps = &zlib_ncStmOps;
|
||||
|
||||
static THREAD_LOCAL ZlibRet zlib_errStat = 0;
|
||||
|
||||
static void Zlib_SetErrStat(ZlibRet ret)
|
||||
{
|
||||
if (ret == Z_ERRNO) {
|
||||
Sint64 err = errno;
|
||||
|
||||
ret |= (Sint32) (err << 32);
|
||||
}
|
||||
zlib_errStat = ret;
|
||||
}
|
||||
|
||||
ZlibRet Zlib_GetErrStat(void)
|
||||
{
|
||||
return zlib_errStat;
|
||||
}
|
||||
|
||||
const char *Zlib_ErrorString(ZlibRet ret)
|
||||
{
|
||||
if (ret == ZSTM_EIO)
|
||||
return "I/O error";
|
||||
if (ret == ZSTM_EBADSTREAM)
|
||||
return "Bad stream operation";
|
||||
|
||||
Sint32 zerrno = (Sint32) (ret & 0xffffffffu);
|
||||
Sint32 err = (Sint32) (ret >> 32);
|
||||
|
||||
switch (zerrno) {
|
||||
case Z_OK: return "Success";
|
||||
case Z_ERRNO: return strerror(err);
|
||||
case Z_STREAM_ERROR: return "Stream error";
|
||||
case Z_DATA_ERROR: return "Data error";
|
||||
case Z_MEM_ERROR: return "Memory allocation failure";
|
||||
case Z_BUF_ERROR: return "Buffer error";
|
||||
case Z_VERSION_ERROR: return "Zlib version error";
|
||||
default: return "Unknown Zlib error";
|
||||
}
|
||||
}
|
||||
|
||||
ZlibStmHn Zlib_InflateOpen(void *streamp,
|
||||
const StmOps *ops,
|
||||
const InflateOpts *opts)
|
||||
{
|
||||
const InflateOpts default_opts = {
|
||||
15,
|
||||
ZFMT_RFC1952,
|
||||
ZSTM_BUFSIZ
|
||||
};
|
||||
|
||||
if (!ops->Write) {
|
||||
Zlib_SetErrStat(ZSTM_EBADSTREAM);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!opts)
|
||||
opts = &default_opts;
|
||||
|
||||
int wbits = CLAMP(opts->win_bits, 8, 15);
|
||||
|
||||
// Mangle window bits according to the required RFC
|
||||
switch (opts->format) {
|
||||
case ZFMT_RFC1951:
|
||||
wbits = -wbits;
|
||||
break;
|
||||
case ZFMT_RFC1950:
|
||||
break;
|
||||
|
||||
case ZFMT_RFC1952:
|
||||
default:
|
||||
wbits += 16;
|
||||
break;
|
||||
}
|
||||
|
||||
size_t bufsiz = MIN(opts->bufsiz, INT_MAX); // safety to avoid short reads
|
||||
if (bufsiz == 0)
|
||||
bufsiz = ZSTM_BUFSIZ;
|
||||
|
||||
ZlibStmObj *hn = (ZlibStmObj *) malloc(offsetof(ZlibStmObj, buf[bufsiz]));
|
||||
if (!hn) {
|
||||
Zlib_SetErrStat(Z_MEM_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(&hn->zs, 0, sizeof(hn->zs));
|
||||
|
||||
int err = inflateInit2(&hn->zs, wbits);
|
||||
if (err != Z_OK) {
|
||||
Zlib_SetErrStat(err);
|
||||
free(hn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hn->streamp = streamp;
|
||||
hn->ops = ops;
|
||||
hn->bufsiz = bufsiz;
|
||||
hn->deflating = FALSE;
|
||||
Zlib_SetErrStat(Z_OK);
|
||||
return hn;
|
||||
}
|
||||
|
||||
ZlibStmHn Zlib_DeflateOpen(void *streamp,
|
||||
const StmOps *ops,
|
||||
const DeflateOpts *opts)
|
||||
{
|
||||
const DeflateOpts default_opts = {
|
||||
Z_DEFAULT_COMPRESSION,
|
||||
15,
|
||||
ZFMT_RFC1952,
|
||||
ZSTM_BUFSIZ
|
||||
};
|
||||
|
||||
if (!ops->Read) {
|
||||
Zlib_SetErrStat(ZSTM_EBADSTREAM);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!opts)
|
||||
opts = &default_opts;
|
||||
|
||||
// Setup open options
|
||||
int compression = opts->compression;
|
||||
if (compression != Z_DEFAULT_COMPRESSION)
|
||||
compression = CLAMP(compression, 0, 9);
|
||||
|
||||
int wbits = CLAMP(opts->win_bits, 8, 15);
|
||||
|
||||
// Mangle window bits according to the required RFC
|
||||
switch (opts->format) {
|
||||
case ZFMT_RFC1951:
|
||||
wbits = -wbits;
|
||||
break;
|
||||
case ZFMT_RFC1950:
|
||||
break;
|
||||
|
||||
case ZFMT_RFC1952:
|
||||
default:
|
||||
wbits += 16;
|
||||
break;
|
||||
}
|
||||
|
||||
size_t bufsiz = MIN(opts->bufsiz, INT_MAX); // safety to avoid short reads
|
||||
if (bufsiz == 0)
|
||||
bufsiz = ZSTM_BUFSIZ;
|
||||
|
||||
ZlibStmObj *hn = (ZlibStmObj *) malloc(offsetof(ZlibStmObj, buf[bufsiz]));
|
||||
if (!hn) {
|
||||
Zlib_SetErrStat(Z_MEM_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(&hn->zs, 0, sizeof(hn->zs));
|
||||
hn->streamp = streamp;
|
||||
hn->ops = ops;
|
||||
hn->bufsiz = bufsiz;
|
||||
hn->deflating = TRUE;
|
||||
|
||||
int err = deflateInit2(
|
||||
&hn->zs,
|
||||
compression,
|
||||
Z_DEFLATED,
|
||||
wbits,
|
||||
8,
|
||||
Z_DEFAULT_STRATEGY
|
||||
);
|
||||
if (err != Z_OK) {
|
||||
Zlib_SetErrStat(err);
|
||||
free(hn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hn->zs.next_out = hn->buf;
|
||||
hn->zs.avail_out = hn->bufsiz;
|
||||
Zlib_SetErrStat(Z_OK);
|
||||
return hn;
|
||||
}
|
||||
|
||||
Sint64 Zlib_Read(ZlibStmHn hn, void *buf, size_t nbytes)
|
||||
{
|
||||
if (hn->deflating) {
|
||||
Zlib_SetErrStat(ZSTM_EBADSTREAM);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ZlibRet ret = Z_OK; // unless found otherwise
|
||||
|
||||
hn->zs.next_out = (Uint8 *) buf;
|
||||
hn->zs.avail_out = nbytes;
|
||||
while (hn->zs.avail_out > 0) {
|
||||
if (hn->zs.avail_in == 0) {
|
||||
// Fill buffer
|
||||
Sint64 n = hn->ops->Read(hn->streamp, hn->buf, hn->bufsiz);
|
||||
if (n <= 0) {
|
||||
if (n < 0) ret = ZSTM_EIO;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
hn->zs.next_in = hn->buf;
|
||||
hn->zs.avail_in = n;
|
||||
}
|
||||
|
||||
int err = inflate(&hn->zs, Z_NO_FLUSH);
|
||||
if (err == Z_NEED_DICT)
|
||||
err = Z_DATA_ERROR;
|
||||
if (err != Z_OK && err != Z_STREAM_END) {
|
||||
ret = err;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Zlib_SetErrStat(ret);
|
||||
return nbytes - hn->zs.avail_out;
|
||||
}
|
||||
|
||||
Sint64 Zlib_Write(ZlibStmHn hn, const void *buf, size_t nbytes)
|
||||
{
|
||||
if (!hn->deflating) {
|
||||
Zlib_SetErrStat(ZSTM_EBADSTREAM);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ZlibRet ret = Z_OK;
|
||||
|
||||
hn->zs.next_in = (Uint8 *) buf;
|
||||
hn->zs.avail_in = nbytes;
|
||||
while (hn->zs.avail_in > 0) {
|
||||
if (hn->zs.avail_out == 0) {
|
||||
Sint64 n = Zlib_FlushData(hn);
|
||||
if (n <= 0) {
|
||||
if (n < 0) ret = ZSTM_EIO;
|
||||
|
||||
break; // short-write
|
||||
}
|
||||
}
|
||||
|
||||
int err = deflate(&hn->zs, Z_NO_FLUSH);
|
||||
if (err == Z_NEED_DICT)
|
||||
err = Z_DATA_ERROR;
|
||||
if (err != Z_OK) {
|
||||
ret = err;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Zlib_SetErrStat(ret);
|
||||
return nbytes - hn->zs.avail_in;
|
||||
}
|
||||
|
||||
Judgement Zlib_Finish(ZlibStmHn hn)
|
||||
{
|
||||
if (!hn->deflating) {
|
||||
Zlib_SetErrStat(ZSTM_EBADSTREAM);
|
||||
return NG;
|
||||
}
|
||||
|
||||
int err;
|
||||
do {
|
||||
err = deflate(&hn->zs, Z_FINISH);
|
||||
if (err != Z_STREAM_END && err != Z_BUF_ERROR && err != Z_OK) {
|
||||
Zlib_SetErrStat(err);
|
||||
return NG;
|
||||
}
|
||||
if (Zlib_FlushData(hn) == -1) {
|
||||
Zlib_SetErrStat(ZSTM_EIO);
|
||||
return NG;
|
||||
}
|
||||
} while (err != Z_STREAM_END);
|
||||
|
||||
if (hn->zs.avail_out != hn->bufsiz) {
|
||||
Zlib_SetErrStat(Z_BUF_ERROR);
|
||||
return NG;
|
||||
}
|
||||
|
||||
Zlib_SetErrStat(Z_OK);
|
||||
return OK;
|
||||
}
|
||||
|
||||
void Zlib_Close(ZlibStmHn hn)
|
||||
{
|
||||
// Close stream
|
||||
if (hn->ops->Close)
|
||||
hn->ops->Close(hn->streamp);
|
||||
|
||||
// Finalize Z_stream
|
||||
if (hn->deflating)
|
||||
deflateEnd(&hn->zs);
|
||||
else
|
||||
inflateEnd(&hn->zs);
|
||||
|
||||
free(hn); // Free memory
|
||||
}
|
@ -0,0 +1,354 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file cpr/xz.c
|
||||
*
|
||||
* Interfaces with `liblzma` and implements LZMA compressor/decompressor.
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*/
|
||||
|
||||
#include "cpr/xz.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <lzma.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// some equivalency we assume to be true
|
||||
STATIC_ASSERT((lzma_check) XZCHK_NONE == LZMA_CHECK_NONE,
|
||||
"Incorrect XZCHK_NONE definition");
|
||||
STATIC_ASSERT((lzma_check) XZCHK_CRC32 == LZMA_CHECK_CRC32,
|
||||
"Incorrect XZCHK_CRC32 definition");
|
||||
STATIC_ASSERT((lzma_check) XZCHK_CRC64 == LZMA_CHECK_CRC64,
|
||||
"Incorrect XZCHK_CRC64 definition");
|
||||
STATIC_ASSERT((lzma_check) XZCHK_SHA256 == LZMA_CHECK_SHA256,
|
||||
"Incorrect XZCHK_SHA256 definition");
|
||||
|
||||
typedef struct XzStmObj XzStmObj;
|
||||
struct XzStmObj {
|
||||
lzma_stream xz;
|
||||
void *streamp;
|
||||
const StmOps *ops;
|
||||
unsigned bufsiz;
|
||||
lzma_action action;
|
||||
Boolean8 encoding;
|
||||
Uint8 buf[FLEX_ARRAY]; // `bufsiz` bytes
|
||||
};
|
||||
|
||||
#define XZ_BUFSIZ (32 * 1024)
|
||||
|
||||
#define XZ_EIO -2
|
||||
#define XZ_EBADSTREAM -1
|
||||
|
||||
static Sint64 Xz_FlushData(XzStmHn hn)
|
||||
{
|
||||
size_t nbytes = hn->bufsiz - hn->xz.avail_out;
|
||||
Sint64 n = hn->ops->Write(hn->streamp, hn->buf, nbytes);
|
||||
if (n < 0)
|
||||
return -1;
|
||||
|
||||
size_t left = nbytes - n;
|
||||
memmove(hn->buf, hn->buf + n, left);
|
||||
|
||||
hn->xz.next_out = hn->buf + left;
|
||||
hn->xz.avail_out = hn->bufsiz - left;
|
||||
return n;
|
||||
}
|
||||
|
||||
static Sint64 Xz_StmRead(void *streamp, void *buf, size_t nbytes)
|
||||
{
|
||||
return Xz_Read((XzStmHn) streamp, buf, nbytes);
|
||||
}
|
||||
|
||||
static Sint64 Xz_StmWrite(void *streamp, const void *buf, size_t nbytes)
|
||||
{
|
||||
return Xz_Write((XzStmHn) streamp, buf, nbytes);
|
||||
}
|
||||
|
||||
static Sint64 Xz_StmTell(void *streamp)
|
||||
{
|
||||
XzStmHn hn = (XzStmHn) streamp;
|
||||
return hn->encoding ? hn->xz.total_out : hn->xz.total_in;
|
||||
}
|
||||
|
||||
static Judgement Xz_StmFinish(void *streamp)
|
||||
{
|
||||
return Xz_Finish((XzStmHn) streamp);
|
||||
}
|
||||
|
||||
static void Xz_StmClose(void *streamp)
|
||||
{
|
||||
Xz_Close((XzStmHn) streamp);
|
||||
}
|
||||
|
||||
static const StmOps xz_stmOps = {
|
||||
Xz_StmRead,
|
||||
Xz_StmWrite,
|
||||
NULL,
|
||||
Xz_StmTell,
|
||||
Xz_StmFinish,
|
||||
Xz_StmClose
|
||||
};
|
||||
static const StmOps xz_ncStmOps = {
|
||||
Xz_StmRead,
|
||||
Xz_StmWrite,
|
||||
NULL,
|
||||
Xz_StmTell,
|
||||
Xz_StmFinish,
|
||||
NULL
|
||||
};
|
||||
|
||||
const StmOps *const Xz_StmOps = &xz_stmOps;
|
||||
const StmOps *const Xz_NcStmOps = &xz_ncStmOps;
|
||||
|
||||
static THREAD_LOCAL XzRet xz_errStat = LZMA_OK;
|
||||
|
||||
static void Xz_SetErrStat(XzRet ret)
|
||||
{
|
||||
xz_errStat = ret;
|
||||
}
|
||||
|
||||
XzRet Xz_GetErrStat(void)
|
||||
{
|
||||
return xz_errStat;
|
||||
}
|
||||
|
||||
const char *Xz_ErrorString(XzRet ret)
|
||||
{
|
||||
assert(ret != LZMA_NO_CHECK);
|
||||
assert(ret != LZMA_GET_CHECK);
|
||||
|
||||
switch (ret) {
|
||||
case XZ_EIO: return "I/O error";
|
||||
case XZ_EBADSTREAM: return "Bad stream operation";
|
||||
|
||||
case LZMA_OK: return "Success";
|
||||
case LZMA_UNSUPPORTED_CHECK: return "Cannot calculate the integrity check";
|
||||
case LZMA_MEM_ERROR: return "Memory allocation failure";
|
||||
case LZMA_MEMLIMIT_ERROR: return "Memory usage limit was reached";
|
||||
case LZMA_FORMAT_ERROR: return "Unrecognized file format";
|
||||
case LZMA_OPTIONS_ERROR: return "Invalid or unsupported options";
|
||||
case LZMA_DATA_ERROR: return "Data is corrupt";
|
||||
case LZMA_BUF_ERROR: return "No progress is possible";
|
||||
case LZMA_PROG_ERROR: return "Programming error";
|
||||
default: return "Unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
XzStmHn Xz_OpenCompress(void *streamp,
|
||||
const StmOps *ops,
|
||||
const XzEncOpts *opts)
|
||||
{
|
||||
const XzEncOpts default_opts = {
|
||||
6,
|
||||
FALSE,
|
||||
XZCHK_CRC32,
|
||||
XZ_BUFSIZ
|
||||
};
|
||||
|
||||
if (!opts)
|
||||
opts = &default_opts;
|
||||
|
||||
Uint32 compression = MIN(opts->compress, 9);
|
||||
|
||||
Uint32 presets = 0;
|
||||
if (opts->extreme)
|
||||
presets |= LZMA_PRESET_EXTREME;
|
||||
|
||||
size_t bufsiz = MIN(opts->bufsiz, INT_MAX);
|
||||
if (bufsiz == 0)
|
||||
bufsiz = XZ_BUFSIZ;
|
||||
|
||||
XzStmObj *hn = (XzStmObj *) malloc(offsetof(XzStmObj, buf[bufsiz]));
|
||||
if (!hn) {
|
||||
Xz_SetErrStat(LZMA_MEM_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(&hn->xz, 0, sizeof(hn->xz));
|
||||
hn->streamp = streamp;
|
||||
hn->ops = ops;
|
||||
hn->bufsiz = bufsiz;
|
||||
hn->action = LZMA_RUN; // ...actually ignored for encoding buffers
|
||||
hn->encoding = TRUE;
|
||||
|
||||
lzma_ret err = lzma_easy_encoder(
|
||||
&hn->xz,
|
||||
compression | presets,
|
||||
(lzma_check) opts->chk
|
||||
);
|
||||
|
||||
if (err != LZMA_OK) {
|
||||
Xz_SetErrStat(err);
|
||||
free(hn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hn->xz.next_out = hn->buf;
|
||||
hn->xz.avail_out = hn->bufsiz;
|
||||
Xz_SetErrStat(LZMA_OK);
|
||||
return hn;
|
||||
}
|
||||
|
||||
XzStmHn Xz_OpenDecompress(void *streamp,
|
||||
const StmOps *ops,
|
||||
const XzDecOpts *opts)
|
||||
{
|
||||
const XzDecOpts default_opts = {
|
||||
U64_C(0xffffffffffffffff),
|
||||
FALSE,
|
||||
FALSE,
|
||||
XZ_BUFSIZ
|
||||
};
|
||||
|
||||
if (!opts)
|
||||
opts = &default_opts;
|
||||
|
||||
Uint32 mask = LZMA_CONCATENATED;
|
||||
if (opts->no_concat)
|
||||
mask &= ~LZMA_CONCATENATED;
|
||||
|
||||
#ifdef LZMA_IGNORE_CHECK
|
||||
if (opts->no_chk)
|
||||
mask |= LZMA_IGNORE_CHECK;
|
||||
#endif
|
||||
|
||||
size_t bufsiz = MIN(opts->bufsiz, INT_MAX);
|
||||
if (bufsiz == 0)
|
||||
bufsiz = XZ_BUFSIZ;
|
||||
|
||||
XzStmObj *hn = (XzStmObj *) malloc(offsetof(XzStmObj, buf[bufsiz]));
|
||||
if (!hn) {
|
||||
Xz_SetErrStat(LZMA_MEM_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(&hn->xz, 0, sizeof(hn->xz));
|
||||
hn->streamp = streamp;
|
||||
hn->ops = ops;
|
||||
hn->action = LZMA_RUN; // used to force LZMA_FINISH on EOF
|
||||
hn->bufsiz = bufsiz;
|
||||
hn->encoding = FALSE;
|
||||
|
||||
lzma_ret err = lzma_auto_decoder(&hn->xz, opts->memlimit, mask);
|
||||
if (err != LZMA_OK) {
|
||||
Xz_SetErrStat(err);
|
||||
free(hn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Xz_SetErrStat(LZMA_OK);
|
||||
return hn;
|
||||
}
|
||||
|
||||
Sint64 Xz_Read(XzStmHn hn, void *buf, size_t nbytes)
|
||||
{
|
||||
if (hn->encoding) {
|
||||
Xz_SetErrStat(XZ_EBADSTREAM);
|
||||
return -1;
|
||||
}
|
||||
|
||||
XzRet ret = LZMA_OK;
|
||||
|
||||
hn->xz.next_out = (Uint8 *) buf;
|
||||
hn->xz.avail_out = nbytes;
|
||||
while (hn->xz.avail_out > 0) {
|
||||
if (hn->xz.avail_in == 0) {
|
||||
Sint64 n = hn->ops->Read(hn->streamp, hn->buf, hn->bufsiz);
|
||||
if (n <= 0) {
|
||||
if (n < 0) {
|
||||
ret = XZ_EIO;
|
||||
break;
|
||||
}
|
||||
|
||||
hn->action = LZMA_FINISH;
|
||||
}
|
||||
|
||||
hn->xz.next_in = hn->buf;
|
||||
hn->xz.avail_in = n;
|
||||
}
|
||||
|
||||
lzma_ret err = lzma_code(&hn->xz, hn->action);
|
||||
if (err == LZMA_STREAM_END)
|
||||
break; // NOTE: shouldn't happen for a stream to end before EOF...
|
||||
if (err != LZMA_OK) {
|
||||
ret = err;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Xz_SetErrStat(ret);
|
||||
return nbytes - hn->xz.avail_out;
|
||||
}
|
||||
|
||||
Sint64 Xz_Write(XzStmHn hn, const void *buf, size_t nbytes)
|
||||
{
|
||||
if (!hn->encoding) {
|
||||
Xz_SetErrStat(XZ_EBADSTREAM);
|
||||
return -1;
|
||||
}
|
||||