diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml new file mode 100644 index 0000000..ccf0db9 --- /dev/null +++ b/.github/workflows/cd.yml @@ -0,0 +1,45 @@ +name: CD + +on: + release: + types: [created] + push: + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + include: + - arch: amd64 + - arch: x86 + - arch: armhf + - arch: arm64 + name: Build on ${{ matrix.arch }} + steps: + - name: Git Checkout + uses: actions/checkout@v2 + + - name: Setup Docker + run: | + docker run --rm --privileged multiarch/qemu-user-static:register --reset + + - name: Build + run: | + docker run --workdir /github/workspace --rm --entrypoint "./build_static_alpine.sh" -v "$(pwd)":"/github/workspace" multiarch/alpine:${{ matrix.arch }}-v3.14 + + - name: Upload Binary + if: github.event_name != 'release' + uses: actions/upload-artifact@v2 + with: + name: dpitunnel-cli-${{ matrix.arch }} + path: build/DPITunnel-cli-exec + + - name: Upload Binary to Release + if: github.event_name == 'release' + uses: svenstaro/upload-release-action@2.2.1 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: build/DPITunnel-cli-exec + asset_name: dpitunnel-cli-${{ matrix.arch }} + tag: ${{ github.ref }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..31ee5f4 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,25 @@ +name: CI + +on: + pull_request: + types: [opened, edited] + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + include: + - arch: amd64 + name: Build on ${{ matrix.arch }} + steps: + - name: Git Checkout + uses: actions/checkout@v2 + + - name: Setup Docker + run: | + docker run --rm --privileged multiarch/qemu-user-static:register --reset + + - name: Build + run: | + docker run --workdir /github/workspace --rm --entrypoint "./build_static_alpine.sh" -v "$(pwd)":"/github/workspace" multiarch/alpine:${{ matrix.arch }}-v3.14 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46f42f8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..0706e3f --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,72 @@ +# Set the minimum version of CMake that can be used +cmake_minimum_required(VERSION 3.5) + +set(CMAKE_CXX_STANDARD 14) +cmake_policy(SET CMP0065 NEW) + +if (STATIC_BINARY) + message("Building static portable binary with small size") +endif () + + +# Set the project name +project(DPITunnel-cli) + +# Add dependencies +add_subdirectory(RawSocket) +add_subdirectory(cpp-httplib) +add_subdirectory(dnslib) +add_subdirectory(libnl) + +# Add an executable +add_executable(DPITunnel-cli-exec + autoconf.cpp + desync.cpp + dns.cpp + dpitunnel-cli.cpp + netiface.cpp + packet.cpp + profiles.cpp + socket.cpp + ssl.cpp + utils.cpp + ) + +if (STATIC_BINARY) + target_link_libraries(DPITunnel-cli-exec -static) + target_link_options(DPITunnel-cli-exec PRIVATE "LINKER:--gc-sections") + target_link_options(DPITunnel-cli-exec PRIVATE "LINKER:-s") + target_compile_options(DPITunnel-cli-exec PRIVATE -ffunction-sections) +endif () + +# Set the directories that should be included in the build command for this target +target_include_directories(DPITunnel-cli-exec + PRIVATE + ${PROJECT_SOURCE_DIR}/include + ${PROJECT_SOURCE_DIR}/RawSocket/include + ${PROJECT_SOURCE_DIR}/cpp-httplib/include + ${PROJECT_SOURCE_DIR}/dnslib/include + ${PROJECT_SOURCE_DIR}/libnl/include + ) + +set(THREADS_PREFER_PTHREAD_FLAG ON) +find_package(Threads REQUIRED) + +if (STATIC_BINARY) + set(OPENSSL_USE_STATIC_LIBS TRUE) +endif () +find_package(OpenSSL REQUIRED) + +target_link_libraries(DPITunnel-cli-exec + RawSocket + cpp-httplib + dnslib + libnl + Threads::Threads + OpenSSL::SSL + OpenSSL::Crypto + ) + +if (STATIC_BINARY) + target_link_libraries(DPITunnel-cli-exec ${CMAKE_DL_LIBS}) +endif () diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU 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. + + + Copyright (C) + + 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 . + +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: + + Copyright (C) + 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 +. + + 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 +. diff --git a/README.md b/README.md new file mode 100644 index 0000000..3df80db --- /dev/null +++ b/README.md @@ -0,0 +1,105 @@ +
+DPI Tunnel logo +

DPI Tunnel for Linux

+Free, simple and serverless solution against censorship for Linux PCs and routers + +Telegram chat +
+Want version for Android? + +License +Latest release +Downloads +
+ +### What is it +DPI Tunnel is a proxy server, that allows you to bypass censorship + +It is NOT VPN and won't change your IP + +DPI Tunnel uses desync attacks to fool DPI filters + +RUN IT AS ROOT + +### Features +* Bypass many restrictions: blocked or throttled resources +* Create profiles for different ISP and automatically change them when switch connection +* Easily auto configure for your ISP +* Has HTTP and transparent proxy modes + +## Configuring +#### For the most of ISPs one of the these 2 profiles will be enough: +``` +--ca-bundle-path= --desync-attacks=fake,disorder_fake --split-position=2 --auto-ttl=1-4-10 --min-ttl=3 --doh --doh-server=https://dns.google/dns-query --wsize=1 --wsfactor=6 +``` +``` +--ca-bundle-path= --desync-attacks=fake,disorder_fake --split-position=2 --wrong-seq --doh --doh-server=https://dns.google/dns-query --wsize=1 --wsfactor=6 +``` +*CA Bundle is a file that contains root and intermediate SSL certificates. Required for DoH and autoconfig to work. You can get it for example from [curl](https://curl.se/ca/cacert.pem) site* + +#### For other ISPs program has ```--auto``` key to automatically find proper settings + +## Running +### HTTP mode (default) +This mode is good for PC or any other device which will only use the proxy for itself. + +Run executable with options either from autoconfig or from one of the suggested profiles. The program will tell IP and port on which the proxy server is running. 0.0.0.0 IP means any of IPs this machine has. + +Set this proxy in browser or system settings + +### Transparent mode +This mode is good for router which will use the proxy for the entire local network. + +Run executable with ```--mode transparent``` and append options either from autoconfig or from one of the suggested profiles. The program will tell IP and port on which the proxy server is running. 0.0.0.0 IP means any of IPs this machine has. + +#### If proxy running on router: +##### 1. Enable IP forwarding +``` +sysctl -w net.ipv4.ip_forward=1 +``` +##### 2. Disable ICMP redirects +``` +sysctl -w net.ipv4.conf.all.send_redirects=0 +``` +##### 3. Enter something like the following ```iptables``` rules: +``` +iptables -t nat -A PREROUTING -i -p tcp --dport 80 -j REDIRECT --to-port +iptables -t nat -A PREROUTING -i -p tcp --dport 443 -j REDIRECT --to-port +``` + +#### If proxy running on machine in local network (Raspberry PI for example): +##### 1. On router: +``` +iptables -t mangle -A PREROUTING -j ACCEPT -p tcp -m multiport --dports 80,443 -s +iptables -t mangle -A PREROUTING -j MARK --set-mark 3 -p tcp -m multiport --dports 80,443 +ip rule add fwmark 3 table 2 +ip route add default via dev table 2 +``` +##### 2. On proxy machine: +1. Enable IP forwarding +``` +sysctl -w net.ipv4.ip_forward=1 +``` +2. Disable ICMP redirects +``` +sysctl -w net.ipv4.conf.all.send_redirects=0 +``` +3. Enter something like the following ```iptables``` rules: +``` +iptables -t nat -A PREROUTING -i -p tcp --dport 80 -j REDIRECT --to-port +iptables -t nat -A PREROUTING -i -p tcp --dport 443 -j REDIRECT --to-port +``` + +## Links +[Telegram chat](https://t.me/DPITunnelOFFICIAL) + +[4PDA](https://4pda.to/forum/index.php?showtopic=1043778) + +## Thanks +* [ValdikSS (GoodbyeDPI)](https://github.com/ValdikSS/GoodbyeDPI) + +## Dependencies +* [RawSocket](https://github.com/chkpk/RawSocket) +* [cpp-httplib](https://github.com/yhirose/cpp-httplib) +* [dnslib](https://github.com/mnezerka/dnslib) +* [libnl](https://www.infradead.org/~tgr/libnl) diff --git a/RawSocket/CMakeLists.txt b/RawSocket/CMakeLists.txt new file mode 100644 index 0000000..da68cf6 --- /dev/null +++ b/RawSocket/CMakeLists.txt @@ -0,0 +1,7 @@ +# Set the project name +project(RawSocket) + +add_library(${PROJECT_NAME} CheckSum.cpp) +target_include_directories(${PROJECT_NAME} + PUBLIC ${PROJECT_SOURCE_DIR}/include +) diff --git a/RawSocket/CheckSum.cpp b/RawSocket/CheckSum.cpp new file mode 100644 index 0000000..72b5659 --- /dev/null +++ b/RawSocket/CheckSum.cpp @@ -0,0 +1,51 @@ +#include +#include + +struct PseudoHead{ + uint8_t zero; + uint8_t type; + uint16_t len; + uint32_t src_ip; + uint32_t dst_ip; +}; + +static uint32_t CalSum(const uint8_t* buf, int len) { + uint32_t sum = 0; + const uint8_t* p = buf; + for(; len > 1; len -= 2) { + sum += (*p << 8)+ *(p + 1); + p += 2; + } + if (len == 1) + sum += *p << 8; // + //sum += *p; // + return sum; +} + +static uint32_t CalPseudoHeadSum(const iphdr* pIpHead, uint8_t type) { + PseudoHead head; + head.zero = 0; + head.type = type; + head.len = htons(static_cast(ntohs(pIpHead->tot_len) - pIpHead->ihl * 4)); + head.src_ip = pIpHead->saddr; + head.dst_ip = pIpHead->daddr; + return CalSum((uint8_t*)&head, sizeof(PseudoHead)); +} + +uint16_t cksumIp(iphdr* pIpHead){ + pIpHead->check = 0; + uint32_t ckSum = CalSum((uint8_t*)pIpHead, pIpHead->ihl * 4); + ckSum = (ckSum >> 16) + (ckSum & 0xffff); + ckSum += ckSum >> 16; + return htons((uint16_t)~ckSum); +} + +uint16_t cksumTcp(iphdr* pIpHead, tcphdr* pTcpHead){ + pTcpHead->check = 0; + uint32_t ckSum = CalPseudoHeadSum(pIpHead, 0x06); + ckSum += CalSum((uint8_t*)pTcpHead, + ntohs(pIpHead->tot_len) - pIpHead->ihl * 4); + ckSum = (ckSum >> 16) + (ckSum & 0xffff); + ckSum += ckSum >> 16; + return htons((uint16_t)~ckSum); +} diff --git a/RawSocket/include/RawSocket/CheckSum.h b/RawSocket/include/RawSocket/CheckSum.h new file mode 100644 index 0000000..614dd75 --- /dev/null +++ b/RawSocket/include/RawSocket/CheckSum.h @@ -0,0 +1,20 @@ +#ifndef CHECKSUM_H +#define CHECKSUM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include + +uint16_t cksumIp(iphdr* pIpHead); +uint16_t cksumTcp(iphdr* pIpHead, tcphdr* pTcpHead); + +#ifdef __cplusplus +} +#endif + +#endif // CHECKSUM_H diff --git a/assets/logo.webp b/assets/logo.webp new file mode 100644 index 0000000..629bb86 Binary files /dev/null and b/assets/logo.webp differ diff --git a/autoconf.cpp b/autoconf.cpp new file mode 100644 index 0000000..59ad462 --- /dev/null +++ b/autoconf.cpp @@ -0,0 +1,572 @@ +#include "dpitunnel-cli.h" + +#include "autoconf.h" +#include "dns.h" +#include "desync.h" +#include "socket.h" +#include "ssl.h" +#include "utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern struct Profile_s Profile; +extern struct Settings_perst_s Settings_perst; + +bool verify_cert_common_name(X509 *server_cert, std::string host) { + const auto subject_name = X509_get_subject_name(server_cert); + if (subject_name != nullptr) { + char name[254]; + auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName, + name, sizeof(name)); + if (name_len != -1) + return check_host_name(name, static_cast(name_len), host); + } + + return false; +} + +bool verify_cert_subject_alt_name(X509 *server_cert, std::string host) { + auto ret = false; + auto alt_names = static_cast( + X509_get_ext_d2i(server_cert, NID_subject_alt_name, NULL, NULL)); + + if (alt_names) { + auto dns_matched = false; + auto count = sk_GENERAL_NAME_num(alt_names); + for (decltype(count) i = 0; i < count && !dns_matched; i++) { + auto val = sk_GENERAL_NAME_value(alt_names, i); + if (val->type == GEN_DNS) { + auto name = (const char *) ASN1_STRING_get0_data(val->d.ia5); + auto name_len = (size_t) ASN1_STRING_length(val->d.ia5); + dns_matched = check_host_name(name, name_len, host); + } + } + ret = dns_matched; + } + GENERAL_NAMES_free((STACK_OF(GENERAL_NAME) *) alt_names); + return ret; +} + +bool verify_cert(X509 *server_cert, std::string host) { + return verify_cert_subject_alt_name(server_cert, host) || + verify_cert_common_name(server_cert, host); +} + +int check_https_response(int socket, std::string host, std::string ip, int port, int local_port, + const std::string &sniffed_packet, SSL_CTX *ctx, X509_STORE *store) { + BIO *rbio = BIO_new(BIO_s_mem()); + BIO *wbio = BIO_new(BIO_s_mem()); + SSL *ssl = SSL_new(ctx); + SSL_set_connect_state(ssl); + SSL_set_bio(ssl, rbio, wbio); + SSL_set_tlsext_host_name(ssl, host.c_str()); + SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL); + + int res = 0; + unsigned int last_char; + size_t offset = 0; + bool is_first_time = true; // apply desync attack only on ClientHello + bool is_failure = false; + std::string buffer(Profile.buffer_size, ' '); + auto start = std::chrono::high_resolution_clock::now(); + while (SSL_do_handshake(ssl) == -1) { + res = BIO_read(wbio, &buffer[0], buffer.size()); + if (res > 0) { + if (is_first_time) { + // Split packet at the middle of SNI or at user specified position + unsigned int sni_start, sni_len; + unsigned int split_pos; + // If it's https connection + if (Profile.split_at_sni) { + get_tls_sni(buffer, res, sni_start, sni_len); + if (sni_start + sni_len > res || sni_start == 0 || sni_len == 0) + split_pos = Profile.split_position; + else + split_pos = sni_start + sni_len / 2; + } else + split_pos = std::min((int) Profile.split_position, res); + if (do_desync_attack(socket, ip, port, local_port, + true, sniffed_packet, + buffer, res, split_pos) == -1) { + SSL_free(ssl); + close(socket); + return -1; + } + // Send packet to synchronize SEQ/ACK + std::string data_empty(res, '\x00'); + if (Profile.desync_first_attack == DESYNC_FIRST_NONE) { + if (send_string(socket, data_empty, res) == -1) { + SSL_free(ssl); + close(socket); + return -1; + } + } else { + if (send_string(socket, data_empty, split_pos) == -1 || + send_string(socket, data_empty, res - split_pos) == -1) { + SSL_free(ssl); + close(socket); + return -1; + } + } + is_first_time = false; + } else { + if (send_string(socket, buffer, res) == -1) { + SSL_free(ssl); + close(socket); + return -1; + } + } + } else { + if (recv_string(socket, buffer, last_char) == -1) { + SSL_free(ssl); + close(socket); + return -1; + } + offset = 0; + while (last_char - offset != 0) { + res = BIO_write(rbio, &buffer[0] + offset, last_char); + if (res <= 0) { + std::cerr << "BIO write failure" << std::endl; + SSL_free(ssl); + close(socket); + return -1; + } + offset += res; + } + } + // Check timeout + auto stop = std::chrono::high_resolution_clock::now(); + if (std::chrono::duration_cast(stop - start).count() > + Settings_perst.test_ssl_handshake_timeout) { + std::cout << "SSL handshake timeout" << std::endl; + SSL_free(ssl); + close(socket); + return -1; + } + } + + // Verify certificate + if (SSL_get_verify_result(ssl) != X509_V_OK) { + std::cout << "Failed to verify server certificate" << std::endl; + SSL_free(ssl); + close(socket); + return -1; + } + auto server_cert = SSL_get_peer_certificate(ssl); + if (server_cert == NULL) { + std::cout << "Failed to verify server certificate" << std::endl; + SSL_free(ssl); + close(socket); + return -1; + } + if (!verify_cert(server_cert, host)) { + X509_free(server_cert); + std::cout << "Failed to verify server certificate" << std::endl; + SSL_free(ssl); + close(socket); + return -1; + } + + X509_free(server_cert); + SSL_free(ssl); + close(socket); + + return 0; +} + +int check_http_response(int socket, std::string host, std::string ip, int port, int local_port, + const std::string &sniffed_packet, unsigned int connect_time) { + unsigned int last_char; + std::string buffer(Profile.buffer_size, ' '); + + // Receive with timeout + struct timeval timeout_recv; + timeout_recv.tv_sec = 5; + timeout_recv.tv_usec = 0; + + std::string request; + request += "GET / HTTP/1.1\r\nHost: "; + request += host; + request += "\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0\r\n" + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*" "/" "*;q=0.8\r\n" + "Accept-Encoding: gzip, deflate\r\n\r\n"; + + unsigned int split_pos = std::min(Profile.split_position, (unsigned int) request.size()); + if (do_desync_attack(socket, ip, port, local_port, + true, sniffed_packet, request, request.size(), split_pos) == -1) { + close(socket); + return -1; + } + + unsigned int receive_time; + if (recv_string(socket, buffer, last_char, &timeout_recv, &receive_time) == -1) { + close(socket); + return -1; + } + + close(socket); + + if (last_char == 0) + return -1; + + // Count factors indicating that packet was send by DPI + unsigned short factors = 0; + // Check time + if (receive_time < connect_time * 2 / 3) + factors++; + // Check status code + size_t status_start_position = buffer.find(' '); + if (status_start_position == std::string::npos || status_start_position == buffer.size() - 1) { + std::cout << "Failed to parse server response" << std::endl; + return -1; + } + size_t status_end_position = buffer.find(' ', status_start_position + 1); + if (status_end_position == std::string::npos) { + std::cout << "Failed to parse server response" << std::endl; + return -1; + } + std::string code = buffer.substr(status_start_position + 1, + status_end_position - status_start_position - 1); + if (code == "301" || code == "302" || code == "303" || code == "307" || code == "308") + factors++; + // Check location + size_t location_start_position = buffer.find("Location: "); + if (location_start_position != std::string::npos || + location_start_position == buffer.size() - 1) { + size_t location_end_position = buffer.find("\r\n", location_start_position + 1); + if (location_end_position != std::string::npos) { + std::string redirect_url = buffer.substr(location_start_position + 10, + location_end_position - + location_start_position - 10); + if (redirect_url.rfind("http://", 0) == 0) + redirect_url.erase(0, 7); + size_t slash_position = redirect_url.find('/'); + redirect_url.erase(slash_position); + if (redirect_url.rfind(host, 0) != 0) + factors++; + } + } + + if (factors >= 2) + return -1; + else + return 0; +} + +int test_desync_attack(std::string host, std::string ip, int port, bool is_https, SSL_CTX *ctx, + X509_STORE *store) { + // Connect to server to check is it blocked by ip and get SYN, ACK packet need for desync attacks + int socket; + std::atomic flag(true); + std::atomic local_port(-1); + std::atomic status; + std::thread sniff_thread; + std::promise sniff_thread_ready = std::promise(); + std::string sniffed_packet; + sniff_thread = std::thread(sniff_handshake_packet, &sniffed_packet, + ip, port, &local_port, &flag, &status, &sniff_thread_ready); + // Wait for sniff thread to init + sniff_thread_ready.get_future().wait(); + auto start = std::chrono::high_resolution_clock::now(); + if (init_remote_server_socket(socket, ip, port) == -1) { + std::cout << "Resource blocked by IP. I can't help. Use VPN or proxy :((" << std::endl; + // Stop sniff thread + flag.store(false); + if (sniff_thread.joinable()) sniff_thread.join(); + close(socket); + return -1; + } + auto stop = std::chrono::high_resolution_clock::now(); + unsigned int connect_time = std::chrono::duration_cast( + stop - start).count(); + + // Disable TCP Nagle's algorithm + int yes = 1; + if (setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char *) &yes, sizeof(yes)) < 0) { + std::cerr << "Can't disable TCP Nagle's algorithm with setsockopt(). Errno: " + << std::strerror(errno) << std::endl; + // Stop sniff thread + flag.store(false); + if (sniff_thread.joinable()) sniff_thread.join(); + close(socket); + return -1; + } + + // Get local port to choose proper SYN, ACK packet + struct sockaddr_in local_addr; + socklen_t len = sizeof(local_addr); + if (getsockname(socket, (struct sockaddr *) &local_addr, &len) == -1) { + std::cerr << "Failed to get local port. Errno: " << std::strerror(errno) << std::endl; + // Stop sniff thread + flag.store(false); + if (sniff_thread.joinable()) sniff_thread.join(); + close(socket); + return -1; + } + local_port.store(ntohs(local_addr.sin_port)); + + // Get received ACK packet + if (sniff_thread.joinable()) sniff_thread.join(); + if (status.load() == -1) { + std::cerr << "Failed to capture handshake packet" << std::endl; + close(socket); + return -1; + } + + return is_https ? + check_https_response(socket, host, ip, port, local_port, sniffed_packet, ctx, store) : + check_http_response(socket, host, ip, port, local_port, sniffed_packet, connect_time); +} + +void +show_configured_options(std::string host, std::string ip, int port, bool is_https, SSL_CTX *ctx, + X509_STORE *store) { + // Find minimum working ttl for fake packets + if (Profile.fake_packets_ttl) { + std::cout << "Calculating minimum working ttl..." << std::endl; + short result = -1; + int fake_packets_ttl = Profile.fake_packets_ttl; + Profile.fake_packets_ttl = 1; + while (Profile.fake_packets_ttl <= fake_packets_ttl && result == -1) { + result = test_desync_attack(host, ip, port, is_https, ctx, store); + // Test attack 3 times to ensure it work all times + if (result != -1) + for (short i = 1; i <= 3; i++) + result = std::min(result, + (short) test_desync_attack(host, ip, port, is_https, ctx, + store)); + Profile.fake_packets_ttl++; + } + Profile.fake_packets_ttl--; + std::cout << std::endl; + } + std::cout << "Configuration successful! Apply these options when run program:" << std::endl; + if (Profile.builtin_dns) { + std::cout << "-builtin-dns "; + std::cout << "-builtin-dns-ip " << Profile.builtin_dns_ip << ' '; + std::cout << "-builtin-dns-port " << Profile.builtin_dns_port << ' '; + } + std::cout << "-doh "; + std::cout << "-doh-server " << Profile.doh_server << ' '; + if (Profile.split_at_sni) + std::cout << "-split-at-sni "; + if (Profile.window_size != 0) + std::cout << "-wsize " << Profile.window_size << ' '; + if (Profile.window_scale_factor != -1) + std::cout << "-wsfactor " << Profile.window_scale_factor << ' '; + if (Profile.fake_packets_ttl) + std::cout << "-ttl " << Profile.fake_packets_ttl << ' '; + if (Profile.wrong_seq) + std::cout << "-wrong-seq "; + if (is_https) + std::cout << "-ca-bundle-path \"" << Settings_perst.ca_bundle_path << "\" "; + if (Profile.desync_zero_attack != DESYNC_ZERO_NONE || + Profile.desync_first_attack != DESYNC_FIRST_NONE) + std::cout << "-desync-attacks "; + if (Profile.desync_zero_attack != DESYNC_ZERO_NONE) { + std::cout << ZERO_ATTACKS_NAMES.at(Profile.desync_zero_attack); + if (Profile.desync_first_attack != DESYNC_FIRST_NONE) + std::cout << ","; + } + if (Profile.desync_first_attack != DESYNC_FIRST_NONE) + std::cout << FIRST_ATTACKS_NAMES.at(Profile.desync_first_attack); + std::cout << std::endl; +} + +int +test_desync_attack_wrapper(std::string host, std::string ip, int port, bool is_https, SSL_CTX *ctx, + X509_STORE *store) { + if (test_desync_attack(host, ip, port, is_https, ctx, store) == -1) + std::cout << "\tFail" << std::endl << std::endl; + else { + // Check does attack work all times + short res = 0; + for (short i = 1; i <= 3; i++) + res = std::min(res, (short) test_desync_attack(host, ip, port, is_https, ctx, store)); + if (res == -1) + std::cout << "\tFail. Attack don't work all times" << std::endl << std::endl; + else { + std::cout << "\tSuccess" << std::endl << std::endl; + show_configured_options(host, ip, port, is_https, ctx, store); + if (is_https) + SSL_CTX_free(ctx); + return 0; + } + } + + return -1; +} + +void set_profile(const std::string &doh_server, bool builtin_dns, Desync_zero_attacks zero_attack, + Desync_first_attacks first_attack, + const std::string &fake_type, short ttl, bool win_size_scale) { + Profile_s default_profile; + default_profile.doh = true; + default_profile.doh_server = doh_server; + if (builtin_dns) + default_profile.builtin_dns = true; + default_profile.desync_zero_attack = zero_attack; + default_profile.desync_first_attack = first_attack; + if (fake_type == "ttl") + default_profile.fake_packets_ttl = ttl; + else if (fake_type == "wrong-seq") + default_profile.wrong_seq = true; + if (win_size_scale) { + default_profile.window_size = 1; + default_profile.window_scale_factor = 6; + } + Profile = default_profile; +} + +int run_autoconf() { + bool is_https; + int port; + std::string host; + std::string tmp; + std::cout << "Site domain you want to unblock " << std::endl + << "(http://example.com or https://example.com or example.com. Can contain port): "; + std::getline(std::cin, host); + std::cout << "DoH server (press enter to use default " << Profile.doh_server << "): "; + std::getline(std::cin, tmp); + if (!tmp.empty()) + Profile.doh_server = tmp; + + if (host.rfind("http://", 0) == 0) { + is_https = false; + port = 80; + host.erase(0, 7); + } else if (host.rfind("https://", 0) == 0) { + is_https = true; + port = 443; + host.erase(0, 8); + } else { + is_https = true; + port = 443; + } + + // Extract port + size_t port_start_position = host.find(':'); + if (port_start_position != std::string::npos) { + port = std::stoi(host.substr(port_start_position + 1, host.size() - port_start_position)); + host.erase(port_start_position, host.size() - port_start_position + 1); + } + + // Load CA store to validate SSL certificates and connect to DoH server + X509_STORE *store; + SSL_CTX *ctx; + std::cout << "CA bundle path (press enter to use default location " + << Settings_perst.ca_bundle_path << "): "; + std::getline(std::cin, tmp); + if (!tmp.empty()) + Settings_perst.ca_bundle_path = tmp; + + if (load_ca_bundle() == -1) + return -1; + + if (is_https) { + // Init openssl + SSL_library_init(); + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); + ERR_load_BIO_strings(); + ERR_load_crypto_strings(); + + store = gen_x509_store(); + if (store == NULL) { + std::cout << "Failed to parse CA Bundle" << std::endl; + return -1; + } + + ctx = SSL_CTX_new(SSLv23_method()); + if (!ctx) { + std::cout << "Failed to init SSL context" << std::endl; + return -1; + } + SSL_CTX_set_cert_store(ctx, store); + } + + // Resolve over DoH + std::cout << "Resolving host over DoH server " << Profile.doh_server << std::endl; + Profile.doh = true; + std::string ip; + bool builtin_dns = false; + if (resolve_host(host, ip) == -1) { + // Try with builtin DNS + std::cout << "DNS server (press enter to use default " << Profile.builtin_dns_ip + << ". Can contain port): "; + std::getline(std::cin, tmp); + Profile.builtin_dns = builtin_dns = true; + if (!tmp.empty()) { + // Check if port exists + size_t port_start_position = tmp.find(':'); + if (port_start_position != std::string::npos) { + Profile.builtin_dns_ip = tmp.substr(0, port_start_position); + Profile.builtin_dns_port = std::stoi( + tmp.substr(port_start_position + 1, tmp.size() - port_start_position)); + } else Profile.builtin_dns_ip = tmp; + } + + if (resolve_host(host, ip) == -1) { + std::cout << "Failed to resolve host " << host << std::endl; + if (is_https) + SSL_CTX_free(ctx); + return -1; + } + } + std::cout << host << " IP is " << ip << std::endl << std::endl; + + std::cout << "\tCalculating network distance to server..." << std::endl; + short fakes_ttl = count_hops(ip, port); + if (fakes_ttl == -1) { + std::cout << "\tFail" << std::endl; + if (is_https) + SSL_CTX_free(ctx); + return -1; + } + std::cout << "\tHops to site: " << fakes_ttl << std::endl << std::endl; + fakes_ttl--; + + // Iterate through all combinations + const std::vector zero_attacks = {DESYNC_ZERO_NONE, DESYNC_ZERO_FAKE, + DESYNC_ZERO_RST, DESYNC_ZERO_RSTACK}; + const std::vector first_attacks = {DESYNC_FIRST_DISORDER_FAKE, + DESYNC_FIRST_SPLIT_FAKE}; + const std::vector fake_types = {"ttl", "wrong-seq"}; + const std::vector win_size_scales = {false, true}; + unsigned int comb_all = + zero_attacks.size() * first_attacks.size() * fake_types.size() * win_size_scales.size(); + unsigned int comb_curr = 1; + for (const Desync_zero_attacks &zero_attack: zero_attacks) + for (const Desync_first_attacks &first_attack: first_attacks) + for (const std::string &fake_type: fake_types) + for (const bool win_size_scale: win_size_scales) { + std::cout << "\tTrying " << comb_curr << '/' << comb_all << "..." << std::endl; + set_profile(Profile.doh_server, builtin_dns, zero_attack, first_attack, fake_type, + fakes_ttl, win_size_scale); + if (test_desync_attack_wrapper(host, ip, port, is_https, ctx, store) == + 0) + return 0; + comb_curr++; + } + std::cout << "Failed to find any working attack!" << std::endl; + + if (is_https) + SSL_CTX_free(ctx); + + return 0; +} diff --git a/build_static_alpine.sh b/build_static_alpine.sh new file mode 100755 index 0000000..395ce3f --- /dev/null +++ b/build_static_alpine.sh @@ -0,0 +1,6 @@ +#!/bin/ash + +apk update +apk add build-base cmake openssl openssl-dev openssl-libs-static linux-headers +cmake -B./build -DCMAKE_BUILD_TYPE=RELEASE -DSTATIC_BINARY=true . +make -C ./build -j $(nproc) diff --git a/cpp-httplib/CMakeLists.txt b/cpp-httplib/CMakeLists.txt new file mode 100644 index 0000000..63f3c70 --- /dev/null +++ b/cpp-httplib/CMakeLists.txt @@ -0,0 +1,9 @@ +# Set the project name +project(cpp-httplib) + +add_library(${PROJECT_NAME} INTERFACE) + +target_include_directories(${PROJECT_NAME} + INTERFACE + ${PROJECT_SOURCE_DIR}/include +) diff --git a/cpp-httplib/include/cpp-httplib/httplib.h b/cpp-httplib/include/cpp-httplib/httplib.h new file mode 100644 index 0000000..0d3ebe1 --- /dev/null +++ b/cpp-httplib/include/cpp-httplib/httplib.h @@ -0,0 +1,7979 @@ +// +// httplib.h +// +// Copyright (c) 2021 Yuji Hirose. All rights reserved. +// MIT License +// + +#ifndef CPPHTTPLIB_HTTPLIB_H +#define CPPHTTPLIB_HTTPLIB_H + +/* + * Configuration + */ + +#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND +#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5 +#endif + +#ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT +#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 5 +#endif + +#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND +#define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND 300 +#endif + +#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND +#define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND 0 +#endif + +#ifndef CPPHTTPLIB_READ_TIMEOUT_SECOND +#define CPPHTTPLIB_READ_TIMEOUT_SECOND 5 +#endif + +#ifndef CPPHTTPLIB_READ_TIMEOUT_USECOND +#define CPPHTTPLIB_READ_TIMEOUT_USECOND 0 +#endif + +#ifndef CPPHTTPLIB_WRITE_TIMEOUT_SECOND +#define CPPHTTPLIB_WRITE_TIMEOUT_SECOND 5 +#endif + +#ifndef CPPHTTPLIB_WRITE_TIMEOUT_USECOND +#define CPPHTTPLIB_WRITE_TIMEOUT_USECOND 0 +#endif + +#ifndef CPPHTTPLIB_IDLE_INTERVAL_SECOND +#define CPPHTTPLIB_IDLE_INTERVAL_SECOND 0 +#endif + +#ifndef CPPHTTPLIB_IDLE_INTERVAL_USECOND +#ifdef _WIN32 +#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 10000 +#else +#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 0 +#endif +#endif + +#ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH +#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192 +#endif + +#ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT +#define CPPHTTPLIB_REDIRECT_MAX_COUNT 20 +#endif + +#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH +#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH ((std::numeric_limits::max)()) +#endif + +#ifndef CPPHTTPLIB_TCP_NODELAY +#define CPPHTTPLIB_TCP_NODELAY false +#endif + +#ifndef CPPHTTPLIB_RECV_BUFSIZ +#define CPPHTTPLIB_RECV_BUFSIZ size_t(4096u) +#endif + +#ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ +#define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(16384u) +#endif + +#ifndef CPPHTTPLIB_THREAD_POOL_COUNT +#define CPPHTTPLIB_THREAD_POOL_COUNT \ + ((std::max)(8u, std::thread::hardware_concurrency() > 0 \ + ? std::thread::hardware_concurrency() - 1 \ + : 0)) +#endif + +#ifndef CPPHTTPLIB_RECV_FLAGS +#define CPPHTTPLIB_RECV_FLAGS 0 +#endif + +#ifndef CPPHTTPLIB_SEND_FLAGS +#define CPPHTTPLIB_SEND_FLAGS 0 +#endif + +/* + * Headers + */ + +#ifdef _WIN32 +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif //_CRT_SECURE_NO_WARNINGS + +#ifndef _CRT_NONSTDC_NO_DEPRECATE +#define _CRT_NONSTDC_NO_DEPRECATE +#endif //_CRT_NONSTDC_NO_DEPRECATE + +#if defined(_MSC_VER) +#ifdef _WIN64 +using ssize_t = __int64; +#else +using ssize_t = int; +#endif + +#if _MSC_VER < 1900 +#define snprintf _snprintf_s +#endif +#endif // _MSC_VER + +#ifndef S_ISREG +#define S_ISREG(m) (((m)&S_IFREG) == S_IFREG) +#endif // S_ISREG + +#ifndef S_ISDIR +#define S_ISDIR(m) (((m)&S_IFDIR) == S_IFDIR) +#endif // S_ISDIR + +#ifndef NOMINMAX +#define NOMINMAX +#endif // NOMINMAX + +#include +#include + +#include +#include + +#ifndef WSA_FLAG_NO_HANDLE_INHERIT +#define WSA_FLAG_NO_HANDLE_INHERIT 0x80 +#endif + +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "crypt32.lib") +#pragma comment(lib, "cryptui.lib") +#endif + +#ifndef strcasecmp +#define strcasecmp _stricmp +#endif // strcasecmp + +using socket_t = SOCKET; +#ifdef CPPHTTPLIB_USE_POLL +#define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout) +#endif + +#else // not _WIN32 + +#include +#include +#include +#include +#include +#ifdef __linux__ +#include +#endif +#include +#ifdef CPPHTTPLIB_USE_POLL +#include +#endif +#include +#include +#include +#include +#include + +using socket_t = int; +#ifndef INVALID_SOCKET +#define INVALID_SOCKET (-1) +#endif +#endif //_WIN32 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +#include +#include +#include +#include + +#if defined(_WIN32) && defined(OPENSSL_USE_APPLINK) +#include +#endif + +#include +#include + +#if OPENSSL_VERSION_NUMBER < 0x1010100fL +#error Sorry, OpenSSL versions prior to 1.1.1 are not supported +#endif + +#if OPENSSL_VERSION_NUMBER < 0x10100000L +#include +inline const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *asn1) { + return M_ASN1_STRING_data(asn1); +} +#endif +#endif + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT +#include +#endif + +#ifdef CPPHTTPLIB_BROTLI_SUPPORT +#include +#include +#endif + +/* + * Declaration + */ +namespace httplib { + +namespace detail { + +/* + * Backport std::make_unique from C++14. + * + * NOTE: This code came up with the following stackoverflow post: + * https://stackoverflow.com/questions/10149840/c-arrays-and-make-unique + * + */ + +template +typename std::enable_if::value, std::unique_ptr>::type +make_unique(Args &&...args) { + return std::unique_ptr(new T(std::forward(args)...)); +} + +template +typename std::enable_if::value, std::unique_ptr>::type +make_unique(std::size_t n) { + typedef typename std::remove_extent::type RT; + return std::unique_ptr(new RT[n]); +} + +struct ci { + bool operator()(const std::string &s1, const std::string &s2) const { + return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), + s2.end(), + [](unsigned char c1, unsigned char c2) { + return ::tolower(c1) < ::tolower(c2); + }); + } +}; + +} // namespace detail + +using Headers = std::multimap; + +using Params = std::multimap; +using Match = std::smatch; + +using Progress = std::function; + +struct Response; +using ResponseHandler = std::function; + +struct MultipartFormData { + std::string name; + std::string content; + std::string filename; + std::string content_type; +}; +using MultipartFormDataItems = std::vector; +using MultipartFormDataMap = std::multimap; + +class DataSink { +public: + DataSink() : os(&sb_), sb_(*this) {} + + DataSink(const DataSink &) = delete; + DataSink &operator=(const DataSink &) = delete; + DataSink(DataSink &&) = delete; + DataSink &operator=(DataSink &&) = delete; + + std::function write; + std::function done; + std::function is_writable; + std::ostream os; + +private: + class data_sink_streambuf : public std::streambuf { + public: + explicit data_sink_streambuf(DataSink &sink) : sink_(sink) {} + + protected: + std::streamsize xsputn(const char *s, std::streamsize n) { + sink_.write(s, static_cast(n)); + return n; + } + + private: + DataSink &sink_; + }; + + data_sink_streambuf sb_; +}; + +using ContentProvider = + std::function; + +using ContentProviderWithoutLength = + std::function; + +using ContentProviderResourceReleaser = std::function; + +using ContentReceiverWithProgress = + std::function; + +using ContentReceiver = + std::function; + +using MultipartContentHeader = + std::function; + +class ContentReader { +public: + using Reader = std::function; + using MultipartReader = std::function; + + ContentReader(Reader reader, MultipartReader multipart_reader) + : reader_(std::move(reader)), + multipart_reader_(std::move(multipart_reader)) {} + + bool operator()(MultipartContentHeader header, + ContentReceiver receiver) const { + return multipart_reader_(std::move(header), std::move(receiver)); + } + + bool operator()(ContentReceiver receiver) const { + return reader_(std::move(receiver)); + } + + Reader reader_; + MultipartReader multipart_reader_; +}; + +using Range = std::pair; +using Ranges = std::vector; + +struct Request { + std::string method; + std::string path; + Headers headers; + std::string body; + + std::string remote_addr; + int remote_port = -1; + + // for server + std::string version; + std::string target; + Params params; + MultipartFormDataMap files; + Ranges ranges; + Match matches; + + // for client + ResponseHandler response_handler; + ContentReceiverWithProgress content_receiver; + Progress progress; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + const SSL *ssl = nullptr; +#endif + + bool has_header(const char *key) const; + std::string get_header_value(const char *key, size_t id = 0) const; + template + T get_header_value(const char *key, size_t id = 0) const; + size_t get_header_value_count(const char *key) const; + void set_header(const char *key, const char *val); + void set_header(const char *key, const std::string &val); + + bool has_param(const char *key) const; + std::string get_param_value(const char *key, size_t id = 0) const; + size_t get_param_value_count(const char *key) const; + + bool is_multipart_form_data() const; + + bool has_file(const char *key) const; + MultipartFormData get_file_value(const char *key) const; + + // private members... + size_t redirect_count_ = CPPHTTPLIB_REDIRECT_MAX_COUNT; + size_t content_length_ = 0; + ContentProvider content_provider_; + bool is_chunked_content_provider_ = false; + size_t authorization_count_ = 0; +}; + +struct Response { + std::string version; + int status = -1; + std::string reason; + Headers headers; + std::string body; + std::string location; // Redirect location + + bool has_header(const char *key) const; + std::string get_header_value(const char *key, size_t id = 0) const; + template + T get_header_value(const char *key, size_t id = 0) const; + size_t get_header_value_count(const char *key) const; + void set_header(const char *key, const char *val); + void set_header(const char *key, const std::string &val); + + void set_redirect(const char *url, int status = 302); + void set_redirect(const std::string &url, int status = 302); + void set_content(const char *s, size_t n, const char *content_type); + void set_content(const std::string &s, const char *content_type); + + void set_content_provider( + size_t length, const char *content_type, ContentProvider provider, + ContentProviderResourceReleaser resource_releaser = nullptr); + + void set_content_provider( + const char *content_type, ContentProviderWithoutLength provider, + ContentProviderResourceReleaser resource_releaser = nullptr); + + void set_chunked_content_provider( + const char *content_type, ContentProviderWithoutLength provider, + ContentProviderResourceReleaser resource_releaser = nullptr); + + Response() = default; + Response(const Response &) = default; + Response &operator=(const Response &) = default; + Response(Response &&) = default; + Response &operator=(Response &&) = default; + ~Response() { + if (content_provider_resource_releaser_) { + content_provider_resource_releaser_(content_provider_success_); + } + } + + // private members... + size_t content_length_ = 0; + ContentProvider content_provider_; + ContentProviderResourceReleaser content_provider_resource_releaser_; + bool is_chunked_content_provider_ = false; + bool content_provider_success_ = false; +}; + +class Stream { +public: + virtual ~Stream() = default; + + virtual bool is_readable() const = 0; + virtual bool is_writable() const = 0; + + virtual ssize_t read(char *ptr, size_t size) = 0; + virtual ssize_t write(const char *ptr, size_t size) = 0; + virtual void get_remote_ip_and_port(std::string &ip, int &port) const = 0; + virtual socket_t socket() const = 0; + + template + ssize_t write_format(const char *fmt, const Args &...args); + ssize_t write(const char *ptr); + ssize_t write(const std::string &s); +}; + +class TaskQueue { +public: + TaskQueue() = default; + virtual ~TaskQueue() = default; + + virtual void enqueue(std::function fn) = 0; + virtual void shutdown() = 0; + + virtual void on_idle(){}; +}; + +class ThreadPool : public TaskQueue { +public: + explicit ThreadPool(size_t n) : shutdown_(false) { + while (n) { + threads_.emplace_back(worker(*this)); + n--; + } + } + + ThreadPool(const ThreadPool &) = delete; + ~ThreadPool() override = default; + + void enqueue(std::function fn) override { + std::unique_lock lock(mutex_); + jobs_.push_back(std::move(fn)); + cond_.notify_one(); + } + + void shutdown() override { + // Stop all worker threads... + { + std::unique_lock lock(mutex_); + shutdown_ = true; + } + + cond_.notify_all(); + + // Join... + for (auto &t : threads_) { + t.join(); + } + } + +private: + struct worker { + explicit worker(ThreadPool &pool) : pool_(pool) {} + + void operator()() { + for (;;) { + std::function fn; + { + std::unique_lock lock(pool_.mutex_); + + pool_.cond_.wait( + lock, [&] { return !pool_.jobs_.empty() || pool_.shutdown_; }); + + if (pool_.shutdown_ && pool_.jobs_.empty()) { break; } + + fn = pool_.jobs_.front(); + pool_.jobs_.pop_front(); + } + + assert(true == static_cast(fn)); + fn(); + } + } + + ThreadPool &pool_; + }; + friend struct worker; + + std::vector threads_; + std::list> jobs_; + + bool shutdown_; + + std::condition_variable cond_; + std::mutex mutex_; +}; + +using Logger = std::function; + +using SocketOptions = std::function; + +void default_socket_options(socket_t sock); + +class Server { +public: + using Handler = std::function; + + using ExceptionHandler = + std::function; + + enum class HandlerResponse { + Handled, + Unhandled, + }; + using HandlerWithResponse = + std::function; + + using HandlerWithContentReader = std::function; + + using Expect100ContinueHandler = + std::function; + + Server(); + + virtual ~Server(); + + virtual bool is_valid() const; + + Server &Get(const std::string &pattern, Handler handler); + Server &Post(const std::string &pattern, Handler handler); + Server &Post(const std::string &pattern, HandlerWithContentReader handler); + Server &Put(const std::string &pattern, Handler handler); + Server &Put(const std::string &pattern, HandlerWithContentReader handler); + Server &Patch(const std::string &pattern, Handler handler); + Server &Patch(const std::string &pattern, HandlerWithContentReader handler); + Server &Delete(const std::string &pattern, Handler handler); + Server &Delete(const std::string &pattern, HandlerWithContentReader handler); + Server &Options(const std::string &pattern, Handler handler); + + bool set_base_dir(const std::string &dir, + const std::string &mount_point = std::string()); + bool set_mount_point(const std::string &mount_point, const std::string &dir, + Headers headers = Headers()); + bool remove_mount_point(const std::string &mount_point); + Server &set_file_extension_and_mimetype_mapping(const char *ext, + const char *mime); + Server &set_file_request_handler(Handler handler); + + Server &set_error_handler(HandlerWithResponse handler); + Server &set_error_handler(Handler handler); + Server &set_exception_handler(ExceptionHandler handler); + Server &set_pre_routing_handler(HandlerWithResponse handler); + Server &set_post_routing_handler(Handler handler); + + Server &set_expect_100_continue_handler(Expect100ContinueHandler handler); + Server &set_logger(Logger logger); + + Server &set_address_family(int family); + Server &set_tcp_nodelay(bool on); + Server &set_socket_options(SocketOptions socket_options); + + Server &set_default_headers(Headers headers); + + Server &set_keep_alive_max_count(size_t count); + Server &set_keep_alive_timeout(time_t sec); + + Server &set_read_timeout(time_t sec, time_t usec = 0); + template + Server &set_read_timeout(const std::chrono::duration &duration); + + Server &set_write_timeout(time_t sec, time_t usec = 0); + template + Server &set_write_timeout(const std::chrono::duration &duration); + + Server &set_idle_interval(time_t sec, time_t usec = 0); + template + Server &set_idle_interval(const std::chrono::duration &duration); + + Server &set_payload_max_length(size_t length); + + bool bind_to_port(const char *host, int port, int socket_flags = 0); + int bind_to_any_port(const char *host, int socket_flags = 0); + bool listen_after_bind(); + + bool listen(const char *host, int port, int socket_flags = 0); + + bool is_running() const; + void stop(); + + std::function new_task_queue; + +protected: + bool process_request(Stream &strm, bool close_connection, + bool &connection_closed, + const std::function &setup_request); + + std::atomic svr_sock_; + size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT; + time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND; + time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND; + time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND; + time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND; + time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND; + time_t idle_interval_sec_ = CPPHTTPLIB_IDLE_INTERVAL_SECOND; + time_t idle_interval_usec_ = CPPHTTPLIB_IDLE_INTERVAL_USECOND; + size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH; + +private: + using Handlers = std::vector>; + using HandlersForContentReader = + std::vector>; + + socket_t create_server_socket(const char *host, int port, int socket_flags, + SocketOptions socket_options) const; + int bind_internal(const char *host, int port, int socket_flags); + bool listen_internal(); + + bool routing(Request &req, Response &res, Stream &strm); + bool handle_file_request(const Request &req, Response &res, + bool head = false); + bool dispatch_request(Request &req, Response &res, const Handlers &handlers); + bool + dispatch_request_for_content_reader(Request &req, Response &res, + ContentReader content_reader, + const HandlersForContentReader &handlers); + + bool parse_request_line(const char *s, Request &req); + void apply_ranges(const Request &req, Response &res, + std::string &content_type, std::string &boundary); + bool write_response(Stream &strm, bool close_connection, const Request &req, + Response &res); + bool write_response_with_content(Stream &strm, bool close_connection, + const Request &req, Response &res); + bool write_response_core(Stream &strm, bool close_connection, + const Request &req, Response &res, + bool need_apply_ranges); + bool write_content_with_provider(Stream &strm, const Request &req, + Response &res, const std::string &boundary, + const std::string &content_type); + bool read_content(Stream &strm, Request &req, Response &res); + bool + read_content_with_content_receiver(Stream &strm, Request &req, Response &res, + ContentReceiver receiver, + MultipartContentHeader multipart_header, + ContentReceiver multipart_receiver); + bool read_content_core(Stream &strm, Request &req, Response &res, + ContentReceiver receiver, + MultipartContentHeader mulitpart_header, + ContentReceiver multipart_receiver); + + virtual bool process_and_close_socket(socket_t sock); + + struct MountPointEntry { + std::string mount_point; + std::string base_dir; + Headers headers; + }; + std::vector base_dirs_; + + std::atomic is_running_; + std::map file_extension_and_mimetype_map_; + Handler file_request_handler_; + Handlers get_handlers_; + Handlers post_handlers_; + HandlersForContentReader post_handlers_for_content_reader_; + Handlers put_handlers_; + HandlersForContentReader put_handlers_for_content_reader_; + Handlers patch_handlers_; + HandlersForContentReader patch_handlers_for_content_reader_; + Handlers delete_handlers_; + HandlersForContentReader delete_handlers_for_content_reader_; + Handlers options_handlers_; + HandlerWithResponse error_handler_; + ExceptionHandler exception_handler_; + HandlerWithResponse pre_routing_handler_; + Handler post_routing_handler_; + Logger logger_; + Expect100ContinueHandler expect_100_continue_handler_; + + int address_family_ = AF_UNSPEC; + bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY; + SocketOptions socket_options_ = default_socket_options; + + Headers default_headers_; +}; + +enum class Error { + Success = 0, + Unknown, + Connection, + BindIPAddress, + Read, + Write, + ExceedRedirectCount, + Canceled, + SSLConnection, + SSLLoadingCerts, + SSLServerVerification, + UnsupportedMultipartBoundaryChars, + Compression, +}; + +std::string to_string(const Error error); + +std::ostream &operator<<(std::ostream &os, const Error &obj); + +class Result { +public: + Result(std::unique_ptr &&res, Error err, + Headers &&request_headers = Headers{}) + : res_(std::move(res)), err_(err), + request_headers_(std::move(request_headers)) {} + // Response + operator bool() const { return res_ != nullptr; } + bool operator==(std::nullptr_t) const { return res_ == nullptr; } + bool operator!=(std::nullptr_t) const { return res_ != nullptr; } + const Response &value() const { return *res_; } + Response &value() { return *res_; } + const Response &operator*() const { return *res_; } + Response &operator*() { return *res_; } + const Response *operator->() const { return res_.get(); } + Response *operator->() { return res_.get(); } + + // Error + Error error() const { return err_; } + + // Request Headers + bool has_request_header(const char *key) const; + std::string get_request_header_value(const char *key, size_t id = 0) const; + template + T get_request_header_value(const char *key, size_t id = 0) const; + size_t get_request_header_value_count(const char *key) const; + +private: + std::unique_ptr res_; + Error err_; + Headers request_headers_; +}; + +class ClientImpl { +public: + explicit ClientImpl(const std::string &host); + + explicit ClientImpl(const std::string &host, int port); + + explicit ClientImpl(const std::string &host, int port, + const std::string &client_cert_path, + const std::string &client_key_path); + + virtual ~ClientImpl(); + + virtual bool is_valid() const; + + Result Get(const char *path); + Result Get(const char *path, const Headers &headers); + Result Get(const char *path, Progress progress); + Result Get(const char *path, const Headers &headers, Progress progress); + Result Get(const char *path, ContentReceiver content_receiver); + Result Get(const char *path, const Headers &headers, + ContentReceiver content_receiver); + Result Get(const char *path, ContentReceiver content_receiver, + Progress progress); + Result Get(const char *path, const Headers &headers, + ContentReceiver content_receiver, Progress progress); + Result Get(const char *path, ResponseHandler response_handler, + ContentReceiver content_receiver); + Result Get(const char *path, const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver); + Result Get(const char *path, ResponseHandler response_handler, + ContentReceiver content_receiver, Progress progress); + Result Get(const char *path, const Headers &headers, + ResponseHandler response_handler, ContentReceiver content_receiver, + Progress progress); + + Result Get(const char *path, const Params ¶ms, const Headers &headers, + Progress progress = nullptr); + Result Get(const char *path, const Params ¶ms, const Headers &headers, + ContentReceiver content_receiver, Progress progress = nullptr); + Result Get(const char *path, const Params ¶ms, const Headers &headers, + ResponseHandler response_handler, ContentReceiver content_receiver, + Progress progress = nullptr); + + Result Head(const char *path); + Result Head(const char *path, const Headers &headers); + + Result Post(const char *path); + Result Post(const char *path, const char *body, size_t content_length, + const char *content_type); + Result Post(const char *path, const Headers &headers, const char *body, + size_t content_length, const char *content_type); + Result Post(const char *path, const std::string &body, + const char *content_type); + Result Post(const char *path, const Headers &headers, const std::string &body, + const char *content_type); + Result Post(const char *path, size_t content_length, + ContentProvider content_provider, const char *content_type); + Result Post(const char *path, ContentProviderWithoutLength content_provider, + const char *content_type); + Result Post(const char *path, const Headers &headers, size_t content_length, + ContentProvider content_provider, const char *content_type); + Result Post(const char *path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const char *content_type); + Result Post(const char *path, const Params ¶ms); + Result Post(const char *path, const Headers &headers, const Params ¶ms); + Result Post(const char *path, const MultipartFormDataItems &items); + Result Post(const char *path, const Headers &headers, + const MultipartFormDataItems &items); + Result Post(const char *path, const Headers &headers, + const MultipartFormDataItems &items, const std::string &boundary); + + Result Put(const char *path); + Result Put(const char *path, const char *body, size_t content_length, + const char *content_type); + Result Put(const char *path, const Headers &headers, const char *body, + size_t content_length, const char *content_type); + Result Put(const char *path, const std::string &body, + const char *content_type); + Result Put(const char *path, const Headers &headers, const std::string &body, + const char *content_type); + Result Put(const char *path, size_t content_length, + ContentProvider content_provider, const char *content_type); + Result Put(const char *path, ContentProviderWithoutLength content_provider, + const char *content_type); + Result Put(const char *path, const Headers &headers, size_t content_length, + ContentProvider content_provider, const char *content_type); + Result Put(const char *path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const char *content_type); + Result Put(const char *path, const Params ¶ms); + Result Put(const char *path, const Headers &headers, const Params ¶ms); + + Result Patch(const char *path); + Result Patch(const char *path, const char *body, size_t content_length, + const char *content_type); + Result Patch(const char *path, const Headers &headers, const char *body, + size_t content_length, const char *content_type); + Result Patch(const char *path, const std::string &body, + const char *content_type); + Result Patch(const char *path, const Headers &headers, + const std::string &body, const char *content_type); + Result Patch(const char *path, size_t content_length, + ContentProvider content_provider, const char *content_type); + Result Patch(const char *path, ContentProviderWithoutLength content_provider, + const char *content_type); + Result Patch(const char *path, const Headers &headers, size_t content_length, + ContentProvider content_provider, const char *content_type); + Result Patch(const char *path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const char *content_type); + + Result Delete(const char *path); + Result Delete(const char *path, const Headers &headers); + Result Delete(const char *path, const char *body, size_t content_length, + const char *content_type); + Result Delete(const char *path, const Headers &headers, const char *body, + size_t content_length, const char *content_type); + Result Delete(const char *path, const std::string &body, + const char *content_type); + Result Delete(const char *path, const Headers &headers, + const std::string &body, const char *content_type); + + Result Options(const char *path); + Result Options(const char *path, const Headers &headers); + + bool send(Request &req, Response &res, Error &error); + Result send(const Request &req); + + size_t is_socket_open() const; + + void stop(); + + void set_hostname_addr_map(const std::map addr_map); + + void set_default_headers(Headers headers); + + void set_address_family(int family); + void set_tcp_nodelay(bool on); + void set_socket_options(SocketOptions socket_options); + + void set_connection_timeout(time_t sec, time_t usec = 0); + template + void + set_connection_timeout(const std::chrono::duration &duration); + + void set_read_timeout(time_t sec, time_t usec = 0); + template + void set_read_timeout(const std::chrono::duration &duration); + + void set_write_timeout(time_t sec, time_t usec = 0); + template + void set_write_timeout(const std::chrono::duration &duration); + + void set_basic_auth(const char *username, const char *password); + void set_bearer_token_auth(const char *token); +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void set_digest_auth(const char *username, const char *password); +#endif + + void set_keep_alive(bool on); + void set_follow_location(bool on); + + void set_url_encode(bool on); + + void set_compress(bool on); + + void set_decompress(bool on); + + void set_interface(const char *intf); + + void set_proxy(const char *host, int port); + void set_proxy_basic_auth(const char *username, const char *password); + void set_proxy_bearer_token_auth(const char *token); +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void set_proxy_digest_auth(const char *username, const char *password); +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void set_ca_cert_path(const char *ca_cert_file_path, + const char *ca_cert_dir_path = nullptr); + void set_ca_cert_store(X509_STORE *ca_cert_store); +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void enable_server_certificate_verification(bool enabled); +#endif + + void set_logger(Logger logger); + +protected: + struct Socket { + socket_t sock = INVALID_SOCKET; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + SSL *ssl = nullptr; +#endif + + bool is_open() const { return sock != INVALID_SOCKET; } + }; + + Result send_(Request &&req); + + virtual bool create_and_connect_socket(Socket &socket, Error &error); + + // All of: + // shutdown_ssl + // shutdown_socket + // close_socket + // should ONLY be called when socket_mutex_ is locked. + // Also, shutdown_ssl and close_socket should also NOT be called concurrently + // with a DIFFERENT thread sending requests using that socket. + virtual void shutdown_ssl(Socket &socket, bool shutdown_gracefully); + void shutdown_socket(Socket &socket); + void close_socket(Socket &socket); + + bool process_request(Stream &strm, Request &req, Response &res, + bool close_connection, Error &error); + + bool write_content_with_provider(Stream &strm, const Request &req, + Error &error); + + void copy_settings(const ClientImpl &rhs); + + // Socket endoint information + const std::string host_; + const int port_; + const std::string host_and_port_; + + // Current open socket + Socket socket_; + mutable std::mutex socket_mutex_; + std::recursive_mutex request_mutex_; + + // These are all protected under socket_mutex + size_t socket_requests_in_flight_ = 0; + std::thread::id socket_requests_are_from_thread_ = std::thread::id(); + bool socket_should_be_closed_when_request_is_done_ = false; + + // Hostname-IP map + std::map addr_map_; + + // Default headers + Headers default_headers_; + + // Settings + std::string client_cert_path_; + std::string client_key_path_; + + time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND; + time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND; + time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND; + time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND; + time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND; + time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND; + + std::string basic_auth_username_; + std::string basic_auth_password_; + std::string bearer_token_auth_token_; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + std::string digest_auth_username_; + std::string digest_auth_password_; +#endif + + bool keep_alive_ = false; + bool follow_location_ = false; + + bool url_encode_ = true; + + int address_family_ = AF_UNSPEC; + bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY; + SocketOptions socket_options_ = nullptr; + + bool compress_ = false; + bool decompress_ = true; + + std::string interface_; + + std::string proxy_host_; + int proxy_port_ = -1; + + std::string proxy_basic_auth_username_; + std::string proxy_basic_auth_password_; + std::string proxy_bearer_token_auth_token_; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + std::string proxy_digest_auth_username_; + std::string proxy_digest_auth_password_; +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + std::string ca_cert_file_path_; + std::string ca_cert_dir_path_; + + X509_STORE *ca_cert_store_ = nullptr; +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + bool server_certificate_verification_ = true; +#endif + + Logger logger_; + +private: + socket_t create_client_socket(Error &error) const; + bool read_response_line(Stream &strm, const Request &req, Response &res); + bool write_request(Stream &strm, Request &req, bool close_connection, + Error &error); + bool redirect(Request &req, Response &res, Error &error); + bool handle_request(Stream &strm, Request &req, Response &res, + bool close_connection, Error &error); + std::unique_ptr send_with_content_provider( + Request &req, + // const char *method, const char *path, const Headers &headers, + const char *body, size_t content_length, ContentProvider content_provider, + ContentProviderWithoutLength content_provider_without_length, + const char *content_type, Error &error); + Result send_with_content_provider( + const char *method, const char *path, const Headers &headers, + const char *body, size_t content_length, ContentProvider content_provider, + ContentProviderWithoutLength content_provider_without_length, + const char *content_type); + + std::string adjust_host_string(const std::string &host) const; + + virtual bool process_socket(const Socket &socket, + std::function callback); + virtual bool is_ssl() const; +}; + +class Client { +public: + // Universal interface + explicit Client(const std::string &scheme_host_port); + + explicit Client(const std::string &scheme_host_port, + const std::string &client_cert_path, + const std::string &client_key_path); + + // HTTP only interface + explicit Client(const std::string &host, int port); + + explicit Client(const std::string &host, int port, + const std::string &client_cert_path, + const std::string &client_key_path); + + Client(Client &&) = default; + + ~Client(); + + bool is_valid() const; + + Result Get(const char *path); + Result Get(const char *path, const Headers &headers); + Result Get(const char *path, Progress progress); + Result Get(const char *path, const Headers &headers, Progress progress); + Result Get(const char *path, ContentReceiver content_receiver); + Result Get(const char *path, const Headers &headers, + ContentReceiver content_receiver); + Result Get(const char *path, ContentReceiver content_receiver, + Progress progress); + Result Get(const char *path, const Headers &headers, + ContentReceiver content_receiver, Progress progress); + Result Get(const char *path, ResponseHandler response_handler, + ContentReceiver content_receiver); + Result Get(const char *path, const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver); + Result Get(const char *path, const Headers &headers, + ResponseHandler response_handler, ContentReceiver content_receiver, + Progress progress); + Result Get(const char *path, ResponseHandler response_handler, + ContentReceiver content_receiver, Progress progress); + + Result Get(const char *path, const Params ¶ms, const Headers &headers, + Progress progress = nullptr); + Result Get(const char *path, const Params ¶ms, const Headers &headers, + ContentReceiver content_receiver, Progress progress = nullptr); + Result Get(const char *path, const Params ¶ms, const Headers &headers, + ResponseHandler response_handler, ContentReceiver content_receiver, + Progress progress = nullptr); + + Result Head(const char *path); + Result Head(const char *path, const Headers &headers); + + Result Post(const char *path); + Result Post(const char *path, const char *body, size_t content_length, + const char *content_type); + Result Post(const char *path, const Headers &headers, const char *body, + size_t content_length, const char *content_type); + Result Post(const char *path, const std::string &body, + const char *content_type); + Result Post(const char *path, const Headers &headers, const std::string &body, + const char *content_type); + Result Post(const char *path, size_t content_length, + ContentProvider content_provider, const char *content_type); + Result Post(const char *path, ContentProviderWithoutLength content_provider, + const char *content_type); + Result Post(const char *path, const Headers &headers, size_t content_length, + ContentProvider content_provider, const char *content_type); + Result Post(const char *path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const char *content_type); + Result Post(const char *path, const Params ¶ms); + Result Post(const char *path, const Headers &headers, const Params ¶ms); + Result Post(const char *path, const MultipartFormDataItems &items); + Result Post(const char *path, const Headers &headers, + const MultipartFormDataItems &items); + Result Post(const char *path, const Headers &headers, + const MultipartFormDataItems &items, const std::string &boundary); + Result Put(const char *path); + Result Put(const char *path, const char *body, size_t content_length, + const char *content_type); + Result Put(const char *path, const Headers &headers, const char *body, + size_t content_length, const char *content_type); + Result Put(const char *path, const std::string &body, + const char *content_type); + Result Put(const char *path, const Headers &headers, const std::string &body, + const char *content_type); + Result Put(const char *path, size_t content_length, + ContentProvider content_provider, const char *content_type); + Result Put(const char *path, ContentProviderWithoutLength content_provider, + const char *content_type); + Result Put(const char *path, const Headers &headers, size_t content_length, + ContentProvider content_provider, const char *content_type); + Result Put(const char *path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const char *content_type); + Result Put(const char *path, const Params ¶ms); + Result Put(const char *path, const Headers &headers, const Params ¶ms); + Result Patch(const char *path); + Result Patch(const char *path, const char *body, size_t content_length, + const char *content_type); + Result Patch(const char *path, const Headers &headers, const char *body, + size_t content_length, const char *content_type); + Result Patch(const char *path, const std::string &body, + const char *content_type); + Result Patch(const char *path, const Headers &headers, + const std::string &body, const char *content_type); + Result Patch(const char *path, size_t content_length, + ContentProvider content_provider, const char *content_type); + Result Patch(const char *path, ContentProviderWithoutLength content_provider, + const char *content_type); + Result Patch(const char *path, const Headers &headers, size_t content_length, + ContentProvider content_provider, const char *content_type); + Result Patch(const char *path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const char *content_type); + + Result Delete(const char *path); + Result Delete(const char *path, const Headers &headers); + Result Delete(const char *path, const char *body, size_t content_length, + const char *content_type); + Result Delete(const char *path, const Headers &headers, const char *body, + size_t content_length, const char *content_type); + Result Delete(const char *path, const std::string &body, + const char *content_type); + Result Delete(const char *path, const Headers &headers, + const std::string &body, const char *content_type); + + Result Options(const char *path); + Result Options(const char *path, const Headers &headers); + + bool send(Request &req, Response &res, Error &error); + Result send(const Request &req); + + size_t is_socket_open() const; + + void stop(); + + void set_hostname_addr_map(const std::map addr_map); + + void set_default_headers(Headers headers); + + void set_address_family(int family); + void set_tcp_nodelay(bool on); + void set_socket_options(SocketOptions socket_options); + + void set_connection_timeout(time_t sec, time_t usec = 0); + template + void + set_connection_timeout(const std::chrono::duration &duration); + + void set_read_timeout(time_t sec, time_t usec = 0); + template + void set_read_timeout(const std::chrono::duration &duration); + + void set_write_timeout(time_t sec, time_t usec = 0); + template + void set_write_timeout(const std::chrono::duration &duration); + + void set_basic_auth(const char *username, const char *password); + void set_bearer_token_auth(const char *token); +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void set_digest_auth(const char *username, const char *password); +#endif + + void set_keep_alive(bool on); + void set_follow_location(bool on); + + void set_url_encode(bool on); + + void set_compress(bool on); + + void set_decompress(bool on); + + void set_interface(const char *intf); + + void set_proxy(const char *host, int port); + void set_proxy_basic_auth(const char *username, const char *password); + void set_proxy_bearer_token_auth(const char *token); +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void set_proxy_digest_auth(const char *username, const char *password); +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void enable_server_certificate_verification(bool enabled); +#endif + + void set_logger(Logger logger); + + // SSL +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void set_ca_cert_path(const char *ca_cert_file_path, + const char *ca_cert_dir_path = nullptr); + + void set_ca_cert_store(X509_STORE *ca_cert_store); + + long get_openssl_verify_result() const; + + SSL_CTX *ssl_context() const; +#endif + +private: + std::unique_ptr cli_; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + bool is_ssl_ = false; +#endif +}; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +class SSLServer : public Server { +public: + SSLServer(const char *cert_path, const char *private_key_path, + const char *client_ca_cert_file_path = nullptr, + const char *client_ca_cert_dir_path = nullptr); + + SSLServer(X509 *cert, EVP_PKEY *private_key, + X509_STORE *client_ca_cert_store = nullptr); + + ~SSLServer() override; + + bool is_valid() const override; + +private: + bool process_and_close_socket(socket_t sock) override; + + SSL_CTX *ctx_; + std::mutex ctx_mutex_; +}; + +class SSLClient : public ClientImpl { +public: + explicit SSLClient(const std::string &host); + + explicit SSLClient(const std::string &host, int port); + + explicit SSLClient(const std::string &host, int port, + const std::string &client_cert_path, + const std::string &client_key_path); + + explicit SSLClient(const std::string &host, int port, X509 *client_cert, + EVP_PKEY *client_key); + + ~SSLClient() override; + + bool is_valid() const override; + + void set_ca_cert_store(X509_STORE *ca_cert_store); + + long get_openssl_verify_result() const; + + SSL_CTX *ssl_context() const; + +private: + bool create_and_connect_socket(Socket &socket, Error &error) override; + void shutdown_ssl(Socket &socket, bool shutdown_gracefully) override; + void shutdown_ssl_impl(Socket &socket, bool shutdown_socket); + + bool process_socket(const Socket &socket, + std::function callback) override; + bool is_ssl() const override; + + bool connect_with_proxy(Socket &sock, Response &res, bool &success, + Error &error); + bool initialize_ssl(Socket &socket, Error &error); + + bool load_certs(); + + bool verify_host(X509 *server_cert) const; + bool verify_host_with_subject_alt_name(X509 *server_cert) const; + bool verify_host_with_common_name(X509 *server_cert) const; + bool check_host_name(const char *pattern, size_t pattern_len) const; + + SSL_CTX *ctx_; + std::mutex ctx_mutex_; + std::once_flag initialize_cert_; + + std::vector host_components_; + + long verify_result_ = 0; + + friend class ClientImpl; +}; +#endif + +/* + * Implementation of template methods. + */ + +namespace detail { + +template +inline void duration_to_sec_and_usec(const T &duration, U callback) { + auto sec = std::chrono::duration_cast(duration).count(); + auto usec = std::chrono::duration_cast( + duration - std::chrono::seconds(sec)) + .count(); + callback(sec, usec); +} + +template +inline T get_header_value(const Headers & /*headers*/, const char * /*key*/, + size_t /*id*/ = 0, uint64_t /*def*/ = 0) {} + +template <> +inline uint64_t get_header_value(const Headers &headers, + const char *key, size_t id, + uint64_t def) { + auto rng = headers.equal_range(key); + auto it = rng.first; + std::advance(it, static_cast(id)); + if (it != rng.second) { + return std::strtoull(it->second.data(), nullptr, 10); + } + return def; +} + +} // namespace detail + +template +inline T Request::get_header_value(const char *key, size_t id) const { + return detail::get_header_value(headers, key, id, 0); +} + +template +inline T Response::get_header_value(const char *key, size_t id) const { + return detail::get_header_value(headers, key, id, 0); +} + +template +inline ssize_t Stream::write_format(const char *fmt, const Args &...args) { + const auto bufsiz = 2048; + std::array buf; + +#if defined(_MSC_VER) && _MSC_VER < 1900 + auto sn = _snprintf_s(buf.data(), bufsiz - 1, buf.size() - 1, fmt, args...); +#else + auto sn = snprintf(buf.data(), buf.size() - 1, fmt, args...); +#endif + if (sn <= 0) { return sn; } + + auto n = static_cast(sn); + + if (n >= buf.size() - 1) { + std::vector glowable_buf(buf.size()); + + while (n >= glowable_buf.size() - 1) { + glowable_buf.resize(glowable_buf.size() * 2); +#if defined(_MSC_VER) && _MSC_VER < 1900 + n = static_cast(_snprintf_s(&glowable_buf[0], glowable_buf.size(), + glowable_buf.size() - 1, fmt, + args...)); +#else + n = static_cast( + snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...)); +#endif + } + return write(&glowable_buf[0], n); + } else { + return write(buf.data(), n); + } +} + +inline void default_socket_options(socket_t sock) { + int yes = 1; +#ifdef _WIN32 + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&yes), + sizeof(yes)); + setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, + reinterpret_cast(&yes), sizeof(yes)); +#else +#ifdef SO_REUSEPORT + setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast(&yes), + sizeof(yes)); +#else + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&yes), + sizeof(yes)); +#endif +#endif +} + +template +inline Server & +Server::set_read_timeout(const std::chrono::duration &duration) { + detail::duration_to_sec_and_usec( + duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); }); + return *this; +} + +template +inline Server & +Server::set_write_timeout(const std::chrono::duration &duration) { + detail::duration_to_sec_and_usec( + duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); }); + return *this; +} + +template +inline Server & +Server::set_idle_interval(const std::chrono::duration &duration) { + detail::duration_to_sec_and_usec( + duration, [&](time_t sec, time_t usec) { set_idle_interval(sec, usec); }); + return *this; +} + +inline std::string to_string(const Error error) { + switch (error) { + case Error::Success: return "Success"; + case Error::Connection: return "Connection"; + case Error::BindIPAddress: return "BindIPAddress"; + case Error::Read: return "Read"; + case Error::Write: return "Write"; + case Error::ExceedRedirectCount: return "ExceedRedirectCount"; + case Error::Canceled: return "Canceled"; + case Error::SSLConnection: return "SSLConnection"; + case Error::SSLLoadingCerts: return "SSLLoadingCerts"; + case Error::SSLServerVerification: return "SSLServerVerification"; + case Error::UnsupportedMultipartBoundaryChars: + return "UnsupportedMultipartBoundaryChars"; + case Error::Compression: return "Compression"; + case Error::Unknown: return "Unknown"; + default: break; + } + + return "Invalid"; +} + +inline std::ostream &operator<<(std::ostream &os, const Error &obj) { + os << to_string(obj); + os << " (" << static_cast::type>(obj) << ')'; + return os; +} + +template +inline T Result::get_request_header_value(const char *key, size_t id) const { + return detail::get_header_value(request_headers_, key, id, 0); +} + +template +inline void ClientImpl::set_connection_timeout( + const std::chrono::duration &duration) { + detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec) { + set_connection_timeout(sec, usec); + }); +} + +template +inline void ClientImpl::set_read_timeout( + const std::chrono::duration &duration) { + detail::duration_to_sec_and_usec( + duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); }); +} + +template +inline void ClientImpl::set_write_timeout( + const std::chrono::duration &duration) { + detail::duration_to_sec_and_usec( + duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); }); +} + +template +inline void Client::set_connection_timeout( + const std::chrono::duration &duration) { + cli_->set_connection_timeout(duration); +} + +template +inline void +Client::set_read_timeout(const std::chrono::duration &duration) { + cli_->set_read_timeout(duration); +} + +template +inline void +Client::set_write_timeout(const std::chrono::duration &duration) { + cli_->set_write_timeout(duration); +} + +/* + * Forward declarations and types that will be part of the .h file if split into + * .h + .cc. + */ + +std::string append_query_params(const char *path, const Params ¶ms); + +std::pair make_range_header(Ranges ranges); + +std::pair +make_basic_authentication_header(const std::string &username, + const std::string &password, + bool is_proxy = false); + +namespace detail { + +std::string encode_query_param(const std::string &value); + +void read_file(const std::string &path, std::string &out); + +std::string trim_copy(const std::string &s); + +void split(const char *b, const char *e, char d, + std::function fn); + +bool process_client_socket(socket_t sock, time_t read_timeout_sec, + time_t read_timeout_usec, time_t write_timeout_sec, + time_t write_timeout_usec, + std::function callback); + +socket_t create_client_socket(const char *host, const char *ip, int port, int address_family, + bool tcp_nodelay, SocketOptions socket_options, + time_t connection_timeout_sec, + time_t connection_timeout_usec, + time_t read_timeout_sec, time_t read_timeout_usec, + time_t write_timeout_sec, + time_t write_timeout_usec, + const std::string &intf, Error &error); + +const char *get_header_value(const Headers &headers, const char *key, + size_t id = 0, const char *def = nullptr); + +std::string params_to_query_str(const Params ¶ms); + +void parse_query_text(const std::string &s, Params ¶ms); + +bool parse_range_header(const std::string &s, Ranges &ranges); + +int close_socket(socket_t sock); + +ssize_t send_socket(socket_t sock, const void *ptr, size_t size, int flags); + +ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags); + +enum class EncodingType { None = 0, Gzip, Brotli }; + +EncodingType encoding_type(const Request &req, const Response &res); + +class BufferStream : public Stream { +public: + BufferStream() = default; + ~BufferStream() override = default; + + bool is_readable() const override; + bool is_writable() const override; + ssize_t read(char *ptr, size_t size) override; + ssize_t write(const char *ptr, size_t size) override; + void get_remote_ip_and_port(std::string &ip, int &port) const override; + socket_t socket() const override; + + const std::string &get_buffer() const; + +private: + std::string buffer; + size_t position = 0; +}; + +class compressor { +public: + virtual ~compressor() = default; + + typedef std::function Callback; + virtual bool compress(const char *data, size_t data_length, bool last, + Callback callback) = 0; +}; + +class decompressor { +public: + virtual ~decompressor() = default; + + virtual bool is_valid() const = 0; + + typedef std::function Callback; + virtual bool decompress(const char *data, size_t data_length, + Callback callback) = 0; +}; + +class nocompressor : public compressor { +public: + virtual ~nocompressor() = default; + + bool compress(const char *data, size_t data_length, bool /*last*/, + Callback callback) override; +}; + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT +class gzip_compressor : public compressor { +public: + gzip_compressor(); + ~gzip_compressor(); + + bool compress(const char *data, size_t data_length, bool last, + Callback callback) override; + +private: + bool is_valid_ = false; + z_stream strm_; +}; + +class gzip_decompressor : public decompressor { +public: + gzip_decompressor(); + ~gzip_decompressor(); + + bool is_valid() const override; + + bool decompress(const char *data, size_t data_length, + Callback callback) override; + +private: + bool is_valid_ = false; + z_stream strm_; +}; +#endif + +#ifdef CPPHTTPLIB_BROTLI_SUPPORT +class brotli_compressor : public compressor { +public: + brotli_compressor(); + ~brotli_compressor(); + + bool compress(const char *data, size_t data_length, bool last, + Callback callback) override; + +private: + BrotliEncoderState *state_ = nullptr; +}; + +class brotli_decompressor : public decompressor { +public: + brotli_decompressor(); + ~brotli_decompressor(); + + bool is_valid() const override; + + bool decompress(const char *data, size_t data_length, + Callback callback) override; + +private: + BrotliDecoderResult decoder_r; + BrotliDecoderState *decoder_s = nullptr; +}; +#endif + +// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer` +// to store data. The call can set memory on stack for performance. +class stream_line_reader { +public: + stream_line_reader(Stream &strm, char *fixed_buffer, + size_t fixed_buffer_size); + const char *ptr() const; + size_t size() const; + bool end_with_crlf() const; + bool getline(); + +private: + void append(char c); + + Stream &strm_; + char *fixed_buffer_; + const size_t fixed_buffer_size_; + size_t fixed_buffer_used_size_ = 0; + std::string glowable_buffer_; +}; + +} // namespace detail + +// ---------------------------------------------------------------------------- + +/* + * Implementation that will be part of the .cc file if split into .h + .cc. + */ + +namespace detail { + +inline bool is_hex(char c, int &v) { + if (0x20 <= c && isdigit(c)) { + v = c - '0'; + return true; + } else if ('A' <= c && c <= 'F') { + v = c - 'A' + 10; + return true; + } else if ('a' <= c && c <= 'f') { + v = c - 'a' + 10; + return true; + } + return false; +} + +inline bool from_hex_to_i(const std::string &s, size_t i, size_t cnt, + int &val) { + if (i >= s.size()) { return false; } + + val = 0; + for (; cnt; i++, cnt--) { + if (!s[i]) { return false; } + int v = 0; + if (is_hex(s[i], v)) { + val = val * 16 + v; + } else { + return false; + } + } + return true; +} + +inline std::string from_i_to_hex(size_t n) { + const char *charset = "0123456789abcdef"; + std::string ret; + do { + ret = charset[n & 15] + ret; + n >>= 4; + } while (n > 0); + return ret; +} + +inline size_t to_utf8(int code, char *buff) { + if (code < 0x0080) { + buff[0] = (code & 0x7F); + return 1; + } else if (code < 0x0800) { + buff[0] = static_cast(0xC0 | ((code >> 6) & 0x1F)); + buff[1] = static_cast(0x80 | (code & 0x3F)); + return 2; + } else if (code < 0xD800) { + buff[0] = static_cast(0xE0 | ((code >> 12) & 0xF)); + buff[1] = static_cast(0x80 | ((code >> 6) & 0x3F)); + buff[2] = static_cast(0x80 | (code & 0x3F)); + return 3; + } else if (code < 0xE000) { // D800 - DFFF is invalid... + return 0; + } else if (code < 0x10000) { + buff[0] = static_cast(0xE0 | ((code >> 12) & 0xF)); + buff[1] = static_cast(0x80 | ((code >> 6) & 0x3F)); + buff[2] = static_cast(0x80 | (code & 0x3F)); + return 3; + } else if (code < 0x110000) { + buff[0] = static_cast(0xF0 | ((code >> 18) & 0x7)); + buff[1] = static_cast(0x80 | ((code >> 12) & 0x3F)); + buff[2] = static_cast(0x80 | ((code >> 6) & 0x3F)); + buff[3] = static_cast(0x80 | (code & 0x3F)); + return 4; + } + + // NOTREACHED + return 0; +} + +// NOTE: This code came up with the following stackoverflow post: +// https://stackoverflow.com/questions/180947/base64-decode-snippet-in-c +inline std::string base64_encode(const std::string &in) { + static const auto lookup = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + std::string out; + out.reserve(in.size()); + + int val = 0; + int valb = -6; + + for (auto c : in) { + val = (val << 8) + static_cast(c); + valb += 8; + while (valb >= 0) { + out.push_back(lookup[(val >> valb) & 0x3F]); + valb -= 6; + } + } + + if (valb > -6) { out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]); } + + while (out.size() % 4) { + out.push_back('='); + } + + return out; +} + +inline bool is_file(const std::string &path) { + struct stat st; + return stat(path.c_str(), &st) >= 0 && S_ISREG(st.st_mode); +} + +inline bool is_dir(const std::string &path) { + struct stat st; + return stat(path.c_str(), &st) >= 0 && S_ISDIR(st.st_mode); +} + +inline bool is_valid_path(const std::string &path) { + size_t level = 0; + size_t i = 0; + + // Skip slash + while (i < path.size() && path[i] == '/') { + i++; + } + + while (i < path.size()) { + // Read component + auto beg = i; + while (i < path.size() && path[i] != '/') { + i++; + } + + auto len = i - beg; + assert(len > 0); + + if (!path.compare(beg, len, ".")) { + ; + } else if (!path.compare(beg, len, "..")) { + if (level == 0) { return false; } + level--; + } else { + level++; + } + + // Skip slash + while (i < path.size() && path[i] == '/') { + i++; + } + } + + return true; +} + +inline std::string encode_query_param(const std::string &value) { + std::ostringstream escaped; + escaped.fill('0'); + escaped << std::hex; + + for (auto c : value) { + if (std::isalnum(static_cast(c)) || c == '-' || c == '_' || + c == '.' || c == '!' || c == '~' || c == '*' || c == '\'' || c == '(' || + c == ')') { + escaped << c; + } else { + escaped << std::uppercase; + escaped << '%' << std::setw(2) + << static_cast(static_cast(c)); + escaped << std::nouppercase; + } + } + + return escaped.str(); +} + +inline std::string encode_url(const std::string &s) { + std::string result; + result.reserve(s.size()); + + for (size_t i = 0; s[i]; i++) { + switch (s[i]) { + case ' ': result += "%20"; break; + case '+': result += "%2B"; break; + case '\r': result += "%0D"; break; + case '\n': result += "%0A"; break; + case '\'': result += "%27"; break; + case ',': result += "%2C"; break; + // case ':': result += "%3A"; break; // ok? probably... + case ';': result += "%3B"; break; + default: + auto c = static_cast(s[i]); + if (c >= 0x80) { + result += '%'; + char hex[4]; + auto len = snprintf(hex, sizeof(hex) - 1, "%02X", c); + assert(len == 2); + result.append(hex, static_cast(len)); + } else { + result += s[i]; + } + break; + } + } + + return result; +} + +inline std::string decode_url(const std::string &s, + bool convert_plus_to_space) { + std::string result; + + for (size_t i = 0; i < s.size(); i++) { + if (s[i] == '%' && i + 1 < s.size()) { + if (s[i + 1] == 'u') { + int val = 0; + if (from_hex_to_i(s, i + 2, 4, val)) { + // 4 digits Unicode codes + char buff[4]; + size_t len = to_utf8(val, buff); + if (len > 0) { result.append(buff, len); } + i += 5; // 'u0000' + } else { + result += s[i]; + } + } else { + int val = 0; + if (from_hex_to_i(s, i + 1, 2, val)) { + // 2 digits hex codes + result += static_cast(val); + i += 2; // '00' + } else { + result += s[i]; + } + } + } else if (convert_plus_to_space && s[i] == '+') { + result += ' '; + } else { + result += s[i]; + } + } + + return result; +} + +inline void read_file(const std::string &path, std::string &out) { + std::ifstream fs(path, std::ios_base::binary); + fs.seekg(0, std::ios_base::end); + auto size = fs.tellg(); + fs.seekg(0); + out.resize(static_cast(size)); + fs.read(&out[0], static_cast(size)); +} + +inline std::string file_extension(const std::string &path) { + std::smatch m; + static auto re = std::regex("\\.([a-zA-Z0-9]+)$"); + if (std::regex_search(path, m, re)) { return m[1].str(); } + return std::string(); +} + +inline bool is_space_or_tab(char c) { return c == ' ' || c == '\t'; } + +inline std::pair trim(const char *b, const char *e, size_t left, + size_t right) { + while (b + left < e && is_space_or_tab(b[left])) { + left++; + } + while (right > 0 && is_space_or_tab(b[right - 1])) { + right--; + } + return std::make_pair(left, right); +} + +inline std::string trim_copy(const std::string &s) { + auto r = trim(s.data(), s.data() + s.size(), 0, s.size()); + return s.substr(r.first, r.second - r.first); +} + +inline void split(const char *b, const char *e, char d, + std::function fn) { + size_t i = 0; + size_t beg = 0; + + while (e ? (b + i < e) : (b[i] != '\0')) { + if (b[i] == d) { + auto r = trim(b, e, beg, i); + if (r.first < r.second) { fn(&b[r.first], &b[r.second]); } + beg = i + 1; + } + i++; + } + + if (i) { + auto r = trim(b, e, beg, i); + if (r.first < r.second) { fn(&b[r.first], &b[r.second]); } + } +} + +inline stream_line_reader::stream_line_reader(Stream &strm, char *fixed_buffer, + size_t fixed_buffer_size) + : strm_(strm), fixed_buffer_(fixed_buffer), + fixed_buffer_size_(fixed_buffer_size) {} + +inline const char *stream_line_reader::ptr() const { + if (glowable_buffer_.empty()) { + return fixed_buffer_; + } else { + return glowable_buffer_.data(); + } +} + +inline size_t stream_line_reader::size() const { + if (glowable_buffer_.empty()) { + return fixed_buffer_used_size_; + } else { + return glowable_buffer_.size(); + } +} + +inline bool stream_line_reader::end_with_crlf() const { + auto end = ptr() + size(); + return size() >= 2 && end[-2] == '\r' && end[-1] == '\n'; +} + +inline bool stream_line_reader::getline() { + fixed_buffer_used_size_ = 0; + glowable_buffer_.clear(); + + for (size_t i = 0;; i++) { + char byte; + auto n = strm_.read(&byte, 1); + + if (n < 0) { + return false; + } else if (n == 0) { + if (i == 0) { + return false; + } else { + break; + } + } + + append(byte); + + if (byte == '\n') { break; } + } + + return true; +} + +inline void stream_line_reader::append(char c) { + if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) { + fixed_buffer_[fixed_buffer_used_size_++] = c; + fixed_buffer_[fixed_buffer_used_size_] = '\0'; + } else { + if (glowable_buffer_.empty()) { + assert(fixed_buffer_[fixed_buffer_used_size_] == '\0'); + glowable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_); + } + glowable_buffer_ += c; + } +} + +inline int close_socket(socket_t sock) { +#ifdef _WIN32 + return closesocket(sock); +#else + return close(sock); +#endif +} + +template inline ssize_t handle_EINTR(T fn) { + ssize_t res = false; + while (true) { + res = fn(); + if (res < 0 && errno == EINTR) { continue; } + break; + } + return res; +} + +inline ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags) { + return handle_EINTR([&]() { + return recv(sock, +#ifdef _WIN32 + static_cast(ptr), + static_cast(size), +#else + ptr, + size, +#endif + flags); + }); +} + +inline ssize_t send_socket(socket_t sock, const void *ptr, size_t size, int flags) { + return handle_EINTR([&]() { + return send(sock, +#ifdef _WIN32 + static_cast(ptr), + static_cast(size), +#else + ptr, + size, +#endif + flags); + }); +} + +inline ssize_t select_read(socket_t sock, time_t sec, time_t usec) { +#ifdef CPPHTTPLIB_USE_POLL + struct pollfd pfd_read; + pfd_read.fd = sock; + pfd_read.events = POLLIN; + + auto timeout = static_cast(sec * 1000 + usec / 1000); + + return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); }); +#else +#ifndef _WIN32 + if (sock >= FD_SETSIZE) { return 1; } +#endif + + fd_set fds; + FD_ZERO(&fds); + FD_SET(sock, &fds); + + timeval tv; + tv.tv_sec = static_cast(sec); + tv.tv_usec = static_cast(usec); + + return handle_EINTR([&]() { + return select(static_cast(sock + 1), &fds, nullptr, nullptr, &tv); + }); +#endif +} + +inline ssize_t select_write(socket_t sock, time_t sec, time_t usec) { +#ifdef CPPHTTPLIB_USE_POLL + struct pollfd pfd_read; + pfd_read.fd = sock; + pfd_read.events = POLLOUT; + + auto timeout = static_cast(sec * 1000 + usec / 1000); + + return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); }); +#else +#ifndef _WIN32 + if (sock >= FD_SETSIZE) { return 1; } +#endif + + fd_set fds; + FD_ZERO(&fds); + FD_SET(sock, &fds); + + timeval tv; + tv.tv_sec = static_cast(sec); + tv.tv_usec = static_cast(usec); + + return handle_EINTR([&]() { + return select(static_cast(sock + 1), nullptr, &fds, nullptr, &tv); + }); +#endif +} + +inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) { +#ifdef CPPHTTPLIB_USE_POLL + struct pollfd pfd_read; + pfd_read.fd = sock; + pfd_read.events = POLLIN | POLLOUT; + + auto timeout = static_cast(sec * 1000 + usec / 1000); + + auto poll_res = handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); }); + + if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) { + int error = 0; + socklen_t len = sizeof(error); + auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR, + reinterpret_cast(&error), &len); + return res >= 0 && !error; + } + return false; +#else +#ifndef _WIN32 + if (sock >= FD_SETSIZE) { return false; } +#endif + + fd_set fdsr; + FD_ZERO(&fdsr); + FD_SET(sock, &fdsr); + + auto fdsw = fdsr; + auto fdse = fdsr; + + timeval tv; + tv.tv_sec = static_cast(sec); + tv.tv_usec = static_cast(usec); + + auto ret = handle_EINTR([&]() { + return select(static_cast(sock + 1), &fdsr, &fdsw, &fdse, &tv); + }); + + if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) { + int error = 0; + socklen_t len = sizeof(error); + return getsockopt(sock, SOL_SOCKET, SO_ERROR, + reinterpret_cast(&error), &len) >= 0 && + !error; + } + return false; +#endif +} + +class SocketStream : public Stream { +public: + SocketStream(socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec, + time_t write_timeout_sec, time_t write_timeout_usec); + ~SocketStream() override; + + bool is_readable() const override; + bool is_writable() const override; + ssize_t read(char *ptr, size_t size) override; + ssize_t write(const char *ptr, size_t size) override; + void get_remote_ip_and_port(std::string &ip, int &port) const override; + socket_t socket() const override; + +private: + socket_t sock_; + time_t read_timeout_sec_; + time_t read_timeout_usec_; + time_t write_timeout_sec_; + time_t write_timeout_usec_; + + std::vector read_buff_; + size_t read_buff_off_ = 0; + size_t read_buff_content_size_ = 0; + + static const size_t read_buff_size_ = 1024 * 4; +}; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +class SSLSocketStream : public Stream { +public: + SSLSocketStream(socket_t sock, SSL *ssl, time_t read_timeout_sec, + time_t read_timeout_usec, time_t write_timeout_sec, + time_t write_timeout_usec); + ~SSLSocketStream() override; + + bool is_readable() const override; + bool is_writable() const override; + ssize_t read(char *ptr, size_t size) override; + ssize_t write(const char *ptr, size_t size) override; + void get_remote_ip_and_port(std::string &ip, int &port) const override; + socket_t socket() const override; + +private: + socket_t sock_; + SSL *ssl_; + time_t read_timeout_sec_; + time_t read_timeout_usec_; + time_t write_timeout_sec_; + time_t write_timeout_usec_; +}; +#endif + +inline bool keep_alive(socket_t sock, time_t keep_alive_timeout_sec) { + using namespace std::chrono; + auto start = steady_clock::now(); + while (true) { + auto val = select_read(sock, 0, 10000); + if (val < 0) { + return false; + } else if (val == 0) { + auto current = steady_clock::now(); + auto duration = duration_cast(current - start); + auto timeout = keep_alive_timeout_sec * 1000; + if (duration.count() > timeout) { return false; } + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } else { + return true; + } + } +} + +template +inline bool +process_server_socket_core(socket_t sock, size_t keep_alive_max_count, + time_t keep_alive_timeout_sec, T callback) { + assert(keep_alive_max_count > 0); + auto ret = false; + auto count = keep_alive_max_count; + while (count > 0 && keep_alive(sock, keep_alive_timeout_sec)) { + auto close_connection = count == 1; + auto connection_closed = false; + ret = callback(close_connection, connection_closed); + if (!ret || connection_closed) { break; } + count--; + } + return ret; +} + +template +inline bool +process_server_socket(socket_t sock, size_t keep_alive_max_count, + time_t keep_alive_timeout_sec, time_t read_timeout_sec, + time_t read_timeout_usec, time_t write_timeout_sec, + time_t write_timeout_usec, T callback) { + return process_server_socket_core( + sock, keep_alive_max_count, keep_alive_timeout_sec, + [&](bool close_connection, bool &connection_closed) { + SocketStream strm(sock, read_timeout_sec, read_timeout_usec, + write_timeout_sec, write_timeout_usec); + return callback(strm, close_connection, connection_closed); + }); +} + +inline bool process_client_socket(socket_t sock, time_t read_timeout_sec, + time_t read_timeout_usec, + time_t write_timeout_sec, + time_t write_timeout_usec, + std::function callback) { + SocketStream strm(sock, read_timeout_sec, read_timeout_usec, + write_timeout_sec, write_timeout_usec); + return callback(strm); +} + +inline int shutdown_socket(socket_t sock) { +#ifdef _WIN32 + return shutdown(sock, SD_BOTH); +#else + return shutdown(sock, SHUT_RDWR); +#endif +} + +template +socket_t create_socket(const char *host, const char *ip, int port, int address_family, + int socket_flags, bool tcp_nodelay, + SocketOptions socket_options, + BindOrConnect bind_or_connect) { + // Get address info + struct addrinfo hints; + struct addrinfo *result; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = address_family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = socket_flags; + hints.ai_protocol = 0; + + // Ask getaddrinfo to convert IP in c-string to address + if(ip[0] != '\0') { + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_NUMERICHOST; + } + + auto service = std::to_string(port); + + if (ip[0] != '\0' ? + getaddrinfo(ip, service.c_str(), &hints, &result) : + getaddrinfo(host, service.c_str(), &hints, &result)) { +#if defined __linux__ && !defined __ANDROID__ + res_init(); +#endif + return INVALID_SOCKET; + } + + for (auto rp = result; rp; rp = rp->ai_next) { + // Create a socket +#ifdef _WIN32 + auto sock = + WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol, nullptr, 0, + WSA_FLAG_NO_HANDLE_INHERIT | WSA_FLAG_OVERLAPPED); + /** + * Since the WSA_FLAG_NO_HANDLE_INHERIT is only supported on Windows 7 SP1 + * and above the socket creation fails on older Windows Systems. + * + * Let's try to create a socket the old way in this case. + * + * Reference: + * https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketa + * + * WSA_FLAG_NO_HANDLE_INHERIT: + * This flag is supported on Windows 7 with SP1, Windows Server 2008 R2 with + * SP1, and later + * + */ + if (sock == INVALID_SOCKET) { + sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + } +#else + auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); +#endif + if (sock == INVALID_SOCKET) { continue; } + +#ifndef _WIN32 + if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) { continue; } +#endif + + if (tcp_nodelay) { + int yes = 1; + setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&yes), + sizeof(yes)); + } + + if (socket_options) { socket_options(sock); } + + if (rp->ai_family == AF_INET6) { + int no = 0; + setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&no), + sizeof(no)); + } + + // bind or connect + if (bind_or_connect(sock, *rp)) { + freeaddrinfo(result); + return sock; + } + + close_socket(sock); + } + + freeaddrinfo(result); + return INVALID_SOCKET; +} + +inline void set_nonblocking(socket_t sock, bool nonblocking) { +#ifdef _WIN32 + auto flags = nonblocking ? 1UL : 0UL; + ioctlsocket(sock, FIONBIO, &flags); +#else + auto flags = fcntl(sock, F_GETFL, 0); + fcntl(sock, F_SETFL, + nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK))); +#endif +} + +inline bool is_connection_error() { +#ifdef _WIN32 + return WSAGetLastError() != WSAEWOULDBLOCK; +#else + return errno != EINPROGRESS; +#endif +} + +inline bool bind_ip_address(socket_t sock, const char *host) { + struct addrinfo hints; + struct addrinfo *result; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + + if (getaddrinfo(host, "0", &hints, &result)) { return false; } + + auto ret = false; + for (auto rp = result; rp; rp = rp->ai_next) { + const auto &ai = *rp; + if (!::bind(sock, ai.ai_addr, static_cast(ai.ai_addrlen))) { + ret = true; + break; + } + } + + freeaddrinfo(result); + return ret; +} + +#if !defined _WIN32 && !defined ANDROID +#define USE_IF2IP +#endif + +#ifdef USE_IF2IP +inline std::string if2ip(const std::string &ifn) { + struct ifaddrs *ifap; + getifaddrs(&ifap); + for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr && ifn == ifa->ifa_name) { + if (ifa->ifa_addr->sa_family == AF_INET) { + auto sa = reinterpret_cast(ifa->ifa_addr); + char buf[INET_ADDRSTRLEN]; + if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) { + freeifaddrs(ifap); + return std::string(buf, INET_ADDRSTRLEN); + } + } + } + } + freeifaddrs(ifap); + return std::string(); +} +#endif + +inline socket_t create_client_socket( + const char *host, const char *ip, int port, int address_family, bool tcp_nodelay, + SocketOptions socket_options, time_t connection_timeout_sec, + time_t connection_timeout_usec, time_t read_timeout_sec, + time_t read_timeout_usec, time_t write_timeout_sec, + time_t write_timeout_usec, const std::string &intf, Error &error) { + auto sock = create_socket( + host, ip, port, address_family, 0, tcp_nodelay, std::move(socket_options), + [&](socket_t sock2, struct addrinfo &ai) -> bool { + if (!intf.empty()) { +#ifdef USE_IF2IP + auto ip = if2ip(intf); + if (ip.empty()) { ip = intf; } + if (!bind_ip_address(sock2, ip.c_str())) { + error = Error::BindIPAddress; + return false; + } +#endif + } + + set_nonblocking(sock2, true); + + auto ret = + ::connect(sock2, ai.ai_addr, static_cast(ai.ai_addrlen)); + + if (ret < 0) { + if (is_connection_error() || + !wait_until_socket_is_ready(sock2, connection_timeout_sec, + connection_timeout_usec)) { + error = Error::Connection; + return false; + } + } + + set_nonblocking(sock2, false); + + { + timeval tv; + tv.tv_sec = static_cast(read_timeout_sec); + tv.tv_usec = static_cast(read_timeout_usec); + setsockopt(sock2, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)); + } + { + timeval tv; + tv.tv_sec = static_cast(write_timeout_sec); + tv.tv_usec = static_cast(write_timeout_usec); + setsockopt(sock2, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof(tv)); + } + + error = Error::Success; + return true; + }); + + if (sock != INVALID_SOCKET) { + error = Error::Success; + } else { + if (error == Error::Success) { error = Error::Connection; } + } + + return sock; +} + +inline void get_remote_ip_and_port(const struct sockaddr_storage &addr, + socklen_t addr_len, std::string &ip, + int &port) { + if (addr.ss_family == AF_INET) { + port = ntohs(reinterpret_cast(&addr)->sin_port); + } else if (addr.ss_family == AF_INET6) { + port = + ntohs(reinterpret_cast(&addr)->sin6_port); + } + + std::array ipstr{}; + if (!getnameinfo(reinterpret_cast(&addr), addr_len, + ipstr.data(), static_cast(ipstr.size()), nullptr, + 0, NI_NUMERICHOST)) { + ip = ipstr.data(); + } +} + +inline void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port) { + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + + if (!getpeername(sock, reinterpret_cast(&addr), + &addr_len)) { + get_remote_ip_and_port(addr, addr_len, ip, port); + } +} + +inline constexpr unsigned int str2tag_core(const char *s, size_t l, + unsigned int h) { + return (l == 0) ? h + : str2tag_core(s + 1, l - 1, + (h * 33) ^ static_cast(*s)); +} + +inline unsigned int str2tag(const std::string &s) { + return str2tag_core(s.data(), s.size(), 0); +} + +namespace udl { + +inline constexpr unsigned int operator"" _t(const char *s, size_t l) { + return str2tag_core(s, l, 0); +} + +} // namespace udl + +inline const char * +find_content_type(const std::string &path, + const std::map &user_data) { + auto ext = file_extension(path); + + auto it = user_data.find(ext); + if (it != user_data.end()) { return it->second.c_str(); } + + using udl::operator""_t; + + switch (str2tag(ext)) { + default: return nullptr; + case "css"_t: return "text/css"; + case "csv"_t: return "text/csv"; + case "txt"_t: return "text/plain"; + case "vtt"_t: return "text/vtt"; + case "htm"_t: + case "html"_t: return "text/html"; + + case "apng"_t: return "image/apng"; + case "avif"_t: return "image/avif"; + case "bmp"_t: return "image/bmp"; + case "gif"_t: return "image/gif"; + case "png"_t: return "image/png"; + case "svg"_t: return "image/svg+xml"; + case "webp"_t: return "image/webp"; + case "ico"_t: return "image/x-icon"; + case "tif"_t: return "image/tiff"; + case "tiff"_t: return "image/tiff"; + case "jpg"_t: + case "jpeg"_t: return "image/jpeg"; + + case "mp4"_t: return "video/mp4"; + case "mpeg"_t: return "video/mpeg"; + case "webm"_t: return "video/webm"; + + case "mp3"_t: return "audio/mp3"; + case "mpga"_t: return "audio/mpeg"; + case "weba"_t: return "audio/webm"; + case "wav"_t: return "audio/wave"; + + case "otf"_t: return "font/otf"; + case "ttf"_t: return "font/ttf"; + case "woff"_t: return "font/woff"; + case "woff2"_t: return "font/woff2"; + + case "7z"_t: return "application/x-7z-compressed"; + case "atom"_t: return "application/atom+xml"; + case "pdf"_t: return "application/pdf"; + case "js"_t: + case "mjs"_t: return "application/javascript"; + case "json"_t: return "application/json"; + case "rss"_t: return "application/rss+xml"; + case "tar"_t: return "application/x-tar"; + case "xht"_t: + case "xhtml"_t: return "application/xhtml+xml"; + case "xslt"_t: return "application/xslt+xml"; + case "xml"_t: return "application/xml"; + case "gz"_t: return "application/gzip"; + case "zip"_t: return "application/zip"; + case "wasm"_t: return "application/wasm"; + } +} + +inline const char *status_message(int status) { + switch (status) { + case 100: return "Continue"; + case 101: return "Switching Protocol"; + case 102: return "Processing"; + case 103: return "Early Hints"; + case 200: return "OK"; + case 201: return "Created"; + case 202: return "Accepted"; + case 203: return "Non-Authoritative Information"; + case 204: return "No Content"; + case 205: return "Reset Content"; + case 206: return "Partial Content"; + case 207: return "Multi-Status"; + case 208: return "Already Reported"; + case 226: return "IM Used"; + case 300: return "Multiple Choice"; + case 301: return "Moved Permanently"; + case 302: return "Found"; + case 303: return "See Other"; + case 304: return "Not Modified"; + case 305: return "Use Proxy"; + case 306: return "unused"; + case 307: return "Temporary Redirect"; + case 308: return "Permanent Redirect"; + case 400: return "Bad Request"; + case 401: return "Unauthorized"; + case 402: return "Payment Required"; + case 403: return "Forbidden"; + case 404: return "Not Found"; + case 405: return "Method Not Allowed"; + case 406: return "Not Acceptable"; + case 407: return "Proxy Authentication Required"; + case 408: return "Request Timeout"; + case 409: return "Conflict"; + case 410: return "Gone"; + case 411: return "Length Required"; + case 412: return "Precondition Failed"; + case 413: return "Payload Too Large"; + case 414: return "URI Too Long"; + case 415: return "Unsupported Media Type"; + case 416: return "Range Not Satisfiable"; + case 417: return "Expectation Failed"; + case 418: return "I'm a teapot"; + case 421: return "Misdirected Request"; + case 422: return "Unprocessable Entity"; + case 423: return "Locked"; + case 424: return "Failed Dependency"; + case 425: return "Too Early"; + case 426: return "Upgrade Required"; + case 428: return "Precondition Required"; + case 429: return "Too Many Requests"; + case 431: return "Request Header Fields Too Large"; + case 451: return "Unavailable For Legal Reasons"; + case 501: return "Not Implemented"; + case 502: return "Bad Gateway"; + case 503: return "Service Unavailable"; + case 504: return "Gateway Timeout"; + case 505: return "HTTP Version Not Supported"; + case 506: return "Variant Also Negotiates"; + case 507: return "Insufficient Storage"; + case 508: return "Loop Detected"; + case 510: return "Not Extended"; + case 511: return "Network Authentication Required"; + + default: + case 500: return "Internal Server Error"; + } +} + +inline bool can_compress_content_type(const std::string &content_type) { + return (!content_type.find("text/") && content_type != "text/event-stream") || + content_type == "image/svg+xml" || + content_type == "application/javascript" || + content_type == "application/json" || + content_type == "application/xml" || + content_type == "application/xhtml+xml"; +} + +inline EncodingType encoding_type(const Request &req, const Response &res) { + auto ret = + detail::can_compress_content_type(res.get_header_value("Content-Type")); + if (!ret) { return EncodingType::None; } + + const auto &s = req.get_header_value("Accept-Encoding"); + (void)(s); + +#ifdef CPPHTTPLIB_BROTLI_SUPPORT + // TODO: 'Accept-Encoding' has br, not br;q=0 + ret = s.find("br") != std::string::npos; + if (ret) { return EncodingType::Brotli; } +#endif + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + // TODO: 'Accept-Encoding' has gzip, not gzip;q=0 + ret = s.find("gzip") != std::string::npos; + if (ret) { return EncodingType::Gzip; } +#endif + + return EncodingType::None; +} + +inline bool nocompressor::compress(const char *data, size_t data_length, + bool /*last*/, Callback callback) { + if (!data_length) { return true; } + return callback(data, data_length); +} + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT +inline gzip_compressor::gzip_compressor() { + std::memset(&strm_, 0, sizeof(strm_)); + strm_.zalloc = Z_NULL; + strm_.zfree = Z_NULL; + strm_.opaque = Z_NULL; + + is_valid_ = deflateInit2(&strm_, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, + Z_DEFAULT_STRATEGY) == Z_OK; +} + +inline gzip_compressor::~gzip_compressor() { deflateEnd(&strm_); } + +inline bool gzip_compressor::compress(const char *data, size_t data_length, + bool last, Callback callback) { + assert(is_valid_); + + do { + constexpr size_t max_avail_in = + (std::numeric_limits::max)(); + + strm_.avail_in = static_cast( + (std::min)(data_length, max_avail_in)); + strm_.next_in = const_cast(reinterpret_cast(data)); + + data_length -= strm_.avail_in; + data += strm_.avail_in; + + auto flush = (last && data_length == 0) ? Z_FINISH : Z_NO_FLUSH; + int ret = Z_OK; + + std::array buff{}; + do { + strm_.avail_out = static_cast(buff.size()); + strm_.next_out = reinterpret_cast(buff.data()); + + ret = deflate(&strm_, flush); + if (ret == Z_STREAM_ERROR) { return false; } + + if (!callback(buff.data(), buff.size() - strm_.avail_out)) { + return false; + } + } while (strm_.avail_out == 0); + + assert((flush == Z_FINISH && ret == Z_STREAM_END) || + (flush == Z_NO_FLUSH && ret == Z_OK)); + assert(strm_.avail_in == 0); + + } while (data_length > 0); + + return true; +} + +inline gzip_decompressor::gzip_decompressor() { + std::memset(&strm_, 0, sizeof(strm_)); + strm_.zalloc = Z_NULL; + strm_.zfree = Z_NULL; + strm_.opaque = Z_NULL; + + // 15 is the value of wbits, which should be at the maximum possible value + // to ensure that any gzip stream can be decoded. The offset of 32 specifies + // that the stream type should be automatically detected either gzip or + // deflate. + is_valid_ = inflateInit2(&strm_, 32 + 15) == Z_OK; +} + +inline gzip_decompressor::~gzip_decompressor() { inflateEnd(&strm_); } + +inline bool gzip_decompressor::is_valid() const { return is_valid_; } + +inline bool gzip_decompressor::decompress(const char *data, size_t data_length, + Callback callback) { + assert(is_valid_); + + int ret = Z_OK; + + do { + constexpr size_t max_avail_in = + (std::numeric_limits::max)(); + + strm_.avail_in = static_cast( + (std::min)(data_length, max_avail_in)); + strm_.next_in = const_cast(reinterpret_cast(data)); + + data_length -= strm_.avail_in; + data += strm_.avail_in; + + std::array buff{}; + while (strm_.avail_in > 0) { + strm_.avail_out = static_cast(buff.size()); + strm_.next_out = reinterpret_cast(buff.data()); + + auto prev_avail_in = strm_.avail_in; + + ret = inflate(&strm_, Z_NO_FLUSH); + + if (prev_avail_in - strm_.avail_in == 0) { + return false; + } + + assert(ret != Z_STREAM_ERROR); + switch (ret) { + case Z_NEED_DICT: + case Z_DATA_ERROR: + case Z_MEM_ERROR: inflateEnd(&strm_); return false; + } + + if (!callback(buff.data(), buff.size() - strm_.avail_out)) { + return false; + } + } + + if (ret != Z_OK && ret != Z_STREAM_END) return false; + + } while (data_length > 0); + + return true; +} +#endif + +#ifdef CPPHTTPLIB_BROTLI_SUPPORT +inline brotli_compressor::brotli_compressor() { + state_ = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr); +} + +inline brotli_compressor::~brotli_compressor() { + BrotliEncoderDestroyInstance(state_); +} + +inline bool brotli_compressor::compress(const char *data, size_t data_length, + bool last, Callback callback) { + std::array buff{}; + + auto operation = last ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS; + auto available_in = data_length; + auto next_in = reinterpret_cast(data); + + for (;;) { + if (last) { + if (BrotliEncoderIsFinished(state_)) { break; } + } else { + if (!available_in) { break; } + } + + auto available_out = buff.size(); + auto next_out = buff.data(); + + if (!BrotliEncoderCompressStream(state_, operation, &available_in, &next_in, + &available_out, &next_out, nullptr)) { + return false; + } + + auto output_bytes = buff.size() - available_out; + if (output_bytes) { + callback(reinterpret_cast(buff.data()), output_bytes); + } + } + + return true; +} + +inline brotli_decompressor::brotli_decompressor() { + decoder_s = BrotliDecoderCreateInstance(0, 0, 0); + decoder_r = decoder_s ? BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT + : BROTLI_DECODER_RESULT_ERROR; +} + +inline brotli_decompressor::~brotli_decompressor() { + if (decoder_s) { BrotliDecoderDestroyInstance(decoder_s); } +} + +inline bool brotli_decompressor::is_valid() const { return decoder_s; } + +inline bool brotli_decompressor::decompress(const char *data, + size_t data_length, + Callback callback) { + if (decoder_r == BROTLI_DECODER_RESULT_SUCCESS || + decoder_r == BROTLI_DECODER_RESULT_ERROR) { + return 0; + } + + const uint8_t *next_in = (const uint8_t *)data; + size_t avail_in = data_length; + size_t total_out; + + decoder_r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT; + + std::array buff{}; + while (decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) { + char *next_out = buff.data(); + size_t avail_out = buff.size(); + + decoder_r = BrotliDecoderDecompressStream( + decoder_s, &avail_in, &next_in, &avail_out, + reinterpret_cast(&next_out), &total_out); + + if (decoder_r == BROTLI_DECODER_RESULT_ERROR) { return false; } + + if (!callback(buff.data(), buff.size() - avail_out)) { return false; } + } + + return decoder_r == BROTLI_DECODER_RESULT_SUCCESS || + decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT; +} +#endif + +inline bool has_header(const Headers &headers, const char *key) { + return headers.find(key) != headers.end(); +} + +inline const char *get_header_value(const Headers &headers, const char *key, + size_t id, const char *def) { + auto rng = headers.equal_range(key); + auto it = rng.first; + std::advance(it, static_cast(id)); + if (it != rng.second) { return it->second.c_str(); } + return def; +} + +template +inline bool parse_header(const char *beg, const char *end, T fn) { + // Skip trailing spaces and tabs. + while (beg < end && is_space_or_tab(end[-1])) { + end--; + } + + auto p = beg; + while (p < end && *p != ':') { + p++; + } + + if (p == end) { return false; } + + auto key_end = p; + + if (*p++ != ':') { return false; } + + while (p < end && is_space_or_tab(*p)) { + p++; + } + + if (p < end) { + fn(std::string(beg, key_end), decode_url(std::string(p, end), false)); + return true; + } + + return false; +} + +inline bool read_headers(Stream &strm, Headers &headers) { + const auto bufsiz = 2048; + char buf[bufsiz]; + stream_line_reader line_reader(strm, buf, bufsiz); + + for (;;) { + if (!line_reader.getline()) { return false; } + + // Check if the line ends with CRLF. + if (line_reader.end_with_crlf()) { + // Blank line indicates end of headers. + if (line_reader.size() == 2) { break; } + } else { + continue; // Skip invalid line. + } + + // Exclude CRLF + auto end = line_reader.ptr() + line_reader.size() - 2; + + parse_header(line_reader.ptr(), end, + [&](std::string &&key, std::string &&val) { + headers.emplace(std::move(key), std::move(val)); + }); + } + + return true; +} + +inline bool read_content_with_length(Stream &strm, uint64_t len, + Progress progress, + ContentReceiverWithProgress out) { + char buf[CPPHTTPLIB_RECV_BUFSIZ]; + + uint64_t r = 0; + while (r < len) { + auto read_len = static_cast(len - r); + auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ)); + if (n <= 0) { return false; } + + if (!out(buf, static_cast(n), r, len)) { return false; } + r += static_cast(n); + + if (progress) { + if (!progress(r, len)) { return false; } + } + } + + return true; +} + +inline void skip_content_with_length(Stream &strm, uint64_t len) { + char buf[CPPHTTPLIB_RECV_BUFSIZ]; + uint64_t r = 0; + while (r < len) { + auto read_len = static_cast(len - r); + auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ)); + if (n <= 0) { return; } + r += static_cast(n); + } +} + +inline bool read_content_without_length(Stream &strm, + ContentReceiverWithProgress out) { + char buf[CPPHTTPLIB_RECV_BUFSIZ]; + uint64_t r = 0; + for (;;) { + auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ); + if (n < 0) { + return false; + } else if (n == 0) { + return true; + } + + if (!out(buf, static_cast(n), r, 0)) { return false; } + r += static_cast(n); + } + + return true; +} + +inline bool read_content_chunked(Stream &strm, + ContentReceiverWithProgress out) { + const auto bufsiz = 16; + char buf[bufsiz]; + + stream_line_reader line_reader(strm, buf, bufsiz); + + if (!line_reader.getline()) { return false; } + + unsigned long chunk_len; + while (true) { + char *end_ptr; + + chunk_len = std::strtoul(line_reader.ptr(), &end_ptr, 16); + + if (end_ptr == line_reader.ptr()) { return false; } + if (chunk_len == ULONG_MAX) { return false; } + + if (chunk_len == 0) { break; } + + if (!read_content_with_length(strm, chunk_len, nullptr, out)) { + return false; + } + + if (!line_reader.getline()) { return false; } + + if (strcmp(line_reader.ptr(), "\r\n")) { break; } + + if (!line_reader.getline()) { return false; } + } + + if (chunk_len == 0) { + // Reader terminator after chunks + if (!line_reader.getline() || strcmp(line_reader.ptr(), "\r\n")) + return false; + } + + return true; +} + +inline bool is_chunked_transfer_encoding(const Headers &headers) { + return !strcasecmp(get_header_value(headers, "Transfer-Encoding", 0, ""), + "chunked"); +} + +template +bool prepare_content_receiver(T &x, int &status, + ContentReceiverWithProgress receiver, + bool decompress, U callback) { + if (decompress) { + std::string encoding = x.get_header_value("Content-Encoding"); + std::unique_ptr decompressor; + + if (encoding == "gzip" || encoding == "deflate") { +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + decompressor = detail::make_unique(); +#else + status = 415; + return false; +#endif + } else if (encoding.find("br") != std::string::npos) { +#ifdef CPPHTTPLIB_BROTLI_SUPPORT + decompressor = detail::make_unique(); +#else + status = 415; + return false; +#endif + } + + if (decompressor) { + if (decompressor->is_valid()) { + ContentReceiverWithProgress out = [&](const char *buf, size_t n, + uint64_t off, uint64_t len) { + return decompressor->decompress(buf, n, + [&](const char *buf2, size_t n2) { + return receiver(buf2, n2, off, len); + }); + }; + return callback(std::move(out)); + } else { + status = 500; + return false; + } + } + } + + ContentReceiverWithProgress out = [&](const char *buf, size_t n, uint64_t off, + uint64_t len) { + return receiver(buf, n, off, len); + }; + return callback(std::move(out)); +} + +template +bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status, + Progress progress, ContentReceiverWithProgress receiver, + bool decompress) { + return prepare_content_receiver( + x, status, std::move(receiver), decompress, + [&](const ContentReceiverWithProgress &out) { + auto ret = true; + auto exceed_payload_max_length = false; + + if (is_chunked_transfer_encoding(x.headers)) { + ret = read_content_chunked(strm, out); + } else if (!has_header(x.headers, "Content-Length")) { + ret = read_content_without_length(strm, out); + } else { + auto len = get_header_value(x.headers, "Content-Length"); + if (len > payload_max_length) { + exceed_payload_max_length = true; + skip_content_with_length(strm, len); + ret = false; + } else if (len > 0) { + ret = read_content_with_length(strm, len, std::move(progress), out); + } + } + + if (!ret) { status = exceed_payload_max_length ? 413 : 400; } + return ret; + }); +} + +inline ssize_t write_headers(Stream &strm, const Headers &headers) { + ssize_t write_len = 0; + for (const auto &x : headers) { + auto len = + strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str()); + if (len < 0) { return len; } + write_len += len; + } + auto len = strm.write("\r\n"); + if (len < 0) { return len; } + write_len += len; + return write_len; +} + +inline bool write_data(Stream &strm, const char *d, size_t l) { + size_t offset = 0; + while (offset < l) { + auto length = strm.write(d + offset, l - offset); + if (length < 0) { return false; } + offset += static_cast(length); + } + return true; +} + +template +inline bool write_content(Stream &strm, const ContentProvider &content_provider, + size_t offset, size_t length, T is_shutting_down, + Error &error) { + size_t end_offset = offset + length; + auto ok = true; + DataSink data_sink; + + data_sink.write = [&](const char *d, size_t l) -> bool { + if (ok) { + if (write_data(strm, d, l)) { + offset += l; + } else { + ok = false; + } + } + return ok; + }; + + data_sink.is_writable = [&](void) { return ok && strm.is_writable(); }; + + while (offset < end_offset && !is_shutting_down()) { + if (!content_provider(offset, end_offset - offset, data_sink)) { + error = Error::Canceled; + return false; + } + if (!ok) { + error = Error::Write; + return false; + } + } + + error = Error::Success; + return true; +} + +template +inline bool write_content(Stream &strm, const ContentProvider &content_provider, + size_t offset, size_t length, + const T &is_shutting_down) { + auto error = Error::Success; + return write_content(strm, content_provider, offset, length, is_shutting_down, + error); +} + +template +inline bool +write_content_without_length(Stream &strm, + const ContentProvider &content_provider, + const T &is_shutting_down) { + size_t offset = 0; + auto data_available = true; + auto ok = true; + DataSink data_sink; + + data_sink.write = [&](const char *d, size_t l) -> bool { + if (ok) { + offset += l; + if (!write_data(strm, d, l)) { ok = false; } + } + return ok; + }; + + data_sink.done = [&](void) { data_available = false; }; + + data_sink.is_writable = [&](void) { return ok && strm.is_writable(); }; + + while (data_available && !is_shutting_down()) { + if (!content_provider(offset, 0, data_sink)) { return false; } + if (!ok) { return false; } + } + return true; +} + +template +inline bool +write_content_chunked(Stream &strm, const ContentProvider &content_provider, + const T &is_shutting_down, U &compressor, Error &error) { + size_t offset = 0; + auto data_available = true; + auto ok = true; + DataSink data_sink; + + data_sink.write = [&](const char *d, size_t l) -> bool { + if (ok) { + data_available = l > 0; + offset += l; + + std::string payload; + if (compressor.compress(d, l, false, + [&](const char *data, size_t data_len) { + payload.append(data, data_len); + return true; + })) { + if (!payload.empty()) { + // Emit chunked response header and footer for each chunk + auto chunk = + from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n"; + if (!write_data(strm, chunk.data(), chunk.size())) { ok = false; } + } + } else { + ok = false; + } + } + return ok; + }; + + data_sink.done = [&](void) { + if (!ok) { return; } + + data_available = false; + + std::string payload; + if (!compressor.compress(nullptr, 0, true, + [&](const char *data, size_t data_len) { + payload.append(data, data_len); + return true; + })) { + ok = false; + return; + } + + if (!payload.empty()) { + // Emit chunked response header and footer for each chunk + auto chunk = from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n"; + if (!write_data(strm, chunk.data(), chunk.size())) { + ok = false; + return; + } + } + + static const std::string done_marker("0\r\n\r\n"); + if (!write_data(strm, done_marker.data(), done_marker.size())) { + ok = false; + } + }; + + data_sink.is_writable = [&](void) { return ok && strm.is_writable(); }; + + while (data_available && !is_shutting_down()) { + if (!content_provider(offset, 0, data_sink)) { + error = Error::Canceled; + return false; + } + if (!ok) { + error = Error::Write; + return false; + } + } + + error = Error::Success; + return true; +} + +template +inline bool write_content_chunked(Stream &strm, + const ContentProvider &content_provider, + const T &is_shutting_down, U &compressor) { + auto error = Error::Success; + return write_content_chunked(strm, content_provider, is_shutting_down, + compressor, error); +} + +template +inline bool redirect(T &cli, Request &req, Response &res, + const std::string &path, const std::string &location, + Error &error) { + Request new_req = req; + new_req.path = path; + new_req.redirect_count_ -= 1; + + if (res.status == 303 && (req.method != "GET" && req.method != "HEAD")) { + new_req.method = "GET"; + new_req.body.clear(); + new_req.headers.clear(); + } + + Response new_res; + + auto ret = cli.send(new_req, new_res, error); + if (ret) { + req = new_req; + res = new_res; + res.location = location; + } + return ret; +} + +inline std::string params_to_query_str(const Params ¶ms) { + std::string query; + + for (auto it = params.begin(); it != params.end(); ++it) { + if (it != params.begin()) { query += "&"; } + query += it->first; + query += "="; + query += encode_query_param(it->second); + } + return query; +} + +inline void parse_query_text(const std::string &s, Params ¶ms) { + std::set cache; + split(s.data(), s.data() + s.size(), '&', [&](const char *b, const char *e) { + std::string kv(b, e); + if (cache.find(kv) != cache.end()) { return; } + cache.insert(kv); + + std::string key; + std::string val; + split(b, e, '=', [&](const char *b2, const char *e2) { + if (key.empty()) { + key.assign(b2, e2); + } else { + val.assign(b2, e2); + } + }); + + if (!key.empty()) { + params.emplace(decode_url(key, true), decode_url(val, true)); + } + }); +} + +inline bool parse_multipart_boundary(const std::string &content_type, + std::string &boundary) { + auto pos = content_type.find("boundary="); + if (pos == std::string::npos) { return false; } + boundary = content_type.substr(pos + 9); + if (boundary.length() >= 2 && boundary.front() == '"' && + boundary.back() == '"') { + boundary = boundary.substr(1, boundary.size() - 2); + } + return !boundary.empty(); +} + +inline bool parse_range_header(const std::string &s, Ranges &ranges) try { + static auto re_first_range = std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))"); + std::smatch m; + if (std::regex_match(s, m, re_first_range)) { + auto pos = static_cast(m.position(1)); + auto len = static_cast(m.length(1)); + bool all_valid_ranges = true; + split(&s[pos], &s[pos + len], ',', [&](const char *b, const char *e) { + if (!all_valid_ranges) return; + static auto re_another_range = std::regex(R"(\s*(\d*)-(\d*))"); + std::cmatch cm; + if (std::regex_match(b, e, cm, re_another_range)) { + ssize_t first = -1; + if (!cm.str(1).empty()) { + first = static_cast(std::stoll(cm.str(1))); + } + + ssize_t last = -1; + if (!cm.str(2).empty()) { + last = static_cast(std::stoll(cm.str(2))); + } + + if (first != -1 && last != -1 && first > last) { + all_valid_ranges = false; + return; + } + ranges.emplace_back(std::make_pair(first, last)); + } + }); + return all_valid_ranges; + } + return false; +} catch (...) { return false; } + +class MultipartFormDataParser { +public: + MultipartFormDataParser() = default; + + void set_boundary(std::string &&boundary) { boundary_ = boundary; } + + bool is_valid() const { return is_valid_; } + + bool parse(const char *buf, size_t n, const ContentReceiver &content_callback, + const MultipartContentHeader &header_callback) { + + static const std::regex re_content_disposition( + "^Content-Disposition:\\s*form-data;\\s*name=\"(.*?)\"(?:;\\s*filename=" + "\"(.*?)\")?\\s*$", + std::regex_constants::icase); + static const std::string dash_ = "--"; + static const std::string crlf_ = "\r\n"; + + buf_.append(buf, n); // TODO: performance improvement + + while (!buf_.empty()) { + switch (state_) { + case 0: { // Initial boundary + auto pattern = dash_ + boundary_ + crlf_; + if (pattern.size() > buf_.size()) { return true; } + auto pos = buf_.find(pattern); + if (pos != 0) { return false; } + buf_.erase(0, pattern.size()); + off_ += pattern.size(); + state_ = 1; + break; + } + case 1: { // New entry + clear_file_info(); + state_ = 2; + break; + } + case 2: { // Headers + auto pos = buf_.find(crlf_); + while (pos != std::string::npos) { + // Empty line + if (pos == 0) { + if (!header_callback(file_)) { + is_valid_ = false; + return false; + } + buf_.erase(0, crlf_.size()); + off_ += crlf_.size(); + state_ = 3; + break; + } + + static const std::string header_name = "content-type:"; + const auto header = buf_.substr(0, pos); + if (start_with_case_ignore(header, header_name)) { + file_.content_type = trim_copy(header.substr(header_name.size())); + } else { + std::smatch m; + if (std::regex_match(header, m, re_content_disposition)) { + file_.name = m[1]; + file_.filename = m[2]; + } + } + + buf_.erase(0, pos + crlf_.size()); + off_ += pos + crlf_.size(); + pos = buf_.find(crlf_); + } + if (state_ != 3) { return true; } + break; + } + case 3: { // Body + { + auto pattern = crlf_ + dash_; + if (pattern.size() > buf_.size()) { return true; } + + auto pos = find_string(buf_, pattern); + + if (!content_callback(buf_.data(), pos)) { + is_valid_ = false; + return false; + } + + off_ += pos; + buf_.erase(0, pos); + } + { + auto pattern = crlf_ + dash_ + boundary_; + if (pattern.size() > buf_.size()) { return true; } + + auto pos = buf_.find(pattern); + if (pos != std::string::npos) { + if (!content_callback(buf_.data(), pos)) { + is_valid_ = false; + return false; + } + + off_ += pos + pattern.size(); + buf_.erase(0, pos + pattern.size()); + state_ = 4; + } else { + if (!content_callback(buf_.data(), pattern.size())) { + is_valid_ = false; + return false; + } + + off_ += pattern.size(); + buf_.erase(0, pattern.size()); + } + } + break; + } + case 4: { // Boundary + if (crlf_.size() > buf_.size()) { return true; } + if (buf_.compare(0, crlf_.size(), crlf_) == 0) { + buf_.erase(0, crlf_.size()); + off_ += crlf_.size(); + state_ = 1; + } else { + auto pattern = dash_ + crlf_; + if (pattern.size() > buf_.size()) { return true; } + if (buf_.compare(0, pattern.size(), pattern) == 0) { + buf_.erase(0, pattern.size()); + off_ += pattern.size(); + is_valid_ = true; + state_ = 5; + } else { + return true; + } + } + break; + } + case 5: { // Done + is_valid_ = false; + return false; + } + } + } + + return true; + } + +private: + void clear_file_info() { + file_.name.clear(); + file_.filename.clear(); + file_.content_type.clear(); + } + + bool start_with_case_ignore(const std::string &a, + const std::string &b) const { + if (a.size() < b.size()) { return false; } + for (size_t i = 0; i < b.size(); i++) { + if (::tolower(a[i]) != ::tolower(b[i])) { return false; } + } + return true; + } + + bool start_with(const std::string &a, size_t off, + const std::string &b) const { + if (a.size() - off < b.size()) { return false; } + for (size_t i = 0; i < b.size(); i++) { + if (a[i + off] != b[i]) { return false; } + } + return true; + } + + size_t find_string(const std::string &s, const std::string &pattern) const { + auto c = pattern.front(); + + size_t off = 0; + while (off < s.size()) { + auto pos = s.find(c, off); + if (pos == std::string::npos) { return s.size(); } + + auto rem = s.size() - pos; + if (pattern.size() > rem) { return pos; } + + if (start_with(s, pos, pattern)) { return pos; } + + off = pos + 1; + } + + return s.size(); + } + + std::string boundary_; + + std::string buf_; + size_t state_ = 0; + bool is_valid_ = false; + size_t off_ = 0; + MultipartFormData file_; +}; + +inline std::string to_lower(const char *beg, const char *end) { + std::string out; + auto it = beg; + while (it != end) { + out += static_cast(::tolower(*it)); + it++; + } + return out; +} + +inline std::string make_multipart_data_boundary() { + static const char data[] = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + + // std::random_device might actually be deterministic on some + // platforms, but due to lack of support in the c++ standard library, + // doing better requires either some ugly hacks or breaking portability. + std::random_device seed_gen; + // Request 128 bits of entropy for initialization + std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(), seed_gen()}; + std::mt19937 engine(seed_sequence); + + std::string result = "--cpp-httplib-multipart-data-"; + + for (auto i = 0; i < 16; i++) { + result += data[engine() % (sizeof(data) - 1)]; + } + + return result; +} + +inline std::pair +get_range_offset_and_length(const Request &req, size_t content_length, + size_t index) { + auto r = req.ranges[index]; + + if (r.first == -1 && r.second == -1) { + return std::make_pair(0, content_length); + } + + auto slen = static_cast(content_length); + + if (r.first == -1) { + r.first = (std::max)(static_cast(0), slen - r.second); + r.second = slen - 1; + } + + if (r.second == -1) { r.second = slen - 1; } + return std::make_pair(r.first, static_cast(r.second - r.first) + 1); +} + +inline std::string make_content_range_header_field(size_t offset, size_t length, + size_t content_length) { + std::string field = "bytes "; + field += std::to_string(offset); + field += "-"; + field += std::to_string(offset + length - 1); + field += "/"; + field += std::to_string(content_length); + return field; +} + +template +bool process_multipart_ranges_data(const Request &req, Response &res, + const std::string &boundary, + const std::string &content_type, + SToken stoken, CToken ctoken, + Content content) { + for (size_t i = 0; i < req.ranges.size(); i++) { + ctoken("--"); + stoken(boundary); + ctoken("\r\n"); + if (!content_type.empty()) { + ctoken("Content-Type: "); + stoken(content_type); + ctoken("\r\n"); + } + + auto offsets = get_range_offset_and_length(req, res.body.size(), i); + auto offset = offsets.first; + auto length = offsets.second; + + ctoken("Content-Range: "); + stoken(make_content_range_header_field(offset, length, res.body.size())); + ctoken("\r\n"); + ctoken("\r\n"); + if (!content(offset, length)) { return false; } + ctoken("\r\n"); + } + + ctoken("--"); + stoken(boundary); + ctoken("--\r\n"); + + return true; +} + +inline bool make_multipart_ranges_data(const Request &req, Response &res, + const std::string &boundary, + const std::string &content_type, + std::string &data) { + return process_multipart_ranges_data( + req, res, boundary, content_type, + [&](const std::string &token) { data += token; }, + [&](const char *token) { data += token; }, + [&](size_t offset, size_t length) { + if (offset < res.body.size()) { + data += res.body.substr(offset, length); + return true; + } + return false; + }); +} + +inline size_t +get_multipart_ranges_data_length(const Request &req, Response &res, + const std::string &boundary, + const std::string &content_type) { + size_t data_length = 0; + + process_multipart_ranges_data( + req, res, boundary, content_type, + [&](const std::string &token) { data_length += token.size(); }, + [&](const char *token) { data_length += strlen(token); }, + [&](size_t /*offset*/, size_t length) { + data_length += length; + return true; + }); + + return data_length; +} + +template +inline bool write_multipart_ranges_data(Stream &strm, const Request &req, + Response &res, + const std::string &boundary, + const std::string &content_type, + const T &is_shutting_down) { + return process_multipart_ranges_data( + req, res, boundary, content_type, + [&](const std::string &token) { strm.write(token); }, + [&](const char *token) { strm.write(token); }, + [&](size_t offset, size_t length) { + return write_content(strm, res.content_provider_, offset, length, + is_shutting_down); + }); +} + +inline std::pair +get_range_offset_and_length(const Request &req, const Response &res, + size_t index) { + auto r = req.ranges[index]; + + if (r.second == -1) { + r.second = static_cast(res.content_length_) - 1; + } + + return std::make_pair(r.first, r.second - r.first + 1); +} + +inline bool expect_content(const Request &req) { + if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" || + req.method == "PRI" || req.method == "DELETE") { + return true; + } + // TODO: check if Content-Length is set + return false; +} + +inline bool has_crlf(const char *s) { + auto p = s; + while (*p) { + if (*p == '\r' || *p == '\n') { return true; } + p++; + } + return false; +} + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +template +inline std::string message_digest(const std::string &s, Init init, + Update update, Final final, + size_t digest_length) { + std::vector md(digest_length, 0); + CTX ctx; + init(&ctx); + update(&ctx, s.data(), s.size()); + final(md.data(), &ctx); + + std::stringstream ss; + for (auto c : md) { + ss << std::setfill('0') << std::setw(2) << std::hex << (unsigned int)c; + } + return ss.str(); +} + +inline std::string MD5(const std::string &s) { + return message_digest(s, MD5_Init, MD5_Update, MD5_Final, + MD5_DIGEST_LENGTH); +} + +inline std::string SHA_256(const std::string &s) { + return message_digest(s, SHA256_Init, SHA256_Update, SHA256_Final, + SHA256_DIGEST_LENGTH); +} + +inline std::string SHA_512(const std::string &s) { + return message_digest(s, SHA512_Init, SHA512_Update, SHA512_Final, + SHA512_DIGEST_LENGTH); +} +#endif + +#ifdef _WIN32 +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +// NOTE: This code came up with the following stackoverflow post: +// https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store +inline bool load_system_certs_on_windows(X509_STORE *store) { + auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L"ROOT"); + + if (!hStore) { return false; } + + PCCERT_CONTEXT pContext = NULL; + while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) != + nullptr) { + auto encoded_cert = + static_cast(pContext->pbCertEncoded); + + auto x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded); + if (x509) { + X509_STORE_add_cert(store, x509); + X509_free(x509); + } + } + + CertFreeCertificateContext(pContext); + CertCloseStore(hStore, 0); + + return true; +} +#endif + +class WSInit { +public: + WSInit() { + WSADATA wsaData; + WSAStartup(0x0002, &wsaData); + } + + ~WSInit() { WSACleanup(); } +}; + +static WSInit wsinit_; +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +inline std::pair make_digest_authentication_header( + const Request &req, const std::map &auth, + size_t cnonce_count, const std::string &cnonce, const std::string &username, + const std::string &password, bool is_proxy = false) { + std::string nc; + { + std::stringstream ss; + ss << std::setfill('0') << std::setw(8) << std::hex << cnonce_count; + nc = ss.str(); + } + + std::string qop; + if (auth.find("qop") != auth.end()) { + qop = auth.at("qop"); + if (qop.find("auth-int") != std::string::npos) { + qop = "auth-int"; + } else if (qop.find("auth") != std::string::npos) { + qop = "auth"; + } else { + qop.clear(); + } + } + + std::string algo = "MD5"; + if (auth.find("algorithm") != auth.end()) { algo = auth.at("algorithm"); } + + std::string response; + { + auto H = algo == "SHA-256" ? detail::SHA_256 + : algo == "SHA-512" ? detail::SHA_512 + : detail::MD5; + + auto A1 = username + ":" + auth.at("realm") + ":" + password; + + auto A2 = req.method + ":" + req.path; + if (qop == "auth-int") { A2 += ":" + H(req.body); } + + if (qop.empty()) { + response = H(H(A1) + ":" + auth.at("nonce") + ":" + H(A2)); + } else { + response = H(H(A1) + ":" + auth.at("nonce") + ":" + nc + ":" + cnonce + + ":" + qop + ":" + H(A2)); + } + } + + auto field = + "Digest username=\"" + username + "\", realm=\"" + auth.at("realm") + + "\", nonce=\"" + auth.at("nonce") + "\", uri=\"" + req.path + + "\", algorithm=" + algo + + (qop.empty() ? ", response=\"" + : ", qop=" + qop + ", nc=\"" + nc + "\", cnonce=\"" + + cnonce + "\", response=\"") + + response + "\""; + + auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; + return std::make_pair(key, field); +} +#endif + +inline bool parse_www_authenticate(const Response &res, + std::map &auth, + bool is_proxy) { + auto auth_key = is_proxy ? "Proxy-Authenticate" : "WWW-Authenticate"; + if (res.has_header(auth_key)) { + static auto re = std::regex(R"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~"); + auto s = res.get_header_value(auth_key); + auto pos = s.find(' '); + if (pos != std::string::npos) { + auto type = s.substr(0, pos); + if (type == "Basic") { + return false; + } else if (type == "Digest") { + s = s.substr(pos + 1); + auto beg = std::sregex_iterator(s.begin(), s.end(), re); + for (auto i = beg; i != std::sregex_iterator(); ++i) { + auto m = *i; + auto key = s.substr(static_cast(m.position(1)), + static_cast(m.length(1))); + auto val = m.length(2) > 0 + ? s.substr(static_cast(m.position(2)), + static_cast(m.length(2))) + : s.substr(static_cast(m.position(3)), + static_cast(m.length(3))); + auth[key] = val; + } + return true; + } + } + } + return false; +} + +// https://stackoverflow.com/questions/440133/how-do-i-create-a-random-alpha-numeric-string-in-c/440240#answer-440240 +inline std::string random_string(size_t length) { + auto randchar = []() -> char { + const char charset[] = "0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz"; + const size_t max_index = (sizeof(charset) - 1); + return charset[static_cast(std::rand()) % max_index]; + }; + std::string str(length, 0); + std::generate_n(str.begin(), length, randchar); + return str; +} + +class ContentProviderAdapter { +public: + explicit ContentProviderAdapter( + ContentProviderWithoutLength &&content_provider) + : content_provider_(content_provider) {} + + bool operator()(size_t offset, size_t, DataSink &sink) { + return content_provider_(offset, sink); + } + +private: + ContentProviderWithoutLength content_provider_; +}; + +} // namespace detail + +inline std::string append_query_params(const char *path, const Params ¶ms) { + std::string path_with_query = path; + const static std::regex re("[^?]+\\?.*"); + auto delm = std::regex_match(path, re) ? '&' : '?'; + path_with_query += delm + detail::params_to_query_str(params); + return path_with_query; +} + +// Header utilities +inline std::pair make_range_header(Ranges ranges) { + std::string field = "bytes="; + auto i = 0; + for (auto r : ranges) { + if (i != 0) { field += ", "; } + if (r.first != -1) { field += std::to_string(r.first); } + field += '-'; + if (r.second != -1) { field += std::to_string(r.second); } + i++; + } + return std::make_pair("Range", std::move(field)); +} + +inline std::pair +make_basic_authentication_header(const std::string &username, + const std::string &password, bool is_proxy) { + auto field = "Basic " + detail::base64_encode(username + ":" + password); + auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; + return std::make_pair(key, std::move(field)); +} + +inline std::pair +make_bearer_token_authentication_header(const std::string &token, + bool is_proxy = false) { + auto field = "Bearer " + token; + auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; + return std::make_pair(key, std::move(field)); +} + +// Request implementation +inline bool Request::has_header(const char *key) const { + return detail::has_header(headers, key); +} + +inline std::string Request::get_header_value(const char *key, size_t id) const { + return detail::get_header_value(headers, key, id, ""); +} + +inline size_t Request::get_header_value_count(const char *key) const { + auto r = headers.equal_range(key); + return static_cast(std::distance(r.first, r.second)); +} + +inline void Request::set_header(const char *key, const char *val) { + if (!detail::has_crlf(key) && !detail::has_crlf(val)) { + headers.emplace(key, val); + } +} + +inline void Request::set_header(const char *key, const std::string &val) { + if (!detail::has_crlf(key) && !detail::has_crlf(val.c_str())) { + headers.emplace(key, val); + } +} + +inline bool Request::has_param(const char *key) const { + return params.find(key) != params.end(); +} + +inline std::string Request::get_param_value(const char *key, size_t id) const { + auto rng = params.equal_range(key); + auto it = rng.first; + std::advance(it, static_cast(id)); + if (it != rng.second) { return it->second; } + return std::string(); +} + +inline size_t Request::get_param_value_count(const char *key) const { + auto r = params.equal_range(key); + return static_cast(std::distance(r.first, r.second)); +} + +inline bool Request::is_multipart_form_data() const { + const auto &content_type = get_header_value("Content-Type"); + return !content_type.find("multipart/form-data"); +} + +inline bool Request::has_file(const char *key) const { + return files.find(key) != files.end(); +} + +inline MultipartFormData Request::get_file_value(const char *key) const { + auto it = files.find(key); + if (it != files.end()) { return it->second; } + return MultipartFormData(); +} + +// Response implementation +inline bool Response::has_header(const char *key) const { + return headers.find(key) != headers.end(); +} + +inline std::string Response::get_header_value(const char *key, + size_t id) const { + return detail::get_header_value(headers, key, id, ""); +} + +inline size_t Response::get_header_value_count(const char *key) const { + auto r = headers.equal_range(key); + return static_cast(std::distance(r.first, r.second)); +} + +inline void Response::set_header(const char *key, const char *val) { + if (!detail::has_crlf(key) && !detail::has_crlf(val)) { + headers.emplace(key, val); + } +} + +inline void Response::set_header(const char *key, const std::string &val) { + if (!detail::has_crlf(key) && !detail::has_crlf(val.c_str())) { + headers.emplace(key, val); + } +} + +inline void Response::set_redirect(const char *url, int stat) { + if (!detail::has_crlf(url)) { + set_header("Location", url); + if (300 <= stat && stat < 400) { + this->status = stat; + } else { + this->status = 302; + } + } +} + +inline void Response::set_redirect(const std::string &url, int stat) { + set_redirect(url.c_str(), stat); +} + +inline void Response::set_content(const char *s, size_t n, + const char *content_type) { + body.assign(s, n); + + auto rng = headers.equal_range("Content-Type"); + headers.erase(rng.first, rng.second); + set_header("Content-Type", content_type); +} + +inline void Response::set_content(const std::string &s, + const char *content_type) { + set_content(s.data(), s.size(), content_type); +} + +inline void Response::set_content_provider( + size_t in_length, const char *content_type, ContentProvider provider, + ContentProviderResourceReleaser resource_releaser) { + assert(in_length > 0); + set_header("Content-Type", content_type); + content_length_ = in_length; + content_provider_ = std::move(provider); + content_provider_resource_releaser_ = resource_releaser; + is_chunked_content_provider_ = false; +} + +inline void Response::set_content_provider( + const char *content_type, ContentProviderWithoutLength provider, + ContentProviderResourceReleaser resource_releaser) { + set_header("Content-Type", content_type); + content_length_ = 0; + content_provider_ = detail::ContentProviderAdapter(std::move(provider)); + content_provider_resource_releaser_ = resource_releaser; + is_chunked_content_provider_ = false; +} + +inline void Response::set_chunked_content_provider( + const char *content_type, ContentProviderWithoutLength provider, + ContentProviderResourceReleaser resource_releaser) { + set_header("Content-Type", content_type); + content_length_ = 0; + content_provider_ = detail::ContentProviderAdapter(std::move(provider)); + content_provider_resource_releaser_ = resource_releaser; + is_chunked_content_provider_ = true; +} + +// Result implementation +inline bool Result::has_request_header(const char *key) const { + return request_headers_.find(key) != request_headers_.end(); +} + +inline std::string Result::get_request_header_value(const char *key, + size_t id) const { + return detail::get_header_value(request_headers_, key, id, ""); +} + +inline size_t Result::get_request_header_value_count(const char *key) const { + auto r = request_headers_.equal_range(key); + return static_cast(std::distance(r.first, r.second)); +} + +// Stream implementation +inline ssize_t Stream::write(const char *ptr) { + return write(ptr, strlen(ptr)); +} + +inline ssize_t Stream::write(const std::string &s) { + return write(s.data(), s.size()); +} + +namespace detail { + +// Socket stream implementation +inline SocketStream::SocketStream(socket_t sock, time_t read_timeout_sec, + time_t read_timeout_usec, + time_t write_timeout_sec, + time_t write_timeout_usec) + : sock_(sock), read_timeout_sec_(read_timeout_sec), + read_timeout_usec_(read_timeout_usec), + write_timeout_sec_(write_timeout_sec), + write_timeout_usec_(write_timeout_usec), + read_buff_(read_buff_size_, 0) {} + +inline SocketStream::~SocketStream() {} + +inline bool SocketStream::is_readable() const { + return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0; +} + +inline bool SocketStream::is_writable() const { + return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0; +} + +inline ssize_t SocketStream::read(char *ptr, size_t size) { +#ifdef _WIN32 + size = (std::min)(size, static_cast((std::numeric_limits::max)())); +#else + size = (std::min)(size, static_cast((std::numeric_limits::max)())); +#endif + + if (read_buff_off_ < read_buff_content_size_) { + auto remaining_size = read_buff_content_size_ - read_buff_off_; + if (size <= remaining_size) { + memcpy(ptr, read_buff_.data() + read_buff_off_, size); + read_buff_off_ += size; + return static_cast(size); + } else { + memcpy(ptr, read_buff_.data() + read_buff_off_, remaining_size); + read_buff_off_ += remaining_size; + return static_cast(remaining_size); + } + } + + if (!is_readable()) { return -1; } + + read_buff_off_ = 0; + read_buff_content_size_ = 0; + + if (size < read_buff_size_) { + auto n = read_socket(sock_, read_buff_.data(), read_buff_size_, CPPHTTPLIB_RECV_FLAGS); + if (n <= 0) { + return n; + } else if (n <= static_cast(size)) { + memcpy(ptr, read_buff_.data(), static_cast(n)); + return n; + } else { + memcpy(ptr, read_buff_.data(), size); + read_buff_off_ = size; + read_buff_content_size_ = static_cast(n); + return static_cast(size); + } + } else { + return read_socket(sock_, ptr, size, CPPHTTPLIB_RECV_FLAGS); + } +} + +inline ssize_t SocketStream::write(const char *ptr, size_t size) { + if (!is_writable()) { return -1; } + +#ifdef _WIN32 + size = (std::min)(size, static_cast((std::numeric_limits::max)())); +#endif + + return send_socket(sock_, ptr, size, CPPHTTPLIB_SEND_FLAGS); +} + +inline void SocketStream::get_remote_ip_and_port(std::string &ip, + int &port) const { + return detail::get_remote_ip_and_port(sock_, ip, port); +} + +inline socket_t SocketStream::socket() const { return sock_; } + +// Buffer stream implementation +inline bool BufferStream::is_readable() const { return true; } + +inline bool BufferStream::is_writable() const { return true; } + +inline ssize_t BufferStream::read(char *ptr, size_t size) { +#if defined(_MSC_VER) && _MSC_VER <= 1900 + auto len_read = buffer._Copy_s(ptr, size, size, position); +#else + auto len_read = buffer.copy(ptr, size, position); +#endif + position += static_cast(len_read); + return static_cast(len_read); +} + +inline ssize_t BufferStream::write(const char *ptr, size_t size) { + buffer.append(ptr, size); + return static_cast(size); +} + +inline void BufferStream::get_remote_ip_and_port(std::string & /*ip*/, + int & /*port*/) const {} + +inline socket_t BufferStream::socket() const { return 0; } + +inline const std::string &BufferStream::get_buffer() const { return buffer; } + +} // namespace detail + +// HTTP server implementation +inline Server::Server() + : new_task_queue( + [] { return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }), + svr_sock_(INVALID_SOCKET), is_running_(false) { +#ifndef _WIN32 + signal(SIGPIPE, SIG_IGN); +#endif +} + +inline Server::~Server() {} + +inline Server &Server::Get(const std::string &pattern, Handler handler) { + get_handlers_.push_back( + std::make_pair(std::regex(pattern), std::move(handler))); + return *this; +} + +inline Server &Server::Post(const std::string &pattern, Handler handler) { + post_handlers_.push_back( + std::make_pair(std::regex(pattern), std::move(handler))); + return *this; +} + +inline Server &Server::Post(const std::string &pattern, + HandlerWithContentReader handler) { + post_handlers_for_content_reader_.push_back( + std::make_pair(std::regex(pattern), std::move(handler))); + return *this; +} + +inline Server &Server::Put(const std::string &pattern, Handler handler) { + put_handlers_.push_back( + std::make_pair(std::regex(pattern), std::move(handler))); + return *this; +} + +inline Server &Server::Put(const std::string &pattern, + HandlerWithContentReader handler) { + put_handlers_for_content_reader_.push_back( + std::make_pair(std::regex(pattern), std::move(handler))); + return *this; +} + +inline Server &Server::Patch(const std::string &pattern, Handler handler) { + patch_handlers_.push_back( + std::make_pair(std::regex(pattern), std::move(handler))); + return *this; +} + +inline Server &Server::Patch(const std::string &pattern, + HandlerWithContentReader handler) { + patch_handlers_for_content_reader_.push_back( + std::make_pair(std::regex(pattern), std::move(handler))); + return *this; +} + +inline Server &Server::Delete(const std::string &pattern, Handler handler) { + delete_handlers_.push_back( + std::make_pair(std::regex(pattern), std::move(handler))); + return *this; +} + +inline Server &Server::Delete(const std::string &pattern, + HandlerWithContentReader handler) { + delete_handlers_for_content_reader_.push_back( + std::make_pair(std::regex(pattern), std::move(handler))); + return *this; +} + +inline Server &Server::Options(const std::string &pattern, Handler handler) { + options_handlers_.push_back( + std::make_pair(std::regex(pattern), std::move(handler))); + return *this; +} + +inline bool Server::set_base_dir(const std::string &dir, + const std::string &mount_point) { + return set_mount_point(mount_point, dir); +} + +inline bool Server::set_mount_point(const std::string &mount_point, + const std::string &dir, Headers headers) { + if (detail::is_dir(dir)) { + std::string mnt = !mount_point.empty() ? mount_point : "/"; + if (!mnt.empty() && mnt[0] == '/') { + base_dirs_.push_back({mnt, dir, std::move(headers)}); + return true; + } + } + return false; +} + +inline bool Server::remove_mount_point(const std::string &mount_point) { + for (auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) { + if (it->mount_point == mount_point) { + base_dirs_.erase(it); + return true; + } + } + return false; +} + +inline Server & +Server::set_file_extension_and_mimetype_mapping(const char *ext, + const char *mime) { + file_extension_and_mimetype_map_[ext] = mime; + return *this; +} + +inline Server &Server::set_file_request_handler(Handler handler) { + file_request_handler_ = std::move(handler); + return *this; +} + +inline Server &Server::set_error_handler(HandlerWithResponse handler) { + error_handler_ = std::move(handler); + return *this; +} + +inline Server &Server::set_error_handler(Handler handler) { + error_handler_ = [handler](const Request &req, Response &res) { + handler(req, res); + return HandlerResponse::Handled; + }; + return *this; +} + +inline Server &Server::set_exception_handler(ExceptionHandler handler) { + exception_handler_ = std::move(handler); + return *this; +} + +inline Server &Server::set_pre_routing_handler(HandlerWithResponse handler) { + pre_routing_handler_ = std::move(handler); + return *this; +} + +inline Server &Server::set_post_routing_handler(Handler handler) { + post_routing_handler_ = std::move(handler); + return *this; +} + +inline Server &Server::set_logger(Logger logger) { + logger_ = std::move(logger); + return *this; +} + +inline Server & +Server::set_expect_100_continue_handler(Expect100ContinueHandler handler) { + expect_100_continue_handler_ = std::move(handler); + + return *this; +} + +inline Server &Server::set_address_family(int family) { + address_family_ = family; + return *this; +} + +inline Server &Server::set_tcp_nodelay(bool on) { + tcp_nodelay_ = on; + return *this; +} + +inline Server &Server::set_socket_options(SocketOptions socket_options) { + socket_options_ = std::move(socket_options); + return *this; +} + +inline Server &Server::set_default_headers(Headers headers) { + default_headers_ = std::move(headers); + return *this; +} + +inline Server &Server::set_keep_alive_max_count(size_t count) { + keep_alive_max_count_ = count; + return *this; +} + +inline Server &Server::set_keep_alive_timeout(time_t sec) { + keep_alive_timeout_sec_ = sec; + return *this; +} + +inline Server &Server::set_read_timeout(time_t sec, time_t usec) { + read_timeout_sec_ = sec; + read_timeout_usec_ = usec; + return *this; +} + +inline Server &Server::set_write_timeout(time_t sec, time_t usec) { + write_timeout_sec_ = sec; + write_timeout_usec_ = usec; + return *this; +} + +inline Server &Server::set_idle_interval(time_t sec, time_t usec) { + idle_interval_sec_ = sec; + idle_interval_usec_ = usec; + return *this; +} + +inline Server &Server::set_payload_max_length(size_t length) { + payload_max_length_ = length; + return *this; +} + +inline bool Server::bind_to_port(const char *host, int port, int socket_flags) { + if (bind_internal(host, port, socket_flags) < 0) return false; + return true; +} +inline int Server::bind_to_any_port(const char *host, int socket_flags) { + return bind_internal(host, 0, socket_flags); +} + +inline bool Server::listen_after_bind() { return listen_internal(); } + +inline bool Server::listen(const char *host, int port, int socket_flags) { + return bind_to_port(host, port, socket_flags) && listen_internal(); +} + +inline bool Server::is_running() const { return is_running_; } + +inline void Server::stop() { + if (is_running_) { + assert(svr_sock_ != INVALID_SOCKET); + std::atomic sock(svr_sock_.exchange(INVALID_SOCKET)); + detail::shutdown_socket(sock); + detail::close_socket(sock); + } +} + +inline bool Server::parse_request_line(const char *s, Request &req) { + auto len = strlen(s); + if (len < 2 || s[len - 2] != '\r' || s[len - 1] != '\n') { return false; } + len -= 2; + + { + size_t count = 0; + + detail::split(s, s + len, ' ', [&](const char *b, const char *e) { + switch (count) { + case 0: req.method = std::string(b, e); break; + case 1: req.target = std::string(b, e); break; + case 2: req.version = std::string(b, e); break; + default: break; + } + count++; + }); + + if (count != 3) { return false; } + } + + static const std::set methods{ + "GET", "HEAD", "POST", "PUT", "DELETE", + "CONNECT", "OPTIONS", "TRACE", "PATCH", "PRI"}; + + if (methods.find(req.method) == methods.end()) { return false; } + + if (req.version != "HTTP/1.1" && req.version != "HTTP/1.0") { return false; } + + { + size_t count = 0; + + detail::split(req.target.data(), req.target.data() + req.target.size(), '?', + [&](const char *b, const char *e) { + switch (count) { + case 0: + req.path = detail::decode_url(std::string(b, e), false); + break; + case 1: { + if (e - b > 0) { + detail::parse_query_text(std::string(b, e), req.params); + } + break; + } + default: break; + } + count++; + }); + + if (count > 2) { return false; } + } + + return true; +} + +inline bool Server::write_response(Stream &strm, bool close_connection, + const Request &req, Response &res) { + return write_response_core(strm, close_connection, req, res, false); +} + +inline bool Server::write_response_with_content(Stream &strm, + bool close_connection, + const Request &req, + Response &res) { + return write_response_core(strm, close_connection, req, res, true); +} + +inline bool Server::write_response_core(Stream &strm, bool close_connection, + const Request &req, Response &res, + bool need_apply_ranges) { + assert(res.status != -1); + + if (400 <= res.status && error_handler_ && + error_handler_(req, res) == HandlerResponse::Handled) { + need_apply_ranges = true; + } + + std::string content_type; + std::string boundary; + if (need_apply_ranges) { apply_ranges(req, res, content_type, boundary); } + + // Prepare additional headers + if (close_connection || req.get_header_value("Connection") == "close") { + res.set_header("Connection", "close"); + } else { + std::stringstream ss; + ss << "timeout=" << keep_alive_timeout_sec_ + << ", max=" << keep_alive_max_count_; + res.set_header("Keep-Alive", ss.str()); + } + + if (!res.has_header("Content-Type") && + (!res.body.empty() || res.content_length_ > 0 || res.content_provider_)) { + res.set_header("Content-Type", "text/plain"); + } + + if (!res.has_header("Content-Length") && res.body.empty() && + !res.content_length_ && !res.content_provider_) { + res.set_header("Content-Length", "0"); + } + + if (!res.has_header("Accept-Ranges") && req.method == "HEAD") { + res.set_header("Accept-Ranges", "bytes"); + } + + if (post_routing_handler_) { post_routing_handler_(req, res); } + + // Response line and headers + { + detail::BufferStream bstrm; + + if (!bstrm.write_format("HTTP/1.1 %d %s\r\n", res.status, + detail::status_message(res.status))) { + return false; + } + + if (!detail::write_headers(bstrm, res.headers)) { return false; } + + // Flush buffer + auto &data = bstrm.get_buffer(); + strm.write(data.data(), data.size()); + } + + // Body + auto ret = true; + if (req.method != "HEAD") { + if (!res.body.empty()) { + if (!strm.write(res.body)) { ret = false; } + } else if (res.content_provider_) { + if (write_content_with_provider(strm, req, res, boundary, content_type)) { + res.content_provider_success_ = true; + } else { + res.content_provider_success_ = false; + ret = false; + } + } + } + + // Log + if (logger_) { logger_(req, res); } + + return ret; +} + +inline bool +Server::write_content_with_provider(Stream &strm, const Request &req, + Response &res, const std::string &boundary, + const std::string &content_type) { + auto is_shutting_down = [this]() { + return this->svr_sock_ == INVALID_SOCKET; + }; + + if (res.content_length_ > 0) { + if (req.ranges.empty()) { + return detail::write_content(strm, res.content_provider_, 0, + res.content_length_, is_shutting_down); + } else if (req.ranges.size() == 1) { + auto offsets = + detail::get_range_offset_and_length(req, res.content_length_, 0); + auto offset = offsets.first; + auto length = offsets.second; + return detail::write_content(strm, res.content_provider_, offset, length, + is_shutting_down); + } else { + return detail::write_multipart_ranges_data( + strm, req, res, boundary, content_type, is_shutting_down); + } + } else { + if (res.is_chunked_content_provider_) { + auto type = detail::encoding_type(req, res); + + std::unique_ptr compressor; + if (type == detail::EncodingType::Gzip) { +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + compressor = detail::make_unique(); +#endif + } else if (type == detail::EncodingType::Brotli) { +#ifdef CPPHTTPLIB_BROTLI_SUPPORT + compressor = detail::make_unique(); +#endif + } else { + compressor = detail::make_unique(); + } + assert(compressor != nullptr); + + return detail::write_content_chunked(strm, res.content_provider_, + is_shutting_down, *compressor); + } else { + return detail::write_content_without_length(strm, res.content_provider_, + is_shutting_down); + } + } +} + +inline bool Server::read_content(Stream &strm, Request &req, Response &res) { + MultipartFormDataMap::iterator cur; + if (read_content_core( + strm, req, res, + // Regular + [&](const char *buf, size_t n) { + if (req.body.size() + n > req.body.max_size()) { return false; } + req.body.append(buf, n); + return true; + }, + // Multipart + [&](const MultipartFormData &file) { + cur = req.files.emplace(file.name, file); + return true; + }, + [&](const char *buf, size_t n) { + auto &content = cur->second.content; + if (content.size() + n > content.max_size()) { return false; } + content.append(buf, n); + return true; + })) { + const auto &content_type = req.get_header_value("Content-Type"); + if (!content_type.find("application/x-www-form-urlencoded")) { + detail::parse_query_text(req.body, req.params); + } + return true; + } + return false; +} + +inline bool Server::read_content_with_content_receiver( + Stream &strm, Request &req, Response &res, ContentReceiver receiver, + MultipartContentHeader multipart_header, + ContentReceiver multipart_receiver) { + return read_content_core(strm, req, res, std::move(receiver), + std::move(multipart_header), + std::move(multipart_receiver)); +} + +inline bool Server::read_content_core(Stream &strm, Request &req, Response &res, + ContentReceiver receiver, + MultipartContentHeader mulitpart_header, + ContentReceiver multipart_receiver) { + detail::MultipartFormDataParser multipart_form_data_parser; + ContentReceiverWithProgress out; + + if (req.is_multipart_form_data()) { + const auto &content_type = req.get_header_value("Content-Type"); + std::string boundary; + if (!detail::parse_multipart_boundary(content_type, boundary)) { + res.status = 400; + return false; + } + + multipart_form_data_parser.set_boundary(std::move(boundary)); + out = [&](const char *buf, size_t n, uint64_t /*off*/, uint64_t /*len*/) { + /* For debug + size_t pos = 0; + while (pos < n) { + auto read_size = (std::min)(1, n - pos); + auto ret = multipart_form_data_parser.parse( + buf + pos, read_size, multipart_receiver, mulitpart_header); + if (!ret) { return false; } + pos += read_size; + } + return true; + */ + return multipart_form_data_parser.parse(buf, n, multipart_receiver, + mulitpart_header); + }; + } else { + out = [receiver](const char *buf, size_t n, uint64_t /*off*/, + uint64_t /*len*/) { return receiver(buf, n); }; + } + + if (req.method == "DELETE" && !req.has_header("Content-Length")) { + return true; + } + + if (!detail::read_content(strm, req, payload_max_length_, res.status, nullptr, + out, true)) { + return false; + } + + if (req.is_multipart_form_data()) { + if (!multipart_form_data_parser.is_valid()) { + res.status = 400; + return false; + } + } + + return true; +} + +inline bool Server::handle_file_request(const Request &req, Response &res, + bool head) { + for (const auto &entry : base_dirs_) { + // Prefix match + if (!req.path.compare(0, entry.mount_point.size(), entry.mount_point)) { + std::string sub_path = "/" + req.path.substr(entry.mount_point.size()); + if (detail::is_valid_path(sub_path)) { + auto path = entry.base_dir + sub_path; + if (path.back() == '/') { path += "index.html"; } + + if (detail::is_file(path)) { + detail::read_file(path, res.body); + auto type = + detail::find_content_type(path, file_extension_and_mimetype_map_); + if (type) { res.set_header("Content-Type", type); } + for (const auto &kv : entry.headers) { + res.set_header(kv.first.c_str(), kv.second); + } + res.status = req.has_header("Range") ? 206 : 200; + if (!head && file_request_handler_) { + file_request_handler_(req, res); + } + return true; + } + } + } + } + return false; +} + +inline socket_t +Server::create_server_socket(const char *host, int port, int socket_flags, + SocketOptions socket_options) const { + return detail::create_socket( + host, "", port, address_family_, socket_flags, tcp_nodelay_, + std::move(socket_options), + [](socket_t sock, struct addrinfo &ai) -> bool { + if (::bind(sock, ai.ai_addr, static_cast(ai.ai_addrlen))) { + return false; + } + if (::listen(sock, 5)) { // Listen through 5 channels + return false; + } + return true; + }); +} + +inline int Server::bind_internal(const char *host, int port, int socket_flags) { + if (!is_valid()) { return -1; } + + svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_); + if (svr_sock_ == INVALID_SOCKET) { return -1; } + + if (port == 0) { + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + if (getsockname(svr_sock_, reinterpret_cast(&addr), + &addr_len) == -1) { + return -1; + } + if (addr.ss_family == AF_INET) { + return ntohs(reinterpret_cast(&addr)->sin_port); + } else if (addr.ss_family == AF_INET6) { + return ntohs(reinterpret_cast(&addr)->sin6_port); + } else { + return -1; + } + } else { + return port; + } +} + +inline bool Server::listen_internal() { + auto ret = true; + is_running_ = true; + + { + std::unique_ptr task_queue(new_task_queue()); + + while (svr_sock_ != INVALID_SOCKET) { +#ifndef _WIN32 + if (idle_interval_sec_ > 0 || idle_interval_usec_ > 0) { +#endif + auto val = detail::select_read(svr_sock_, idle_interval_sec_, + idle_interval_usec_); + if (val == 0) { // Timeout + task_queue->on_idle(); + continue; + } +#ifndef _WIN32 + } +#endif + socket_t sock = accept(svr_sock_, nullptr, nullptr); + + if (sock == INVALID_SOCKET) { + if (errno == EMFILE) { + // The per-process limit of open file descriptors has been reached. + // Try to accept new connections after a short sleep. + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + continue; + } + if (svr_sock_ != INVALID_SOCKET) { + detail::close_socket(svr_sock_); + ret = false; + } else { + ; // The server socket was closed by user. + } + break; + } + + { + timeval tv; + tv.tv_sec = static_cast(read_timeout_sec_); + tv.tv_usec = static_cast(read_timeout_usec_); + setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)); + } + { + timeval tv; + tv.tv_sec = static_cast(write_timeout_sec_); + tv.tv_usec = static_cast(write_timeout_usec_); + setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof(tv)); + } + +#if __cplusplus > 201703L + task_queue->enqueue([=, this]() { process_and_close_socket(sock); }); +#else + task_queue->enqueue([=]() { process_and_close_socket(sock); }); +#endif + } + + task_queue->shutdown(); + } + + is_running_ = false; + return ret; +} + +inline bool Server::routing(Request &req, Response &res, Stream &strm) { + if (pre_routing_handler_ && + pre_routing_handler_(req, res) == HandlerResponse::Handled) { + return true; + } + + // File handler + bool is_head_request = req.method == "HEAD"; + if ((req.method == "GET" || is_head_request) && + handle_file_request(req, res, is_head_request)) { + return true; + } + + if (detail::expect_content(req)) { + // Content reader handler + { + ContentReader reader( + [&](ContentReceiver receiver) { + return read_content_with_content_receiver( + strm, req, res, std::move(receiver), nullptr, nullptr); + }, + [&](MultipartContentHeader header, ContentReceiver receiver) { + return read_content_with_content_receiver(strm, req, res, nullptr, + std::move(header), + std::move(receiver)); + }); + + if (req.method == "POST") { + if (dispatch_request_for_content_reader( + req, res, std::move(reader), + post_handlers_for_content_reader_)) { + return true; + } + } else if (req.method == "PUT") { + if (dispatch_request_for_content_reader( + req, res, std::move(reader), + put_handlers_for_content_reader_)) { + return true; + } + } else if (req.method == "PATCH") { + if (dispatch_request_for_content_reader( + req, res, std::move(reader), + patch_handlers_for_content_reader_)) { + return true; + } + } else if (req.method == "DELETE") { + if (dispatch_request_for_content_reader( + req, res, std::move(reader), + delete_handlers_for_content_reader_)) { + return true; + } + } + } + + // Read content into `req.body` + if (!read_content(strm, req, res)) { return false; } + } + + // Regular handler + if (req.method == "GET" || req.method == "HEAD") { + return dispatch_request(req, res, get_handlers_); + } else if (req.method == "POST") { + return dispatch_request(req, res, post_handlers_); + } else if (req.method == "PUT") { + return dispatch_request(req, res, put_handlers_); + } else if (req.method == "DELETE") { + return dispatch_request(req, res, delete_handlers_); + } else if (req.method == "OPTIONS") { + return dispatch_request(req, res, options_handlers_); + } else if (req.method == "PATCH") { + return dispatch_request(req, res, patch_handlers_); + } + + res.status = 400; + return false; +} + +inline bool Server::dispatch_request(Request &req, Response &res, + const Handlers &handlers) { + for (const auto &x : handlers) { + const auto &pattern = x.first; + const auto &handler = x.second; + + if (std::regex_match(req.path, req.matches, pattern)) { + handler(req, res); + return true; + } + } + return false; +} + +inline void Server::apply_ranges(const Request &req, Response &res, + std::string &content_type, + std::string &boundary) { + if (req.ranges.size() > 1) { + boundary = detail::make_multipart_data_boundary(); + + auto it = res.headers.find("Content-Type"); + if (it != res.headers.end()) { + content_type = it->second; + res.headers.erase(it); + } + + res.headers.emplace("Content-Type", + "multipart/byteranges; boundary=" + boundary); + } + + auto type = detail::encoding_type(req, res); + + if (res.body.empty()) { + if (res.content_length_ > 0) { + size_t length = 0; + if (req.ranges.empty()) { + length = res.content_length_; + } else if (req.ranges.size() == 1) { + auto offsets = + detail::get_range_offset_and_length(req, res.content_length_, 0); + auto offset = offsets.first; + length = offsets.second; + auto content_range = detail::make_content_range_header_field( + offset, length, res.content_length_); + res.set_header("Content-Range", content_range); + } else { + length = detail::get_multipart_ranges_data_length(req, res, boundary, + content_type); + } + res.set_header("Content-Length", std::to_string(length)); + } else { + if (res.content_provider_) { + if (res.is_chunked_content_provider_) { + res.set_header("Transfer-Encoding", "chunked"); + if (type == detail::EncodingType::Gzip) { + res.set_header("Content-Encoding", "gzip"); + } else if (type == detail::EncodingType::Brotli) { + res.set_header("Content-Encoding", "br"); + } + } + } + } + } else { + if (req.ranges.empty()) { + ; + } else if (req.ranges.size() == 1) { + auto offsets = + detail::get_range_offset_and_length(req, res.body.size(), 0); + auto offset = offsets.first; + auto length = offsets.second; + auto content_range = detail::make_content_range_header_field( + offset, length, res.body.size()); + res.set_header("Content-Range", content_range); + if (offset < res.body.size()) { + res.body = res.body.substr(offset, length); + } else { + res.body.clear(); + res.status = 416; + } + } else { + std::string data; + if (detail::make_multipart_ranges_data(req, res, boundary, content_type, + data)) { + res.body.swap(data); + } else { + res.body.clear(); + res.status = 416; + } + } + + if (type != detail::EncodingType::None) { + std::unique_ptr compressor; + std::string content_encoding; + + if (type == detail::EncodingType::Gzip) { +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + compressor = detail::make_unique(); + content_encoding = "gzip"; +#endif + } else if (type == detail::EncodingType::Brotli) { +#ifdef CPPHTTPLIB_BROTLI_SUPPORT + compressor = detail::make_unique(); + content_encoding = "br"; +#endif + } + + if (compressor) { + std::string compressed; + if (compressor->compress(res.body.data(), res.body.size(), true, + [&](const char *data, size_t data_len) { + compressed.append(data, data_len); + return true; + })) { + res.body.swap(compressed); + res.set_header("Content-Encoding", content_encoding); + } + } + } + + auto length = std::to_string(res.body.size()); + res.set_header("Content-Length", length); + } +} + +inline bool Server::dispatch_request_for_content_reader( + Request &req, Response &res, ContentReader content_reader, + const HandlersForContentReader &handlers) { + for (const auto &x : handlers) { + const auto &pattern = x.first; + const auto &handler = x.second; + + if (std::regex_match(req.path, req.matches, pattern)) { + handler(req, res, content_reader); + return true; + } + } + return false; +} + +inline bool +Server::process_request(Stream &strm, bool close_connection, + bool &connection_closed, + const std::function &setup_request) { + std::array buf{}; + + detail::stream_line_reader line_reader(strm, buf.data(), buf.size()); + + // Connection has been closed on client + if (!line_reader.getline()) { return false; } + + Request req; + Response res; + + res.version = "HTTP/1.1"; + + for (const auto &header : default_headers_) { + if (res.headers.find(header.first) == res.headers.end()) { + res.headers.insert(header); + } + } + +#ifdef _WIN32 + // TODO: Increase FD_SETSIZE statically (libzmq), dynamically (MySQL). +#else +#ifndef CPPHTTPLIB_USE_POLL + // Socket file descriptor exceeded FD_SETSIZE... + if (strm.socket() >= FD_SETSIZE) { + Headers dummy; + detail::read_headers(strm, dummy); + res.status = 500; + return write_response(strm, close_connection, req, res); + } +#endif +#endif + + // Check if the request URI doesn't exceed the limit + if (line_reader.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) { + Headers dummy; + detail::read_headers(strm, dummy); + res.status = 414; + return write_response(strm, close_connection, req, res); + } + + // Request line and headers + if (!parse_request_line(line_reader.ptr(), req) || + !detail::read_headers(strm, req.headers)) { + res.status = 400; + return write_response(strm, close_connection, req, res); + } + + if (req.get_header_value("Connection") == "close") { + connection_closed = true; + } + + if (req.version == "HTTP/1.0" && + req.get_header_value("Connection") != "Keep-Alive") { + connection_closed = true; + } + + strm.get_remote_ip_and_port(req.remote_addr, req.remote_port); + req.set_header("REMOTE_ADDR", req.remote_addr); + req.set_header("REMOTE_PORT", std::to_string(req.remote_port)); + + if (req.has_header("Range")) { + const auto &range_header_value = req.get_header_value("Range"); + if (!detail::parse_range_header(range_header_value, req.ranges)) { + res.status = 416; + return write_response(strm, close_connection, req, res); + } + } + + if (setup_request) { setup_request(req); } + + if (req.get_header_value("Expect") == "100-continue") { + auto status = 100; + if (expect_100_continue_handler_) { + status = expect_100_continue_handler_(req, res); + } + switch (status) { + case 100: + case 417: + strm.write_format("HTTP/1.1 %d %s\r\n\r\n", status, + detail::status_message(status)); + break; + default: return write_response(strm, close_connection, req, res); + } + } + + // Rounting + bool routed = false; + try { + routed = routing(req, res, strm); + } catch (std::exception &e) { + if (exception_handler_) { + exception_handler_(req, res, e); + routed = true; + } else { + res.status = 500; + res.set_header("EXCEPTION_WHAT", e.what()); + } + } catch (...) { + res.status = 500; + res.set_header("EXCEPTION_WHAT", "UNKNOWN"); + } + + if (routed) { + if (res.status == -1) { res.status = req.ranges.empty() ? 200 : 206; } + return write_response_with_content(strm, close_connection, req, res); + } else { + if (res.status == -1) { res.status = 404; } + return write_response(strm, close_connection, req, res); + } +} + +inline bool Server::is_valid() const { return true; } + +inline bool Server::process_and_close_socket(socket_t sock) { + auto ret = detail::process_server_socket( + sock, keep_alive_max_count_, keep_alive_timeout_sec_, read_timeout_sec_, + read_timeout_usec_, write_timeout_sec_, write_timeout_usec_, + [this](Stream &strm, bool close_connection, bool &connection_closed) { + return process_request(strm, close_connection, connection_closed, + nullptr); + }); + + detail::shutdown_socket(sock); + detail::close_socket(sock); + return ret; +} + +// HTTP client implementation +inline ClientImpl::ClientImpl(const std::string &host) + : ClientImpl(host, 80, std::string(), std::string()) {} + +inline ClientImpl::ClientImpl(const std::string &host, int port) + : ClientImpl(host, port, std::string(), std::string()) {} + +inline ClientImpl::ClientImpl(const std::string &host, int port, + const std::string &client_cert_path, + const std::string &client_key_path) + : host_(host), port_(port), + host_and_port_(adjust_host_string(host) + ":" + std::to_string(port)), + client_cert_path_(client_cert_path), client_key_path_(client_key_path) {} + +inline ClientImpl::~ClientImpl() { + std::lock_guard guard(socket_mutex_); + shutdown_socket(socket_); + close_socket(socket_); +} + +inline bool ClientImpl::is_valid() const { return true; } + +inline void ClientImpl::copy_settings(const ClientImpl &rhs) { + client_cert_path_ = rhs.client_cert_path_; + client_key_path_ = rhs.client_key_path_; + connection_timeout_sec_ = rhs.connection_timeout_sec_; + read_timeout_sec_ = rhs.read_timeout_sec_; + read_timeout_usec_ = rhs.read_timeout_usec_; + write_timeout_sec_ = rhs.write_timeout_sec_; + write_timeout_usec_ = rhs.write_timeout_usec_; + basic_auth_username_ = rhs.basic_auth_username_; + basic_auth_password_ = rhs.basic_auth_password_; + bearer_token_auth_token_ = rhs.bearer_token_auth_token_; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + digest_auth_username_ = rhs.digest_auth_username_; + digest_auth_password_ = rhs.digest_auth_password_; +#endif + keep_alive_ = rhs.keep_alive_; + follow_location_ = rhs.follow_location_; + url_encode_ = rhs.url_encode_; + address_family_ = rhs.address_family_; + tcp_nodelay_ = rhs.tcp_nodelay_; + socket_options_ = rhs.socket_options_; + compress_ = rhs.compress_; + decompress_ = rhs.decompress_; + interface_ = rhs.interface_; + proxy_host_ = rhs.proxy_host_; + proxy_port_ = rhs.proxy_port_; + proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_; + proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_; + proxy_bearer_token_auth_token_ = rhs.proxy_bearer_token_auth_token_; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_; + proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_; +#endif +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + ca_cert_file_path_ = rhs.ca_cert_file_path_; + ca_cert_dir_path_ = rhs.ca_cert_dir_path_; + ca_cert_store_ = rhs.ca_cert_store_; +#endif +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + server_certificate_verification_ = rhs.server_certificate_verification_; +#endif + logger_ = rhs.logger_; +} + +inline socket_t ClientImpl::create_client_socket(Error &error) const { + if (!proxy_host_.empty() && proxy_port_ != -1) { + return detail::create_client_socket( + proxy_host_.c_str(), "", proxy_port_, address_family_, tcp_nodelay_, + socket_options_, connection_timeout_sec_, connection_timeout_usec_, + read_timeout_sec_, read_timeout_usec_, write_timeout_sec_, + write_timeout_usec_, interface_, error); + } + // Check is custom IP specified for host_ + std::string ip; + auto it = addr_map_.find(host_); + if(it != addr_map_.end()) + ip = it->second; + + return detail::create_client_socket( + host_.c_str(), ip.c_str(), port_, address_family_, tcp_nodelay_, socket_options_, + connection_timeout_sec_, connection_timeout_usec_, read_timeout_sec_, + read_timeout_usec_, write_timeout_sec_, write_timeout_usec_, interface_, + error); +} + +inline bool ClientImpl::create_and_connect_socket(Socket &socket, + Error &error) { + auto sock = create_client_socket(error); + if (sock == INVALID_SOCKET) { return false; } + socket.sock = sock; + return true; +} + +inline void ClientImpl::shutdown_ssl(Socket & /*socket*/, + bool /*shutdown_gracefully*/) { + // If there are any requests in flight from threads other than us, then it's + // a thread-unsafe race because individual ssl* objects are not thread-safe. + assert(socket_requests_in_flight_ == 0 || + socket_requests_are_from_thread_ == std::this_thread::get_id()); +} + +inline void ClientImpl::shutdown_socket(Socket &socket) { + if (socket.sock == INVALID_SOCKET) { return; } + detail::shutdown_socket(socket.sock); +} + +inline void ClientImpl::close_socket(Socket &socket) { + // If there are requests in flight in another thread, usually closing + // the socket will be fine and they will simply receive an error when + // using the closed socket, but it is still a bug since rarely the OS + // may reassign the socket id to be used for a new socket, and then + // suddenly they will be operating on a live socket that is different + // than the one they intended! + assert(socket_requests_in_flight_ == 0 || + socket_requests_are_from_thread_ == std::this_thread::get_id()); + + // It is also a bug if this happens while SSL is still active +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + assert(socket.ssl == nullptr); +#endif + if (socket.sock == INVALID_SOCKET) { return; } + detail::close_socket(socket.sock); + socket.sock = INVALID_SOCKET; +} + +inline bool ClientImpl::read_response_line(Stream &strm, const Request &req, + Response &res) { + std::array buf; + + detail::stream_line_reader line_reader(strm, buf.data(), buf.size()); + + if (!line_reader.getline()) { return false; } + + const static std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n"); + + std::cmatch m; + if (!std::regex_match(line_reader.ptr(), m, re)) { + return req.method == "CONNECT"; + } + res.version = std::string(m[1]); + res.status = std::stoi(std::string(m[2])); + res.reason = std::string(m[3]); + + // Ignore '100 Continue' + while (res.status == 100) { + if (!line_reader.getline()) { return false; } // CRLF + if (!line_reader.getline()) { return false; } // next response line + + if (!std::regex_match(line_reader.ptr(), m, re)) { return false; } + res.version = std::string(m[1]); + res.status = std::stoi(std::string(m[2])); + res.reason = std::string(m[3]); + } + + return true; +} + +inline bool ClientImpl::send(Request &req, Response &res, Error &error) { + std::lock_guard request_mutex_guard(request_mutex_); + + { + std::lock_guard guard(socket_mutex_); + // Set this to false immediately - if it ever gets set to true by the end of + // the request, we know another thread instructed us to close the socket. + socket_should_be_closed_when_request_is_done_ = false; + + auto is_alive = false; + if (socket_.is_open()) { + is_alive = detail::select_write(socket_.sock, 0, 0) > 0; + if (!is_alive) { + // Attempt to avoid sigpipe by shutting down nongracefully if it seems + // like the other side has already closed the connection Also, there + // cannot be any requests in flight from other threads since we locked + // request_mutex_, so safe to close everything immediately + const bool shutdown_gracefully = false; + shutdown_ssl(socket_, shutdown_gracefully); + shutdown_socket(socket_); + close_socket(socket_); + } + } + + if (!is_alive) { + if (!create_and_connect_socket(socket_, error)) { return false; } + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + // TODO: refactoring + if (is_ssl()) { + auto &scli = static_cast(*this); + if (!proxy_host_.empty() && proxy_port_ != -1) { + bool success = false; + if (!scli.connect_with_proxy(socket_, res, success, error)) { + return success; + } + } + + if (!scli.initialize_ssl(socket_, error)) { return false; } + } +#endif + } + + // Mark the current socket as being in use so that it cannot be closed by + // anyone else while this request is ongoing, even though we will be + // releasing the mutex. + if (socket_requests_in_flight_ > 1) { + assert(socket_requests_are_from_thread_ == std::this_thread::get_id()); + } + socket_requests_in_flight_ += 1; + socket_requests_are_from_thread_ = std::this_thread::get_id(); + } + + for (const auto &header : default_headers_) { + if (req.headers.find(header.first) == req.headers.end()) { + req.headers.insert(header); + } + } + + auto close_connection = !keep_alive_; + auto ret = process_socket(socket_, [&](Stream &strm) { + return handle_request(strm, req, res, close_connection, error); + }); + + // Briefly lock mutex in order to mark that a request is no longer ongoing + { + std::lock_guard guard(socket_mutex_); + socket_requests_in_flight_ -= 1; + if (socket_requests_in_flight_ <= 0) { + assert(socket_requests_in_flight_ == 0); + socket_requests_are_from_thread_ = std::thread::id(); + } + + if (socket_should_be_closed_when_request_is_done_ || close_connection || + !ret) { + shutdown_ssl(socket_, true); + shutdown_socket(socket_); + close_socket(socket_); + } + } + + if (!ret) { + if (error == Error::Success) { error = Error::Unknown; } + } + + return ret; +} + +inline Result ClientImpl::send(const Request &req) { + auto req2 = req; + return send_(std::move(req2)); +} + +inline Result ClientImpl::send_(Request &&req) { + auto res = detail::make_unique(); + auto error = Error::Success; + auto ret = send(req, *res, error); + return Result{ret ? std::move(res) : nullptr, error, std::move(req.headers)}; +} + +inline bool ClientImpl::handle_request(Stream &strm, Request &req, + Response &res, bool close_connection, + Error &error) { + if (req.path.empty()) { + error = Error::Connection; + return false; + } + + auto req_save = req; + + bool ret; + + if (!is_ssl() && !proxy_host_.empty() && proxy_port_ != -1) { + auto req2 = req; + req2.path = "http://" + host_and_port_ + req.path; + ret = process_request(strm, req2, res, close_connection, error); + req = req2; + req.path = req_save.path; + } else { + ret = process_request(strm, req, res, close_connection, error); + } + + if (!ret) { return false; } + + if (300 < res.status && res.status < 400 && follow_location_) { + req = req_save; + ret = redirect(req, res, error); + } + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + if ((res.status == 401 || res.status == 407) && + req.authorization_count_ < 5) { + auto is_proxy = res.status == 407; + const auto &username = + is_proxy ? proxy_digest_auth_username_ : digest_auth_username_; + const auto &password = + is_proxy ? proxy_digest_auth_password_ : digest_auth_password_; + + if (!username.empty() && !password.empty()) { + std::map auth; + if (detail::parse_www_authenticate(res, auth, is_proxy)) { + Request new_req = req; + new_req.authorization_count_ += 1; + new_req.headers.erase(is_proxy ? "Proxy-Authorization" + : "Authorization"); + new_req.headers.insert(detail::make_digest_authentication_header( + req, auth, new_req.authorization_count_, detail::random_string(10), + username, password, is_proxy)); + + Response new_res; + + ret = send(new_req, new_res, error); + if (ret) { res = new_res; } + } + } + } +#endif + + return ret; +} + +inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) { + if (req.redirect_count_ == 0) { + error = Error::ExceedRedirectCount; + return false; + } + + auto location = detail::decode_url(res.get_header_value("location"), true); + if (location.empty()) { return false; } + + const static std::regex re( + R"((?:(https?):)?(?://(?:\[([\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*(?:\?[^#]*)?)(?:#.*)?)"); + + std::smatch m; + if (!std::regex_match(location, m, re)) { return false; } + + auto scheme = is_ssl() ? "https" : "http"; + + auto next_scheme = m[1].str(); + auto next_host = m[2].str(); + if (next_host.empty()) { next_host = m[3].str(); } + auto port_str = m[4].str(); + auto next_path = m[5].str(); + + auto next_port = port_; + if (!port_str.empty()) { + next_port = std::stoi(port_str); + } else if (!next_scheme.empty()) { + next_port = next_scheme == "https" ? 443 : 80; + } + + if (next_scheme.empty()) { next_scheme = scheme; } + if (next_host.empty()) { next_host = host_; } + if (next_path.empty()) { next_path = "/"; } + + if (next_scheme == scheme && next_host == host_ && next_port == port_) { + return detail::redirect(*this, req, res, next_path, location, error); + } else { + if (next_scheme == "https") { +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + SSLClient cli(next_host.c_str(), next_port); + cli.copy_settings(*this); + if (ca_cert_store_) { cli.set_ca_cert_store(ca_cert_store_); } + return detail::redirect(cli, req, res, next_path, location, error); +#else + return false; +#endif + } else { + ClientImpl cli(next_host.c_str(), next_port); + cli.copy_settings(*this); + return detail::redirect(cli, req, res, next_path, location, error); + } + } +} + +inline bool ClientImpl::write_content_with_provider(Stream &strm, + const Request &req, + Error &error) { + auto is_shutting_down = []() { return false; }; + + if (req.is_chunked_content_provider_) { + // TODO: Brotli suport + std::unique_ptr compressor; +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + if (compress_) { + compressor = detail::make_unique(); + } else +#endif + { + compressor = detail::make_unique(); + } + + return detail::write_content_chunked(strm, req.content_provider_, + is_shutting_down, *compressor, error); + } else { + return detail::write_content(strm, req.content_provider_, 0, + req.content_length_, is_shutting_down, error); + } +} // namespace httplib + +inline bool ClientImpl::write_request(Stream &strm, Request &req, + bool close_connection, Error &error) { + // Prepare additional headers + if (close_connection) { + if (!req.has_header("Connection")) { + req.headers.emplace("Connection", "close"); + } + } + + if (!req.has_header("Host")) { + if (is_ssl()) { + if (port_ == 443) { + req.headers.emplace("Host", host_); + } else { + req.headers.emplace("Host", host_and_port_); + } + } else { + if (port_ == 80) { + req.headers.emplace("Host", host_); + } else { + req.headers.emplace("Host", host_and_port_); + } + } + } + + if (!req.has_header("Accept")) { req.headers.emplace("Accept", "*/*"); } + + if (!req.has_header("User-Agent")) { + req.headers.emplace("User-Agent", "cpp-httplib/0.9"); + } + + if (req.body.empty()) { + if (req.content_provider_) { + if (!req.is_chunked_content_provider_) { + if (!req.has_header("Content-Length")) { + auto length = std::to_string(req.content_length_); + req.headers.emplace("Content-Length", length); + } + } + } else { + if (req.method == "POST" || req.method == "PUT" || + req.method == "PATCH") { + req.headers.emplace("Content-Length", "0"); + } + } + } else { + if (!req.has_header("Content-Type")) { + req.headers.emplace("Content-Type", "text/plain"); + } + + if (!req.has_header("Content-Length")) { + auto length = std::to_string(req.body.size()); + req.headers.emplace("Content-Length", length); + } + } + + if (!basic_auth_password_.empty() || !basic_auth_username_.empty()) { + if (!req.has_header("Authorization")) { + req.headers.insert(make_basic_authentication_header( + basic_auth_username_, basic_auth_password_, false)); + } + } + + if (!proxy_basic_auth_username_.empty() && + !proxy_basic_auth_password_.empty()) { + if (!req.has_header("Proxy-Authorization")) { + req.headers.insert(make_basic_authentication_header( + proxy_basic_auth_username_, proxy_basic_auth_password_, true)); + } + } + + if (!bearer_token_auth_token_.empty()) { + if (!req.has_header("Authorization")) { + req.headers.insert(make_bearer_token_authentication_header( + bearer_token_auth_token_, false)); + } + } + + if (!proxy_bearer_token_auth_token_.empty()) { + if (!req.has_header("Proxy-Authorization")) { + req.headers.insert(make_bearer_token_authentication_header( + proxy_bearer_token_auth_token_, true)); + } + } + + // Request line and headers + { + detail::BufferStream bstrm; + + const auto &path = url_encode_ ? detail::encode_url(req.path) : req.path; + bstrm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str()); + + detail::write_headers(bstrm, req.headers); + + // Flush buffer + auto &data = bstrm.get_buffer(); + if (!detail::write_data(strm, data.data(), data.size())) { + error = Error::Write; + return false; + } + } + + // Body + if (req.body.empty()) { + return write_content_with_provider(strm, req, error); + } + + if (!detail::write_data(strm, req.body.data(), req.body.size())) { + error = Error::Write; + return false; + } + + return true; +} + +inline std::unique_ptr ClientImpl::send_with_content_provider( + Request &req, + // const char *method, const char *path, const Headers &headers, + const char *body, size_t content_length, ContentProvider content_provider, + ContentProviderWithoutLength content_provider_without_length, + const char *content_type, Error &error) { + + // Request req; + // req.method = method; + // req.headers = headers; + // req.path = path; + + if (content_type) { req.headers.emplace("Content-Type", content_type); } + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + if (compress_) { req.headers.emplace("Content-Encoding", "gzip"); } +#endif + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + if (compress_ && !content_provider_without_length) { + // TODO: Brotli support + detail::gzip_compressor compressor; + + if (content_provider) { + auto ok = true; + size_t offset = 0; + DataSink data_sink; + + data_sink.write = [&](const char *data, size_t data_len) -> bool { + if (ok) { + auto last = offset + data_len == content_length; + + auto ret = compressor.compress( + data, data_len, last, [&](const char *data, size_t data_len) { + req.body.append(data, data_len); + return true; + }); + + if (ret) { + offset += data_len; + } else { + ok = false; + } + } + return ok; + }; + + data_sink.is_writable = [&](void) { return ok && true; }; + + while (ok && offset < content_length) { + if (!content_provider(offset, content_length - offset, data_sink)) { + error = Error::Canceled; + return nullptr; + } + } + } else { + if (!compressor.compress(body, content_length, true, + [&](const char *data, size_t data_len) { + req.body.append(data, data_len); + return true; + })) { + error = Error::Compression; + return nullptr; + } + } + } else +#endif + { + if (content_provider) { + req.content_length_ = content_length; + req.content_provider_ = std::move(content_provider); + req.is_chunked_content_provider_ = false; + } else if (content_provider_without_length) { + req.content_length_ = 0; + req.content_provider_ = detail::ContentProviderAdapter( + std::move(content_provider_without_length)); + req.is_chunked_content_provider_ = true; + req.headers.emplace("Transfer-Encoding", "chunked"); + } else { + req.body.assign(body, content_length); + ; + } + } + + auto res = detail::make_unique(); + return send(req, *res, error) ? std::move(res) : nullptr; +} + +inline Result ClientImpl::send_with_content_provider( + const char *method, const char *path, const Headers &headers, + const char *body, size_t content_length, ContentProvider content_provider, + ContentProviderWithoutLength content_provider_without_length, + const char *content_type) { + Request req; + req.method = method; + req.headers = headers; + req.path = path; + + auto error = Error::Success; + + auto res = send_with_content_provider( + req, + // method, path, headers, + body, content_length, std::move(content_provider), + std::move(content_provider_without_length), content_type, error); + + return Result{std::move(res), error, std::move(req.headers)}; +} + +inline std::string +ClientImpl::adjust_host_string(const std::string &host) const { + if (host.find(':') != std::string::npos) { return "[" + host + "]"; } + return host; +} + +inline bool ClientImpl::process_request(Stream &strm, Request &req, + Response &res, bool close_connection, + Error &error) { + // Send request + if (!write_request(strm, req, close_connection, error)) { return false; } + + // Receive response and headers + if (!read_response_line(strm, req, res) || + !detail::read_headers(strm, res.headers)) { + error = Error::Read; + return false; + } + + // Body + if ((res.status != 204) && req.method != "HEAD" && req.method != "CONNECT") { + auto redirect = 300 < res.status && res.status < 400 && follow_location_; + + if (req.response_handler && !redirect) { + if (!req.response_handler(res)) { + error = Error::Canceled; + return false; + } + } + + auto out = + req.content_receiver + ? static_cast( + [&](const char *buf, size_t n, uint64_t off, uint64_t len) { + if (redirect) { return true; } + auto ret = req.content_receiver(buf, n, off, len); + if (!ret) { error = Error::Canceled; } + return ret; + }) + : static_cast( + [&](const char *buf, size_t n, uint64_t /*off*/, + uint64_t /*len*/) { + if (res.body.size() + n > res.body.max_size()) { + return false; + } + res.body.append(buf, n); + return true; + }); + + auto progress = [&](uint64_t current, uint64_t total) { + if (!req.progress || redirect) { return true; } + auto ret = req.progress(current, total); + if (!ret) { error = Error::Canceled; } + return ret; + }; + + int dummy_status; + if (!detail::read_content(strm, res, (std::numeric_limits::max)(), + dummy_status, std::move(progress), std::move(out), + decompress_)) { + if (error != Error::Canceled) { error = Error::Read; } + return false; + } + } + + if (res.get_header_value("Connection") == "close" || + (res.version == "HTTP/1.0" && res.reason != "Connection established")) { + // TODO this requires a not-entirely-obvious chain of calls to be correct + // for this to be safe. Maybe a code refactor (such as moving this out to + // the send function and getting rid of the recursiveness of the mutex) + // could make this more obvious. + + // This is safe to call because process_request is only called by + // handle_request which is only called by send, which locks the request + // mutex during the process. It would be a bug to call it from a different + // thread since it's a thread-safety issue to do these things to the socket + // if another thread is using the socket. + std::lock_guard guard(socket_mutex_); + shutdown_ssl(socket_, true); + shutdown_socket(socket_); + close_socket(socket_); + } + + // Log + if (logger_) { logger_(req, res); } + + return true; +} + +inline bool +ClientImpl::process_socket(const Socket &socket, + std::function callback) { + return detail::process_client_socket( + socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_, + write_timeout_usec_, std::move(callback)); +} + +inline bool ClientImpl::is_ssl() const { return false; } + +inline Result ClientImpl::Get(const char *path) { + return Get(path, Headers(), Progress()); +} + +inline Result ClientImpl::Get(const char *path, Progress progress) { + return Get(path, Headers(), std::move(progress)); +} + +inline Result ClientImpl::Get(const char *path, const Headers &headers) { + return Get(path, headers, Progress()); +} + +inline Result ClientImpl::Get(const char *path, const Headers &headers, + Progress progress) { + Request req; + req.method = "GET"; + req.path = path; + req.headers = headers; + req.progress = std::move(progress); + + return send_(std::move(req)); +} + +inline Result ClientImpl::Get(const char *path, + ContentReceiver content_receiver) { + return Get(path, Headers(), nullptr, std::move(content_receiver), nullptr); +} + +inline Result ClientImpl::Get(const char *path, + ContentReceiver content_receiver, + Progress progress) { + return Get(path, Headers(), nullptr, std::move(content_receiver), + std::move(progress)); +} + +inline Result ClientImpl::Get(const char *path, const Headers &headers, + ContentReceiver content_receiver) { + return Get(path, headers, nullptr, std::move(content_receiver), nullptr); +} + +inline Result ClientImpl::Get(const char *path, const Headers &headers, + ContentReceiver content_receiver, + Progress progress) { + return Get(path, headers, nullptr, std::move(content_receiver), + std::move(progress)); +} + +inline Result ClientImpl::Get(const char *path, + ResponseHandler response_handler, + ContentReceiver content_receiver) { + return Get(path, Headers(), std::move(response_handler), + std::move(content_receiver), nullptr); +} + +inline Result ClientImpl::Get(const char *path, const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver) { + return Get(path, headers, std::move(response_handler), + std::move(content_receiver), nullptr); +} + +inline Result ClientImpl::Get(const char *path, + ResponseHandler response_handler, + ContentReceiver content_receiver, + Progress progress) { + return Get(path, Headers(), std::move(response_handler), + std::move(content_receiver), std::move(progress)); +} + +inline Result ClientImpl::Get(const char *path, const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver, + Progress progress) { + Request req; + req.method = "GET"; + req.path = path; + req.headers = headers; + req.response_handler = std::move(response_handler); + req.content_receiver = + [content_receiver](const char *data, size_t data_length, + uint64_t /*offset*/, uint64_t /*total_length*/) { + return content_receiver(data, data_length); + }; + req.progress = std::move(progress); + + return send_(std::move(req)); +} + +inline Result ClientImpl::Get(const char *path, const Params ¶ms, + const Headers &headers, Progress progress) { + if (params.empty()) { return Get(path, headers); } + + std::string path_with_query = append_query_params(path, params); + return Get(path_with_query.c_str(), headers, progress); +} + +inline Result ClientImpl::Get(const char *path, const Params ¶ms, + const Headers &headers, + ContentReceiver content_receiver, + Progress progress) { + return Get(path, params, headers, nullptr, content_receiver, progress); +} + +inline Result ClientImpl::Get(const char *path, const Params ¶ms, + const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver, + Progress progress) { + if (params.empty()) { + return Get(path, headers, response_handler, content_receiver, progress); + } + + std::string path_with_query = append_query_params(path, params); + return Get(path_with_query.c_str(), headers, response_handler, + content_receiver, progress); +} + +inline Result ClientImpl::Head(const char *path) { + return Head(path, Headers()); +} + +inline Result ClientImpl::Head(const char *path, const Headers &headers) { + Request req; + req.method = "HEAD"; + req.headers = headers; + req.path = path; + + return send_(std::move(req)); +} + +inline Result ClientImpl::Post(const char *path) { + return Post(path, std::string(), nullptr); +} + +inline Result ClientImpl::Post(const char *path, const char *body, + size_t content_length, + const char *content_type) { + return Post(path, Headers(), body, content_length, content_type); +} + +inline Result ClientImpl::Post(const char *path, const Headers &headers, + const char *body, size_t content_length, + const char *content_type) { + return send_with_content_provider("POST", path, headers, body, content_length, + nullptr, nullptr, content_type); +} + +inline Result ClientImpl::Post(const char *path, const std::string &body, + const char *content_type) { + return Post(path, Headers(), body, content_type); +} + +inline Result ClientImpl::Post(const char *path, const Headers &headers, + const std::string &body, + const char *content_type) { + return send_with_content_provider("POST", path, headers, body.data(), + body.size(), nullptr, nullptr, + content_type); +} + +inline Result ClientImpl::Post(const char *path, const Params ¶ms) { + return Post(path, Headers(), params); +} + +inline Result ClientImpl::Post(const char *path, size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return Post(path, Headers(), content_length, std::move(content_provider), + content_type); +} + +inline Result ClientImpl::Post(const char *path, + ContentProviderWithoutLength content_provider, + const char *content_type) { + return Post(path, Headers(), std::move(content_provider), content_type); +} + +inline Result ClientImpl::Post(const char *path, const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return send_with_content_provider("POST", path, headers, nullptr, + content_length, std::move(content_provider), + nullptr, content_type); +} + +inline Result ClientImpl::Post(const char *path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const char *content_type) { + return send_with_content_provider("POST", path, headers, nullptr, 0, nullptr, + std::move(content_provider), content_type); +} + +inline Result ClientImpl::Post(const char *path, const Headers &headers, + const Params ¶ms) { + auto query = detail::params_to_query_str(params); + return Post(path, headers, query, "application/x-www-form-urlencoded"); +} + +inline Result ClientImpl::Post(const char *path, + const MultipartFormDataItems &items) { + return Post(path, Headers(), items); +} + +inline Result ClientImpl::Post(const char *path, const Headers &headers, + const MultipartFormDataItems &items) { + return Post(path, headers, items, detail::make_multipart_data_boundary()); +} +inline Result ClientImpl::Post(const char *path, const Headers &headers, + const MultipartFormDataItems &items, + const std::string &boundary) { + for (size_t i = 0; i < boundary.size(); i++) { + char c = boundary[i]; + if (!std::isalnum(c) && c != '-' && c != '_') { + return Result{nullptr, Error::UnsupportedMultipartBoundaryChars}; + } + } + + std::string body; + + for (const auto &item : items) { + body += "--" + boundary + "\r\n"; + body += "Content-Disposition: form-data; name=\"" + item.name + "\""; + if (!item.filename.empty()) { + body += "; filename=\"" + item.filename + "\""; + } + body += "\r\n"; + if (!item.content_type.empty()) { + body += "Content-Type: " + item.content_type + "\r\n"; + } + body += "\r\n"; + body += item.content + "\r\n"; + } + + body += "--" + boundary + "--\r\n"; + + std::string content_type = "multipart/form-data; boundary=" + boundary; + return Post(path, headers, body, content_type.c_str()); +} + +inline Result ClientImpl::Put(const char *path) { + return Put(path, std::string(), nullptr); +} + +inline Result ClientImpl::Put(const char *path, const char *body, + size_t content_length, const char *content_type) { + return Put(path, Headers(), body, content_length, content_type); +} + +inline Result ClientImpl::Put(const char *path, const Headers &headers, + const char *body, size_t content_length, + const char *content_type) { + return send_with_content_provider("PUT", path, headers, body, content_length, + nullptr, nullptr, content_type); +} + +inline Result ClientImpl::Put(const char *path, const std::string &body, + const char *content_type) { + return Put(path, Headers(), body, content_type); +} + +inline Result ClientImpl::Put(const char *path, const Headers &headers, + const std::string &body, + const char *content_type) { + return send_with_content_provider("PUT", path, headers, body.data(), + body.size(), nullptr, nullptr, + content_type); +} + +inline Result ClientImpl::Put(const char *path, size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return Put(path, Headers(), content_length, std::move(content_provider), + content_type); +} + +inline Result ClientImpl::Put(const char *path, + ContentProviderWithoutLength content_provider, + const char *content_type) { + return Put(path, Headers(), std::move(content_provider), content_type); +} + +inline Result ClientImpl::Put(const char *path, const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return send_with_content_provider("PUT", path, headers, nullptr, + content_length, std::move(content_provider), + nullptr, content_type); +} + +inline Result ClientImpl::Put(const char *path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const char *content_type) { + return send_with_content_provider("PUT", path, headers, nullptr, 0, nullptr, + std::move(content_provider), content_type); +} + +inline Result ClientImpl::Put(const char *path, const Params ¶ms) { + return Put(path, Headers(), params); +} + +inline Result ClientImpl::Put(const char *path, const Headers &headers, + const Params ¶ms) { + auto query = detail::params_to_query_str(params); + return Put(path, headers, query, "application/x-www-form-urlencoded"); +} + +inline Result ClientImpl::Patch(const char *path) { + return Patch(path, std::string(), nullptr); +} + +inline Result ClientImpl::Patch(const char *path, const char *body, + size_t content_length, + const char *content_type) { + return Patch(path, Headers(), body, content_length, content_type); +} + +inline Result ClientImpl::Patch(const char *path, const Headers &headers, + const char *body, size_t content_length, + const char *content_type) { + return send_with_content_provider("PATCH", path, headers, body, + content_length, nullptr, nullptr, + content_type); +} + +inline Result ClientImpl::Patch(const char *path, const std::string &body, + const char *content_type) { + return Patch(path, Headers(), body, content_type); +} + +inline Result ClientImpl::Patch(const char *path, const Headers &headers, + const std::string &body, + const char *content_type) { + return send_with_content_provider("PATCH", path, headers, body.data(), + body.size(), nullptr, nullptr, + content_type); +} + +inline Result ClientImpl::Patch(const char *path, size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return Patch(path, Headers(), content_length, std::move(content_provider), + content_type); +} + +inline Result ClientImpl::Patch(const char *path, + ContentProviderWithoutLength content_provider, + const char *content_type) { + return Patch(path, Headers(), std::move(content_provider), content_type); +} + +inline Result ClientImpl::Patch(const char *path, const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return send_with_content_provider("PATCH", path, headers, nullptr, + content_length, std::move(content_provider), + nullptr, content_type); +} + +inline Result ClientImpl::Patch(const char *path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const char *content_type) { + return send_with_content_provider("PATCH", path, headers, nullptr, 0, nullptr, + std::move(content_provider), content_type); +} + +inline Result ClientImpl::Delete(const char *path) { + return Delete(path, Headers(), std::string(), nullptr); +} + +inline Result ClientImpl::Delete(const char *path, const Headers &headers) { + return Delete(path, headers, std::string(), nullptr); +} + +inline Result ClientImpl::Delete(const char *path, const char *body, + size_t content_length, + const char *content_type) { + return Delete(path, Headers(), body, content_length, content_type); +} + +inline Result ClientImpl::Delete(const char *path, const Headers &headers, + const char *body, size_t content_length, + const char *content_type) { + Request req; + req.method = "DELETE"; + req.headers = headers; + req.path = path; + + if (content_type) { req.headers.emplace("Content-Type", content_type); } + req.body.assign(body, content_length); + + return send_(std::move(req)); +} + +inline Result ClientImpl::Delete(const char *path, const std::string &body, + const char *content_type) { + return Delete(path, Headers(), body.data(), body.size(), content_type); +} + +inline Result ClientImpl::Delete(const char *path, const Headers &headers, + const std::string &body, + const char *content_type) { + return Delete(path, headers, body.data(), body.size(), content_type); +} + +inline Result ClientImpl::Options(const char *path) { + return Options(path, Headers()); +} + +inline Result ClientImpl::Options(const char *path, const Headers &headers) { + Request req; + req.method = "OPTIONS"; + req.headers = headers; + req.path = path; + + return send_(std::move(req)); +} + +inline size_t ClientImpl::is_socket_open() const { + std::lock_guard guard(socket_mutex_); + return socket_.is_open(); +} + +inline void ClientImpl::stop() { + std::lock_guard guard(socket_mutex_); + + // If there is anything ongoing right now, the ONLY thread-safe thing we can + // do is to shutdown_socket, so that threads using this socket suddenly + // discover they can't read/write any more and error out. Everything else + // (closing the socket, shutting ssl down) is unsafe because these actions are + // not thread-safe. + if (socket_requests_in_flight_ > 0) { + shutdown_socket(socket_); + + // Aside from that, we set a flag for the socket to be closed when we're + // done. + socket_should_be_closed_when_request_is_done_ = true; + return; + } + + // Otherwise, sitll holding the mutex, we can shut everything down ourselves + shutdown_ssl(socket_, true); + shutdown_socket(socket_); + close_socket(socket_); +} + +inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) { + connection_timeout_sec_ = sec; + connection_timeout_usec_ = usec; +} + +inline void ClientImpl::set_read_timeout(time_t sec, time_t usec) { + read_timeout_sec_ = sec; + read_timeout_usec_ = usec; +} + +inline void ClientImpl::set_write_timeout(time_t sec, time_t usec) { + write_timeout_sec_ = sec; + write_timeout_usec_ = usec; +} + +inline void ClientImpl::set_basic_auth(const char *username, + const char *password) { + basic_auth_username_ = username; + basic_auth_password_ = password; +} + +inline void ClientImpl::set_bearer_token_auth(const char *token) { + bearer_token_auth_token_ = token; +} + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +inline void ClientImpl::set_digest_auth(const char *username, + const char *password) { + digest_auth_username_ = username; + digest_auth_password_ = password; +} +#endif + +inline void ClientImpl::set_keep_alive(bool on) { keep_alive_ = on; } + +inline void ClientImpl::set_follow_location(bool on) { follow_location_ = on; } + +inline void ClientImpl::set_url_encode(bool on) { url_encode_ = on; } + +inline void ClientImpl::set_hostname_addr_map(const std::map addr_map) { + addr_map_ = std::move(addr_map); +} + +inline void ClientImpl::set_default_headers(Headers headers) { + default_headers_ = std::move(headers); +} + +inline void ClientImpl::set_address_family(int family) { + address_family_ = family; +} + +inline void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; } + +inline void ClientImpl::set_socket_options(SocketOptions socket_options) { + socket_options_ = std::move(socket_options); +} + +inline void ClientImpl::set_compress(bool on) { compress_ = on; } + +inline void ClientImpl::set_decompress(bool on) { decompress_ = on; } + +inline void ClientImpl::set_interface(const char *intf) { interface_ = intf; } + +inline void ClientImpl::set_proxy(const char *host, int port) { + proxy_host_ = host; + proxy_port_ = port; +} + +inline void ClientImpl::set_proxy_basic_auth(const char *username, + const char *password) { + proxy_basic_auth_username_ = username; + proxy_basic_auth_password_ = password; +} + +inline void ClientImpl::set_proxy_bearer_token_auth(const char *token) { + proxy_bearer_token_auth_token_ = token; +} + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +inline void ClientImpl::set_proxy_digest_auth(const char *username, + const char *password) { + proxy_digest_auth_username_ = username; + proxy_digest_auth_password_ = password; +} +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +inline void ClientImpl::set_ca_cert_path(const char *ca_cert_file_path, + const char *ca_cert_dir_path) { + if (ca_cert_file_path) { ca_cert_file_path_ = ca_cert_file_path; } + if (ca_cert_dir_path) { ca_cert_dir_path_ = ca_cert_dir_path; } +} + +inline void ClientImpl::set_ca_cert_store(X509_STORE *ca_cert_store) { + if (ca_cert_store && ca_cert_store != ca_cert_store_) { + ca_cert_store_ = ca_cert_store; + } +} +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +inline void ClientImpl::enable_server_certificate_verification(bool enabled) { + server_certificate_verification_ = enabled; +} +#endif + +inline void ClientImpl::set_logger(Logger logger) { + logger_ = std::move(logger); +} + +/* + * SSL Implementation + */ +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +namespace detail { + +template +inline SSL *ssl_new(socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex, + U SSL_connect_or_accept, V setup) { + SSL *ssl = nullptr; + { + std::lock_guard guard(ctx_mutex); + ssl = SSL_new(ctx); + } + + if (ssl) { + set_nonblocking(sock, true); + auto bio = BIO_new_socket(static_cast(sock), BIO_NOCLOSE); + BIO_set_nbio(bio, 1); + SSL_set_bio(ssl, bio, bio); + + if (!setup(ssl) || SSL_connect_or_accept(ssl) != 1) { + SSL_shutdown(ssl); + { + std::lock_guard guard(ctx_mutex); + SSL_free(ssl); + } + set_nonblocking(sock, false); + return nullptr; + } + BIO_set_nbio(bio, 0); + set_nonblocking(sock, false); + } + + return ssl; +} + +inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl, + bool shutdown_gracefully) { + // sometimes we may want to skip this to try to avoid SIGPIPE if we know + // the remote has closed the network connection + // Note that it is not always possible to avoid SIGPIPE, this is merely a + // best-efforts. + if (shutdown_gracefully) { SSL_shutdown(ssl); } + + std::lock_guard guard(ctx_mutex); + SSL_free(ssl); +} + +template +bool ssl_connect_or_accept_nonblocking(socket_t sock, SSL *ssl, + U ssl_connect_or_accept, + time_t timeout_sec, + time_t timeout_usec) { + int res = 0; + while ((res = ssl_connect_or_accept(ssl)) != 1) { + auto err = SSL_get_error(ssl, res); + switch (err) { + case SSL_ERROR_WANT_READ: + if (select_read(sock, timeout_sec, timeout_usec) > 0) { continue; } + break; + case SSL_ERROR_WANT_WRITE: + if (select_write(sock, timeout_sec, timeout_usec) > 0) { continue; } + break; + default: break; + } + return false; + } + return true; +} + +template +inline bool +process_server_socket_ssl(SSL *ssl, socket_t sock, size_t keep_alive_max_count, + time_t keep_alive_timeout_sec, + time_t read_timeout_sec, time_t read_timeout_usec, + time_t write_timeout_sec, time_t write_timeout_usec, + T callback) { + return process_server_socket_core( + sock, keep_alive_max_count, keep_alive_timeout_sec, + [&](bool close_connection, bool &connection_closed) { + SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec, + write_timeout_sec, write_timeout_usec); + return callback(strm, close_connection, connection_closed); + }); +} + +template +inline bool +process_client_socket_ssl(SSL *ssl, socket_t sock, time_t read_timeout_sec, + time_t read_timeout_usec, time_t write_timeout_sec, + time_t write_timeout_usec, T callback) { + SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec, + write_timeout_sec, write_timeout_usec); + return callback(strm); +} + +#if OPENSSL_VERSION_NUMBER < 0x10100000L +static std::shared_ptr> openSSL_locks_; + +class SSLThreadLocks { +public: + SSLThreadLocks() { + openSSL_locks_ = + std::make_shared>(CRYPTO_num_locks()); + CRYPTO_set_locking_callback(locking_callback); + } + + ~SSLThreadLocks() { CRYPTO_set_locking_callback(nullptr); } + +private: + static void locking_callback(int mode, int type, const char * /*file*/, + int /*line*/) { + auto &lk = (*openSSL_locks_)[static_cast(type)]; + if (mode & CRYPTO_LOCK) { + lk.lock(); + } else { + lk.unlock(); + } + } +}; + +#endif + +class SSLInit { +public: + SSLInit() { +#if OPENSSL_VERSION_NUMBER < 0x1010001fL + SSL_load_error_strings(); + SSL_library_init(); +#else + OPENSSL_init_ssl( + OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); +#endif + } + + ~SSLInit() { +#if OPENSSL_VERSION_NUMBER < 0x1010001fL + ERR_free_strings(); +#endif + } + +private: +#if OPENSSL_VERSION_NUMBER < 0x10100000L + SSLThreadLocks thread_init_; +#endif +}; + +// SSL socket stream implementation +inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl, + time_t read_timeout_sec, + time_t read_timeout_usec, + time_t write_timeout_sec, + time_t write_timeout_usec) + : sock_(sock), ssl_(ssl), read_timeout_sec_(read_timeout_sec), + read_timeout_usec_(read_timeout_usec), + write_timeout_sec_(write_timeout_sec), + write_timeout_usec_(write_timeout_usec) { + SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY); +} + +inline SSLSocketStream::~SSLSocketStream() {} + +inline bool SSLSocketStream::is_readable() const { + return detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0; +} + +inline bool SSLSocketStream::is_writable() const { + return detail::select_write(sock_, write_timeout_sec_, write_timeout_usec_) > + 0; +} + +inline ssize_t SSLSocketStream::read(char *ptr, size_t size) { + if (SSL_pending(ssl_) > 0) { + return SSL_read(ssl_, ptr, static_cast(size)); + } else if (is_readable()) { + auto ret = SSL_read(ssl_, ptr, static_cast(size)); + if (ret < 0) { + auto err = SSL_get_error(ssl_, ret); + int n = 1000; +#ifdef _WIN32 + while (--n >= 0 && + (err == SSL_ERROR_WANT_READ || + err == SSL_ERROR_SYSCALL && WSAGetLastError() == WSAETIMEDOUT)) { +#else + while (--n >= 0 && err == SSL_ERROR_WANT_READ) { +#endif + if (SSL_pending(ssl_) > 0) { + return SSL_read(ssl_, ptr, static_cast(size)); + } else if (is_readable()) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + ret = SSL_read(ssl_, ptr, static_cast(size)); + if (ret >= 0) { return ret; } + err = SSL_get_error(ssl_, ret); + } else { + return -1; + } + } + } + return ret; + } + return -1; +} + +inline ssize_t SSLSocketStream::write(const char *ptr, size_t size) { + if (is_writable()) { + auto ret = SSL_write(ssl_, ptr, static_cast(size)); + if (ret < 0) { + auto err = SSL_get_error(ssl_, ret); + int n = 1000; +#ifdef _WIN32 + while (--n >= 0 && + (err == SSL_ERROR_WANT_WRITE || + err == SSL_ERROR_SYSCALL && WSAGetLastError() == WSAETIMEDOUT)) { +#else + while (--n >= 0 && err == SSL_ERROR_WANT_WRITE) { +#endif + if (is_writable()) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + ret = SSL_write(ssl_, ptr, static_cast(size)); + if (ret >= 0) { return ret; } + err = SSL_get_error(ssl_, ret); + } else { + return -1; + } + } + } + return ret; + } + return -1; +} + +inline void SSLSocketStream::get_remote_ip_and_port(std::string &ip, + int &port) const { + detail::get_remote_ip_and_port(sock_, ip, port); +} + +inline socket_t SSLSocketStream::socket() const { return sock_; } + +static SSLInit sslinit_; + +} // namespace detail + +// SSL HTTP server implementation +inline SSLServer::SSLServer(const char *cert_path, const char *private_key_path, + const char *client_ca_cert_file_path, + const char *client_ca_cert_dir_path) { + ctx_ = SSL_CTX_new(TLS_method()); + + if (ctx_) { + SSL_CTX_set_options(ctx_, + SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | + SSL_OP_NO_COMPRESSION | + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); + + // auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + // SSL_CTX_set_tmp_ecdh(ctx_, ecdh); + // EC_KEY_free(ecdh); + + if (SSL_CTX_use_certificate_chain_file(ctx_, cert_path) != 1 || + SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) != + 1) { + SSL_CTX_free(ctx_); + ctx_ = nullptr; + } else if (client_ca_cert_file_path || client_ca_cert_dir_path) { + // if (client_ca_cert_file_path) { + // auto list = SSL_load_client_CA_file(client_ca_cert_file_path); + // SSL_CTX_set_client_CA_list(ctx_, list); + // } + + SSL_CTX_load_verify_locations(ctx_, client_ca_cert_file_path, + client_ca_cert_dir_path); + + SSL_CTX_set_verify( + ctx_, + SSL_VERIFY_PEER | + SSL_VERIFY_FAIL_IF_NO_PEER_CERT, // SSL_VERIFY_CLIENT_ONCE, + nullptr); + } + } +} + +inline SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key, + X509_STORE *client_ca_cert_store) { + ctx_ = SSL_CTX_new(SSLv23_server_method()); + + if (ctx_) { + SSL_CTX_set_options(ctx_, + SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | + SSL_OP_NO_COMPRESSION | + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); + + if (SSL_CTX_use_certificate(ctx_, cert) != 1 || + SSL_CTX_use_PrivateKey(ctx_, private_key) != 1) { + SSL_CTX_free(ctx_); + ctx_ = nullptr; + } else if (client_ca_cert_store) { + + SSL_CTX_set_cert_store(ctx_, client_ca_cert_store); + + SSL_CTX_set_verify( + ctx_, + SSL_VERIFY_PEER | + SSL_VERIFY_FAIL_IF_NO_PEER_CERT, // SSL_VERIFY_CLIENT_ONCE, + nullptr); + } + } +} + +inline SSLServer::~SSLServer() { + if (ctx_) { SSL_CTX_free(ctx_); } +} + +inline bool SSLServer::is_valid() const { return ctx_; } + +inline bool SSLServer::process_and_close_socket(socket_t sock) { + auto ssl = detail::ssl_new( + sock, ctx_, ctx_mutex_, + [&](SSL *ssl) { + return detail::ssl_connect_or_accept_nonblocking( + sock, ssl, SSL_accept, read_timeout_sec_, read_timeout_usec_); + }, + [](SSL * /*ssl*/) { return true; }); + + bool ret = false; + if (ssl) { + ret = detail::process_server_socket_ssl( + ssl, sock, keep_alive_max_count_, keep_alive_timeout_sec_, + read_timeout_sec_, read_timeout_usec_, write_timeout_sec_, + write_timeout_usec_, + [this, ssl](Stream &strm, bool close_connection, + bool &connection_closed) { + return process_request(strm, close_connection, connection_closed, + [&](Request &req) { req.ssl = ssl; }); + }); + + // Shutdown gracefully if the result seemed successful, non-gracefully if + // the connection appeared to be closed. + const bool shutdown_gracefully = ret; + detail::ssl_delete(ctx_mutex_, ssl, shutdown_gracefully); + } + + detail::shutdown_socket(sock); + detail::close_socket(sock); + return ret; +} + +// SSL HTTP client implementation +inline SSLClient::SSLClient(const std::string &host) + : SSLClient(host, 443, std::string(), std::string()) {} + +inline SSLClient::SSLClient(const std::string &host, int port) + : SSLClient(host, port, std::string(), std::string()) {} + +inline SSLClient::SSLClient(const std::string &host, int port, + const std::string &client_cert_path, + const std::string &client_key_path) + : ClientImpl(host, port, client_cert_path, client_key_path) { + ctx_ = SSL_CTX_new(SSLv23_client_method()); + + detail::split(&host_[0], &host_[host_.size()], '.', + [&](const char *b, const char *e) { + host_components_.emplace_back(std::string(b, e)); + }); + if (!client_cert_path.empty() && !client_key_path.empty()) { + if (SSL_CTX_use_certificate_file(ctx_, client_cert_path.c_str(), + SSL_FILETYPE_PEM) != 1 || + SSL_CTX_use_PrivateKey_file(ctx_, client_key_path.c_str(), + SSL_FILETYPE_PEM) != 1) { + SSL_CTX_free(ctx_); + ctx_ = nullptr; + } + } +} + +inline SSLClient::SSLClient(const std::string &host, int port, + X509 *client_cert, EVP_PKEY *client_key) + : ClientImpl(host, port) { + ctx_ = SSL_CTX_new(SSLv23_client_method()); + + detail::split(&host_[0], &host_[host_.size()], '.', + [&](const char *b, const char *e) { + host_components_.emplace_back(std::string(b, e)); + }); + if (client_cert != nullptr && client_key != nullptr) { + if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 || + SSL_CTX_use_PrivateKey(ctx_, client_key) != 1) { + SSL_CTX_free(ctx_); + ctx_ = nullptr; + } + } +} + +inline SSLClient::~SSLClient() { + if (ctx_) { SSL_CTX_free(ctx_); } + // Make sure to shut down SSL since shutdown_ssl will resolve to the + // base function rather than the derived function once we get to the + // base class destructor, and won't free the SSL (causing a leak). + shutdown_ssl_impl(socket_, true); +} + +inline bool SSLClient::is_valid() const { return ctx_; } + +inline void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) { + if (ca_cert_store) { + if (ctx_) { + if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store) { + // Free memory allocated for old cert and use new store `ca_cert_store` + SSL_CTX_set_cert_store(ctx_, ca_cert_store); + } + } else { + X509_STORE_free(ca_cert_store); + } + } +} + +inline long SSLClient::get_openssl_verify_result() const { + return verify_result_; +} + +inline SSL_CTX *SSLClient::ssl_context() const { return ctx_; } + +inline bool SSLClient::create_and_connect_socket(Socket &socket, Error &error) { + return is_valid() && ClientImpl::create_and_connect_socket(socket, error); +} + +// Assumes that socket_mutex_ is locked and that there are no requests in flight +inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res, + bool &success, Error &error) { + success = true; + Response res2; + if (!detail::process_client_socket( + socket.sock, read_timeout_sec_, read_timeout_usec_, + write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) { + Request req2; + req2.method = "CONNECT"; + req2.path = host_and_port_; + return process_request(strm, req2, res2, false, error); + })) { + // Thread-safe to close everything because we are assuming there are no + // requests in flight + shutdown_ssl(socket, true); + shutdown_socket(socket); + close_socket(socket); + success = false; + return false; + } + + if (res2.status == 407) { + if (!proxy_digest_auth_username_.empty() && + !proxy_digest_auth_password_.empty()) { + std::map auth; + if (detail::parse_www_authenticate(res2, auth, true)) { + Response res3; + if (!detail::process_client_socket( + socket.sock, read_timeout_sec_, read_timeout_usec_, + write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) { + Request req3; + req3.method = "CONNECT"; + req3.path = host_and_port_; + req3.headers.insert(detail::make_digest_authentication_header( + req3, auth, 1, detail::random_string(10), + proxy_digest_auth_username_, proxy_digest_auth_password_, + true)); + return process_request(strm, req3, res3, false, error); + })) { + // Thread-safe to close everything because we are assuming there are + // no requests in flight + shutdown_ssl(socket, true); + shutdown_socket(socket); + close_socket(socket); + success = false; + return false; + } + } + } else { + res = res2; + return false; + } + } + + return true; +} + +inline bool SSLClient::load_certs() { + bool ret = true; + + std::call_once(initialize_cert_, [&]() { + std::lock_guard guard(ctx_mutex_); + if (!ca_cert_file_path_.empty()) { + if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(), + nullptr)) { + ret = false; + } + } else if (!ca_cert_dir_path_.empty()) { + if (!SSL_CTX_load_verify_locations(ctx_, nullptr, + ca_cert_dir_path_.c_str())) { + ret = false; + } + } else { +#ifdef _WIN32 + detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_)); +#else + SSL_CTX_set_default_verify_paths(ctx_); +#endif + } + }); + + return ret; +} + +inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) { + auto ssl = detail::ssl_new( + socket.sock, ctx_, ctx_mutex_, + [&](SSL *ssl) { + if (server_certificate_verification_) { + if (!load_certs()) { + error = Error::SSLLoadingCerts; + return false; + } + SSL_set_verify(ssl, SSL_VERIFY_NONE, nullptr); + } + + if (!detail::ssl_connect_or_accept_nonblocking( + socket.sock, ssl, SSL_connect, connection_timeout_sec_, + connection_timeout_usec_)) { + error = Error::SSLConnection; + return false; + } + + if (server_certificate_verification_) { + verify_result_ = SSL_get_verify_result(ssl); + + if (verify_result_ != X509_V_OK) { + error = Error::SSLServerVerification; + return false; + } + + auto server_cert = SSL_get_peer_certificate(ssl); + + if (server_cert == nullptr) { + error = Error::SSLServerVerification; + return false; + } + + if (!verify_host(server_cert)) { + X509_free(server_cert); + error = Error::SSLServerVerification; + return false; + } + X509_free(server_cert); + } + + return true; + }, + [&](SSL *ssl) { + SSL_set_tlsext_host_name(ssl, host_.c_str()); + return true; + }); + + if (ssl) { + socket.ssl = ssl; + return true; + } + + shutdown_socket(socket); + close_socket(socket); + return false; +} + +inline void SSLClient::shutdown_ssl(Socket &socket, bool shutdown_gracefully) { + shutdown_ssl_impl(socket, shutdown_gracefully); +} + +inline void SSLClient::shutdown_ssl_impl(Socket &socket, + bool shutdown_gracefully) { + if (socket.sock == INVALID_SOCKET) { + assert(socket.ssl == nullptr); + return; + } + if (socket.ssl) { + detail::ssl_delete(ctx_mutex_, socket.ssl, shutdown_gracefully); + socket.ssl = nullptr; + } + assert(socket.ssl == nullptr); +} + +inline bool +SSLClient::process_socket(const Socket &socket, + std::function callback) { + assert(socket.ssl); + return detail::process_client_socket_ssl( + socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_, + write_timeout_sec_, write_timeout_usec_, std::move(callback)); +} + +inline bool SSLClient::is_ssl() const { return true; } + +inline bool SSLClient::verify_host(X509 *server_cert) const { + /* Quote from RFC2818 section 3.1 "Server Identity" + + If a subjectAltName extension of type dNSName is present, that MUST + be used as the identity. Otherwise, the (most specific) Common Name + field in the Subject field of the certificate MUST be used. Although + the use of the Common Name is existing practice, it is deprecated and + Certification Authorities are encouraged to use the dNSName instead. + + Matching is performed using the matching rules specified by + [RFC2459]. If more than one identity of a given type is present in + the certificate (e.g., more than one dNSName name, a match in any one + of the set is considered acceptable.) Names may contain the wildcard + character * which is considered to match any single domain name + component or component fragment. E.g., *.a.com matches foo.a.com but + not bar.foo.a.com. f*.com matches foo.com but not bar.com. + + In some cases, the URI is specified as an IP address rather than a + hostname. In this case, the iPAddress subjectAltName must be present + in the certificate and must exactly match the IP in the URI. + + */ + return verify_host_with_subject_alt_name(server_cert) || + verify_host_with_common_name(server_cert); +} + +inline bool +SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const { + auto ret = false; + + auto type = GEN_DNS; + + struct in6_addr addr6; + struct in_addr addr; + size_t addr_len = 0; + +#ifndef __MINGW32__ + if (inet_pton(AF_INET6, host_.c_str(), &addr6)) { + type = GEN_IPADD; + addr_len = sizeof(struct in6_addr); + } else if (inet_pton(AF_INET, host_.c_str(), &addr)) { + type = GEN_IPADD; + addr_len = sizeof(struct in_addr); + } +#endif + + auto alt_names = static_cast( + X509_get_ext_d2i(server_cert, NID_subject_alt_name, nullptr, nullptr)); + + if (alt_names) { + auto dsn_matched = false; + auto ip_mached = false; + + auto count = sk_GENERAL_NAME_num(alt_names); + + for (decltype(count) i = 0; i < count && !dsn_matched; i++) { + auto val = sk_GENERAL_NAME_value(alt_names, i); + if (val->type == type) { + auto name = (const char *)ASN1_STRING_get0_data(val->d.ia5); + auto name_len = (size_t)ASN1_STRING_length(val->d.ia5); + + switch (type) { + case GEN_DNS: dsn_matched = check_host_name(name, name_len); break; + + case GEN_IPADD: + if (!memcmp(&addr6, name, addr_len) || + !memcmp(&addr, name, addr_len)) { + ip_mached = true; + } + break; + } + } + } + + if (dsn_matched || ip_mached) { ret = true; } + } + + GENERAL_NAMES_free((STACK_OF(GENERAL_NAME) *)alt_names); + return ret; +} + +inline bool SSLClient::verify_host_with_common_name(X509 *server_cert) const { + const auto subject_name = X509_get_subject_name(server_cert); + + if (subject_name != nullptr) { + char name[BUFSIZ]; + auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName, + name, sizeof(name)); + + if (name_len != -1) { + return check_host_name(name, static_cast(name_len)); + } + } + + return false; +} + +inline bool SSLClient::check_host_name(const char *pattern, + size_t pattern_len) const { + if (host_.size() == pattern_len && host_ == pattern) { return true; } + + // Wildcard match + // https://bugs.launchpad.net/ubuntu/+source/firefox-3.0/+bug/376484 + std::vector pattern_components; + detail::split(&pattern[0], &pattern[pattern_len], '.', + [&](const char *b, const char *e) { + pattern_components.emplace_back(std::string(b, e)); + }); + + if (host_components_.size() != pattern_components.size()) { return false; } + + auto itr = pattern_components.begin(); + for (const auto &h : host_components_) { + auto &p = *itr; + if (p != h && p != "*") { + auto partial_match = (p.size() > 0 && p[p.size() - 1] == '*' && + !p.compare(0, p.size() - 1, h)); + if (!partial_match) { return false; } + } + ++itr; + } + + return true; +} +#endif + +// Universal client implementation +inline Client::Client(const std::string &scheme_host_port) + : Client(scheme_host_port, std::string(), std::string()) {} + +inline Client::Client(const std::string &scheme_host_port, + const std::string &client_cert_path, + const std::string &client_key_path) { + const static std::regex re( + R"((?:([a-z]+):\/\/)?(?:\[([\d:]+)\]|([^:/?#]+))(?::(\d+))?)"); + + std::smatch m; + if (std::regex_match(scheme_host_port, m, re)) { + auto scheme = m[1].str(); + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + if (!scheme.empty() && (scheme != "http" && scheme != "https")) { +#else + if (!scheme.empty() && scheme != "http") { +#endif + std::string msg = "'" + scheme + "' scheme is not supported."; + throw std::invalid_argument(msg); + return; + } + + auto is_ssl = scheme == "https"; + + auto host = m[2].str(); + if (host.empty()) { host = m[3].str(); } + + auto port_str = m[4].str(); + auto port = !port_str.empty() ? std::stoi(port_str) : (is_ssl ? 443 : 80); + + if (is_ssl) { +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + cli_ = detail::make_unique(host.c_str(), port, + client_cert_path, client_key_path); + is_ssl_ = is_ssl; +#endif + } else { + cli_ = detail::make_unique(host.c_str(), port, + client_cert_path, client_key_path); + } + } else { + cli_ = detail::make_unique(scheme_host_port, 80, + client_cert_path, client_key_path); + } +} + +inline Client::Client(const std::string &host, int port) + : cli_(detail::make_unique(host, port)) {} + +inline Client::Client(const std::string &host, int port, + const std::string &client_cert_path, + const std::string &client_key_path) + : cli_(detail::make_unique(host, port, client_cert_path, + client_key_path)) {} + +inline Client::~Client() {} + +inline bool Client::is_valid() const { + return cli_ != nullptr && cli_->is_valid(); +} + +inline Result Client::Get(const char *path) { return cli_->Get(path); } +inline Result Client::Get(const char *path, const Headers &headers) { + return cli_->Get(path, headers); +} +inline Result Client::Get(const char *path, Progress progress) { + return cli_->Get(path, std::move(progress)); +} +inline Result Client::Get(const char *path, const Headers &headers, + Progress progress) { + return cli_->Get(path, headers, std::move(progress)); +} +inline Result Client::Get(const char *path, ContentReceiver content_receiver) { + return cli_->Get(path, std::move(content_receiver)); +} +inline Result Client::Get(const char *path, const Headers &headers, + ContentReceiver content_receiver) { + return cli_->Get(path, headers, std::move(content_receiver)); +} +inline Result Client::Get(const char *path, ContentReceiver content_receiver, + Progress progress) { + return cli_->Get(path, std::move(content_receiver), std::move(progress)); +} +inline Result Client::Get(const char *path, const Headers &headers, + ContentReceiver content_receiver, Progress progress) { + return cli_->Get(path, headers, std::move(content_receiver), + std::move(progress)); +} +inline Result Client::Get(const char *path, ResponseHandler response_handler, + ContentReceiver content_receiver) { + return cli_->Get(path, std::move(response_handler), + std::move(content_receiver)); +} +inline Result Client::Get(const char *path, const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver) { + return cli_->Get(path, headers, std::move(response_handler), + std::move(content_receiver)); +} +inline Result Client::Get(const char *path, ResponseHandler response_handler, + ContentReceiver content_receiver, Progress progress) { + return cli_->Get(path, std::move(response_handler), + std::move(content_receiver), std::move(progress)); +} +inline Result Client::Get(const char *path, const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver, Progress progress) { + return cli_->Get(path, headers, std::move(response_handler), + std::move(content_receiver), std::move(progress)); +} +inline Result Client::Get(const char *path, const Params ¶ms, + const Headers &headers, Progress progress) { + return cli_->Get(path, params, headers, progress); +} +inline Result Client::Get(const char *path, const Params ¶ms, + const Headers &headers, + ContentReceiver content_receiver, Progress progress) { + return cli_->Get(path, params, headers, content_receiver, progress); +} +inline Result Client::Get(const char *path, const Params ¶ms, + const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver, Progress progress) { + return cli_->Get(path, params, headers, response_handler, content_receiver, + progress); +} + +inline Result Client::Head(const char *path) { return cli_->Head(path); } +inline Result Client::Head(const char *path, const Headers &headers) { + return cli_->Head(path, headers); +} + +inline Result Client::Post(const char *path) { return cli_->Post(path); } +inline Result Client::Post(const char *path, const char *body, + size_t content_length, const char *content_type) { + return cli_->Post(path, body, content_length, content_type); +} +inline Result Client::Post(const char *path, const Headers &headers, + const char *body, size_t content_length, + const char *content_type) { + return cli_->Post(path, headers, body, content_length, content_type); +} +inline Result Client::Post(const char *path, const std::string &body, + const char *content_type) { + return cli_->Post(path, body, content_type); +} +inline Result Client::Post(const char *path, const Headers &headers, + const std::string &body, const char *content_type) { + return cli_->Post(path, headers, body, content_type); +} +inline Result Client::Post(const char *path, size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return cli_->Post(path, content_length, std::move(content_provider), + content_type); +} +inline Result Client::Post(const char *path, + ContentProviderWithoutLength content_provider, + const char *content_type) { + return cli_->Post(path, std::move(content_provider), content_type); +} +inline Result Client::Post(const char *path, const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return cli_->Post(path, headers, content_length, std::move(content_provider), + content_type); +} +inline Result Client::Post(const char *path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const char *content_type) { + return cli_->Post(path, headers, std::move(content_provider), content_type); +} +inline Result Client::Post(const char *path, const Params ¶ms) { + return cli_->Post(path, params); +} +inline Result Client::Post(const char *path, const Headers &headers, + const Params ¶ms) { + return cli_->Post(path, headers, params); +} +inline Result Client::Post(const char *path, + const MultipartFormDataItems &items) { + return cli_->Post(path, items); +} +inline Result Client::Post(const char *path, const Headers &headers, + const MultipartFormDataItems &items) { + return cli_->Post(path, headers, items); +} +inline Result Client::Post(const char *path, const Headers &headers, + const MultipartFormDataItems &items, + const std::string &boundary) { + return cli_->Post(path, headers, items, boundary); +} +inline Result Client::Put(const char *path) { return cli_->Put(path); } +inline Result Client::Put(const char *path, const char *body, + size_t content_length, const char *content_type) { + return cli_->Put(path, body, content_length, content_type); +} +inline Result Client::Put(const char *path, const Headers &headers, + const char *body, size_t content_length, + const char *content_type) { + return cli_->Put(path, headers, body, content_length, content_type); +} +inline Result Client::Put(const char *path, const std::string &body, + const char *content_type) { + return cli_->Put(path, body, content_type); +} +inline Result Client::Put(const char *path, const Headers &headers, + const std::string &body, const char *content_type) { + return cli_->Put(path, headers, body, content_type); +} +inline Result Client::Put(const char *path, size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return cli_->Put(path, content_length, std::move(content_provider), + content_type); +} +inline Result Client::Put(const char *path, + ContentProviderWithoutLength content_provider, + const char *content_type) { + return cli_->Put(path, std::move(content_provider), content_type); +} +inline Result Client::Put(const char *path, const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return cli_->Put(path, headers, content_length, std::move(content_provider), + content_type); +} +inline Result Client::Put(const char *path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const char *content_type) { + return cli_->Put(path, headers, std::move(content_provider), content_type); +} +inline Result Client::Put(const char *path, const Params ¶ms) { + return cli_->Put(path, params); +} +inline Result Client::Put(const char *path, const Headers &headers, + const Params ¶ms) { + return cli_->Put(path, headers, params); +} +inline Result Client::Patch(const char *path) { return cli_->Patch(path); } +inline Result Client::Patch(const char *path, const char *body, + size_t content_length, const char *content_type) { + return cli_->Patch(path, body, content_length, content_type); +} +inline Result Client::Patch(const char *path, const Headers &headers, + const char *body, size_t content_length, + const char *content_type) { + return cli_->Patch(path, headers, body, content_length, content_type); +} +inline Result Client::Patch(const char *path, const std::string &body, + const char *content_type) { + return cli_->Patch(path, body, content_type); +} +inline Result Client::Patch(const char *path, const Headers &headers, + const std::string &body, const char *content_type) { + return cli_->Patch(path, headers, body, content_type); +} +inline Result Client::Patch(const char *path, size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return cli_->Patch(path, content_length, std::move(content_provider), + content_type); +} +inline Result Client::Patch(const char *path, + ContentProviderWithoutLength content_provider, + const char *content_type) { + return cli_->Patch(path, std::move(content_provider), content_type); +} +inline Result Client::Patch(const char *path, const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return cli_->Patch(path, headers, content_length, std::move(content_provider), + content_type); +} +inline Result Client::Patch(const char *path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const char *content_type) { + return cli_->Patch(path, headers, std::move(content_provider), content_type); +} +inline Result Client::Delete(const char *path) { return cli_->Delete(path); } +inline Result Client::Delete(const char *path, const Headers &headers) { + return cli_->Delete(path, headers); +} +inline Result Client::Delete(const char *path, const char *body, + size_t content_length, const char *content_type) { + return cli_->Delete(path, body, content_length, content_type); +} +inline Result Client::Delete(const char *path, const Headers &headers, + const char *body, size_t content_length, + const char *content_type) { + return cli_->Delete(path, headers, body, content_length, content_type); +} +inline Result Client::Delete(const char *path, const std::string &body, + const char *content_type) { + return cli_->Delete(path, body, content_type); +} +inline Result Client::Delete(const char *path, const Headers &headers, + const std::string &body, + const char *content_type) { + return cli_->Delete(path, headers, body, content_type); +} +inline Result Client::Options(const char *path) { return cli_->Options(path); } +inline Result Client::Options(const char *path, const Headers &headers) { + return cli_->Options(path, headers); +} + +inline bool Client::send(Request &req, Response &res, Error &error) { + return cli_->send(req, res, error); +} + +inline Result Client::send(const Request &req) { return cli_->send(req); } + +inline size_t Client::is_socket_open() const { return cli_->is_socket_open(); } + +inline void Client::stop() { cli_->stop(); } + +inline void Client::set_hostname_addr_map(const std::map addr_map) { + cli_->set_hostname_addr_map(std::move(addr_map)); +} + +inline void Client::set_default_headers(Headers headers) { + cli_->set_default_headers(std::move(headers)); +} + +inline void Client::set_address_family(int family) { + cli_->set_address_family(family); +} + +inline void Client::set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); } + +inline void Client::set_socket_options(SocketOptions socket_options) { + cli_->set_socket_options(std::move(socket_options)); +} + +inline void Client::set_connection_timeout(time_t sec, time_t usec) { + cli_->set_connection_timeout(sec, usec); +} + +inline void Client::set_read_timeout(time_t sec, time_t usec) { + cli_->set_read_timeout(sec, usec); +} + +inline void Client::set_write_timeout(time_t sec, time_t usec) { + cli_->set_write_timeout(sec, usec); +} + +inline void Client::set_basic_auth(const char *username, const char *password) { + cli_->set_basic_auth(username, password); +} +inline void Client::set_bearer_token_auth(const char *token) { + cli_->set_bearer_token_auth(token); +} +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +inline void Client::set_digest_auth(const char *username, + const char *password) { + cli_->set_digest_auth(username, password); +} +#endif + +inline void Client::set_keep_alive(bool on) { cli_->set_keep_alive(on); } +inline void Client::set_follow_location(bool on) { + cli_->set_follow_location(on); +} + +inline void Client::set_url_encode(bool on) { cli_->set_url_encode(on); } + +inline void Client::set_compress(bool on) { cli_->set_compress(on); } + +inline void Client::set_decompress(bool on) { cli_->set_decompress(on); } + +inline void Client::set_interface(const char *intf) { + cli_->set_interface(intf); +} + +inline void Client::set_proxy(const char *host, int port) { + cli_->set_proxy(host, port); +} +inline void Client::set_proxy_basic_auth(const char *username, + const char *password) { + cli_->set_proxy_basic_auth(username, password); +} +inline void Client::set_proxy_bearer_token_auth(const char *token) { + cli_->set_proxy_bearer_token_auth(token); +} +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +inline void Client::set_proxy_digest_auth(const char *username, + const char *password) { + cli_->set_proxy_digest_auth(username, password); +} +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +inline void Client::enable_server_certificate_verification(bool enabled) { + cli_->enable_server_certificate_verification(enabled); +} +#endif + +inline void Client::set_logger(Logger logger) { cli_->set_logger(logger); } + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +inline void Client::set_ca_cert_path(const char *ca_cert_file_path, + const char *ca_cert_dir_path) { + cli_->set_ca_cert_path(ca_cert_file_path, ca_cert_dir_path); +} + +inline void Client::set_ca_cert_store(X509_STORE *ca_cert_store) { + if (is_ssl_) { + static_cast(*cli_).set_ca_cert_store(ca_cert_store); + } else { + cli_->set_ca_cert_store(ca_cert_store); + } +} + +inline long Client::get_openssl_verify_result() const { + if (is_ssl_) { + return static_cast(*cli_).get_openssl_verify_result(); + } + return -1; // NOTE: -1 doesn't match any of X509_V_ERR_??? +} + +inline SSL_CTX *Client::ssl_context() const { + if (is_ssl_) { return static_cast(*cli_).ssl_context(); } + return nullptr; +} +#endif + +// ---------------------------------------------------------------------------- + +} // namespace httplib + +#endif // CPPHTTPLIB_HTTPLIB_H diff --git a/desync.cpp b/desync.cpp new file mode 100644 index 0000000..19a0db0 --- /dev/null +++ b/desync.cpp @@ -0,0 +1,589 @@ +#include "dpitunnel-cli.h" + +#include "desync.h" +#include "socket.h" +#include "utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +extern struct Settings_perst_s Settings_perst; +extern struct Profile_s Profile; + +const std::string FAKE_TLS_PACKET( + "\x16\x03\x01\x02\x00\x01\x00\x01\xfc\x03\x03\x9a\x8f\xa7\x6a\x5d" + "\x57\xf3\x62\x19\xbe\x46\x82\x45\xe2\x59\x5c\xb4\x48\x31\x12\x15" + "\x14\x79\x2c\xaa\xcd\xea\xda\xf0\xe1\xfd\xbb\x20\xf4\x83\x2a\x94" + "\xf1\x48\x3b\x9d\xb6\x74\xba\x3c\x81\x63\xbc\x18\xcc\x14\x45\x57" + "\x6c\x80\xf9\x25\xcf\x9c\x86\x60\x50\x31\x2e\xe9\x00\x22\x13\x01" + "\x13\x03\x13\x02\xc0\x2b\xc0\x2f\xcc\xa9\xcc\xa8\xc0\x2c\xc0\x30" + "\xc0\x0a\xc0\x09\xc0\x13\xc0\x14\x00\x33\x00\x39\x00\x2f\x00\x35" + "\x01\x00\x01\x91\x00\x00\x00\x0f\x00\x0d\x00\x00\x0a\x77\x77\x77" + "\x2e\x77\x33\x2e\x6f\x72\x67\x00\x17\x00\x00\xff\x01\x00\x01\x00" + "\x00\x0a\x00\x0e\x00\x0c\x00\x1d\x00\x17\x00\x18\x00\x19\x01\x00" + "\x01\x01\x00\x0b\x00\x02\x01\x00\x00\x23\x00\x00\x00\x10\x00\x0e" + "\x00\x0c\x02\x68\x32\x08\x68\x74\x74\x70\x2f\x31\x2e\x31\x00\x05" + "\x00\x05\x01\x00\x00\x00\x00\x00\x33\x00\x6b\x00\x69\x00\x1d\x00" + "\x20\xb0\xe4\xda\x34\xb4\x29\x8d\xd3\x5c\x70\xd3\xbe\xe8\xa7\x2a" + "\x6b\xe4\x11\x19\x8b\x18\x9d\x83\x9a\x49\x7c\x83\x7f\xa9\x03\x8c" + "\x3c\x00\x17\x00\x41\x04\x4c\x04\xa4\x71\x4c\x49\x75\x55\xd1\x18" + "\x1e\x22\x62\x19\x53\x00\xde\x74\x2f\xb3\xde\x13\x54\xe6\x78\x07" + "\x94\x55\x0e\xb2\x6c\xb0\x03\xee\x79\xa9\x96\x1e\x0e\x98\x17\x78" + "\x24\x44\x0c\x88\x80\x06\x8b\xd4\x80\xbf\x67\x7c\x37\x6a\x5b\x46" + "\x4c\xa7\x98\x6f\xb9\x22\x00\x2b\x00\x09\x08\x03\x04\x03\x03\x03" + "\x02\x03\x01\x00\x0d\x00\x18\x00\x16\x04\x03\x05\x03\x06\x03\x08" + "\x04\x08\x05\x08\x06\x04\x01\x05\x01\x06\x01\x02\x03\x02\x01\x00" + "\x2d\x00\x02\x01\x01\x00\x1c\x00\x02\x40\x01\x00\x15\x00\x96\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00", + + 517 +); + +const std::string FAKE_HTTP_PACKET( + "GET / HTTP/1.1\r\nHost: www.w3.org\r\n" + "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0\r\n" + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*" "/" "*;q=0.8\r\n" + "Accept-Encoding: gzip, deflate\r\n\r\n" +); + +int sniff_ack_packet(std::string *packet, std::string ip_srv, int port_srv, + int port_local, std::atomic *flag) { + if (packet == NULL || flag == NULL) return -1; + + // Create raw socket to sniff packet + int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP); + if (sockfd == -1) { + std::cerr << "Sniff raw socket creation failure. Errno: " << std::strerror(errno) + << std::endl; + return -1; + } + + struct pollfd fds[1]; + + // fds[0] is sniff socket + fds[0].fd = sockfd; + fds[0].events = POLLIN; + + // Set poll() timeout + int timeout = 100; + + std::string buffer(100, ' '); + struct sockaddr_in ip_srv_sockaddr; + inet_aton(ip_srv.c_str(), &ip_srv_sockaddr.sin_addr); + + while ((*flag).load()) { + int ret = poll(fds, 1, timeout); + + // Check state + if (ret == -1) { + std::cerr << "Poll error. Errno:" << std::strerror(errno) << std::endl; + break; + } else if (ret == 0) + continue; // Timeout happened + else { + if (fds[0].revents & POLLERR || + fds[0].revents & POLLHUP || + fds[0].revents & POLLNVAL) + break; + + // Get data + if (fds[0].revents & POLLIN) { + ssize_t read_size = recv(sockfd, &buffer[0], buffer.size(), 0); + if (read_size < 0) { + std::cerr << "ACK packet read error. Errno: " + << std::strerror(errno) << std::endl; + break; + } + + // Get IP header of received packet + iphdr *ip_h = (iphdr *) &buffer[0]; + // Get TCP header of received packet + tcphdr *tcp_h = (tcphdr *) (&buffer[0] + ip_h->ihl * 4); + // Get source port (server port) + int port_src_recv = ntohs(tcp_h->source); + // Get dest port (client port) + int port_dst_recv = ntohs(tcp_h->dest); + // Compare received IP/port and IP/port we waiting for + if (ip_h->saddr == ip_srv_sockaddr.sin_addr.s_addr && + port_srv == port_src_recv && port_local == port_dst_recv) { + *packet = buffer; + (*flag).store(false); + } + } + + fds[0].revents = 0; + } + } + + close(sockfd); + return 0; +} + +int sniff_handshake_packet(std::string *packet, std::string ip_srv, + int port_srv, std::atomic *local_port_atom, std::atomic *flag, + std::atomic *status, + std::promise *ready) { + + if (packet == NULL || flag == NULL) return -1; + std::map packets; + + // Create raw socket to sniff packet + int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP); + if (sockfd == -1) { + std::cerr << "Sniff raw socket creation failure. Errno: " << std::strerror(errno) + << std::endl; + return -1; + } + + struct pollfd fds[1]; + + // fds[0] is sniff socket + fds[0].fd = sockfd; + fds[0].events = POLLIN; + + // Set poll() timeout + int timeout = 100; + + std::string buffer(100, ' '); + struct sockaddr_in ip_srv_sockaddr; + inet_aton(ip_srv.c_str(), &ip_srv_sockaddr.sin_addr); + + int local_port = (*local_port_atom).load(); + bool is_searched = false; + + // Sniff thread ready + (*ready).set_value(); + + // Handle timeout + auto start = std::chrono::high_resolution_clock::now(); + + while ((*flag).load()) { + if (!is_searched && (local_port = (*local_port_atom).load()) != -1) { + auto search = packets.find(local_port); + if (search != packets.end()) { + // Found correct packet + *packet = search->second; + break; + } + is_searched = true; + } + + int ret = poll(fds, 1, timeout); + + // Check timeout + auto stop = std::chrono::high_resolution_clock::now(); + if (std::chrono::duration_cast(stop - start).count() > + Settings_perst.packet_capture_timeout) { + close(sockfd); + (*status).store(-1); + return -1; + } + + // Check state + if (ret == -1) { + std::cerr << "Poll error. Errno:" << std::strerror(errno) << std::endl; + break; + } else if (ret == 0) + continue; // Timeout happened + else { + if (fds[0].revents & POLLERR || + fds[0].revents & POLLHUP || + fds[0].revents & POLLNVAL) + break; + + // Get data + if (fds[0].revents & POLLIN) { + ssize_t read_size = recv(sockfd, &buffer[0], buffer.size(), 0); + if (read_size < 0) { + std::cerr << "ACK packet read error. Errno: " + << std::strerror(errno) << std::endl; + break; + } + + // Get IP header of received packet + iphdr *ip_h = (iphdr *) &buffer[0]; + // Get TCP header of received packet + tcphdr *tcp_h = (tcphdr *) (&buffer[0] + ip_h->ihl * 4); + // Get source port (server port) + int port_src_recv = ntohs(tcp_h->source); + // Get dest port (client port) + int port_dst_recv = ntohs(tcp_h->dest); + // Compare received IP/port and IP/port we waiting for + if (ip_h->saddr == ip_srv_sockaddr.sin_addr.s_addr && + port_srv == port_src_recv) { + if (!is_searched) + packets[port_dst_recv] = buffer; + else if (local_port == port_dst_recv) { + // Found correct packet + *packet = buffer; + break; + } + } + } + + fds[0].revents = 0; + } + } + + close(sockfd); + + (*status).store(0); + return 0; +} + +std::string +form_packet(std::string packet_raw, const char *packet_data, unsigned int packet_data_size, + unsigned short id, + unsigned short ttl, unsigned int seq, unsigned int ack_seq, + unsigned int window_size, bool is_swap_addr, uint8_t *flags /*= NULL*/) { + // Save only headers + packet_raw.resize(sizeof(struct iphdr) + sizeof(struct tcphdr)); + // Append data + if (packet_data != NULL && packet_data_size != 0) + packet_raw.append(packet_data, packet_data_size); + // Get IP header + iphdr *ip_h = (iphdr *) &packet_raw[0]; + // Get TCP header + tcphdr *tcp_h = (tcphdr *) (&packet_raw[0] + ip_h->ihl * 4); + // Fill proper data in IP header + ip_h->tos = 0; + ip_h->tot_len = htons(sizeof(struct iphdr) + sizeof(struct tcphdr) + packet_data_size); + ip_h->id = htons(id); + ip_h->frag_off = htons(0x4000); // Don't fragment + ip_h->ttl = ttl; + ip_h->check = 0; + if (is_swap_addr) + std::swap(ip_h->saddr, ip_h->daddr); + // Check sum IP + ip_h->check = cksumIp(ip_h); + // Fill proper data in TCP header + if (is_swap_addr) { + std::swap(tcp_h->source, tcp_h->dest); + std::swap(tcp_h->seq, tcp_h->ack_seq); + } + tcp_h->ack_seq = htonl(ntohl(tcp_h->ack_seq) + ack_seq); + tcp_h->seq = htonl(ntohl(tcp_h->seq) + seq); + tcp_h->window = htons(window_size); + tcp_h->doff = 5; + if (flags == NULL) { + tcp_h->fin = 0; + tcp_h->syn = 0; + tcp_h->rst = 0; + tcp_h->psh = 1; + tcp_h->ack = 1; + } else + *((uint8_t *) tcp_h + 13) = *flags; + tcp_h->urg = 0; + tcp_h->check = 0; + tcp_h->urg_ptr = 0; + // Check sum TCP + tcp_h->check = cksumTcp(ip_h, tcp_h); + ip_h = NULL; + tcp_h = NULL; + + return packet_raw; +} + +int set_ttl(int socket, int ttl) { + if (setsockopt(socket, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) < 0) { + std::cerr << "Failed to set TTL on socket. Errno: " << std::strerror(errno) << std::endl; + return -1; + } + + return 0; +} + +int do_desync_attack(int socket_srv, const std::string &ip_srv, int port_srv, int port_local, + bool is_https, + const std::string &packet_raw, const std::string &packet_data, + unsigned int last_char, unsigned int split_pos) { + + // Map IP header of server SYN, ACK packet + iphdr *srv_pack_ip_h = (iphdr *) &packet_raw[0]; + + // Create raw socket to send fake packets + int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP); + if (sockfd == -1) { + std::cerr << "Fake raw socket creation failure. Errno: " << std::strerror(errno) + << std::endl; + return -1; + } + + // Disable send buffer to send packets immediately + int sndbuf_size = 0; + if (setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &sndbuf_size, sizeof(sndbuf_size)) < 0) { + std::cerr << "Failed to set raw socket buffer size to 0. Errno: " + << std::strerror(errno) << std::endl; + close(sockfd); + return -1; + } + + // Tell system we will include IP header in packet + int yes = 1; + if (setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &yes, sizeof(yes)) < 0) { + std::cerr << "Failed to enable IP_HDRINCL. Errno: " << std::strerror(errno) << std::endl; + close(sockfd); + return -1; + } + + // Store default TTL + int default_ttl; + socklen_t size = sizeof(default_ttl); + if (getsockopt(socket_srv, IPPROTO_IP, IP_TTL, &default_ttl, &size) < 0) { + std::cerr << "Failed to get default ttl from remote server socket. Errno: " + << std::strerror(errno) << std::endl; + close(sockfd); + return -1; + } + + // Store window + int window_size; + size = sizeof(window_size); + if (getsockopt(socket_srv, IPPROTO_TCP, TCP_MAXSEG, &window_size, &size) < 0) { + std::cerr << "Failed to get default MSS from remote server socket. Errno: " + << std::strerror(errno) << std::endl; + close(sockfd); + return -1; + } + + // Fill server address + struct sockaddr_in serv_addr; + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = inet_addr(ip_srv.c_str()); + serv_addr.sin_port = htons(port_srv); + memset(serv_addr.sin_zero, '\0', sizeof(serv_addr.sin_zero)); + // Fill local address + struct sockaddr_in local_addr; + local_addr.sin_family = AF_INET; + local_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + local_addr.sin_port = htons(port_local); + memset(local_addr.sin_zero, '\0', sizeof(local_addr.sin_zero)); + + uint8_t fake_ttl = Profile.fake_packets_ttl != 0 ? Profile.fake_packets_ttl : default_ttl; + std::string packet_fake; + uint8_t flags; + std::string packet_mod; + std::string data_empty(last_char, '\x00'); + unsigned short ip_id_first = rand() % 65535; + if (Profile.auto_ttl) { + fake_ttl = tcp_get_auto_ttl(srv_pack_ip_h->ttl, Profile.auto_ttl_a1, Profile.auto_ttl_a2, + Profile.min_ttl, Profile.auto_ttl_max); + } else if (Profile.min_ttl) { + if (tcp_get_auto_ttl(srv_pack_ip_h->ttl, 0, 0, Profile.min_ttl, 0)) { + // DON'T send fakes + + // Send data packet + packet_mod = form_packet(packet_raw, packet_data.c_str(), last_char, + rand() % 65535, default_ttl, 0, 1, + Profile.window_size == 0 ? window_size : Profile.window_size, + true); + if (send_string_raw(sockfd, packet_mod, packet_mod.size(), + (struct sockaddr *) &serv_addr, sizeof(serv_addr)) == -1) { + close(sockfd); + return -1; + } + + close(sockfd); + return -1; + } + } + + // Do zero type attacks (fake, rst) + switch (Profile.desync_zero_attack) { + case DESYNC_ZERO_FAKE: + // If it's https connection send TLS ClientHello + if (is_https) + packet_fake = form_packet(packet_raw, FAKE_TLS_PACKET.c_str(), + FAKE_TLS_PACKET.size(), + rand() % 65535, fake_ttl, + Profile.wrong_seq ? Profile.wrong_seq_drift_seq : 0, + Profile.wrong_seq ? Profile.wrong_seq_drift_ack : 1, + window_size, true); + // If http send GET request + else + packet_fake = form_packet(packet_raw, FAKE_HTTP_PACKET.c_str(), + FAKE_HTTP_PACKET.size(), + rand() % 65535, fake_ttl, + Profile.wrong_seq ? Profile.wrong_seq_drift_seq : 0, + Profile.wrong_seq ? Profile.wrong_seq_drift_ack : 1, + window_size, true); + + if (send_string_raw(sockfd, packet_fake, packet_fake.size(), + (struct sockaddr *) &serv_addr, sizeof(serv_addr)) == -1) { + close(sockfd); + return -1; + } + + break; + + case DESYNC_ZERO_RST: + case DESYNC_ZERO_RSTACK: + flags = TH_RST; + if (Profile.desync_zero_attack == DESYNC_ZERO_RSTACK) + flags |= TH_ACK; + packet_fake = form_packet(packet_raw, NULL, 0, rand() % 65535, + fake_ttl, Profile.wrong_seq ? Profile.wrong_seq_drift_seq : 0, + Profile.wrong_seq ? Profile.wrong_seq_drift_ack : 1, + window_size, true, &flags); + + if (send_string_raw(sockfd, packet_fake, packet_fake.size(), + (struct sockaddr *) &serv_addr, sizeof(serv_addr)) == -1) { + close(sockfd); + return -1; + } + + break; + + case DESYNC_ZERO_NONE: + break; + default: + std::cerr << "Non valid zero desync attack type" << std::endl; + break; + + } + + // Do first type attacks (disorder, split) + switch (Profile.desync_first_attack) { + case DESYNC_FIRST_DISORDER: + case DESYNC_FIRST_DISORDER_FAKE: + + // Send second data packet(out-of-order) + packet_mod = form_packet(packet_raw, packet_data.c_str() + split_pos, + last_char - split_pos, + rand() % 65535, default_ttl, split_pos, 1, + Profile.window_size == 0 ? window_size : Profile.window_size, + true); + if (send_string_raw(sockfd, packet_mod, packet_mod.size(), + (struct sockaddr *) &serv_addr, sizeof(serv_addr)) == -1) { + close(sockfd); + return -1; + } + + // Send first fake packet + if (Profile.desync_first_attack == DESYNC_FIRST_DISORDER_FAKE) { + packet_fake = form_packet(packet_raw, data_empty.c_str(), split_pos, + ip_id_first, fake_ttl, + Profile.wrong_seq ? Profile.wrong_seq_drift_seq : 0, + Profile.wrong_seq ? Profile.wrong_seq_drift_ack : 1, + window_size, true); + if (send_string_raw(sockfd, packet_fake, packet_fake.size(), + (struct sockaddr *) &serv_addr, sizeof(serv_addr)) == -1) { + close(sockfd); + return -1; + } + } + + // Send first data packet + packet_mod = form_packet(packet_raw, packet_data.c_str(), split_pos, + ip_id_first, default_ttl, 0, 1, + Profile.window_size == 0 ? window_size : Profile.window_size, + true); + if (send_string_raw(sockfd, packet_mod, packet_mod.size(), + (struct sockaddr *) &serv_addr, sizeof(serv_addr)) == -1) { + close(sockfd); + return -1; + } + + // Send first fake packet (again) + if (Profile.desync_first_attack == DESYNC_FIRST_DISORDER_FAKE) + if (send_string_raw(sockfd, packet_fake, packet_fake.size(), + (struct sockaddr *) &serv_addr, sizeof(serv_addr)) == -1) { + close(sockfd); + return -1; + } + + break; + + case DESYNC_FIRST_SPLIT: + case DESYNC_FIRST_SPLIT_FAKE: + + // Send first fake packet + if (Profile.desync_first_attack == DESYNC_FIRST_SPLIT_FAKE) { + packet_fake = form_packet(packet_raw, data_empty.c_str(), split_pos, + ip_id_first, fake_ttl, + Profile.wrong_seq ? Profile.wrong_seq_drift_seq : 0, + Profile.wrong_seq ? Profile.wrong_seq_drift_ack : 1, + window_size, true); + if (send_string_raw(sockfd, packet_fake, packet_fake.size(), + (struct sockaddr *) &serv_addr, sizeof(serv_addr)) == -1) { + close(sockfd); + return -1; + } + } + + // Send first data packet + packet_mod = form_packet(packet_raw, packet_data.c_str(), split_pos, + ip_id_first, default_ttl, 0, 1, + Profile.window_size == 0 ? window_size : Profile.window_size, + true); + if (send_string_raw(sockfd, packet_mod, packet_mod.size(), + (struct sockaddr *) &serv_addr, sizeof(serv_addr)) == -1) { + close(sockfd); + return -1; + } + + // Send first fake packet (again) + if (Profile.desync_first_attack == DESYNC_FIRST_SPLIT_FAKE) + if (send_string_raw(sockfd, packet_fake, packet_fake.size(), + (struct sockaddr *) &serv_addr, sizeof(serv_addr)) == -1) { + close(sockfd); + return -1; + } + + // Send second data packet + packet_mod = form_packet(packet_raw, packet_data.c_str() + split_pos, + last_char - split_pos, + rand() % 65535, default_ttl, split_pos, 1, + Profile.window_size == 0 ? window_size : Profile.window_size, + true); + if (send_string_raw(sockfd, packet_mod, packet_mod.size(), + (struct sockaddr *) &serv_addr, sizeof(serv_addr)) == -1) { + close(sockfd); + return -1; + } + + break; + + case DESYNC_FIRST_NONE: + // Just send packet without bypass techniques + // Send data packet + packet_mod = form_packet(packet_raw, packet_data.c_str(), last_char, + rand() % 65535, default_ttl, 0, 1, + Profile.window_size == 0 ? window_size : Profile.window_size, + true); + if (send_string_raw(sockfd, packet_mod, packet_mod.size(), + (struct sockaddr *) &serv_addr, sizeof(serv_addr)) == -1) { + close(sockfd); + return -1; + } + + break; + default: + std::cerr << "Non valid first desync attack type" << std::endl; + break; + } + + close(sockfd); + + return 0; +} diff --git a/dns.cpp b/dns.cpp new file mode 100644 index 0000000..40daa35 --- /dev/null +++ b/dns.cpp @@ -0,0 +1,331 @@ +#include "dpitunnel-cli.h" + +#include "dns.h" +#include "ssl.h" +#include "utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define CPPHTTPLIB_OPENSSL_SUPPORT + +#include + +#include + +extern struct Settings_perst_s Settings_perst; +extern struct Profile_s Profile; + +int resolve_host_over_system(const std::string &host, std::string &ip) { + + ip.resize(50, ' '); + + struct addrinfo hints, *res; + std::memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + + int err = getaddrinfo(host.c_str(), NULL, &hints, &res); + if (err != 0) { + std::cerr << "Failed to get host address. Error: " << std::strerror(errno) << std::endl; + return -1; + } + + while (res) { + char addrstr[100]; + inet_ntop(res->ai_family, res->ai_addr->sa_data, addrstr, sizeof(addrstr)); + if (res->ai_family == AF_INET) {// If current address is ipv4 address + + void *ptr = &((struct sockaddr_in *) res->ai_addr)->sin_addr; + inet_ntop(res->ai_family, ptr, &ip[0], ip.size()); + + size_t first_zero_char = ip.find(' '); + ip = ip.substr(0, first_zero_char); + + // Free memory + freeaddrinfo(res); + return 0; + } + res = res->ai_next; + } + + // Free memory + freeaddrinfo(res); + + return -1; +} + +std::string form_dns_request(const std::string &host, unsigned short req_id) { + // Build DNS query + dns::Message dns_msg; + dns_msg.setQr(dns::Message::typeQuery); + + // Add A query to find ipv4 address + std::string host_full = host; + if (host_full.back() != '.') host_full.push_back('.'); + dns::QuerySection *qs = new dns::QuerySection(host_full); + qs->setType(dns::RDATA_A); + qs->setClass(dns::QCLASS_IN); + + dns_msg.addQuery(qs); + dns_msg.setId(req_id); + dns_msg.setRD(1); + + // Encode message + uint dns_msg_size; + std::string dns_buf(2048, ' '); + dns_msg.encode(&dns_buf[0], dns_buf.size(), dns_msg_size); + dns_buf.resize(dns_msg_size); + + return dns_buf; +} + +int resolve_host_over_udp(const std::string &host, std::string &ip) { + + unsigned short dns_req_id = rand() % 65535; + std::string dns_req = form_dns_request(host, dns_req_id); + + int sock; + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + std::cerr << "Failed to create DNS client socket. Errno: " << std::strerror(errno) + << std::endl; + return -1; + } + + // Fill server address + struct sockaddr_in server_address; + server_address.sin_family = AF_INET; + server_address.sin_port = htons(Profile.builtin_dns_port); + + if (inet_pton(AF_INET, Profile.builtin_dns_ip.c_str(), &server_address.sin_addr) <= 0) { + std::cerr << "Invalid DNS server ip address" << std::endl; + close(sock); + return -1; + } + + socklen_t server_address_len = sizeof(server_address); + + if (sendto(sock, dns_req.c_str(), dns_req.size(), 0, (const struct sockaddr *) &server_address, + server_address_len) < 0) { + std::cerr << "Failed to send DNS request. Errno: " << std::strerror(errno) << std::endl; + close(sock); + return -1; + } + + std::string response(512, '\x00'); + int bytes_read; + + struct pollfd fds[1]; + fds[0].fd = sock; + fds[0].events = POLLIN; + + // Wait for response with same id as we sent in request + dns::Message dns_msg_resp; + auto start = std::chrono::high_resolution_clock::now(); + for (;;) { + auto stop = std::chrono::high_resolution_clock::now(); + if (std::chrono::duration_cast(stop - start).count() >= + Settings_perst.builtin_dns_req_timeout) { + std::cerr << "DNS request timeout" << std::endl; + close(sock); + return -1; + } + + int ret = poll(fds, 1, Settings_perst.builtin_dns_req_timeout - + std::chrono::duration_cast( + stop - start).count()); + if (ret == -1) { + std::cerr << "Poll error. Errno:" << std::strerror(errno) << std::endl; + close(sock); + return -1; + } else if (ret == 0) + continue; + else { + if (fds[0].revents & POLLERR || + fds[0].revents & POLLHUP || + fds[0].revents & POLLNVAL) { + std::cerr << "POLLERR|POLLHUP|POLLNVAL while making DNS request" << std::endl; + close(sock); + return -1; + } + + if (fds[0].revents & POLLIN) { + if ((bytes_read = recvfrom(sock, &response[0], response.size(), 0, + (struct sockaddr *) &server_address, + &server_address_len)) <= 0) { + std::cerr << "Failed to get response from DNS server. Errno: " + << std::strerror(errno) << std::endl; + close(sock); + return -1; + } + + // Parse response + try { + dns_msg_resp.decode(response.c_str(), bytes_read); + } catch (dns::Exception &e) { + std::cerr << "Exception occured while parsing DNS response: " << e.what() + << std::endl; + close(sock); + return -1; + } + + // Found proper response + if (dns_msg_resp.getId() == dns_req_id) + break; + } + + fds[0].revents = 0; + } + } + close(sock); + + std::vector answers = dns_msg_resp.getAnswers(); + for (dns::ResourceRecord *rr: answers) { + if (rr->getType() != dns::RDATA_A) continue; + dns::RDataA *rdata = (dns::RDataA *) rr->getRData(); + unsigned char *addr = rdata->getAddress(); + std::ostringstream addr_str; + addr_str << (unsigned int) addr[0] << '.' << (unsigned int) addr[1] + << '.' << (unsigned int) addr[2] << '.' << (unsigned int) addr[3]; + ip = addr_str.str(); + + return 0; + } + + return -1; +} + +size_t writeFunction(void *ptr, size_t size, size_t nmemb, std::string *data) { + data->append((char *) ptr, size * nmemb); + return size * nmemb; +} + +int resolve_host_over_doh(const std::string &host, std::string &ip) { + + unsigned short dns_req_id = rand() % 65535; + std::string dns_req = form_dns_request(host, dns_req_id); + + // Encode with base64 + dns_req = base64_encode(dns_req); + + std::string serv_host = Profile.doh_server; + std::string path; + // Remove scheme (https://) + if (serv_host.size() >= 8 && serv_host.substr(0, 8) == "https://") + serv_host.erase(0, 8); + // Properly process test.com and test.com/dns-query urls + if (serv_host.back() == '/') serv_host.pop_back(); + size_t host_path_split_pos = serv_host.find('/'); + if (host_path_split_pos != std::string::npos) { + std::string tmp = serv_host.substr(host_path_split_pos); + serv_host.resize(serv_host.size() - tmp.size()); + path = tmp + "?dns="; + } else { + path = "/dns-query?dns="; + } + path += dns_req; + + // Make request + httplib::SSLClient cli(serv_host); + if (Profile.builtin_dns) { + std::string serv_ip; + + // Check if host is IP + struct sockaddr_in sa; + int result = inet_pton(AF_INET, serv_host.c_str(), &sa.sin_addr); + if (result <= 0) { + if (resolve_host_over_udp(serv_host, serv_ip) != 0) { + std::cerr << "Failed to get DoH IP address" << std::endl; + return -1; + } + } else + serv_ip = serv_host; + + cli.set_hostname_addr_map({{serv_host, serv_ip}}); + } + + // Load CA store + X509_STORE *store = gen_x509_store(); + if (store == NULL) { + std::cerr << "Failed to parse CA Bundle" << std::endl; + return -1; + } + cli.set_ca_cert_store(store); + cli.enable_server_certificate_verification(true); + + // Add header + httplib::Headers headers = { + {"Accept", "application/dns-message"} + }; + + std::string response_string; + httplib::Result res = cli.Get(path.c_str()); + if (res && res->status == 200) + response_string = res->body; + else { + std::cerr << "Failed to make DoH request. Errno: " << res.error() << std::endl; + return -1; + } + + // Parse response + dns::Message dns_msg_resp; + try { + dns_msg_resp.decode(response_string.c_str(), response_string.size()); + } catch (dns::Exception &e) { + std::cerr << "Exception occured while parsing DNS response: " << e.what() << std::endl; + return -1; + } + + std::vector answers = dns_msg_resp.getAnswers(); + for (dns::ResourceRecord *rr: answers) { + if (rr->getType() != dns::RDATA_A) continue; + dns::RDataA *rdata = (dns::RDataA *) rr->getRData(); + unsigned char *addr = rdata->getAddress(); + std::ostringstream addr_str; + addr_str << (unsigned int) addr[0] << '.' << (unsigned int) addr[1] + << '.' << (unsigned int) addr[2] << '.' << (unsigned int) addr[3]; + ip = addr_str.str(); + + return 0; + } + + return -1; +} + +int resolve_host(const std::string &host, std::string &ip) { + + if (host.empty()) + return -1; + + // Check if host is IP + struct sockaddr_in sa; + int result = inet_pton(AF_INET, host.c_str(), &sa.sin_addr); + if (result > 0) { + ip = host; + return 0; + } + + std::string custom_ip = find_custom_ip(host); + if (!custom_ip.empty()) { + ip = custom_ip; + return 0; + } + + if (Profile.doh) + return resolve_host_over_doh(host, ip); + else if (Profile.builtin_dns) + return resolve_host_over_udp(host, ip); + else + return resolve_host_over_system(host, ip); +} diff --git a/dnslib/CMakeLists.txt b/dnslib/CMakeLists.txt new file mode 100644 index 0000000..647f182 --- /dev/null +++ b/dnslib/CMakeLists.txt @@ -0,0 +1,14 @@ +# Set the project name +project(dnslib) + +# Add a library with the above sources +add_library(${PROJECT_NAME} + buffer.cpp + message.cpp + qs.cpp + rr.cpp +) + +target_include_directories(${PROJECT_NAME} + PUBLIC ${PROJECT_SOURCE_DIR}/include +) diff --git a/dnslib/buffer.cpp b/dnslib/buffer.cpp new file mode 100644 index 0000000..99a3bc0 --- /dev/null +++ b/dnslib/buffer.cpp @@ -0,0 +1,486 @@ +/** + * DNS Buffer + * + * Copyright (c) 2014 Michal Nezerka + * All rights reserved. + * + * Developed by: Michal Nezerka + * https://github.com/mnezerka/ + * mailto:michal.nezerka@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal with 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimers. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimers in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Michal Nezerka, nor the names of its contributors + * may be used to endorse or promote products derived from this Software + * without specific prior written permission.  + * + * 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 CONTRIBUTORS 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 WITH THE SOFTWARE. + * + * Message compression used by getDomainName and putDomainName: + * + * In order to reduce the size of messages, the domain system utilizes a + * compression scheme which eliminates the repetition of domain names in a + * message. In this scheme, an entire domain name or a list of labels at + * the end of a domain name is replaced with a pointer to a prior occurance + * of the same name. + * + * The pointer takes the form of a two octet sequence: + * + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | 1 1| OFFSET | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * The first two bits are ones. This allows a pointer to be distinguished + * from a label, since the label must begin with two zero bits because + * labels are restricted to 63 octets or less. (The 10 and 01 combinations + * are reserved for future use.) The OFFSET field specifies an offset from + * the start of the message (i.e., the first octet of the ID field in the + * domain header). A zero offset specifies the first byte of the ID field, + * etc. + * + * The compression scheme allows a domain name in a message to be + * represented as either: + * + * - a sequence of labels ending in a zero octet + * + * - a pointer + * + * - a sequence of labels ending with a pointer + * + * Pointers can only be used for occurances of a domain name where the + * format is not class specific. If this were not the case, a name server + * or resolver would be required to know the format of all RRs it handled. + * As yet, there are no such cases, but they may occur in future RDATA + * formats. + * + * If a domain name is contained in a part of the message subject to a + * length field (such as the RDATA section of an RR), and compression is + * used, the length of the compressed name is used in the length + * calculation, rather than the length of the expanded name. + * + * Programs are free to avoid using pointers in messages they generate, + * although this will reduce datagram capacity, and may cause truncation. + * However all programs are required to understand arriving messages that + * contain pointers. + * + * For example, a datagram might need to use the domain names F.ISI.ARPA, + * FOO.F.ISI.ARPA, ARPA, and the root. Ignoring the other fields of the + * message, these domain names might be represented as: + * + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * 20 | 1 | F | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * 22 | 3 | I | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * 24 | S | I | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * 26 | 4 | A | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * 28 | R | P | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * 30 | A | 0 | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * 40 | 3 | F | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * 42 | O | O | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * 44 | 1 1| 20 | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * 64 | 1 1| 26 | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * 92 | 0 | | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * The domain name for F.ISI.ARPA is shown at offset 20. The domain name + * FOO.F.ISI.ARPA is shown at offset 40; this definition uses a pointer to + * concatenate a label for FOO to the previously defined F.ISI.ARPA. The + * domain name ARPA is defined at offset 64 using a pointer to the ARPA + * component of the name F.ISI.ARPA at 20; note that this pointer relies on + * ARPA being the last label in the string at 20. The root domain name is + * defined by a single octet of zeros at 92; the root domain name has no + * labels. + */ + +#include +#include +#include +#include +#include + +#include +#include + +using namespace dns; +using namespace std; + +uchar Buffer::get8bits() +{ + // check if we are inside buffer + checkAvailableSpace(1); + uchar value = static_cast (mBufferPtr[0]); + mBufferPtr += 1; + + return value; +} + +void Buffer::put8bits(const uchar value) +{ + // check if we are inside buffer + checkAvailableSpace(1); + *mBufferPtr = value & 0xFF; + mBufferPtr++; +} + +dns::uint Buffer::get16bits() +{ + // check if we are inside buffer + checkAvailableSpace(2); + uint value = static_cast (mBufferPtr[0]); + value = value << 8; + value += static_cast (mBufferPtr[1]); + mBufferPtr += 2; + + return value; +} + +void Buffer::put16bits(const uint value) +{ + // check if we are inside buffer + checkAvailableSpace(2); + *mBufferPtr = (value & 0xFF00) >> 8; + mBufferPtr++; + *mBufferPtr = value & 0xFF; + mBufferPtr++; +} + +dns::uint Buffer::get32bits() +{ + // check if we are inside buffer + checkAvailableSpace(4); + uint value = 0; + value += (static_cast (mBufferPtr[0])) << 24; + value += (static_cast (mBufferPtr[1])) << 16; + value += (static_cast (mBufferPtr[2])) << 8; + value += static_cast (mBufferPtr[3]); + mBufferPtr += 4; + + return value; +} + +void Buffer::put32bits(const uint value) +{ + // check if we are inside buffer + checkAvailableSpace(4); + *mBufferPtr = (value & 0xFF000000) >> 24; + mBufferPtr++; + *mBufferPtr = (value & 0x00FF0000) >> 16; + mBufferPtr++; + *mBufferPtr = (value & 0x0000FF00) >> 8; + mBufferPtr++; + *mBufferPtr = value & 0x000000FF; + mBufferPtr++; +} + +void Buffer::setPos(const uint pos) +{ + // check if we are inside buffer + if (pos >= mBufferSize) + throw(Exception("Try to set pos behind buffer")); + mBufferPtr = mBuffer + pos; +} + +char* Buffer::getBytes(const uint count) +{ + checkAvailableSpace(count); + char *result = mBufferPtr; + mBufferPtr += count; + + return result; +} + +void Buffer::putBytes(const char* data, const uint count) +{ + if (count == 0) + return; + + // check if we are inside buffer + checkAvailableSpace(count); + memcpy(mBufferPtr, data, sizeof(char) * count); + mBufferPtr += count; +} + +std::string Buffer::getDnsCharacterString() +{ + std::string result(""); + + // read first octet (byte) to know length of string + uint stringLen = get8bits(); + if (stringLen > 0) + result.append(getBytes(stringLen), stringLen); // read label + + return result; +} + +void Buffer::putDnsCharacterString(const std::string& value) +{ + put8bits(value.length()); + putBytes(value.c_str(), value.length()); +} + +std::string Buffer::getDnsDomainName(const bool compressionAllowed) +{ + std::string domain; + + // store current position to avoid of endless recursion for "bad link addresses" + if (std::find(mLinkPos.begin(), mLinkPos.end(), getPos()) == mLinkPos.end()) + mLinkPos.push_back(getPos()); + else + { + mLinkPos.clear(); + throw (Exception("Decoding of domain failed because labels compression contains endless loop of links")); + } + + // read domain name from buffer + while (true) + { + // get first byte to decide if we are reading link, empty string or string of nonzero length + uint ctrlCode = get8bits(); + // if we are on the end of the string + if (ctrlCode == 0) + { + break; + } + // if we are on the link + else if (ctrlCode >> 6 == 3) + { + // check if compression is allowed + if (!compressionAllowed) + throw(Exception("Decoding of domain failed because compression link found where links are not allowed")); + + // read second byte and get link address + uint ctrlCode2 = get8bits(); + uint linkAddr = ((ctrlCode & 63) << 8) + ctrlCode2; + // change buffer position + uint saveBuffPos = getPos(); + setPos(linkAddr); + std::string linkDomain = getDnsDomainName(); + setPos(saveBuffPos); + if (domain.size() > 0) + domain.append("."); + domain.append(linkDomain); + // link always terminates the domain name (no zero at the end in this case) + break; + } + // we are reading label + else + { + if (ctrlCode > MAX_LABEL_LEN) + throw(Exception("Decoding failed because of too long domain label (max length is 63 characters)")); + + if (domain.size() > 0) + domain.append("."); + + domain.append(getBytes(ctrlCode), ctrlCode); // read label + } + } + + // check if domain contains only [A-Za-z0-9-] characters + /* + for (uint i = 0; i < domain.length(); i++) + { + if (!((domain[i] >= 'a' && domain[i] <= 'z') || + (domain[i] >= 'A' && domain[i] <= 'Z') || + (domain[i] >= '0' && domain[i] <= '9') || + (domain[i] == '0') || (domain[i] == '.'))) + { + cout << "Invalid char: " << domain[i] << endl; + throw (Exception("Decoding failed because domain name contains invalid characters (only [A-Za-z0-9-] are allowed).")); + } + } + */ + mLinkPos.pop_back(); + + if (domain.length() > MAX_DOMAIN_LEN) + throw(Exception("Decoding of domain name failed - domain name is too long.")); + + return domain; +} + +void Buffer::putDnsDomainName(const std::string& value, const bool compressionAllowed) +{ + char domain[MAX_DOMAIN_LEN + 1]; // one additional byte for teminating zero byte + uint domainLabelIndexes[MAX_DOMAIN_LEN + 1]; // one additional byte for teminating zero byte + + if (value.length() > MAX_DOMAIN_LEN) + throw(Exception("Domain name too long to be stored in dns message")); + + // write empty domain + if (value.length() == 0) + { + put8bits(0); + return; + } + + // convert value to without links as defined in RFC + // blue.ims.cz -> |4|b|l|u|e|3|i|m|s|2|c|z|0| + uint labelLen = 0; + uint labelLenPos = 0; + uint domainPos = 1; + uint ix = 0; + uint domainLabelIndexesCount = 0; + while (true) + { + if (value[ix] == '.' || ix == value.length()) + { + if (labelLen > MAX_LABEL_LEN) + throw(Exception("Encoding failed because of too long domain label (max length is 63 characters)")); + domain[labelLenPos] = labelLen; + domainLabelIndexes[domainLabelIndexesCount++] = labelLenPos; + + // ignore dot at the end since we do not want to encode + // empty label (which will produce one extra 0x00 byte) + if (value[ix] == '.' && ix == value.length() - 1) + { + break; + } + + // finish at the end of the string value + if (ix == value.length()) + { + // terminating zero byte + domain[domainPos] = 0; + domainPos++; + break; + } + + labelLenPos = domainPos; + labelLen = 0; + } + else + { + labelLen++; + domain[domainPos] = value[ix]; + } + domainPos++; + ix++; + } + + if (compressionAllowed) + { + // look for domain name parts in buffer and look for fragments for compression + // loop over all domain labels + bool compressionTipFound = false; + uint compressionTipPos = 0; + for (uint i = 0; i < domainLabelIndexesCount; i++) + { + // position of current label in domain buffer + uint domainLabelPos = (uint)domainLabelIndexes[i]; + // pointer to subdomain (including initial byte for first label length) + char* subDomain = domain + domainLabelPos; + // length of subdomain (e.g. |3|i|m|s|2|c|z|0| for blue.ims.cz) + uint subDomainLen = domainPos - domainLabelPos; + + // find buffer range that makes sense to search in + uint buffLen = mBufferPtr - mBuffer; + // search if buffer is large enough for searching + if (buffLen > subDomainLen) + { + // modify buffer length + buffLen -= subDomainLen; + // go through buffer from beginning and try to find occurence of compression tip + for (uint buffPos = 0; buffPos < buffLen ; buffPos++) + { + // compare compression tip and content at current position in buffer + compressionTipFound = (memcmp(mBuffer + buffPos, subDomain, subDomainLen) == 0); + if (compressionTipFound) + { + compressionTipPos = buffPos; + break; + } + } + } + + if (compressionTipFound) + { + // link starts with value bin(1100000000000000) + uint linkValue = 0xc000; + linkValue += compressionTipPos; + put16bits(linkValue); + break; + } + else + { + // write label + uint labelLen = subDomain[0]; + putBytes(subDomain, labelLen + 1); + } + } + + // write terminating zero if no compression tip was found and all labels are writtten to buffer + if (!compressionTipFound) + put8bits(0); + } + else + { + // compression is disabled, domain is written as it is + putBytes(domain, domainPos); + } +} + +void Buffer::dump(const uint count) +{ + cout << "Buffer dump" << endl; + cout << "size: " << mBufferSize << " bytes" << endl; + cout << "---------------------------------" << setfill('0'); + + uint dumpCount = count > 0 ? count : mBufferSize; + + for (uint i = 0; i < dumpCount; i++) { + if ((i % 10) == 0) { + cout << endl << setw(2) << i << ": "; + } + uchar c = mBuffer[i]; + cout << hex << setw(2) << int(c) << " " << dec; + } + cout << endl << setfill(' '); + cout << "---------------------------------" << endl; +} + +void Buffer::checkAvailableSpace(const uint additionalSpace) +{ + // check if buffer pointer is valid + if (mBufferPtr < mBuffer) + throw(Exception("Buffer pointer is invalid")); + + // get position in buffer + uint bufferPos = (mBufferPtr - mBuffer); + + // check if we are inside buffer + if ((bufferPos + additionalSpace) > mBufferSize) + throw(Exception("Try to read behind buffer")); +} + + diff --git a/dnslib/include/dnslib/buffer.h b/dnslib/include/dnslib/buffer.h new file mode 100644 index 0000000..ab90fee --- /dev/null +++ b/dnslib/include/dnslib/buffer.h @@ -0,0 +1,118 @@ +/** + * DNS Buffer + * + * Copyright (c) 2014 Michal Nezerka + * All rights reserved. + * + * Developed by: Michal Nezerka + * https://github.com/mnezerka/ + * mailto:michal.nezerka@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal with 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimers. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimers in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Michal Nezerka, nor the names of its contributors + * may be used to endorse or promote products derived from this Software + * without specific prior written permission.  + * + * 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 CONTRIBUTORS 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 WITH THE SOFTWARE. + * + */ + +#ifndef _DNS_BUFFER_H +#define _DNS_BUFFER_H + +#include +#include + +#include "dns.h" + +namespace dns +{ +/** + * Buffer for DNS protocol parsing and serialization + * + * is a domain name represented as a series of labels, and + * terminated by a label with zero length. + * + * is a single length octet followed by that number + * of characters. is treated as binary information, + * and can be up to 256 characters in length (including the length octet). + * + */ +class Buffer +{ + public: + Buffer(char* buffer, uint bufferSize) : mBuffer(buffer), mBufferSize(bufferSize), mBufferPtr(buffer) { } + + // get current position in buffer + uint getPos() { return mBufferPtr - mBuffer; } + + // set current position in buffer + void setPos(const uint pos); + + // get buffer size in bytes + uint getSize() { return mBufferSize; } + + // Helper function that get 8 bits from the buffer and keeps it an int. + uchar get8bits(); + void put8bits(const uchar value); + + // Helper function that get 16 bits from the buffer and keeps it an int. + uint get16bits(); + void put16bits(const uint value); + + // Helper function that get 32 bits from the buffer and keeps it an int. + uint get32bits(); + void put32bits(const uint value); + + // Helper function that gets number of bytes from the buffer + char* getBytes(uint count); + void putBytes(const char* data, uint count); + + // Helper function that gets (according to RFC 1035) from buffer + std::string getDnsCharacterString(); + void putDnsCharacterString(const std::string& value); + + // Helper function that gets (according to RFC 1035) from buffer + std::string getDnsDomainName(const bool compressionAllowed = true); + + // Helper function that puts (according to RFC 1035) to buffer + void putDnsDomainName(const std::string& value, const bool compressionAllowed = true); + + // Check if there is enough space in buffer + void checkAvailableSpace(const uint additionalSpace); + + // Function that dumps the whole buffer + void dump(const uint count = 0); + + private: + // buffer content + char* mBuffer; + // buffer content size + const uint mBufferSize; + // current position in buffer + char* mBufferPtr; + // list of link positions visited when decoding domain name + std::vector mLinkPos; +}; + +} // namespace +#endif /* _DNS_BUFFER_H */ + diff --git a/dnslib/include/dnslib/dns.h b/dnslib/include/dnslib/dns.h new file mode 100644 index 0000000..b6bad43 --- /dev/null +++ b/dnslib/include/dnslib/dns.h @@ -0,0 +1,126 @@ +/** + * DNS LIB Globals + * + * Copyright (c) 2014 Michal Nezerka + * All rights reserved. + * + * Developed by: Michal Nezerka + * https://github.com/mnezerka/ + * mailto:michal.nezerka@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal with 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimers. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimers in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Michal Nezerka, nor the names of its contributors + * may be used to endorse or promote products derived from this Software + * without specific prior written permission.  + * + * 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 CONTRIBUTORS 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 WITH THE SOFTWARE. + * + */ + +#ifndef _DNS_DNS_H +#define _DNS_DNS_H + +namespace dns { + +typedef unsigned char uchar; +typedef unsigned int uint; +typedef unsigned long ulong; +typedef unsigned char byte; + +// maximal length of domain label name +const uint MAX_MSG_LEN = 512; +const uint MAX_LABEL_LEN = 63; +const uint MAX_DOMAIN_LEN = 255; + +// CLASS types +enum eClass { + // the Internet + CLASS_IN = 1, + // the CSNET class (Obsolete) + CLASS_CS, + // the CHAOS class + CLASS_CH, + // Hesiod + CLASS_HS +}; + + +// QCLASS types +enum eQClass { + // the Internet + QCLASS_IN = 1, + // the CSNET class (Obsolete) + QCLASS_CS, + // the CHAOS class + QCLASS_CH, + // Hesiod + QCLASS_HS, + // Any class - * + QCLASS_ASTERISK = 255 +}; + +// RData types +enum eRDataType { + // a host address + RDATA_A = 1, + // an authoritative name server + RDATA_NS = 2, + // a mail destination (Obsolete - use MX) + RDATA_MD = 3, + // a mail forwarder (Obsolete - use MX) + RDATA_MF = 4, + // the canonical name for an alias + RDATA_CNAME = 5, + // marks the start of a zone of authority + RDATA_SOA = 6, + // a mailbox domain name (EXPERIMENTAL) + RDATA_MB = 7, + // a mail group member (EXPERIMENTAL) + RDATA_MG = 8, + // a mail rename domain name (EXPERIMENTAL) + RDATA_MR = 9, + // a null RR (EXPERIMENTAL) + RDATA_NULL = 10, + // a well known service description + RDATA_WKS = 11, + // a domain name pointer + RDATA_PTR = 12, + // host information + RDATA_HINFO = 13, + // mailbox or mail list information + RDATA_MINFO = 14, + // mail exchange + RDATA_MX = 15, + // text strings + RDATA_TXT = 16, + // IPv6 address + RDATA_AAAA = 28, + // naming authority pointer + RDATA_NAPTR = 35, + RDATA_SRV = 0x0021, + RDATA_A6 = 0x0026, + RDATA_OPT = 0x0029, + RDATA_ANY = 0x00ff +}; + +} // namespace +#endif /* _DNS_DNS_H */ + diff --git a/dnslib/include/dnslib/exception.h b/dnslib/include/dnslib/exception.h new file mode 100644 index 0000000..b32b6e1 --- /dev/null +++ b/dnslib/include/dnslib/exception.h @@ -0,0 +1,69 @@ +/** + * DNS Exception + * + * Copyright (c) 2014 Michal Nezerka + * All rights reserved. + * + * Developed by: Michal Nezerka + * https://github.com/mnezerka/ + * mailto:michal.nezerka@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal with 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimers. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimers in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Michal Nezerka, nor the names of its contributors + * may be used to endorse or promote products derived from this Software + * without specific prior written permission.  + * + * 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 CONTRIBUTORS 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 WITH THE SOFTWARE. + * + */ + +#ifndef _DNS_EXCEPTION_H +#define _DNS_EXCEPTION_H + +#include +#include + +namespace dns { + +/** + * Exception class extends standard exception funtionality and adds it the text + * message to inform about the reason of the exception thrown. + */ +class Exception : public std::exception { +public: + // Constructor + // @param text Information text to be filled with the reasons of the exception + Exception(const std::string& text) : m_text(text) { } + Exception(const char *text) : m_text(text) { } + virtual ~Exception() throw() { } + + // Returns the information text string + virtual const char* what() const throw() + { + return m_text.data(); + } + +private: + std::string m_text; +}; +} +#endif /* _DNS_EXCEPTION_H */ + diff --git a/dnslib/include/dnslib/message.h b/dnslib/include/dnslib/message.h new file mode 100644 index 0000000..6732ff7 --- /dev/null +++ b/dnslib/include/dnslib/message.h @@ -0,0 +1,235 @@ +/** + * DNS Message + * + * Copyright (c) 2014 Michal Nezerka + * All rights reserved. + * + * Developed by: Michal Nezerka + * https://github.com/mnezerka/ + * mailto:michal.nezerka@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal with 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimers. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimers in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Michal Nezerka, nor the names of its contributors + * may be used to endorse or promote products derived from this Software + * without specific prior written permission.  + * + * 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 CONTRIBUTORS 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 WITH THE SOFTWARE. + * + */ + +#ifndef _DNS_MESSAGE_H +#define _DNS_MESSAGE_H + +#include +#include + +#include "dns.h" +#include "rr.h" +#include "qs.h" +#include "buffer.h" + +namespace dns { + +/** + * Class represents the DNS Message. + * + * All communications inside of the domain protocol are carried in a single + * format called a message. The top level format of message is divided + * into 5 sections (some of which are empty in certain cases) shown below: + * + * +---------------------+ + * | Header | + * +---------------------+ + * | Question | the question for the name server + * +---------------------+ + * | Answer | RRs answering the question + * +---------------------+ + * | Authority | RRs pointing toward an authority + * +---------------------+ + * | Additional | RRs holding additional information + * +---------------------+ + * + * The header section is always present. The header includes fields that + * specify which of the remaining sections are present, and also specify + * whether the message is a query or a response, a standard query or some + * other opcode, etc. + * + * Header section format + * + * The header contains the following fields: + * + * 1 1 1 1 1 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | ID | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * |QR| Opcode |AA|TC|RD|RA| Z | RCODE | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | QDCOUNT | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | ANCOUNT | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | NSCOUNT | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | ARCOUNT | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * where: + * + * ID A 16 bit identifier assigned by the program that generates any kind of query. This identifier is copied + * the corresponding reply and can be used by the requester to match up replies to outstanding queries. + * + * QR A one bit field that specifies whether this message is a query (0), or a response (1). + * + * OPCODE A four bit field that specifies kind of query in this message. This value is set by the originator of a query + * and copied into the response. The values are: + * + * 0 a standard query (QUERY) + * 1 an inverse query (IQUERY) + * 2 a server status request (STATUS) + * 3-15 reserved for future use + * + * AA Authoritative Answer - this bit is valid in responses, and specifies that the responding name server is an + * authority for the domain name in question section. + * + * Note that the contents of the answer section may have multiple owner names because of aliases. The AA bit + * corresponds to the name which matches the query name, or the first owner name in the answer section. + * + * TC TrunCation - specifies that this message was truncated due to length greater than that permitted on the + * transmission channel. + * + * RD Recursion Desired - this bit may be set in a query and is copied into the response. If RD is set, it directs + * the name server to pursue the query recursively. Recursive query support is optional. + * + * RA Recursion Available - this be is set or cleared in a response, and denotes whether recursive query support is + * available in the name server. + * + * Z Reserved for future use. Must be zero in all queries and responses. + * + * RCODE Response code - this 4 bit field is set as part of + * responses. The values have the following + * interpretation: + * + * 0 No error condition + * 1 Format error - The name server was unable to interpret the query. + * 2 Server failure - The name server was unable to process this query due to a problem with + * the name server. + * 3 Name Error - Meaningful only for responses from an authoritative name + * server, this code signifies that the domain name referenced in the query does not exist. + * 4 Not Implemented - The name server does not support the requested kind of query. + * 5 Refused - The name server refuses to perform the specified operation for + * policy reasons. For example, a name server may not wish to provide the + * information to the particular requester, or a name server may not wish to perform + * a particular operation (e.g., zone transfer) for particular data. + * 6-15 Reserved for future use. + * + * QDCOUNT an unsigned 16 bit integer specifying the number of entries in the question section. + * ANCOUNT an unsigned 16 bit integer specifying the number of resource records in the answer section. + * NSCOUNT an unsigned 16 bit integer specifying the number of name server resource records in the authority records section. + * ARCOUNT an unsigned 16 bit integer specifying the number of resource records in the additional records section. + */ +class Message { + public: + static const uint typeQuery = 0; + static const uint typeResponse = 1; + + // Constructor. + Message() : mId(0), mQr(typeQuery), mOpCode(0), mAA(0), mTC(0), mRD(0), mRA(0), mRCode(0) { } + + // Virtual desctructor + ~Message(); + + // Decode DNS message from buffer + // @param buffer The buffer to code the message header into. + // @param size - size of buffer + void decode(const char* buffer, const uint size); + + // Function that codes the DNS message + // @param buffer The buffer to code the message header into. + // @param size - size of buffer + // @param validSize - number of bytes that contain encoded message + void encode(char* buffer, const uint size, uint &validSize); + + uint getId() const throw() { return mId; } + void setId(uint id) { mId = id; } + + void setQr(const uint newQr) { mQr = newQr & 1; } + uint getQr() { return mQr; } + + void setOpCode(const uint newOpCode) { mOpCode = newOpCode & 15; } + uint getOpCode() { return mOpCode; } + + void setAA(const uint newAA) { mAA = newAA & 1; } + uint getAA() { return mAA; } + + void setTC(const uint newTC) { mTC = newTC & 1; } + uint getTC() { return mTC; } + + void setRD(const uint newRD) { mRD = newRD & 1; } + uint getRD() { return mRD; } + + void setRA(const uint newRA) { mRA = newRA & 1; } + uint getRA() { return mRA; } + + void setRCode(const uint newRCode) { mRCode = newRCode & 15; } + uint getRCode() { return mRCode; } + + uint getQdCount() { return mQueries.size(); } + uint getAnCount() { return mAnswers.size(); } + uint getNsCount() { return mAuthorities.size(); } + uint getArCount() { return mAdditional.size(); } + + void addQuery(QuerySection* qs) { mQueries.push_back(qs); }; + std::vector getQueries() { return mQueries; }; + void addAnswer(ResourceRecord* rr) { mAnswers.push_back(rr); }; + std::vector getAnswers() { return mAnswers; }; + void addAuthority(ResourceRecord* rr) { mAuthorities.push_back(rr); }; + std::vector getAuthorities() { return mAuthorities; }; + void addAdditional(ResourceRecord* rr) { mAdditional.push_back(rr); }; + std::vector getAdditional() { return mAdditional; }; + + // Returns the DNS message header as a string text. + std::string asString(); + + private: + static const uint HDR_OFFSET = 12; + + uint mId; + uint mQr; + uint mOpCode; + uint mAA; + uint mTC; + uint mRD; + uint mRA; + uint mRCode; + + std::vector mQueries; + std::vector mAnswers; + std::vector mAuthorities; + std::vector mAdditional; + + void decodeResourceRecords(Buffer &buffer, uint count, std::vector &list); + void removeAllRecords(); + +}; +} // namespace +#endif /* _DNS_MESSAGE_H */ + diff --git a/dnslib/include/dnslib/qs.h b/dnslib/include/dnslib/qs.h new file mode 100644 index 0000000..49a535e --- /dev/null +++ b/dnslib/include/dnslib/qs.h @@ -0,0 +1,125 @@ +/** + * DNS Question Section + * + * Copyright (c) 2014 Michal Nezerka + * All rights reserved. + * + * Developed by: Michal Nezerka + * https://github.com/mnezerka/ + * mailto:michal.nezerka@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal with 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimers. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimers in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Michal Nezerka, nor the names of its contributors + * may be used to endorse or promote products derived from this Software + * without specific prior written permission.  + * + * 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 CONTRIBUTORS 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 WITH THE SOFTWARE. + * + */ + +#ifndef _DNS_QS_H +#define _DNS_QS_H + +#include +#include + +#include "dns.h" +#include "buffer.h" + +namespace dns { + +/* Class represents a DNS Question Section Entry + * + * The DNS Question section entry has the following format: + * + * 1 1 1 1 1 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | | + * / QNAME / + * / / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | QTYPE | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | QCLASS | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * where: + * + * QNAME a domain name represented as a sequence of labels, where + * each label consists of a length octet followed by that + * number of octets. The domain name terminates with the + * zero length octet for the null label of the root. Note + * that this field may be an odd number of octets; no + * padding is used. + * + * QTYPE a two octet code which specifies the type of the query. + * The values for this field include all codes valid for a + * TYPE field, together with some more general codes which + * can match more than one type of RR. + * + * QCLASS a two octet code that specifies the class of the query. + * For example, the QCLASS field is IN for the Internet. + */ +class QuerySection +{ +public: + + /* Constructor */ + QuerySection(const std::string& qName = "") : mQName(qName), mQType(0), mQClass(QCLASS_IN) { }; + + /* Set type of the query */ + void setType(uint qType) { mQType = qType; }; + + /* Set type class of the query */ + void setClass(eQClass qClass) { mQClass = qClass; }; + + /* Set name field from a string */ + void setName(const std::string& qName) { mQName = qName; } ; + + /* Get name filed of the query */ + std::string getName() const { return mQName; } ; + + /* Get the type of the query */ + uint getType() const { return mQType; }; + + /* Get the class of the query */ + eQClass getClass() const { return mQClass; } ; + + void encode(Buffer &buffer); + + std::string asString(); + +private: + + // Name of the query + std::string mQName; + + // Type field + uint mQType; + + // Class of the query + eQClass mQClass; +}; + +} // namespace +#endif /* _DNS_QS_H */ + diff --git a/dnslib/include/dnslib/rr.h b/dnslib/include/dnslib/rr.h new file mode 100644 index 0000000..f9f6cd1 --- /dev/null +++ b/dnslib/include/dnslib/rr.h @@ -0,0 +1,571 @@ +/** + * DNS Resource Record + * + * Copyright (c) 2014 Michal Nezerka + * All rights reserved. + * + * Developed by: Michal Nezerka + * https://github.com/mnezerka/ + * mailto:michal.nezerka@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal with 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimers. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimers in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Michal Nezerka, nor the names of its contributors + * may be used to endorse or promote products derived from this Software + * without specific prior written permission.  + * + * 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 CONTRIBUTORS 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 WITH THE SOFTWARE. + */ + +#ifndef _DNS_RR_H +#define _DNS_RR_H + +#include +#include +#include + +#include "dns.h" +#include "buffer.h" + +namespace dns { + +/** Abstract class that act as base for all Resource Record RData types */ +class RData { + public: + virtual ~RData() { }; + virtual eRDataType getType() = 0; + virtual void decode(Buffer &buffer, const uint size) = 0; + virtual void encode(Buffer &buffer) = 0; + virtual std::string asString() = 0; +}; + +/** +* RData with name of type dns domain +*/ +class RDataWithName: public RData { + public: + RDataWithName() : mName("") { }; + virtual ~RDataWithName() { }; + virtual void decode(Buffer &buffer, const uint size); + virtual void encode(Buffer &buffer); + + virtual void setName(const std::string& newName) { mName = newName; }; + virtual std::string getName() { return mName; }; + + private: + // as defined in DNS RFC (sequence of labels) + std::string mName; +}; + +/** + * CName Representation + */ +class RDataCNAME: public RDataWithName { + public: + virtual eRDataType getType() { return RDATA_CNAME; }; + virtual std::string asString(); +}; + +/** + * HINFO Record Representation + */ +class RDataHINFO: public RData { + public: + RDataHINFO() : mCpu(""), mOs("") { }; + virtual ~RDataHINFO() { }; + + virtual eRDataType getType() { return RDATA_HINFO; }; + + void setCpu(const std::string& newCpu) { mCpu = newCpu; }; + std::string getCpu() { return mCpu; }; + + void setOs(const std::string& newOs) { mOs = newOs; }; + std::string getOs() { return mOs; }; + + virtual void decode(Buffer &buffer, const uint size); + virtual void encode(Buffer &buffer); + virtual std::string asString(); + + private: + // CPU type + std::string mCpu; + // Operating system type + std::string mOs; +}; + + /** + * MB RData Representation + * + * A name specifies of host which has the specified mailbox. + */ +class RDataMB: public RDataWithName { + public: + virtual eRDataType getType() { return RDATA_MB; }; + virtual std::string asString(); +}; + + /** + * MD RData Representation + * + * A specifies a host which has a mail agent for the domain + * which should be able to deliver mail for the domain. + */ +class RDataMD: public RDataWithName { + public: + virtual eRDataType getType() { return RDATA_MD; }; + virtual std::string asString(); +}; + + /** + * MF RData Representation + * + * A which specifies a host which has a mail agent for the domain + * which will accept mail for forwarding to the domain. + */ +class RDataMF: public RDataWithName { + public: + virtual eRDataType getType() { return RDATA_MF; }; + virtual std::string asString(); +}; + +/** +* MG RData Representation +* +* A which specifies a mailbox which is a member of the mail group +* specified by the domain name. +*/ +class RDataMG: public RDataWithName { + public: + virtual eRDataType getType() { return RDATA_MG; }; + virtual std::string asString(); +}; + +/** + * MINFO Record Representation + */ +class RDataMINFO: public RData { + public: + RDataMINFO() : mRMailBx(""), mMailBx("") { }; + virtual ~RDataMINFO() { }; + + virtual eRDataType getType() { return RDATA_MINFO; }; + + void setRMailBx(const std::string& newRMailBx) { mRMailBx = newRMailBx; }; + std::string getRMailBx() { return mRMailBx; }; + + void setMailBx(const std::string& newMailBx) { mMailBx = newMailBx; }; + std::string getMailBx() { return mMailBx; }; + + virtual void decode(Buffer &buffer, const uint size); + virtual void encode(Buffer &buffer); + virtual std::string asString(); + + private: + // A which specifies a mailbox which is + // responsible for the mailing list or mailbox. + std::string mRMailBx; + // A which specifies a mailbox which is to + // receive error messages related to the mailing list or + // mailbox specified by the owner of the MINFO RR. + std::string mMailBx; +}; + +/** +* MR RData Representation +* +* A which specifies a mailbox which is the +* proper rename of the specified mailbox. +*/ +class RDataMR: public RDataWithName { + public: + virtual eRDataType getType() { return RDATA_MR; }; + virtual std::string asString(); +}; + +/** + * MX Record Representation + */ +class RDataMX: public RData { + public: + RDataMX() : mPreference(0), mExchange("") { }; + virtual ~RDataMX() { }; + + virtual eRDataType getType() { return RDATA_MX; }; + + void setPreference(const uint newPreference) { mPreference = newPreference; }; + uint getPreference() { return mPreference; }; + + void setExchange(const std::string& newExchange) { mExchange = newExchange; }; + std::string getExchange() { return mExchange; }; + + virtual void decode(Buffer &buffer, const uint size); + virtual void encode(Buffer &buffer); + virtual std::string asString(); + + private: + // A 16 bit integer which specifies the preference given to + // this RR among others at the same owner. Lower values are preferred. + uint mPreference; + // A which specifies a host willing to act + // as a mail exchange for the owner name + std::string mExchange; +}; + +/** Generic RData field which stores raw RData bytes. + * + * This class is used for cases when RData type is not known or + * class for appropriate type is not implemented. */ +class RDataNULL : public RData { + public: + RDataNULL() : mDataSize(0), mData(NULL) { }; + virtual ~RDataNULL(); + virtual eRDataType getType() { return RDATA_NULL; }; + virtual void decode(Buffer &buffer, const uint size); + virtual void encode(Buffer &buffer); + virtual std::string asString(); + + private: + // raw data + uint mDataSize; + char* mData; +}; + +/** +* NS RData Representation +* +* A which specifies a host which should be +* authoritative for the specified class and domain. +*/ +class RDataNS: public RDataWithName { + public: + virtual eRDataType getType() { return RDATA_NS; }; + virtual std::string asString(); +}; + +/** +* PTR RData Representation +* +* A which points to some location in the +* domain name space. +*/ +class RDataPTR: public RDataWithName { + public: + virtual eRDataType getType() { return RDATA_PTR; }; + virtual std::string asString(); +}; + +/** + * SOA Record Representation + */ +class RDataSOA: public RData { + public: + RDataSOA() : mMName(""), mRName(""), mSerial(0), mRefresh(0), mRetry(0), mExpire(0), mMinimum(0) { }; + virtual ~RDataSOA() { }; + + virtual eRDataType getType() { return RDATA_SOA; }; + + void setMName(const std::string& newMName) { mMName = newMName; }; + std::string getMName() { return mMName; }; + + void setRName(const std::string& newRName) { mRName = newRName; }; + std::string getRName() { return mRName; }; + + void setSerial(const uint newSerial) { mSerial = newSerial; }; + uint getSerial() { return mSerial; }; + + void setRefresh(const uint newRefresh) { mRefresh = newRefresh; }; + uint getRefresh() { return mRefresh; }; + + void setRetry(const uint newRetry) { mRetry = newRetry; }; + uint getRetry() { return mRetry; }; + + void setExpire(const uint newExpire) { mExpire = newExpire; }; + uint getExpire() { return mExpire; }; + + void setMinimum(const uint newMinimum) { mMinimum = newMinimum; }; + uint getMinimum() { return mMinimum; }; + + virtual void decode(Buffer &buffer, const uint size); + virtual void encode(Buffer &buffer); + virtual std::string asString(); + + private: + // The of the name server that was the + // original or primary source of data for this zone. + std::string mMName; + // A which specifies the mailbox of the + // person responsible for this zone. + std::string mRName; + // The unsigned 32 bit version number of the original copy + // of the zone. Zone transfers preserve this value. This + // value wraps and should be compared using sequence space + // arithmetic. + uint mSerial; + // A 32 bit time interval before the zone should be refreshed. + uint mRefresh; + // A 32 bit time interval that should elapse before a + // failed refresh should be retried. + uint mRetry; + // A 32 bit time value that specifies the upper limit on + // the time interval that can elapse before the zone is no + // longer authoritative. + uint mExpire; + // The unsigned 32 bit minimum TTL field that should be + // exported with any RR from this zone. + uint mMinimum; +}; + +/** + * TXT Record Representation + * + * TXT RRs are used to hold descriptive text. The semantics of the text + * depends on the domain where it is found. + */ +class RDataTXT: public RData { + public: + RDataTXT() { }; + virtual ~RDataTXT() { }; + virtual eRDataType getType() { return RDATA_TXT; }; + virtual void decode(Buffer &buffer, const uint size); + virtual void encode(Buffer &buffer); + virtual std::string asString(); + + virtual void addTxt(const std::string& newTxt) { mTexts.push_back(newTxt); }; + //virtual std::string getTxt() { return mTxt; }; + + private: + // One or more s. + std::vector mTexts; +}; + +/** + * A Record Representation (IPv4 address) + */ +class RDataA: public RData { + public: + RDataA() { for (uint i = 0; i < 4; i++) mAddr[i] = 0; }; + virtual ~RDataA() { }; + + virtual eRDataType getType() { return RDATA_A; }; + + void setAddress(const uchar *addr) { for (uint i = 0; i < 4; i++) mAddr[i] = addr[i]; }; + void setAddress(const std::string &addr) { inet_pton(AF_INET, addr.c_str(), &mAddr); }; + uchar* getAddress() { return mAddr; }; + + virtual void decode(Buffer &buffer, const uint size); + virtual void encode(Buffer &buffer); + virtual std::string asString(); + + private: + // 32 bit internet address. + uchar mAddr[4]; +}; + +/** + * WKS Record Representation + */ +class RDataWKS: public RData { + public: + RDataWKS() : mProtocol(0), mBitmap(NULL), mBitmapSize(0) { for (uint i = 0; i < 4; i++) mAddr[i] = 0; }; + virtual ~RDataWKS(); + virtual eRDataType getType() { return RDATA_WKS; }; + + void setAddress(const uchar *addr) { for (uint i = 0; i < 4; i++) mAddr[i] = addr[i]; }; + uchar* getAddress() { return mAddr; }; + + void setProtocol(const uint newProtocol) { mProtocol = newProtocol; }; + uint getProtocol() { return mProtocol; }; + + uint getBitmapSize() { return mBitmapSize; } + + virtual void decode(Buffer &buffer, const uint size); + virtual void encode(Buffer &buffer); + virtual std::string asString(); + + private: + // 32 bit internet address. + uchar mAddr[4]; + // An 8 bit IP protocol number + uint mProtocol; + // A variable length bit map. The bit map must be a + // multiple of 8 bits long. + char *mBitmap; + // Size of bitmap + uint mBitmapSize; +}; + +/** + * AAAA Record Representation (IPv6 address) + */ +class RDataAAAA: public RData { + public: + RDataAAAA() { for (uint i = 0; i < 16; i++) mAddr[i] = 0; }; + virtual ~RDataAAAA() { }; + + virtual eRDataType getType() { return RDATA_AAAA; }; + + void setAddress(const uchar *addr) { for (uint i = 0; i < 16; i++) mAddr[i] = addr[i]; }; + uchar* getAddress() { return mAddr; }; + + virtual void decode(Buffer &buffer, const uint size); + virtual void encode(Buffer &buffer); + virtual std::string asString(); + + private: + // 128 bit IPv6 address. + uchar mAddr[16]; +}; + + +// http://www.ietf.org/rfc/rfc2915.txt - NAPTR +class RDataNAPTR : public RData { + public: + RDataNAPTR() : mOrder(0), mPreference(0), mFlags(""), mServices(""), mRegExp(""), mReplacement("") { }; + virtual ~RDataNAPTR() { }; + + virtual eRDataType getType() { return RDATA_NAPTR; }; + + void setOrder(uint newOrder) { mOrder = newOrder; }; + uint getOrder() { return mOrder; }; + void setPreference(uint newPreference) { mPreference = newPreference; }; + uint getPreference() { return mPreference; }; + void setFlags (std::string newFlags) { mFlags = newFlags; }; + std::string getFlags () { return mFlags; }; + void setServices (std::string newServices) { mServices = newServices; }; + std::string getServices () { return mServices; }; + void setRegExp (std::string newRegExp) { mRegExp = newRegExp; }; + std::string getRegExp () { return mRegExp; }; + void setReplacement (std::string newReplacement) { mReplacement = newReplacement; }; + std::string getReplacement () { return mReplacement; }; + + virtual void decode(Buffer &buffer, const uint size); + virtual void encode(Buffer &buffer); + virtual std::string asString(); + + private: + uint mOrder; + uint mPreference; + std::string mFlags; + std::string mServices; + std::string mRegExp; + std::string mReplacement; +}; + +/** Represents DNS Resource Record + * + * Each resource record has the following format: + * + * 1 1 1 1 1 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | | + * / / + * / NAME / + * | | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | TYPE | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | CLASS | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | TTL | + * | | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | RDLENGTH | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--| + * / RDATA / + * / / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * + * where: + * + * NAME a domain name to which this resource record pertains. + * + * TYPE two octets containing one of the RR type codes. This + * field specifies the meaning of the data in the RDATA + * field. + * + * CLASS two octets which specify the class of the data in the + * RDATA field. + * + * TTL a 32 bit unsigned integer that specifies the time + * interval (in seconds) that the resource record may be + * cached before it should be discarded. Zero values are + * interpreted to mean that the RR can only be used for the + * transaction in progress, and should not be cached. + * + * RDLENGTH an unsigned 16 bit integer that specifies the length in + * octets of the RDATA field. + * + * RDATA a variable length string of octets that describes the + * resource. The format of this information varies + * according to the TYPE and CLASS of the resource record. + * For example, the if the TYPE is A and the CLASS is IN, + * the RDATA field is a 4 octet ARPA Internet address. + */ +class ResourceRecord +{ + public: + /* Constructor */ + ResourceRecord() : mName(""), mType (RDATA_NULL), mClass(CLASS_IN), mTtl(0), mRDataSize(0), mRData(NULL) { }; + ~ResourceRecord(); + + void setName(std::string newName) { mName = newName; }; + uint getName() const; + + void setType(const eRDataType type) { mType = type; }; + eRDataType getType() { return mType; }; + + void setClass(eClass newClass) { mClass = newClass; }; + eClass getClass() const; + + void setTtl(uint newTtl) { mTtl = newTtl; }; + uint getTtl() const; + + void setRData(RData *newRData) { mRData = newRData; mType = newRData->getType(); }; + RData *getRData() { return mRData; }; + + void decode(Buffer &buffer); + void encode(Buffer &buffer); + + std::string asString(); + + private: + /* Domain name to which this resource record pertains */ + std::string mName; + + /* Type field */ + eRDataType mType; + + /* Class field */ + eClass mClass; + + /* TTL field */ + uint mTtl; + + /* size of RData */ + uint mRDataSize; + + /* rdata */ + RData *mRData; +}; + +} // namespace +#endif /* _DNS_RR_H */ + diff --git a/dnslib/message.cpp b/dnslib/message.cpp new file mode 100644 index 0000000..4f65554 --- /dev/null +++ b/dnslib/message.cpp @@ -0,0 +1,221 @@ +/** + * DNS Message + * + * Copyright (c) 2014 Michal Nezerka + * All rights reserved. + * + * Developed by: Michal Nezerka + * https://github.com/mnezerka/ + * mailto:michal.nezerka@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal with 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimers. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimers in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Michal Nezerka, nor the names of its contributors + * may be used to endorse or promote products derived from this Software + * without specific prior written permission.  + * + * 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 CONTRIBUTORS 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 WITH THE SOFTWARE. + * + */ + +#include +#include +#include +#include +#ifdef _WIN32 +#include +#else +#include +#endif + +#include +#include + +using namespace dns; +using namespace std; + +Message::~Message() +{ + removeAllRecords(); +} + +void Message::removeAllRecords() +{ + // delete all queries + for(std::vector::iterator it = mQueries.begin(); it != mQueries.end(); ++it) + delete(*it); + mQueries.clear(); + + // delete answers + for(std::vector::iterator it = mAnswers.begin(); it != mAnswers.end(); ++it) + delete(*it); + mAnswers.clear(); + + // delete authorities + for(std::vector::iterator it = mAuthorities.begin(); it != mAuthorities.end(); ++it) + delete(*it); + mAuthorities.clear(); + + // delete additional + for(std::vector::iterator it = mAdditional.begin(); it != mAdditional.end(); ++it) + delete(*it); + mAdditional.clear(); +} + +void Message::decode(const char* buffer, const uint bufferSize) +{ + if (bufferSize > MAX_MSG_LEN) + throw (Exception("Aborting parse of message which exceedes maximal DNS message length.")); + Buffer buff(const_cast(buffer), bufferSize); + + // 1. delete all items in lists of message records (queries, resource records) + removeAllRecords(); + + // 2. read header + mId = buff.get16bits(); + uint fields = buff.get16bits(); + mQr = (fields >> 15) & 1; + mOpCode = (fields >> 11) & 15; + mAA = (fields >> 10) & 1; + mTC = (fields >> 9) & 1; + mRD = (fields >> 8) & 1; + mRA = (fields >> 7) & 1; + uint qdCount = buff.get16bits(); + uint anCount = buff.get16bits(); + uint nsCount = buff.get16bits(); + uint arCount = buff.get16bits(); + + // 3. read Question Sections + for (uint i = 0; i < qdCount; i++) + { + std::string qName = buff.getDnsDomainName(); + uint qType = buff.get16bits(); + eQClass qClass = static_cast(buff.get16bits()); + + QuerySection *qs = new QuerySection(qName); + qs->setType(qType); + qs->setClass(qClass); + mQueries.push_back(qs); + } + + // 4. read Answer Resource Records + Message::decodeResourceRecords(buff, anCount, mAnswers); + Message::decodeResourceRecords(buff, nsCount, mAuthorities); + Message::decodeResourceRecords(buff, arCount, mAdditional); + + // 5. check that buffer is consumed + if (buff.getPos() != buff.getSize()) + throw(Exception("Message buffer not empty after parsing")); +} + +void Message::decodeResourceRecords(Buffer &buffer, uint count, std::vector &list) +{ + for (uint i = 0; i < count; i++) + { + ResourceRecord *rr = new ResourceRecord(); + list.push_back(rr); + rr->decode(buffer); + } +} + +void Message::encode(char* buffer, const uint bufferSize, uint &validSize) +{ + validSize = 0; + Buffer buff(buffer, bufferSize); + + // encode header + + buff.put16bits(mId); + uint fields = ((mQr & 1) << 15); + fields += ((mOpCode & 15) << 11); + fields += ((mAA & 1) << 10); + fields += ((mTC & 1) << 9); + fields += ((mRD & 1) << 8); + fields += ((mRA & 1) << 7); + fields += ((mRCode & 15)); + buff.put16bits(fields); + buff.put16bits(mQueries.size()); + buff.put16bits(mAnswers.size()); + buff.put16bits(mAuthorities.size()); + buff.put16bits(mAdditional.size()); + + // encode queries + for(std::vector::iterator it = mQueries.begin(); it != mQueries.end(); ++it) + (*it)->encode(buff); + + // encode answers + for(std::vector::iterator it = mAnswers.begin(); it != mAnswers.end(); ++it) + (*it)->encode(buff); + + // encode authorities + for(std::vector::iterator it = mAuthorities.begin(); it != mAuthorities.end(); ++it) + (*it)->encode(buff); + + // encode additional + for(std::vector::iterator it = mAdditional.begin(); it != mAdditional.end(); ++it) + (*it)->encode(buff); + + validSize = buff.getPos(); +} + +string Message::asString() +{ + ostringstream text; + text << "Header:" << endl; + text << "ID: " << showbase << hex << mId << endl << noshowbase; + text << " fields: [ QR: " << mQr << " opCode: " << mOpCode << " ]" << endl; + text << " QDcount: " << mQueries.size() << endl; + text << " ANcount: " << mAnswers.size() << endl; + text << " NScount: " << mAuthorities.size() << endl; + text << " ARcount: " << mAdditional.size() << endl; + + if (mQueries.size() > 0) + { + text << "Queries:" << endl; + for(std::vector::iterator it = mQueries.begin(); it != mQueries.end(); ++it) + text << " " << (*it)->asString(); + } + + if (mAnswers.size() > 0) + { + text << "Answers:" << endl; + for(std::vector::iterator it = mAnswers.begin(); it != mAnswers.end(); ++it) + text << " " << (*it)->asString(); + } + + if (mAuthorities.size() > 0) + { + text << "Authorities:" << endl; + for(std::vector::iterator it = mAuthorities.begin(); it != mAuthorities.end(); ++it) + text << " " << (*it)->asString(); + } + + if (mAdditional.size() > 0) + { + text << "Additional:" << endl; + for(std::vector::iterator it = mAdditional.begin(); it != mAdditional.end(); ++it) + text << " " << (*it)->asString(); + } + + + return text.str(); +} + + diff --git a/dnslib/qs.cpp b/dnslib/qs.cpp new file mode 100644 index 0000000..dd9b7eb --- /dev/null +++ b/dnslib/qs.cpp @@ -0,0 +1,62 @@ +/** + * DNS Question Section + * + * Copyright (c) 2014 Michal Nezerka + * All rights reserved. + * + * Developed by: Michal Nezerka + * https://github.com/mnezerka/ + * mailto:michal.nezerka@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal with 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimers. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimers in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Michal Nezerka, nor the names of its contributors + * may be used to endorse or promote products derived from this Software + * without specific prior written permission.  + * + * 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 CONTRIBUTORS 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 WITH THE SOFTWARE. + * + */ + +#include +#include + +#include +#include +#include + +using namespace dns; +using namespace std; + + +string QuerySection::asString() +{ + ostringstream text; + text << " +#include +#include +#include + +#include +#include +#include + +using namespace dns; +using namespace std; + +/////////// RDataWithName /////////// + +void RDataWithName::decode(Buffer &buffer, const uint size) +{ + mName = buffer.getDnsDomainName(); +} + +void RDataWithName::encode(Buffer &buffer) +{ + buffer.putDnsDomainName(mName); +} + +/////////// RDataCNAME ///////////////// + +std::string RDataCNAME::asString() +{ + ostringstream text; + text << "<encode(buffer); + mRDataSize = buffer.getPos() - bufferPosRDataLength - 2; // 2 because two bytes for RData length are not part of RData block + uint bufferLastPos = buffer.getPos(); + buffer.setPos(bufferPosRDataLength); + buffer.put16bits(mRDataSize); // overwritte 0 with actual size of RData + buffer.setPos(bufferLastPos); + } +} + +std::string ResourceRecord::asString() +{ + ostringstream text; + //text << "asString(); + text << endl; + return text.str(); +} + + diff --git a/dpitunnel-cli.cpp b/dpitunnel-cli.cpp new file mode 100644 index 0000000..d9098b4 --- /dev/null +++ b/dpitunnel-cli.cpp @@ -0,0 +1,885 @@ +#include "dpitunnel-cli.h" + +#include "autoconf.h" +#include "desync.h" +#include "dns.h" +#include "netiface.h" +#include "packet.h" +#include "profiles.h" +#include "ssl.h" +#include "socket.h" +#include "utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const std::string CONNECTION_ESTABLISHED_RESPONSE("HTTP/1.1 200 Connection established\r\n\r\n"); +const std::string CONNECTION_ERROR_RESPONSE( + "HTTP/1.1 0 Connection establish problem (read logs)\r\n\r\n"); +const std::string PROCESS_NAME("DPITunnel-cli"); +const std::string HELP_PAGE( + "DPITunnel-cli, program for bypassing internet censorship without a proxy server.\n" + "\n" + "Usage:\n" + " dpitunnel-cli [options]\n" + " dpitunnel-cli [--pid ][--ip ][--port ][--mode ][--ca-bundle-path ][--daemon] <--profile [[:]]|[default] [options]>...\n" + " dpitunnel-cli --auto\n" + "\n" + "Options:\n" + " --auto\t\t\t\t\tchoose the settings for current ISP automatically\n" + " --profile=[\n" + " \t[:]]|[default]\t\tsettings for a specific connection\n" + " --help\t\t\t\t\tshow this message\n" + " --pid=\t\t\t\t\twrite pid to file in daemon mode\n" + " --ip=\t\t\t\t\tIP to bind the http proxy. Default: 0.0.0.0\n" + " --port=\t\t\t\t\tport to bind http proxy. Default: 8080\n" + " --whitelist=\t\t\tdon't apply circumvention tricks to IPs and domains from supplied text file\n" + " \t\t\t\t\t\teach line is entry that contains type (ip or domain) and after a space entry value\n" + " \t\t\t\t\t\tExamples: \'ip 192.0.2.0\', \'domain example.com\'\n" + " --mode=\t\t\t\t\tproxy mode. mode: proxy, transparent. Default: proxy\n" + " --ca-bundle-path=\t\t\tpath to CA certificates bundle in PEM format. Default: ./ca.bundle\n" + " --daemon\t\t\t\t\tdaemonize program\n" + " --buffer-size=\t\t\tsize of buffers. Default: 512\n" + " --desync-attacks=[][,]\t\tmode0: fake rst rstack. mode1: disorder disorder_fake split split_fake\n" + " --split-at-sni\t\t\t\tsplit Client Hello at SNI\n" + " --split-position=\t\tsplit Client Hello at . Default: 3\n" + " --wrong-seq\t\t\t\t\tsend fakes with TCP SEQ/ACK from past\n" + " --ttl=\t\t\t\tTTL for fake packets\n" + " --auto-ttl=--\t\t\tautomatically detect TTL and decrease\n" + " \t\t\t\t\t\tit based on a distance. If the distance is shorter than a2, TTL is decreased\n" + " \t\t\t\t\t\tby a2. If it's longer, (a1; a2) scale is used with the distance as a weight.\n" + " \t\t\t\t\t\tIf the resulting TTL is more than m(ax), set it to m. Default: 1-4-10. And --min-ttl 3\n" + " --min-ttl=\t\t\t\tminimum TTL for which send fake packets\n" + " --doh\t\t\t\t\t\tresolve hosts over DoH server\n" + " --doh-server=\t\t\t\tDoH server URL. Default: https://dns.google/dns-query\n" + " --builtin-dns\t\t\t\t\tindependently resolve hostnames, don't use getaddrinfo. You must enable it on Android\n" + " \t\t\t\t\t\tand other systems that don't have /etc/resolv.conf\n" + " --builtin-dns-ip=\t\t\t\tDNS server IP used by builtin resolver. Default: 8.8.8.8\n" + " --builtin-dns-port=\t\t\tDNS server port used by builtin resolver. Default: 53\n" + " --custom-ips=\t\t\tallows to set custom IPs for specific domains, ignoring DNS/DoH response\n" + " --wsize=\t\t\t\tTCP window size. Used to ask server to split Server Hello\n" + " --wsfactor=\t\t\t\tTCP window scale factor. Used with wsize option" +); +int Interrupt_pipe[2]; +std::atomic stop_flag; +struct Settings_perst_s Settings_perst; +struct Profile_s Profile; +extern std::map Profiles; +std::mutex Threads_map_mutex; +std::unordered_map Threads; + +void process_client_cycle(int client_socket) { + // last_char indicates position of string end + unsigned int last_char; + + // Set timeouts + struct timeval timeout_sock; + timeout_sock.tv_sec = 0; + timeout_sock.tv_usec = 10; + if (setsockopt(client_socket, SOL_SOCKET, SO_SNDTIMEO, (char *) &timeout_sock, + sizeof(timeout_sock)) < 0 || + setsockopt(client_socket, SOL_SOCKET, SO_RCVTIMEO, (char *) &timeout_sock, + sizeof(timeout_sock)) < 0) { + std::cerr << "Can't setsockopt on socket. Errno: " << std::strerror(errno) << std::endl; + close(client_socket); + return; + } + + // Receive with timeout + struct timeval timeout_recv; + timeout_recv.tv_sec = 5; + timeout_recv.tv_usec = 0; + + std::string buffer(Profile.buffer_size, ' '); + + if (recv_string(client_socket, buffer, last_char, &timeout_recv) == -1 || + last_char == 0) { + close(client_socket); + return; + } + + bool is_https; + std::string server_host; + std::string server_ip; + int server_port; + std::string server_method; + bool in_whitelist; + int res; + if ((res = parse_request(buffer, server_method, server_host, server_port, + Settings_perst.proxy_mode == MODE_PROXY)) == -1) { + std::cerr << "Can't parse first request" << std::endl; + send_string(client_socket, CONNECTION_ERROR_RESPONSE, CONNECTION_ERROR_RESPONSE.size()); + close(client_socket); + return; + } + +#ifndef SO_ORIGINAL_DST +#define SO_ORIGINAL_DST 80 +#endif + if (Settings_perst.proxy_mode == MODE_TRANSPARENT) { + is_https = res == -2; + // Get original destination address + struct sockaddr_in server_address; + socklen_t server_address_len = sizeof server_address; + if (getsockopt(client_socket, SOL_IP, SO_ORIGINAL_DST, (struct sockaddr *) &server_address, + &server_address_len) != 0) { + std::cerr << "Can't get original address. Errno: " << std::strerror(errno) << std::endl; + close(client_socket); + return; + } + if (is_https) { + // Get server domain from SNI + unsigned int sni_start, sni_len; + get_tls_sni(buffer, last_char, sni_start, sni_len); + if (sni_start + sni_len > last_char || sni_start == 0 || + sni_len == 0) { // failed to find sni + // Use original IP as server domain + char str[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &(server_address.sin_addr), str, INET_ADDRSTRLEN); + server_host = std::string(str); + } else { + server_host = buffer.substr(sni_start, sni_len); + } + } + // Use original TCP port as server port + server_port = ntohs(server_address.sin_port); + } else + is_https = server_method == "CONNECT"; + + // Remove proxy connection specific parts + if (!is_https && Settings_perst.proxy_mode == MODE_PROXY) + remove_proxy_strings(buffer, last_char); + + // Resolve server ip + if (resolve_host(server_host, server_ip) == -1) { + send_string(client_socket, CONNECTION_ERROR_RESPONSE, CONNECTION_ERROR_RESPONSE.size()); + close(client_socket); + return; + } + + in_whitelist = match_whitelist_ip(server_ip) || match_whitelist_domain(server_host); + + // If need get SYN, ACK packet sent by server during handshake + std::atomic flag(true); + std::atomic local_port(-1); + std::atomic status; + std::thread sniff_thread; + std::promise sniff_thread_ready; + std::string sniffed_packet; + if (Profile.desync_attacks && !in_whitelist) { + sniff_thread_ready = std::promise(); + sniff_thread = std::thread(sniff_handshake_packet, &sniffed_packet, + server_ip, server_port, &local_port, &flag, &status, + &sniff_thread_ready); + // Wait for sniff thread to init + sniff_thread_ready.get_future().wait(); + } + + // Connect to remote server + int server_socket; + if (init_remote_server_socket(server_socket, server_ip, server_port) == -1) { + if (Profile.desync_attacks && !in_whitelist) { + // Stop sniff thread + flag.store(false); + if (sniff_thread.joinable()) sniff_thread.join(); + } + send_string(client_socket, CONNECTION_ERROR_RESPONSE, CONNECTION_ERROR_RESPONSE.size()); + close(server_socket); + close(client_socket); + return; + } + + // Disable TCP Nagle's algorithm + int yes = 1; + if (setsockopt(client_socket, IPPROTO_TCP, TCP_NODELAY, (char *) &yes, sizeof(yes)) < 0 + || setsockopt(server_socket, IPPROTO_TCP, TCP_NODELAY, (char *) &yes, sizeof(yes)) < 0) { + std::cerr << "Can't disable TCP Nagle's algorithm with setsockopt(). Errno: " + << std::strerror(errno) << std::endl; + if (Profile.desync_attacks && !in_whitelist) { + // Stop sniff thread + flag.store(false); + if (sniff_thread.joinable()) sniff_thread.join(); + } + send_string(client_socket, CONNECTION_ERROR_RESPONSE, CONNECTION_ERROR_RESPONSE.size()); + close(server_socket); + close(client_socket); + return; + } + + // Get local port to choose proper SYN, ACK packet + struct sockaddr_in local_addr; + socklen_t len = sizeof(local_addr); + if (getsockname(server_socket, (struct sockaddr *) &local_addr, &len) == -1) { + std::cerr << "Failed to get local port. Errno: " << std::strerror(errno) << std::endl; + if (Profile.desync_attacks && !in_whitelist) { + // Stop sniff thread + flag.store(false); + if (sniff_thread.joinable()) sniff_thread.join(); + } + send_string(client_socket, CONNECTION_ERROR_RESPONSE, CONNECTION_ERROR_RESPONSE.size()); + close(server_socket); + close(client_socket); + return; + } + local_port.store(ntohs(local_addr.sin_port)); + + if (is_https && Settings_perst.proxy_mode == MODE_PROXY) + if (send_string(client_socket, CONNECTION_ESTABLISHED_RESPONSE, + CONNECTION_ESTABLISHED_RESPONSE.size()) == -1) { + close(server_socket); + close(client_socket); + return; + } + + if (Profile.desync_attacks && !in_whitelist) { + // Get received SYN, ACK packet + if (sniff_thread.joinable()) sniff_thread.join(); + if (status.load() == -1) { + std::cerr << "Failed to capture handshake packet" << std::endl; + send_string(client_socket, CONNECTION_ERROR_RESPONSE, CONNECTION_ERROR_RESPONSE.size()); + close(server_socket); + close(client_socket); + return; + } + // Get first client packet + if (is_https && Settings_perst.proxy_mode == MODE_PROXY) { + if (recv_string(client_socket, buffer, last_char, &timeout_recv) == -1 || + last_char == 0) { + close(server_socket); + close(client_socket); + return; + } + } + // Split packet at the middle of SNI or at user specified position + unsigned int sni_start, sni_len; + unsigned int split_pos; + // If it's https connection + if (is_https && Profile.split_at_sni) { + get_tls_sni(buffer, last_char, sni_start, sni_len); + if (sni_start + sni_len > last_char || sni_start == 0 || sni_len == 0) + split_pos = std::min(Profile.split_position, last_char); + else + split_pos = sni_start + sni_len / 2; + } else + split_pos = std::min(Profile.split_position, last_char); + + do_desync_attack(server_socket, server_ip, server_port, local_port, + is_https, sniffed_packet, buffer, last_char, split_pos); + + // Send packet to synchronize SEQ/ACK + std::string data_empty(last_char, '\x00'); + if (Profile.desync_first_attack == DESYNC_FIRST_NONE) { + if (send_string(server_socket, data_empty, last_char) == -1) { + send_string(client_socket, CONNECTION_ERROR_RESPONSE, + CONNECTION_ERROR_RESPONSE.size()); + close(server_socket); + close(client_socket); + return; + } + } else { + if (send_string(server_socket, data_empty, split_pos) == -1 || + send_string(server_socket, data_empty, last_char - split_pos) == -1) { + send_string(client_socket, CONNECTION_ERROR_RESPONSE, + CONNECTION_ERROR_RESPONSE.size()); + close(server_socket); + close(client_socket); + return; + } + } + // Send packet we received previously if it's http connection + } else if (!is_https || Settings_perst.proxy_mode == MODE_TRANSPARENT) { + if (send_string(server_socket, buffer, last_char) == -1) { + send_string(client_socket, CONNECTION_ERROR_RESPONSE, CONNECTION_ERROR_RESPONSE.size()); + close(server_socket); + close(client_socket); + return; + } + } + + // Make sockets non-blocking + if (fcntl(client_socket, F_SETFL, fcntl(client_socket, F_GETFL, 0) | O_NONBLOCK) == -1 || + fcntl(server_socket, F_SETFL, fcntl(server_socket, F_GETFL, 0) | O_NONBLOCK) == -1) { + std::cerr << "Failed to make sockets non-blocking. Errno: " << std::strerror(errno) + << std::endl; + } + + // Client process loop + struct pollfd fds[3]; + + // fds[0] is client socket + fds[0].fd = client_socket; + fds[0].events = POLLIN; + + // fds[1] is remote server socket + fds[1].fd = server_socket; + fds[1].events = POLLIN; + + // fds[2] is interrupt pipe + fds[2].fd = Interrupt_pipe[0]; + fds[2].events = POLLIN; + + // Set poll() timeout + int timeout = -1; + + bool is_transfer_failure = false; + + while (!stop_flag.load() && !is_transfer_failure) { + int ret = poll(fds, 3, timeout); + + // Check state + if (ret == -1) { + std::cerr << "Poll error. Errno:" << std::strerror(errno) << std::endl; + break; + } else if (ret == 0) + continue; + else { + if (fds[0].revents & POLLERR || fds[1].revents & POLLERR || + fds[0].revents & POLLHUP || fds[1].revents & POLLHUP || + fds[0].revents & POLLNVAL || fds[1].revents & POLLNVAL) + break; + + // Process client socket + if (fds[0].revents & POLLIN) { + // Transfer data + if (recv_string(client_socket, buffer, last_char) == -1) + is_transfer_failure = true; + + if (!is_https && Settings_perst.proxy_mode == MODE_PROXY) + remove_proxy_strings(buffer, last_char); + + if (send_string(server_socket, buffer, last_char) == -1) + is_transfer_failure = true; + } + + // Process server socket + if (fds[1].revents & POLLIN) { + // Transfer data + if (recv_string(server_socket, buffer, last_char) == -1) + is_transfer_failure = true; + + if (send_string(client_socket, buffer, last_char) == -1) + is_transfer_failure = true; + } + + fds[0].revents = 0; + fds[1].revents = 0; + fds[2].revents = 0; + } + } + + close(server_socket); + close(client_socket); +} + +void accept_client_cycle(int server_socket) { + + struct pollfd fds[2]; + + // fds[0] is a server socket + fds[0].fd = server_socket; + fds[0].events = POLLIN; + + // fds[1] is an interrupt pipe + fds[1].fd = Interrupt_pipe[0]; + fds[1].events = POLLIN; + + // Set poll() timeout + int timeout = -1; + + while (!stop_flag.load()) { + int ret = poll(fds, 2, timeout); + + // Check state + if (ret == -1) { + std::cerr << "Poll error. Errno:" << std::strerror(errno) << std::endl; + break; + } else if (ret == 0) + continue; // Timeout happened + else { + if (fds[0].revents & POLLERR || + fds[0].revents & POLLHUP || + fds[0].revents & POLLNVAL) + break; + + //Accept client + if (fds[0].revents & POLLIN) { + int client_socket; + struct sockaddr_in client_address; + socklen_t client_address_size = sizeof(client_address); + + client_socket = accept(server_socket, + (sockaddr *) &client_address, + &client_address_size); + if (client_socket == -1) { + std::cerr << "Can't accept client socket. Error: " + << std::strerror(errno) << std::endl; + break; + } + + // Create new thread + auto thread_starter = std::promise(); + std::thread t1( + [starter_future = thread_starter.get_future(), socket = client_socket]() mutable { + starter_future.wait(); + process_client_cycle(socket); + // Remove thread from map, contains all running threads + { + std::lock_guard lock(Threads_map_mutex); + if (!stop_flag.load()) { + auto found = Threads.find(std::this_thread::get_id()); + if (found != Threads.end()) { + found->second.detach(); + Threads.erase(std::this_thread::get_id()); + } + } + } + }); + // Add thread to map, contains all running threads + { + std::lock_guard lock(Threads_map_mutex); + Threads.emplace(t1.get_id(), std::move(t1)); + } + thread_starter.set_value(); + + } + + fds[0].revents = 0; + fds[1].revents = 0; + } + } + + // Wait for all threads to finish + for (auto &imap: Threads) + if (imap.second.joinable()) imap.second.join(); +} + +int parse_cmdline(int argc, char *argv[]) { + + const struct option options[] = { + {"ip", required_argument, 0, 0}, // id 0 + {"port", required_argument, 0, 0}, // id 1 + {"buffer-size", required_argument, 0, 0}, // id 2 + {"split-position", required_argument, 0, 0}, // id 3 + {"ttl", required_argument, 0, 0}, // id 4 + {"doh", no_argument, 0, 0}, // id 5 + {"doh-server", required_argument, 0, 0}, //id 6 + {"ca-bundle-path", required_argument, 0, 0}, // id 7 + {"split-at-sni", no_argument, 0, 0}, // id 8 + {"desync-attacks", required_argument, 0, 0}, // id 9 + {"auto", no_argument, 0, 0}, // id 10 + {"help", no_argument, 0, 0}, // id 11 + {"daemon", no_argument, 0, 0}, // id 12 + {"wsize", required_argument, 0, 0}, // id 13 + {"wsfactor", required_argument, 0, 0}, // id 14 + {"profile", required_argument, 0, 0}, // id 15 + {"builtin-dns", no_argument, 0, 0}, // id 16 + {"builtin-dns-ip", required_argument, 0, 0}, // id 17 + {"builtin-dns-port", required_argument, 0, 0}, // id 18 + {"pid", required_argument, 0, 0}, // id 19 + {"min-ttl", required_argument, 0, 0}, // id 20 + {"auto-ttl", required_argument, 0, 0}, // id 21 + {"wrong-seq", no_argument, 0, 0}, // id 22 + {"mode", required_argument, 0, 0}, // id 23 + {"whitelist", required_argument, 0, 0}, // id 24 + {"custom-ips", required_argument, 0, 0}, // id 25 + {NULL, 0, NULL, 0} + }; + + int res, opt_id = 0; + std::string curr_profile_name = ""; + struct Profile_s profile; + while ((res = getopt_long_only(argc, argv, "", options, &opt_id)) != -1) { + if (res) return -1; + switch (opt_id) { + case 0: // ip + Settings_perst.server_address = std::string(optarg); + + break; + + case 1: // port + Settings_perst.server_port = atoi(optarg); + if (Settings_perst.server_port < 1 || Settings_perst.server_port > 65535) { + std::cerr << "-port invalid argument" << std::endl; + return -1; + } + + break; + + case 2: // buffer-size + profile.buffer_size = atoi(optarg); + if (profile.buffer_size < 128 || profile.buffer_size > 65535) { + std::cerr << "-buffer-size invalid argument" << std::endl; + return -1; + } + + break; + + case 3: // split-position + profile.split_position = atoi(optarg); + if (profile.split_position > 65535) { + std::cerr << "-split-position invalid argument" << std::endl; + return -1; + } + + break; + + case 4: // ttl + profile.fake_packets_ttl = atoi(optarg); + if (profile.fake_packets_ttl < 1 || profile.fake_packets_ttl > 255) { + std::cerr << "-ttl invalid argument" << std::endl; + return -1; + } + + break; + + case 5: // doh + profile.doh = true; + + break; + + case 6: // doh-server + profile.doh_server = optarg; + + break; + + case 7: // ca-bundle-path + Settings_perst.ca_bundle_path = optarg; + + break; + + case 8: // split-at-sni + profile.split_at_sni = true; + + break; + + case 9: // desync-attacks + { + profile.desync_attacks = true; + char *e, *p = optarg; + while (p) { + e = strchr(p, ','); + if (e) *e++ = 0; + + if (!strcmp(p, ZERO_ATTACKS_NAMES.at(DESYNC_ZERO_FAKE).c_str())) + profile.desync_zero_attack = DESYNC_ZERO_FAKE; + else if (!strcmp(p, ZERO_ATTACKS_NAMES.at(DESYNC_ZERO_RST).c_str())) + profile.desync_zero_attack = DESYNC_ZERO_RST; + else if (!strcmp(p, ZERO_ATTACKS_NAMES.at(DESYNC_ZERO_RSTACK).c_str())) + profile.desync_zero_attack = DESYNC_ZERO_RSTACK; + else if (!strcmp(p, FIRST_ATTACKS_NAMES.at(DESYNC_FIRST_DISORDER).c_str())) + profile.desync_first_attack = DESYNC_FIRST_DISORDER; + else if (!strcmp(p, FIRST_ATTACKS_NAMES.at(DESYNC_FIRST_DISORDER_FAKE).c_str())) + profile.desync_first_attack = DESYNC_FIRST_DISORDER_FAKE; + else if (!strcmp(p, FIRST_ATTACKS_NAMES.at(DESYNC_FIRST_SPLIT).c_str())) + profile.desync_first_attack = DESYNC_FIRST_SPLIT; + else if (!strcmp(p, FIRST_ATTACKS_NAMES.at(DESYNC_FIRST_SPLIT_FAKE).c_str())) + profile.desync_first_attack = DESYNC_FIRST_SPLIT_FAKE; + else { + std::cerr << "-desync-attacks invalid argument" << std::endl; + return -1; + } + + p = e; + } + } + + break; + case 10: // auto + run_autoconf(); + + return -2; + + case 11: // help + + return -1; + + case 12: // daemon + Settings_perst.daemon = true; + + break; + + case 13: // wsize + profile.window_size = atoi(optarg); + if (profile.window_size < 1 || profile.window_size > 65535) { + std::cerr << "-wsize invalid argument" << std::endl; + return -1; + } + + break; + + case 14: // wsfactor + profile.window_scale_factor = atoi(optarg); + if (profile.window_scale_factor < 0 || profile.window_scale_factor > 14) { + std::cerr << "-wsfactor invalid argument" << std::endl; + return -1; + } + + break; + + case 15: // profile + { + std::string temp = optarg; + if (!curr_profile_name.empty()) + add_profile(curr_profile_name, profile); + + curr_profile_name = temp; + profile = {}; + } + + break; + + case 16: // builtin-dns + profile.builtin_dns = true; + + break; + + case 17: // builtin-dns-ip + profile.builtin_dns_ip = optarg; + + break; + + case 18: // builtin-dns-port + profile.builtin_dns_port = atoi(optarg); + if (profile.builtin_dns_port < 1 || profile.builtin_dns_port > 65535) { + std::cerr << "-builtin-dns-port invalid argument" << std::endl; + return -1; + } + + break; + + case 19: // pid + Settings_perst.pid_file = optarg; + + break; + + case 20: // min-ttl + profile.min_ttl = atoi(optarg); + if (profile.min_ttl < 1 || profile.min_ttl > 255) { + std::cerr << "-min-ttl invalid argument" << std::endl; + return -1; + } + + break; + + case 21: // auto-ttl + { + profile.auto_ttl = true; + char *autottl_copy = strdup(optarg); + char *pch = strtok(autottl_copy, "-"); + int i; + for (i = 0; pch != NULL; i++) { + if (i == 0) + profile.auto_ttl_a1 = atoi(pch); + else if (i == 1) + profile.auto_ttl_a2 = atoi(pch); + else if (i == 2) + profile.auto_ttl_max = atoi(pch); + pch = strtok(NULL, "-"); + } + free(autottl_copy); + if (i != 3) { + std::cerr << "-auto-ttl invalid argument" << std::endl; + return -1; + } + + // Set default min ttl + if (profile.min_ttl == 0) + profile.min_ttl = 3; + } + + break; + + case 22: // wrong-seq + profile.wrong_seq = true; + + break; + + case 23: // mode + if (!strcmp(optarg, PROXY_MODE_NAMES.at(MODE_PROXY).c_str())) + Settings_perst.proxy_mode = MODE_PROXY; + else if (!strcmp(optarg, PROXY_MODE_NAMES.at(MODE_TRANSPARENT).c_str())) + Settings_perst.proxy_mode = MODE_TRANSPARENT; + else { + std::cerr << "-mode invalid argument" << std::endl; + return -1; + } + + break; + + case 24: // whitelist + Settings_perst.whitelist_path = optarg; + + break; + + case 25: // custom-ips + Settings_perst.custom_ips_path = optarg; + + break; + } + } + + if (!curr_profile_name.empty()) + add_profile(curr_profile_name, profile); + else + Profile = profile; + + return 0; +} + +void print_help() { + std::cout << HELP_PAGE << std::endl; +} + +void print_info() { + std::cout << "Proxy running on " << Settings_perst.server_address << ':' + << Settings_perst.server_port << "..." << std::endl + << "To get help run program with --help argument." << std::endl + << "To auto configure run program with --auto argument" << std::endl; +} + +void sig_int_handler(int signum) { + // Stop program + stop_flag.store(true); + // Interrupt poll() + close(Interrupt_pipe[0]); + close(Interrupt_pipe[1]); +} + +int main(int argc, char *argv[]) { + // Set process name + prctl(PR_SET_NAME, PROCESS_NAME.c_str(), NULL, NULL, NULL); + std::strcpy(argv[0], PROCESS_NAME.c_str()); + + // Init + stop_flag.store(false); + std::srand(std::time(nullptr)); + int res = parse_cmdline(argc, argv); + if (res == -1) { + print_help(); + return -1; //exit_failure(); + } else if (res == -2) + return 0; + if (!Settings_perst.whitelist_path.empty()) + if (load_whitelist() == -1) + return -1; + if (!Settings_perst.custom_ips_path.empty()) + if (load_custom_ips() == -1) + return -1; + ignore_sigpipe(); + + // Init interrupt pipe (used to interrupt poll() calls) + pipe(Interrupt_pipe); + + // If we have profiles, choose profile + if (!Profiles.empty()) { + std::string iface = get_current_iface_name(); + std::string wifi_ap = get_current_wifi_name(iface); + + if (!iface.empty()) { + std::cout << "Netiface: " << iface; + if (!wifi_ap.empty()) + std::cout << ", Wi-Fi point name: " << wifi_ap; + std::cout << std::endl; + } else + std::cout << "Try to set default profile" << std::endl; + + if (change_profile(iface, wifi_ap) == -1) + return -1; //exit_failure(); + } + + if (Profile.doh) + if (load_ca_bundle() == -1) + return -1; //exit_failure(); + + // Create server socket + int server_socket = socket(AF_INET, SOCK_STREAM, 0); + if (server_socket == -1) { + std::cerr << "Server socket creation failure. Errno: " << std::strerror(errno) << std::endl; + return -1; //exit_failure(); + } + + // Make address/port reusable + int opt = 1; + if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) { + std::cerr << "Can't setsockopt on server socket. Errno: " << std::strerror(errno) + << std::endl; + close(server_socket); + return -1; //exit_failure(); + } + + // Server address options + struct sockaddr_in server_address; + server_address.sin_family = AF_INET; + inet_pton(AF_INET, Settings_perst.server_address.c_str(), &(server_address.sin_addr)); + server_address.sin_port = htons(Settings_perst.server_port); + + // Bind socket + if (bind(server_socket, (struct sockaddr *) &server_address, sizeof(server_address)) == -1) { + std::cerr << "Can't bind server socket. Errno: " << std::strerror(errno) << std::endl; + close(server_socket); + return -1; //exit_failure(); + } + + // Listen to socket + if (listen(server_socket, 4096) == -1) { + std::cerr << "Can't listen to server socket. Errno: " << std::strerror(errno) << std::endl; + close(server_socket); + return -1; //exit_failure(); + } + + // Show info + print_info(); + + if (Settings_perst.daemon) { + daemonize(); + if (!Settings_perst.pid_file.empty()) { + std::ofstream file(Settings_perst.pid_file); + if (file) { + file << getpid(); + file.close(); + } + } + } + + // Start route monitor thread to correctly change profiles + std::thread t1; + if (!Profiles.empty()) + t1 = std::thread(route_monitor_thread); + + // Register ctrl-c and terminate handlers + struct sigaction signalAction; + signalAction.sa_handler = sig_int_handler; + sigemptyset(&signalAction.sa_mask); + signalAction.sa_flags = 0; + sigaction(SIGINT, &signalAction, NULL); + sigaction(SIGTERM, &signalAction, NULL); + + // Start accepting clients + std::thread t2(accept_client_cycle, server_socket); + t2.join(); + + // Oops, seems user asked program to exit or accept_client_cycle crashed + + // Deinit + std::cout << "Quitting..." << std::endl; + if (t1.joinable()) t1.join(); + close(server_socket); + + return 0; +} diff --git a/include/autoconf.h b/include/autoconf.h new file mode 100644 index 0000000..656f13b --- /dev/null +++ b/include/autoconf.h @@ -0,0 +1,10 @@ +#ifndef AUTOTEST_H +#define AUTOTEST_H + +#include + +int run_autoconf(); + +void generate_client_hello(const std::string &sni, std::string &buffer); + +#endif //AUTOTEST_H diff --git a/include/base64.h b/include/base64.h new file mode 100644 index 0000000..f05ff7b --- /dev/null +++ b/include/base64.h @@ -0,0 +1,46 @@ +#ifndef BASE64_H +#define BASE64_H + +static std::string base64_encode(const std::string &in) { + + std::string out; + + int val = 0, valb = -6; + for (unsigned char c: in) { + val = (val << 8) + c; + valb += 8; + while (valb >= 0) { + out.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"[ + (val >> valb) & 0x3F]); + valb -= 6; + } + } + if (valb > -6) + out.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"[ + ((val << 8) >> (valb + 8)) & 0x3F]); + //while (out.size()%4) out.push_back('='); + return out; +} + +static std::string base64_decode(const std::string &in) { + + std::string out; + + std::vector T(256, -1); + for (int i = 0; i < 64; i++) + T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"[i]] = i; + + int val = 0, valb = -8; + for (unsigned char c: in) { + if (T[c] == -1) break; + val = (val << 6) + T[c]; + valb += 6; + if (valb >= 0) { + out.push_back(char((val >> valb) & 0xFF)); + valb -= 8; + } + } + return out; +} + +#endif //BASE64_H diff --git a/include/desync.h b/include/desync.h new file mode 100644 index 0000000..cd4d9a4 --- /dev/null +++ b/include/desync.h @@ -0,0 +1,26 @@ +#ifndef DESYNC_H +#define DESYNC_H + +#include +#include +#include + +int sniff_ack_packet(std::string *packet, std::string ip_srv, int port_srv, + int port_local, std::atomic *flag); + +int sniff_handshake_packet(std::string *packet, std::string ip_srv, + int port_srv, std::atomic *local_port_atom, std::atomic *flag, + std::atomic *status, std::promise *ready); + +std::string +form_packet(std::string packet_raw, const char *packet_data, unsigned int packet_data_size, + unsigned short id, + unsigned short ttl, unsigned int seq, unsigned int ack_seq, + unsigned int window_size, bool is_swap_addr, uint8_t *flags = NULL); + +int do_desync_attack(int socket_srv, const std::string &ip_srv, int port_srv, int port_local, + bool is_https, + const std::string &packet_raw, const std::string &packet_data, + unsigned int last_char, unsigned int split_pos); + +#endif //DESYNC_H diff --git a/include/dns.h b/include/dns.h new file mode 100644 index 0000000..52979a1 --- /dev/null +++ b/include/dns.h @@ -0,0 +1,8 @@ +#ifndef DNS_H +#define DNS_H + +#include + +int resolve_host(const std::string &host, std::string &ip); + +#endif //DNS_H diff --git a/include/dpitunnel-cli.h b/include/dpitunnel-cli.h new file mode 100644 index 0000000..0f29e46 --- /dev/null +++ b/include/dpitunnel-cli.h @@ -0,0 +1,105 @@ +#ifndef DPITUNNEL_CLI_H +#define DPITUNNEL_CLI_H + +#include +#include +#include + +enum Desync_zero_attacks { + DESYNC_ZERO_FAKE, + DESYNC_ZERO_RST, + DESYNC_ZERO_RSTACK, + DESYNC_ZERO_NONE +}; + +enum Desync_first_attacks { + DESYNC_FIRST_DISORDER, + DESYNC_FIRST_DISORDER_FAKE, + DESYNC_FIRST_SPLIT, + DESYNC_FIRST_SPLIT_FAKE, + DESYNC_FIRST_NONE +}; + +enum Proxy_mode { + MODE_PROXY, + MODE_TRANSPARENT +}; + +static const std::map ZERO_ATTACKS_NAMES = { + {DESYNC_ZERO_FAKE, "fake"}, + {DESYNC_ZERO_RST, "rst"}, + {DESYNC_ZERO_RSTACK, "rstack"} +}; + +static const std::map FIRST_ATTACKS_NAMES = { + {DESYNC_FIRST_DISORDER, "disorder"}, + {DESYNC_FIRST_DISORDER_FAKE, "disorder_fake"}, + {DESYNC_FIRST_SPLIT, "split"}, + {DESYNC_FIRST_SPLIT_FAKE, "split_fake"} +}; + +static const std::map PROXY_MODE_NAMES = { + {MODE_PROXY, "proxy"}, + {MODE_TRANSPARENT, "transparent"} +}; + +struct Profile_s { + unsigned int buffer_size = 512; + unsigned int split_position = 3; + unsigned short fake_packets_ttl = 0; + unsigned short window_size = 0; + short window_scale_factor = -1; + + bool wrong_seq = false; + // This is the smallest ACK drift Linux can't handle already, since at least v2.6.18. + // https://github.com/torvalds/linux/blob/v2.6.18/net/netfilter/nf_conntrack_proto_tcp.c#L395 + int wrong_seq_drift_ack = -66000; + // This is just random, no specifics about this value. + int wrong_seq_drift_seq = -10000; + + unsigned short min_ttl = 0; + bool auto_ttl = false; + unsigned short auto_ttl_a1 = 1; + unsigned short auto_ttl_a2 = 4; + unsigned short auto_ttl_max = 10; + + std::string doh_server = "https://dns.google/dns-query"; + + bool builtin_dns = false; + std::string builtin_dns_ip = "8.8.8.8"; + int builtin_dns_port = 53; + + bool split_at_sni = false; + bool desync_attacks = false; + bool doh = false; + + Desync_zero_attacks desync_zero_attack = DESYNC_ZERO_NONE; + Desync_first_attacks desync_first_attack = DESYNC_FIRST_NONE; +}; + +struct Settings_perst_s { + unsigned short test_ssl_handshake_timeout = 5; + unsigned short packet_capture_timeout = 5000; + unsigned int builtin_dns_req_timeout = 10000; + unsigned int count_hops_connect_timeout = 1000; + + int server_port = 8080; + std::string server_address = "0.0.0.0"; + + Proxy_mode proxy_mode = MODE_PROXY; + + std::string ca_bundle_path = "./ca.bundle"; + std::string ca_bundle; + + std::string whitelist_path; + std::set whitelist_domains; + std::set whitelist_ips; + + std::string custom_ips_path; + std::map custom_ips; + + std::string pid_file; + bool daemon = false; +}; + +#endif //DPITUNNEL_CLI_H diff --git a/include/netiface.h b/include/netiface.h new file mode 100644 index 0000000..bec27e7 --- /dev/null +++ b/include/netiface.h @@ -0,0 +1,10 @@ +#ifndef NETIFACE_H +#define NETIFACE_H + +void route_monitor_thread(); + +std::string get_current_iface_name(); + +std::string get_current_wifi_name(std::string iface_name); + +#endif //NETIFACE_H diff --git a/include/packet.h b/include/packet.h new file mode 100644 index 0000000..c8afa56 --- /dev/null +++ b/include/packet.h @@ -0,0 +1,11 @@ +#ifndef PACKET_H +#define PACKET_H + +#include + +int parse_request(const std::string &request, std::string &method, std::string &host, int &port, + bool is_proxy); + +void remove_proxy_strings(std::string &request, unsigned int &last_char); + +#endif //PACKET_H diff --git a/include/profiles.h b/include/profiles.h new file mode 100644 index 0000000..e16fdec --- /dev/null +++ b/include/profiles.h @@ -0,0 +1,9 @@ +#ifndef PROFILES_H +#define PROFILES_H + +void add_profile(std::string name, Profile_s profile); + +int change_profile(const std::string &iface, const std::string &wifi_ap, + std::string *choosen_profile_name = NULL); + +#endif //PROFILES_H diff --git a/include/socket.h b/include/socket.h new file mode 100644 index 0000000..7605c15 --- /dev/null +++ b/include/socket.h @@ -0,0 +1,21 @@ +#ifndef SOCKET_H +#define SOCKET_H + +#include +#include + +int count_hops(std::string server_ip, int server_port); + +int recv_string(int socket, std::string &message, unsigned int &last_char, + struct timeval *timeout = NULL, unsigned int *recv_time = NULL); + +int send_string(int socket, const std::string &string_to_send, unsigned int last_char, + unsigned int split_position = 0); + +int init_remote_server_socket(int &server_socket, std::string server_ip, int server_port); + +int send_string_raw(int socket, const std::string &string_to_send, + unsigned int last_char, struct sockaddr *serv_addr, + unsigned int serv_addr_size); + +#endif //SOCKET_H diff --git a/include/ssl.h b/include/ssl.h new file mode 100644 index 0000000..b910fa7 --- /dev/null +++ b/include/ssl.h @@ -0,0 +1,10 @@ +#ifndef SSL_H +#define SSL_H + +#include + +int load_ca_bundle(); + +X509_STORE *gen_x509_store(); + +#endif //SSL_H diff --git a/include/utils.h b/include/utils.h new file mode 100644 index 0000000..10fba28 --- /dev/null +++ b/include/utils.h @@ -0,0 +1,36 @@ +#ifndef UTILS_H +#define UTILS_H + +#include +#include + +bool wildcard_match(char const *needle, char const *haystack); + +bool check_host_name(const char *pattern, size_t pattern_len, std::string host); + +std::string last_n_chars(const std::string &input, unsigned int n); + +void get_tls_sni(const std::string &bytes, unsigned int last_char, unsigned int &start_pos, + unsigned int &len); + +bool validate_http_method(std::string method); + +void daemonize(); + +int ignore_sigpipe(); + +int tcp_get_auto_ttl(const uint8_t ttl, const uint8_t autottl1, + const uint8_t autottl2, const uint8_t minhops, + const uint8_t maxttl); + +bool match_whitelist_domain(const std::string &domain); + +bool match_whitelist_ip(const std::string &ip); + +int load_whitelist(); + +std::string find_custom_ip(const std::string &domain); + +int load_custom_ips(); + +#endif //UTILS_H diff --git a/libnl/CMakeLists.txt b/libnl/CMakeLists.txt new file mode 100644 index 0000000..6123976 --- /dev/null +++ b/libnl/CMakeLists.txt @@ -0,0 +1,125 @@ +# Set the project name +project(libnl) + +# Add a library with the above sources +add_library(${PROJECT_NAME} + lib/addr.c + lib/attr.c + lib/cache.c + lib/cache_mngr.c + lib/cache_mngt.c + lib/data.c + lib/error.c + lib/handlers.c + lib/hash.c + lib/hashtable.c + lib/mpls.c + lib/msg.c + lib/nl.c + lib/object.c + lib/socket.c + lib/utils.c + lib/version.c + lib/genl/ctrl.c + lib/genl/family.c + lib/genl/genl.c + lib/genl/mngt.c + lib/idiag/idiag.c + lib/idiag/idiag_meminfo_obj.c + lib/idiag/idiag_msg_obj.c + lib/idiag/idiag_req_obj.c + lib/idiag/idiag_vegasinfo_obj.c + lib/netfilter/ct.c + lib/netfilter/ct_obj.c + lib/netfilter/exp.c + lib/netfilter/exp_obj.c + lib/netfilter/log.c + lib/netfilter/log_msg.c + lib/netfilter/log_msg_obj.c + lib/netfilter/log_obj.c + lib/netfilter/netfilter.c + lib/netfilter/nfnl.c + lib/netfilter/queue.c + lib/netfilter/queue_msg.c + lib/netfilter/queue_msg_obj.c + lib/netfilter/queue_obj.c + lib/route/act.c + lib/route/addr.c + lib/route/class.c + lib/route/classid.c + lib/route/cls.c + lib/route/link.c + lib/route/neigh.c + lib/route/neightbl.c + lib/route/netconf.c + lib/route/nexthop.c + lib/route/nexthop_encap.c + lib/route/nh_encap_mpls.c + lib/route/pktloc.c + lib/route/qdisc.c + lib/route/route.c + lib/route/route_obj.c + lib/route/route_utils.c + lib/route/rtnl.c + lib/route/rule.c + lib/route/tc.c + lib/route/act/gact.c + lib/route/act/mirred.c + lib/route/act/skbedit.c + lib/route/act/vlan.c + lib/route/cls/basic.c + lib/route/cls/cgroup.c + lib/route/cls/ematch.c + lib/route/cls/fw.c + lib/route/cls/mall.c + lib/route/cls/police.c + lib/route/cls/u32.c + lib/route/cls/ematch/cmp.c + lib/route/cls/ematch/container.c + lib/route/cls/ematch/meta.c + lib/route/cls/ematch/nbyte.c + lib/route/cls/ematch/text.c + lib/route/link/api.c + lib/route/link/bonding.c + lib/route/link/bridge.c + lib/route/link/can.c + lib/route/link/dummy.c + lib/route/link/geneve.c + lib/route/link/ifb.c + lib/route/link/inet.c + lib/route/link/inet6.c + lib/route/link/ip6tnl.c + lib/route/link/ipgre.c + lib/route/link/ipip.c + lib/route/link/ipvlan.c + lib/route/link/ipvti.c + lib/route/link/macsec.c + lib/route/link/macvlan.c + lib/route/link/ppp.c + lib/route/link/sit.c + lib/route/link/sriov.c + lib/route/link/veth.c + lib/route/link/vlan.c + lib/route/link/vrf.c + lib/route/link/vxlan.c + lib/route/link/xfrmi.c + lib/route/qdisc/blackhole.c + lib/route/qdisc/cbq.c + lib/route/qdisc/dsmark.c + lib/route/qdisc/fifo.c + lib/route/qdisc/fq_codel.c + lib/route/qdisc/hfsc.c + lib/route/qdisc/htb.c + lib/route/qdisc/ingress.c + lib/route/qdisc/mqprio.c + lib/route/qdisc/netem.c + lib/route/qdisc/plug.c + lib/route/qdisc/prio.c + lib/route/qdisc/red.c + lib/route/qdisc/sfq.c + lib/route/qdisc/tbf.c +) + +target_include_directories(${PROJECT_NAME} + PUBLIC ${PROJECT_SOURCE_DIR}/include +) diff --git a/libnl/include/linux-private/linux/can/netlink.h b/libnl/include/linux-private/linux/can/netlink.h new file mode 100644 index 0000000..f0c5e58 --- /dev/null +++ b/libnl/include/linux-private/linux/can/netlink.h @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * linux/can/netlink.h + * + * Definitions for the CAN netlink interface + * + * Copyright (c) 2009 Wolfgang Grandegger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _CAN_NETLINK_H +#define _CAN_NETLINK_H + +#include + +/* + * CAN bit-timing parameters + * + * For further information, please read chapter "8 BIT TIMING + * REQUIREMENTS" of the "Bosch CAN Specification version 2.0" + * at http://www.semiconductors.bosch.de/pdf/can2spec.pdf. + */ +struct can_bittiming { + __u32 bitrate; /* Bit-rate in bits/second */ + __u32 sample_point; /* Sample point in one-tenth of a percent */ + __u32 tq; /* Time quanta (TQ) in nanoseconds */ + __u32 prop_seg; /* Propagation segment in TQs */ + __u32 phase_seg1; /* Phase buffer segment 1 in TQs */ + __u32 phase_seg2; /* Phase buffer segment 2 in TQs */ + __u32 sjw; /* Synchronisation jump width in TQs */ + __u32 brp; /* Bit-rate prescaler */ +}; + +/* + * CAN harware-dependent bit-timing constant + * + * Used for calculating and checking bit-timing parameters + */ +struct can_bittiming_const { + char name[16]; /* Name of the CAN controller hardware */ + __u32 tseg1_min; /* Time segement 1 = prop_seg + phase_seg1 */ + __u32 tseg1_max; + __u32 tseg2_min; /* Time segement 2 = phase_seg2 */ + __u32 tseg2_max; + __u32 sjw_max; /* Synchronisation jump width */ + __u32 brp_min; /* Bit-rate prescaler */ + __u32 brp_max; + __u32 brp_inc; +}; + +/* + * CAN clock parameters + */ +struct can_clock { + __u32 freq; /* CAN system clock frequency in Hz */ +}; + +/* + * CAN operational and error states + */ +enum can_state { + CAN_STATE_ERROR_ACTIVE = 0, /* RX/TX error count < 96 */ + CAN_STATE_ERROR_WARNING, /* RX/TX error count < 128 */ + CAN_STATE_ERROR_PASSIVE, /* RX/TX error count < 256 */ + CAN_STATE_BUS_OFF, /* RX/TX error count >= 256 */ + CAN_STATE_STOPPED, /* Device is stopped */ + CAN_STATE_SLEEPING, /* Device is sleeping */ + CAN_STATE_MAX +}; + +/* + * CAN bus error counters + */ +struct can_berr_counter { + __u16 txerr; + __u16 rxerr; +}; + +/* + * CAN controller mode + */ +struct can_ctrlmode { + __u32 mask; + __u32 flags; +}; + +#define CAN_CTRLMODE_LOOPBACK 0x01 /* Loopback mode */ +#define CAN_CTRLMODE_LISTENONLY 0x02 /* Listen-only mode */ +#define CAN_CTRLMODE_3_SAMPLES 0x04 /* Triple sampling mode */ +#define CAN_CTRLMODE_ONE_SHOT 0x08 /* One-Shot mode */ +#define CAN_CTRLMODE_BERR_REPORTING 0x10 /* Bus-error reporting */ +#define CAN_CTRLMODE_FD 0x20 /* CAN FD mode */ +#define CAN_CTRLMODE_PRESUME_ACK 0x40 /* Ignore missing CAN ACKs */ +#define CAN_CTRLMODE_FD_NON_ISO 0x80 /* CAN FD in non-ISO mode */ + +/* + * CAN device statistics + */ +struct can_device_stats { + __u32 bus_error; /* Bus errors */ + __u32 error_warning; /* Changes to error warning state */ + __u32 error_passive; /* Changes to error passive state */ + __u32 bus_off; /* Changes to bus off state */ + __u32 arbitration_lost; /* Arbitration lost errors */ + __u32 restarts; /* CAN controller re-starts */ +}; + +/* + * CAN netlink interface + */ +enum { + IFLA_CAN_UNSPEC, + IFLA_CAN_BITTIMING, + IFLA_CAN_BITTIMING_CONST, + IFLA_CAN_CLOCK, + IFLA_CAN_STATE, + IFLA_CAN_CTRLMODE, + IFLA_CAN_RESTART_MS, + IFLA_CAN_RESTART, + IFLA_CAN_BERR_COUNTER, + IFLA_CAN_DATA_BITTIMING, + IFLA_CAN_DATA_BITTIMING_CONST, + IFLA_CAN_TERMINATION, + IFLA_CAN_TERMINATION_CONST, + IFLA_CAN_BITRATE_CONST, + IFLA_CAN_DATA_BITRATE_CONST, + IFLA_CAN_BITRATE_MAX, + __IFLA_CAN_MAX +}; + +#define IFLA_CAN_MAX (__IFLA_CAN_MAX - 1) + +/* u16 termination range: 1..65535 Ohms */ +#define CAN_TERMINATION_DISABLED 0 + +#endif /* !_UAPI_CAN_NETLINK_H */ diff --git a/libnl/include/linux-private/linux/fib_rules.h b/libnl/include/linux-private/linux/fib_rules.h new file mode 100644 index 0000000..232df14 --- /dev/null +++ b/libnl/include/linux-private/linux/fib_rules.h @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_FIB_RULES_H +#define __LINUX_FIB_RULES_H + +#include +#include + +/* rule is permanent, and cannot be deleted */ +#define FIB_RULE_PERMANENT 0x00000001 +#define FIB_RULE_INVERT 0x00000002 +#define FIB_RULE_UNRESOLVED 0x00000004 +#define FIB_RULE_IIF_DETACHED 0x00000008 +#define FIB_RULE_DEV_DETACHED FIB_RULE_IIF_DETACHED +#define FIB_RULE_OIF_DETACHED 0x00000010 + +/* try to find source address in routing lookups */ +#define FIB_RULE_FIND_SADDR 0x00010000 + +struct fib_rule_hdr { + __u8 family; + __u8 dst_len; + __u8 src_len; + __u8 tos; + + __u8 table; + __u8 res1; /* reserved */ + __u8 res2; /* reserved */ + __u8 action; + + __u32 flags; +}; + +struct fib_rule_uid_range { + __u32 start; + __u32 end; +}; + +struct fib_rule_port_range { + __u16 start; + __u16 end; +}; + +enum { + FRA_UNSPEC, + FRA_DST, /* destination address */ + FRA_SRC, /* source address */ + FRA_IIFNAME, /* interface name */ +#define FRA_IFNAME FRA_IIFNAME + FRA_GOTO, /* target to jump to (FR_ACT_GOTO) */ + FRA_UNUSED2, + FRA_PRIORITY, /* priority/preference */ + FRA_UNUSED3, + FRA_UNUSED4, + FRA_UNUSED5, + FRA_FWMARK, /* mark */ + FRA_FLOW, /* flow/class id */ + FRA_TUN_ID, + FRA_SUPPRESS_IFGROUP, + FRA_SUPPRESS_PREFIXLEN, + FRA_TABLE, /* Extended table id */ + FRA_FWMASK, /* mask for netfilter mark */ + FRA_OIFNAME, + FRA_PAD, + FRA_L3MDEV, /* iif or oif is l3mdev goto its table */ + FRA_UID_RANGE, /* UID range */ + FRA_PROTOCOL, /* Originator of the rule */ + FRA_IP_PROTO, /* ip proto */ + FRA_SPORT_RANGE, /* sport */ + FRA_DPORT_RANGE, /* dport */ + __FRA_MAX +}; + +#define FRA_MAX (__FRA_MAX - 1) + +enum { + FR_ACT_UNSPEC, + FR_ACT_TO_TBL, /* Pass to fixed table */ + FR_ACT_GOTO, /* Jump to another rule */ + FR_ACT_NOP, /* No operation */ + FR_ACT_RES3, + FR_ACT_RES4, + FR_ACT_BLACKHOLE, /* Drop without notification */ + FR_ACT_UNREACHABLE, /* Drop with ENETUNREACH */ + FR_ACT_PROHIBIT, /* Drop with EACCES */ + __FR_ACT_MAX, +}; + +#define FR_ACT_MAX (__FR_ACT_MAX - 1) + +#endif diff --git a/libnl/include/linux-private/linux/gen_stats.h b/libnl/include/linux-private/linux/gen_stats.h new file mode 100644 index 0000000..24a861c --- /dev/null +++ b/libnl/include/linux-private/linux/gen_stats.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_GEN_STATS_H +#define __LINUX_GEN_STATS_H + +#include + +enum { + TCA_STATS_UNSPEC, + TCA_STATS_BASIC, + TCA_STATS_RATE_EST, + TCA_STATS_QUEUE, + TCA_STATS_APP, + TCA_STATS_RATE_EST64, + TCA_STATS_PAD, + __TCA_STATS_MAX, +}; +#define TCA_STATS_MAX (__TCA_STATS_MAX - 1) + +/** + * struct gnet_stats_basic - byte/packet throughput statistics + * @bytes: number of seen bytes + * @packets: number of seen packets + */ +struct gnet_stats_basic { + __u64 bytes; + __u32 packets; +}; +struct gnet_stats_basic_packed { + __u64 bytes; + __u32 packets; +} __attribute__ ((packed)); + +/** + * struct gnet_stats_rate_est - rate estimator + * @bps: current byte rate + * @pps: current packet rate + */ +struct gnet_stats_rate_est { + __u32 bps; + __u32 pps; +}; + +/** + * struct gnet_stats_rate_est64 - rate estimator + * @bps: current byte rate + * @pps: current packet rate + */ +struct gnet_stats_rate_est64 { + __u64 bps; + __u64 pps; +}; + +/** + * struct gnet_stats_queue - queuing statistics + * @qlen: queue length + * @backlog: backlog size of queue + * @drops: number of dropped packets + * @requeues: number of requeues + * @overlimits: number of enqueues over the limit + */ +struct gnet_stats_queue { + __u32 qlen; + __u32 backlog; + __u32 drops; + __u32 requeues; + __u32 overlimits; +}; + +/** + * struct gnet_estimator - rate estimator configuration + * @interval: sampling period + * @ewma_log: the log of measurement window weight + */ +struct gnet_estimator { + signed char interval; + unsigned char ewma_log; +}; + + +#endif /* __LINUX_GEN_STATS_H */ diff --git a/libnl/include/linux-private/linux/genetlink.h b/libnl/include/linux-private/linux/genetlink.h new file mode 100644 index 0000000..1317119 --- /dev/null +++ b/libnl/include/linux-private/linux/genetlink.h @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_GENERIC_NETLINK_H +#define __LINUX_GENERIC_NETLINK_H + +#include +#include + +#define GENL_NAMSIZ 16 /* length of family name */ + +#define GENL_MIN_ID NLMSG_MIN_TYPE +#define GENL_MAX_ID 1023 + +struct genlmsghdr { + __u8 cmd; + __u8 version; + __u16 reserved; +}; + +#define GENL_HDRLEN NLMSG_ALIGN(sizeof(struct genlmsghdr)) + +#define GENL_ADMIN_PERM 0x01 +#define GENL_CMD_CAP_DO 0x02 +#define GENL_CMD_CAP_DUMP 0x04 +#define GENL_CMD_CAP_HASPOL 0x08 +#define GENL_UNS_ADMIN_PERM 0x10 + +/* + * List of reserved static generic netlink identifiers: + */ +#define GENL_ID_CTRL NLMSG_MIN_TYPE +#define GENL_ID_VFS_DQUOT (NLMSG_MIN_TYPE + 1) +#define GENL_ID_PMCRAID (NLMSG_MIN_TYPE + 2) +/* must be last reserved + 1 */ +#define GENL_START_ALLOC (NLMSG_MIN_TYPE + 3) + +/************************************************************************** + * Controller + **************************************************************************/ + +enum { + CTRL_CMD_UNSPEC, + CTRL_CMD_NEWFAMILY, + CTRL_CMD_DELFAMILY, + CTRL_CMD_GETFAMILY, + CTRL_CMD_NEWOPS, + CTRL_CMD_DELOPS, + CTRL_CMD_GETOPS, + CTRL_CMD_NEWMCAST_GRP, + CTRL_CMD_DELMCAST_GRP, + CTRL_CMD_GETMCAST_GRP, /* unused */ + __CTRL_CMD_MAX, +}; + +#define CTRL_CMD_MAX (__CTRL_CMD_MAX - 1) + +enum { + CTRL_ATTR_UNSPEC, + CTRL_ATTR_FAMILY_ID, + CTRL_ATTR_FAMILY_NAME, + CTRL_ATTR_VERSION, + CTRL_ATTR_HDRSIZE, + CTRL_ATTR_MAXATTR, + CTRL_ATTR_OPS, + CTRL_ATTR_MCAST_GROUPS, + __CTRL_ATTR_MAX, +}; + +#define CTRL_ATTR_MAX (__CTRL_ATTR_MAX - 1) + +enum { + CTRL_ATTR_OP_UNSPEC, + CTRL_ATTR_OP_ID, + CTRL_ATTR_OP_FLAGS, + __CTRL_ATTR_OP_MAX, +}; + +#define CTRL_ATTR_OP_MAX (__CTRL_ATTR_OP_MAX - 1) + +enum { + CTRL_ATTR_MCAST_GRP_UNSPEC, + CTRL_ATTR_MCAST_GRP_NAME, + CTRL_ATTR_MCAST_GRP_ID, + __CTRL_ATTR_MCAST_GRP_MAX, +}; + +#define CTRL_ATTR_MCAST_GRP_MAX (__CTRL_ATTR_MCAST_GRP_MAX - 1) + + +#endif /* __LINUX_GENERIC_NETLINK_H */ diff --git a/libnl/include/linux-private/linux/if.h b/libnl/include/linux-private/linux/if.h new file mode 100644 index 0000000..495cdd2 --- /dev/null +++ b/libnl/include/linux-private/linux/if.h @@ -0,0 +1,293 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Global definitions for the INET interface module. + * + * Version: @(#)if.h 1.0.2 04/18/93 + * + * Authors: Original taken from Berkeley UNIX 4.3, (c) UCB 1982-1988 + * Ross Biro + * Fred N. van Kempen, + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#ifndef _LINUX_IF_H +#define _LINUX_IF_H + +#include /* for compatibility with glibc */ +#include /* for "__kernel_caddr_t" et al */ +#include /* for "struct sockaddr" et al */ + /* for "__user" et al */ + +#include /* for struct sockaddr. */ + +#if __UAPI_DEF_IF_IFNAMSIZ +#define IFNAMSIZ 16 +#endif /* __UAPI_DEF_IF_IFNAMSIZ */ +#define IFALIASZ 256 +#include + +/* For glibc compatibility. An empty enum does not compile. */ +#if __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO != 0 || \ + __UAPI_DEF_IF_NET_DEVICE_FLAGS != 0 +/** + * enum net_device_flags - &struct net_device flags + * + * These are the &struct net_device flags, they can be set by drivers, the + * kernel and some can be triggered by userspace. Userspace can query and + * set these flags using userspace utilities but there is also a sysfs + * entry available for all dev flags which can be queried and set. These flags + * are shared for all types of net_devices. The sysfs entries are available + * via /sys/class/net//flags. Flags which can be toggled through sysfs + * are annotated below, note that only a few flags can be toggled and some + * other flags are always preserved from the original net_device flags + * even if you try to set them via sysfs. Flags which are always preserved + * are kept under the flag grouping @IFF_VOLATILE. Flags which are __volatile__ + * are annotated below as such. + * + * You should have a pretty good reason to be extending these flags. + * + * @IFF_UP: interface is up. Can be toggled through sysfs. + * @IFF_BROADCAST: broadcast address valid. Volatile. + * @IFF_DEBUG: turn on debugging. Can be toggled through sysfs. + * @IFF_LOOPBACK: is a loopback net. Volatile. + * @IFF_POINTOPOINT: interface is has p-p link. Volatile. + * @IFF_NOTRAILERS: avoid use of trailers. Can be toggled through sysfs. + * Volatile. + * @IFF_RUNNING: interface RFC2863 OPER_UP. Volatile. + * @IFF_NOARP: no ARP protocol. Can be toggled through sysfs. Volatile. + * @IFF_PROMISC: receive all packets. Can be toggled through sysfs. + * @IFF_ALLMULTI: receive all multicast packets. Can be toggled through + * sysfs. + * @IFF_MASTER: master of a load balancer. Volatile. + * @IFF_SLAVE: slave of a load balancer. Volatile. + * @IFF_MULTICAST: Supports multicast. Can be toggled through sysfs. + * @IFF_PORTSEL: can set media type. Can be toggled through sysfs. + * @IFF_AUTOMEDIA: auto media select active. Can be toggled through sysfs. + * @IFF_DYNAMIC: dialup device with changing addresses. Can be toggled + * through sysfs. + * @IFF_LOWER_UP: driver signals L1 up. Volatile. + * @IFF_DORMANT: driver signals dormant. Volatile. + * @IFF_ECHO: echo sent packets. Volatile. + */ +enum net_device_flags { +/* for compatibility with glibc net/if.h */ +#if __UAPI_DEF_IF_NET_DEVICE_FLAGS + IFF_UP = 1<<0, /* sysfs */ + IFF_BROADCAST = 1<<1, /* __volatile__ */ + IFF_DEBUG = 1<<2, /* sysfs */ + IFF_LOOPBACK = 1<<3, /* __volatile__ */ + IFF_POINTOPOINT = 1<<4, /* __volatile__ */ + IFF_NOTRAILERS = 1<<5, /* sysfs */ + IFF_RUNNING = 1<<6, /* __volatile__ */ + IFF_NOARP = 1<<7, /* sysfs */ + IFF_PROMISC = 1<<8, /* sysfs */ + IFF_ALLMULTI = 1<<9, /* sysfs */ + IFF_MASTER = 1<<10, /* __volatile__ */ + IFF_SLAVE = 1<<11, /* __volatile__ */ + IFF_MULTICAST = 1<<12, /* sysfs */ + IFF_PORTSEL = 1<<13, /* sysfs */ + IFF_AUTOMEDIA = 1<<14, /* sysfs */ + IFF_DYNAMIC = 1<<15, /* sysfs */ +#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS */ +#if __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO + IFF_LOWER_UP = 1<<16, /* __volatile__ */ + IFF_DORMANT = 1<<17, /* __volatile__ */ + IFF_ECHO = 1<<18, /* __volatile__ */ +#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO */ +}; +#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO != 0 || __UAPI_DEF_IF_NET_DEVICE_FLAGS != 0 */ + +/* for compatibility with glibc net/if.h */ +#if __UAPI_DEF_IF_NET_DEVICE_FLAGS +#define IFF_UP IFF_UP +#define IFF_BROADCAST IFF_BROADCAST +#define IFF_DEBUG IFF_DEBUG +#define IFF_LOOPBACK IFF_LOOPBACK +#define IFF_POINTOPOINT IFF_POINTOPOINT +#define IFF_NOTRAILERS IFF_NOTRAILERS +#define IFF_RUNNING IFF_RUNNING +#define IFF_NOARP IFF_NOARP +#define IFF_PROMISC IFF_PROMISC +#define IFF_ALLMULTI IFF_ALLMULTI +#define IFF_MASTER IFF_MASTER +#define IFF_SLAVE IFF_SLAVE +#define IFF_MULTICAST IFF_MULTICAST +#define IFF_PORTSEL IFF_PORTSEL +#define IFF_AUTOMEDIA IFF_AUTOMEDIA +#define IFF_DYNAMIC IFF_DYNAMIC +#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS */ + +#if __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO +#define IFF_LOWER_UP IFF_LOWER_UP +#define IFF_DORMANT IFF_DORMANT +#define IFF_ECHO IFF_ECHO +#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO */ + +#define IFF_VOLATILE (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|IFF_ECHO|\ + IFF_MASTER|IFF_SLAVE|IFF_RUNNING|IFF_LOWER_UP|IFF_DORMANT) + +#define IF_GET_IFACE 0x0001 /* for querying only */ +#define IF_GET_PROTO 0x0002 + +/* For definitions see hdlc.h */ +#define IF_IFACE_V35 0x1000 /* V.35 serial interface */ +#define IF_IFACE_V24 0x1001 /* V.24 serial interface */ +#define IF_IFACE_X21 0x1002 /* X.21 serial interface */ +#define IF_IFACE_T1 0x1003 /* T1 telco serial interface */ +#define IF_IFACE_E1 0x1004 /* E1 telco serial interface */ +#define IF_IFACE_SYNC_SERIAL 0x1005 /* can't be set by software */ +#define IF_IFACE_X21D 0x1006 /* X.21 Dual Clocking (FarSite) */ + +/* For definitions see hdlc.h */ +#define IF_PROTO_HDLC 0x2000 /* raw HDLC protocol */ +#define IF_PROTO_PPP 0x2001 /* PPP protocol */ +#define IF_PROTO_CISCO 0x2002 /* Cisco HDLC protocol */ +#define IF_PROTO_FR 0x2003 /* Frame Relay protocol */ +#define IF_PROTO_FR_ADD_PVC 0x2004 /* Create FR PVC */ +#define IF_PROTO_FR_DEL_PVC 0x2005 /* Delete FR PVC */ +#define IF_PROTO_X25 0x2006 /* X.25 */ +#define IF_PROTO_HDLC_ETH 0x2007 /* raw HDLC, Ethernet emulation */ +#define IF_PROTO_FR_ADD_ETH_PVC 0x2008 /* Create FR Ethernet-bridged PVC */ +#define IF_PROTO_FR_DEL_ETH_PVC 0x2009 /* Delete FR Ethernet-bridged PVC */ +#define IF_PROTO_FR_PVC 0x200A /* for reading PVC status */ +#define IF_PROTO_FR_ETH_PVC 0x200B +#define IF_PROTO_RAW 0x200C /* RAW Socket */ + +/* RFC 2863 operational status */ +enum { + IF_OPER_UNKNOWN, + IF_OPER_NOTPRESENT, + IF_OPER_DOWN, + IF_OPER_LOWERLAYERDOWN, + IF_OPER_TESTING, + IF_OPER_DORMANT, + IF_OPER_UP, +}; + +/* link modes */ +enum { + IF_LINK_MODE_DEFAULT, + IF_LINK_MODE_DORMANT, /* limit upward transition to dormant */ +}; + +/* + * Device mapping structure. I'd just gone off and designed a + * beautiful scheme using only loadable modules with arguments + * for driver options and along come the PCMCIA people 8) + * + * Ah well. The get() side of this is good for WDSETUP, and it'll + * be handy for debugging things. The set side is fine for now and + * being very small might be worth keeping for clean configuration. + */ + +/* for compatibility with glibc net/if.h */ +#if __UAPI_DEF_IF_IFMAP +struct ifmap { + unsigned long mem_start; + unsigned long mem_end; + unsigned short base_addr; + unsigned char irq; + unsigned char dma; + unsigned char port; + /* 3 bytes spare */ +}; +#endif /* __UAPI_DEF_IF_IFMAP */ + +struct if_settings { + unsigned int type; /* Type of physical device or protocol */ + unsigned int size; /* Size of the data allocated by the caller */ + union { + /* {atm/eth/dsl}_settings anyone ? */ + raw_hdlc_proto *raw_hdlc; + cisco_proto *cisco; + fr_proto *fr; + fr_proto_pvc *fr_pvc; + fr_proto_pvc_info *fr_pvc_info; + + /* interface settings */ + sync_serial_settings *sync; + te1_settings *te1; + } ifs_ifsu; +}; + +/* + * Interface request structure used for socket + * ioctl's. All interface ioctl's must have parameter + * definitions which begin with ifr_name. The + * remainder may be interface specific. + */ + +/* for compatibility with glibc net/if.h */ +#if __UAPI_DEF_IF_IFREQ +struct ifreq { +#define IFHWADDRLEN 6 + union + { + char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */ + } ifr_ifrn; + + union { + struct sockaddr ifru_addr; + struct sockaddr ifru_dstaddr; + struct sockaddr ifru_broadaddr; + struct sockaddr ifru_netmask; + struct sockaddr ifru_hwaddr; + short ifru_flags; + int ifru_ivalue; + int ifru_mtu; + struct ifmap ifru_map; + char ifru_slave[IFNAMSIZ]; /* Just fits the size */ + char ifru_newname[IFNAMSIZ]; + void * ifru_data; + struct if_settings ifru_settings; + } ifr_ifru; +}; +#endif /* __UAPI_DEF_IF_IFREQ */ + +#define ifr_name ifr_ifrn.ifrn_name /* interface name */ +#define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */ +#define ifr_addr ifr_ifru.ifru_addr /* address */ +#define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-p lnk */ +#define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */ +#define ifr_netmask ifr_ifru.ifru_netmask /* interface net mask */ +#define ifr_flags ifr_ifru.ifru_flags /* flags */ +#define ifr_metric ifr_ifru.ifru_ivalue /* metric */ +#define ifr_mtu ifr_ifru.ifru_mtu /* mtu */ +#define ifr_map ifr_ifru.ifru_map /* device map */ +#define ifr_slave ifr_ifru.ifru_slave /* slave device */ +#define ifr_data ifr_ifru.ifru_data /* for use by interface */ +#define ifr_ifindex ifr_ifru.ifru_ivalue /* interface index */ +#define ifr_bandwidth ifr_ifru.ifru_ivalue /* link bandwidth */ +#define ifr_qlen ifr_ifru.ifru_ivalue /* Queue length */ +#define ifr_newname ifr_ifru.ifru_newname /* New name */ +#define ifr_settings ifr_ifru.ifru_settings /* Device/proto settings*/ + +/* + * Structure used in SIOCGIFCONF request. + * Used to retrieve interface configuration + * for machine (useful for programs which + * must know all networks accessible). + */ + +/* for compatibility with glibc net/if.h */ +#if __UAPI_DEF_IF_IFCONF +struct ifconf { + int ifc_len; /* size of buffer */ + union { + char *ifcu_buf; + struct ifreq *ifcu_req; + } ifc_ifcu; +}; +#endif /* __UAPI_DEF_IF_IFCONF */ + +#define ifc_buf ifc_ifcu.ifcu_buf /* buffer address */ +#define ifc_req ifc_ifcu.ifcu_req /* array of structures */ + +#endif /* _LINUX_IF_H */ diff --git a/libnl/include/linux-private/linux/if_addr.h b/libnl/include/linux-private/linux/if_addr.h new file mode 100644 index 0000000..a924606 --- /dev/null +++ b/libnl/include/linux-private/linux/if_addr.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_IF_ADDR_H +#define __LINUX_IF_ADDR_H + +#include +#include + +struct ifaddrmsg { + __u8 ifa_family; + __u8 ifa_prefixlen; /* The prefix length */ + __u8 ifa_flags; /* Flags */ + __u8 ifa_scope; /* Address scope */ + __u32 ifa_index; /* Link index */ +}; + +/* + * Important comment: + * IFA_ADDRESS is prefix address, rather than local interface address. + * It makes no difference for normally configured broadcast interfaces, + * but for point-to-point IFA_ADDRESS is DESTINATION address, + * local address is supplied in IFA_LOCAL attribute. + * + * IFA_FLAGS is a u32 attribute that extends the u8 field ifa_flags. + * If present, the value from struct ifaddrmsg will be ignored. + */ +enum { + IFA_UNSPEC, + IFA_ADDRESS, + IFA_LOCAL, + IFA_LABEL, + IFA_BROADCAST, + IFA_ANYCAST, + IFA_CACHEINFO, + IFA_MULTICAST, + IFA_FLAGS, + IFA_RT_PRIORITY, /* u32, priority/metric for prefix route */ + __IFA_MAX, +}; + +#define IFA_MAX (__IFA_MAX - 1) + +/* ifa_flags */ +#define IFA_F_SECONDARY 0x01 +#define IFA_F_TEMPORARY IFA_F_SECONDARY + +#define IFA_F_NODAD 0x02 +#define IFA_F_OPTIMISTIC 0x04 +#define IFA_F_DADFAILED 0x08 +#define IFA_F_HOMEADDRESS 0x10 +#define IFA_F_DEPRECATED 0x20 +#define IFA_F_TENTATIVE 0x40 +#define IFA_F_PERMANENT 0x80 +#define IFA_F_MANAGETEMPADDR 0x100 +#define IFA_F_NOPREFIXROUTE 0x200 +#define IFA_F_MCAUTOJOIN 0x400 +#define IFA_F_STABLE_PRIVACY 0x800 + +struct ifa_cacheinfo { + __u32 ifa_prefered; + __u32 ifa_valid; + __u32 cstamp; /* created timestamp, hundredths of seconds */ + __u32 tstamp; /* updated timestamp, hundredths of seconds */ +}; + +/* backwards compatibility for userspace */ +#define IFA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg)))) +#define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg)) + +#endif diff --git a/libnl/include/linux-private/linux/if_arp.h b/libnl/include/linux-private/linux/if_arp.h new file mode 100644 index 0000000..cd136a6 --- /dev/null +++ b/libnl/include/linux-private/linux/if_arp.h @@ -0,0 +1,164 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Global definitions for the ARP (RFC 826) protocol. + * + * Version: @(#)if_arp.h 1.0.1 04/16/93 + * + * Authors: Original taken from Berkeley UNIX 4.3, (c) UCB 1986-1988 + * Portions taken from the KA9Q/NOS (v2.00m PA0GRI) source. + * Ross Biro + * Fred N. van Kempen, + * Florian La Roche, + * Jonathan Layes + * Arnaldo Carvalho de Melo ARPHRD_HWX25 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#ifndef _LINUX_IF_ARP_H +#define _LINUX_IF_ARP_H + +#include + +/* ARP protocol HARDWARE identifiers. */ +#define ARPHRD_NETROM 0 /* from KA9Q: NET/ROM pseudo */ +#define ARPHRD_ETHER 1 /* Ethernet 10Mbps */ +#define ARPHRD_EETHER 2 /* Experimental Ethernet */ +#define ARPHRD_AX25 3 /* AX.25 Level 2 */ +#define ARPHRD_PRONET 4 /* PROnet token ring */ +#define ARPHRD_CHAOS 5 /* Chaosnet */ +#define ARPHRD_IEEE802 6 /* IEEE 802.2 Ethernet/TR/TB */ +#define ARPHRD_ARCNET 7 /* ARCnet */ +#define ARPHRD_APPLETLK 8 /* APPLEtalk */ +#define ARPHRD_DLCI 15 /* Frame Relay DLCI */ +#define ARPHRD_ATM 19 /* ATM */ +#define ARPHRD_METRICOM 23 /* Metricom STRIP (new IANA id) */ +#define ARPHRD_IEEE1394 24 /* IEEE 1394 IPv4 - RFC 2734 */ +#define ARPHRD_EUI64 27 /* EUI-64 */ +#define ARPHRD_INFINIBAND 32 /* InfiniBand */ + +/* Dummy types for non ARP hardware */ +#define ARPHRD_SLIP 256 +#define ARPHRD_CSLIP 257 +#define ARPHRD_SLIP6 258 +#define ARPHRD_CSLIP6 259 +#define ARPHRD_RSRVD 260 /* Notional KISS type */ +#define ARPHRD_ADAPT 264 +#define ARPHRD_ROSE 270 +#define ARPHRD_X25 271 /* CCITT X.25 */ +#define ARPHRD_HWX25 272 /* Boards with X.25 in firmware */ +#define ARPHRD_CAN 280 /* Controller Area Network */ +#define ARPHRD_PPP 512 +#define ARPHRD_CISCO 513 /* Cisco HDLC */ +#define ARPHRD_HDLC ARPHRD_CISCO +#define ARPHRD_LAPB 516 /* LAPB */ +#define ARPHRD_DDCMP 517 /* Digital's DDCMP protocol */ +#define ARPHRD_RAWHDLC 518 /* Raw HDLC */ +#define ARPHRD_RAWIP 519 /* Raw IP */ + +#define ARPHRD_TUNNEL 768 /* IPIP tunnel */ +#define ARPHRD_TUNNEL6 769 /* IP6IP6 tunnel */ +#define ARPHRD_FRAD 770 /* Frame Relay Access Device */ +#define ARPHRD_SKIP 771 /* SKIP vif */ +#define ARPHRD_LOOPBACK 772 /* Loopback device */ +#define ARPHRD_LOCALTLK 773 /* Localtalk device */ +#define ARPHRD_FDDI 774 /* Fiber Distributed Data Interface */ +#define ARPHRD_BIF 775 /* AP1000 BIF */ +#define ARPHRD_SIT 776 /* sit0 device - IPv6-in-IPv4 */ +#define ARPHRD_IPDDP 777 /* IP over DDP tunneller */ +#define ARPHRD_IPGRE 778 /* GRE over IP */ +#define ARPHRD_PIMREG 779 /* PIMSM register interface */ +#define ARPHRD_HIPPI 780 /* High Performance Parallel Interface */ +#define ARPHRD_ASH 781 /* Nexus 64Mbps Ash */ +#define ARPHRD_ECONET 782 /* Acorn Econet */ +#define ARPHRD_IRDA 783 /* Linux-IrDA */ +/* ARP works differently on different FC media .. so */ +#define ARPHRD_FCPP 784 /* Point to point fibrechannel */ +#define ARPHRD_FCAL 785 /* Fibrechannel arbitrated loop */ +#define ARPHRD_FCPL 786 /* Fibrechannel public loop */ +#define ARPHRD_FCFABRIC 787 /* Fibrechannel fabric */ + /* 787->799 reserved for fibrechannel media types */ +#define ARPHRD_IEEE802_TR 800 /* Magic type ident for TR */ +#define ARPHRD_IEEE80211 801 /* IEEE 802.11 */ +#define ARPHRD_IEEE80211_PRISM 802 /* IEEE 802.11 + Prism2 header */ +#define ARPHRD_IEEE80211_RADIOTAP 803 /* IEEE 802.11 + radiotap header */ +#define ARPHRD_IEEE802154 804 +#define ARPHRD_IEEE802154_MONITOR 805 /* IEEE 802.15.4 network monitor */ + +#define ARPHRD_PHONET 820 /* PhoNet media type */ +#define ARPHRD_PHONET_PIPE 821 /* PhoNet pipe header */ +#define ARPHRD_CAIF 822 /* CAIF media type */ +#define ARPHRD_IP6GRE 823 /* GRE over IPv6 */ +#define ARPHRD_NETLINK 824 /* Netlink header */ +#define ARPHRD_6LOWPAN 825 /* IPv6 over LoWPAN */ +#define ARPHRD_VSOCKMON 826 /* Vsock monitor header */ + +#define ARPHRD_VOID 0xFFFF /* Void type, nothing is known */ +#define ARPHRD_NONE 0xFFFE /* zero header length */ + +/* ARP protocol opcodes. */ +#define ARPOP_REQUEST 1 /* ARP request */ +#define ARPOP_REPLY 2 /* ARP reply */ +#define ARPOP_RREQUEST 3 /* RARP request */ +#define ARPOP_RREPLY 4 /* RARP reply */ +#define ARPOP_InREQUEST 8 /* InARP request */ +#define ARPOP_InREPLY 9 /* InARP reply */ +#define ARPOP_NAK 10 /* (ATM)ARP NAK */ + + +/* ARP ioctl request. */ +struct arpreq { + struct sockaddr arp_pa; /* protocol address */ + struct sockaddr arp_ha; /* hardware address */ + int arp_flags; /* flags */ + struct sockaddr arp_netmask; /* netmask (only for proxy arps) */ + char arp_dev[16]; +}; + +struct arpreq_old { + struct sockaddr arp_pa; /* protocol address */ + struct sockaddr arp_ha; /* hardware address */ + int arp_flags; /* flags */ + struct sockaddr arp_netmask; /* netmask (only for proxy arps) */ +}; + +/* ARP Flag values. */ +#define ATF_COM 0x02 /* completed entry (ha valid) */ +#define ATF_PERM 0x04 /* permanent entry */ +#define ATF_PUBL 0x08 /* publish entry */ +#define ATF_USETRAILERS 0x10 /* has requested trailers */ +#define ATF_NETMASK 0x20 /* want to use a netmask (only + for proxy entries) */ +#define ATF_DONTPUB 0x40 /* don't answer this addresses */ + +/* + * This structure defines an ethernet arp header. + */ + +struct arphdr { + __be16 ar_hrd; /* format of hardware address */ + __be16 ar_pro; /* format of protocol address */ + unsigned char ar_hln; /* length of hardware address */ + unsigned char ar_pln; /* length of protocol address */ + __be16 ar_op; /* ARP opcode (command) */ + +#if 0 + /* + * Ethernet looks like this : This bit is variable sized however... + */ + unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */ + unsigned char ar_sip[4]; /* sender IP address */ + unsigned char ar_tha[ETH_ALEN]; /* target hardware address */ + unsigned char ar_tip[4]; /* target IP address */ +#endif + +}; + + +#endif /* _LINUX_IF_ARP_H */ diff --git a/libnl/include/linux-private/linux/if_bridge.h b/libnl/include/linux-private/linux/if_bridge.h new file mode 100644 index 0000000..bdfecf9 --- /dev/null +++ b/libnl/include/linux-private/linux/if_bridge.h @@ -0,0 +1,295 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * Linux ethernet bridge + * + * Authors: + * Lennert Buytenhek + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_IF_BRIDGE_H +#define _LINUX_IF_BRIDGE_H + +#include +#include +#include + +#define SYSFS_BRIDGE_ATTR "bridge" +#define SYSFS_BRIDGE_FDB "brforward" +#define SYSFS_BRIDGE_PORT_SUBDIR "brif" +#define SYSFS_BRIDGE_PORT_ATTR "brport" +#define SYSFS_BRIDGE_PORT_LINK "bridge" + +#define BRCTL_VERSION 1 + +#define BRCTL_GET_VERSION 0 +#define BRCTL_GET_BRIDGES 1 +#define BRCTL_ADD_BRIDGE 2 +#define BRCTL_DEL_BRIDGE 3 +#define BRCTL_ADD_IF 4 +#define BRCTL_DEL_IF 5 +#define BRCTL_GET_BRIDGE_INFO 6 +#define BRCTL_GET_PORT_LIST 7 +#define BRCTL_SET_BRIDGE_FORWARD_DELAY 8 +#define BRCTL_SET_BRIDGE_HELLO_TIME 9 +#define BRCTL_SET_BRIDGE_MAX_AGE 10 +#define BRCTL_SET_AGEING_TIME 11 +#define BRCTL_SET_GC_INTERVAL 12 +#define BRCTL_GET_PORT_INFO 13 +#define BRCTL_SET_BRIDGE_STP_STATE 14 +#define BRCTL_SET_BRIDGE_PRIORITY 15 +#define BRCTL_SET_PORT_PRIORITY 16 +#define BRCTL_SET_PATH_COST 17 +#define BRCTL_GET_FDB_ENTRIES 18 + +#define BR_STATE_DISABLED 0 +#define BR_STATE_LISTENING 1 +#define BR_STATE_LEARNING 2 +#define BR_STATE_FORWARDING 3 +#define BR_STATE_BLOCKING 4 + +struct __bridge_info { + __u64 designated_root; + __u64 bridge_id; + __u32 root_path_cost; + __u32 max_age; + __u32 hello_time; + __u32 forward_delay; + __u32 bridge_max_age; + __u32 bridge_hello_time; + __u32 bridge_forward_delay; + __u8 topology_change; + __u8 topology_change_detected; + __u8 root_port; + __u8 stp_enabled; + __u32 ageing_time; + __u32 gc_interval; + __u32 hello_timer_value; + __u32 tcn_timer_value; + __u32 topology_change_timer_value; + __u32 gc_timer_value; +}; + +struct __port_info { + __u64 designated_root; + __u64 designated_bridge; + __u16 port_id; + __u16 designated_port; + __u32 path_cost; + __u32 designated_cost; + __u8 state; + __u8 top_change_ack; + __u8 config_pending; + __u8 unused0; + __u32 message_age_timer_value; + __u32 forward_delay_timer_value; + __u32 hold_timer_value; +}; + +struct __fdb_entry { + __u8 mac_addr[ETH_ALEN]; + __u8 port_no; + __u8 is_local; + __u32 ageing_timer_value; + __u8 port_hi; + __u8 pad0; + __u16 unused; +}; + +/* Bridge Flags */ +#define BRIDGE_FLAGS_MASTER 1 /* Bridge command to/from master */ +#define BRIDGE_FLAGS_SELF 2 /* Bridge command to/from lowerdev */ + +#define BRIDGE_MODE_VEB 0 /* Default loopback mode */ +#define BRIDGE_MODE_VEPA 1 /* 802.1Qbg defined VEPA mode */ +#define BRIDGE_MODE_UNDEF 0xFFFF /* mode undefined */ + +/* Bridge management nested attributes + * [IFLA_AF_SPEC] = { + * [IFLA_BRIDGE_FLAGS] + * [IFLA_BRIDGE_MODE] + * [IFLA_BRIDGE_VLAN_INFO] + * } + */ +enum { + IFLA_BRIDGE_FLAGS, + IFLA_BRIDGE_MODE, + IFLA_BRIDGE_VLAN_INFO, + IFLA_BRIDGE_VLAN_TUNNEL_INFO, + __IFLA_BRIDGE_MAX, +}; +#define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1) + +#define BRIDGE_VLAN_INFO_MASTER (1<<0) /* Operate on Bridge device as well */ +#define BRIDGE_VLAN_INFO_PVID (1<<1) /* VLAN is PVID, ingress untagged */ +#define BRIDGE_VLAN_INFO_UNTAGGED (1<<2) /* VLAN egresses untagged */ +#define BRIDGE_VLAN_INFO_RANGE_BEGIN (1<<3) /* VLAN is start of vlan range */ +#define BRIDGE_VLAN_INFO_RANGE_END (1<<4) /* VLAN is end of vlan range */ +#define BRIDGE_VLAN_INFO_BRENTRY (1<<5) /* Global bridge VLAN entry */ + +struct bridge_vlan_info { + __u16 flags; + __u16 vid; +}; + +enum { + IFLA_BRIDGE_VLAN_TUNNEL_UNSPEC, + IFLA_BRIDGE_VLAN_TUNNEL_ID, + IFLA_BRIDGE_VLAN_TUNNEL_VID, + IFLA_BRIDGE_VLAN_TUNNEL_FLAGS, + __IFLA_BRIDGE_VLAN_TUNNEL_MAX, +}; + +#define IFLA_BRIDGE_VLAN_TUNNEL_MAX (__IFLA_BRIDGE_VLAN_TUNNEL_MAX - 1) + +struct bridge_vlan_xstats { + __u64 rx_bytes; + __u64 rx_packets; + __u64 tx_bytes; + __u64 tx_packets; + __u16 vid; + __u16 flags; + __u32 pad2; +}; + +/* Bridge multicast database attributes + * [MDBA_MDB] = { + * [MDBA_MDB_ENTRY] = { + * [MDBA_MDB_ENTRY_INFO] { + * struct br_mdb_entry + * [MDBA_MDB_EATTR attributes] + * } + * } + * } + * [MDBA_ROUTER] = { + * [MDBA_ROUTER_PORT] = { + * u32 ifindex + * [MDBA_ROUTER_PATTR attributes] + * } + * } + */ +enum { + MDBA_UNSPEC, + MDBA_MDB, + MDBA_ROUTER, + __MDBA_MAX, +}; +#define MDBA_MAX (__MDBA_MAX - 1) + +enum { + MDBA_MDB_UNSPEC, + MDBA_MDB_ENTRY, + __MDBA_MDB_MAX, +}; +#define MDBA_MDB_MAX (__MDBA_MDB_MAX - 1) + +enum { + MDBA_MDB_ENTRY_UNSPEC, + MDBA_MDB_ENTRY_INFO, + __MDBA_MDB_ENTRY_MAX, +}; +#define MDBA_MDB_ENTRY_MAX (__MDBA_MDB_ENTRY_MAX - 1) + +/* per mdb entry additional attributes */ +enum { + MDBA_MDB_EATTR_UNSPEC, + MDBA_MDB_EATTR_TIMER, + __MDBA_MDB_EATTR_MAX +}; +#define MDBA_MDB_EATTR_MAX (__MDBA_MDB_EATTR_MAX - 1) + +/* multicast router types */ +enum { + MDB_RTR_TYPE_DISABLED, + MDB_RTR_TYPE_TEMP_QUERY, + MDB_RTR_TYPE_PERM, + MDB_RTR_TYPE_TEMP +}; + +enum { + MDBA_ROUTER_UNSPEC, + MDBA_ROUTER_PORT, + __MDBA_ROUTER_MAX, +}; +#define MDBA_ROUTER_MAX (__MDBA_ROUTER_MAX - 1) + +/* router port attributes */ +enum { + MDBA_ROUTER_PATTR_UNSPEC, + MDBA_ROUTER_PATTR_TIMER, + MDBA_ROUTER_PATTR_TYPE, + __MDBA_ROUTER_PATTR_MAX +}; +#define MDBA_ROUTER_PATTR_MAX (__MDBA_ROUTER_PATTR_MAX - 1) + +struct br_port_msg { + __u8 family; + __u32 ifindex; +}; + +struct br_mdb_entry { + __u32 ifindex; +#define MDB_TEMPORARY 0 +#define MDB_PERMANENT 1 + __u8 state; +#define MDB_FLAGS_OFFLOAD (1 << 0) + __u8 flags; + __u16 vid; + struct { + union { + __be32 ip4; + struct in6_addr ip6; + } u; + __be16 proto; + } addr; +}; + +enum { + MDBA_SET_ENTRY_UNSPEC, + MDBA_SET_ENTRY, + __MDBA_SET_ENTRY_MAX, +}; +#define MDBA_SET_ENTRY_MAX (__MDBA_SET_ENTRY_MAX - 1) + +/* Embedded inside LINK_XSTATS_TYPE_BRIDGE */ +enum { + BRIDGE_XSTATS_UNSPEC, + BRIDGE_XSTATS_VLAN, + BRIDGE_XSTATS_MCAST, + BRIDGE_XSTATS_PAD, + __BRIDGE_XSTATS_MAX +}; +#define BRIDGE_XSTATS_MAX (__BRIDGE_XSTATS_MAX - 1) + +enum { + BR_MCAST_DIR_RX, + BR_MCAST_DIR_TX, + BR_MCAST_DIR_SIZE +}; + +/* IGMP/MLD statistics */ +struct br_mcast_stats { + __u64 igmp_v1queries[BR_MCAST_DIR_SIZE]; + __u64 igmp_v2queries[BR_MCAST_DIR_SIZE]; + __u64 igmp_v3queries[BR_MCAST_DIR_SIZE]; + __u64 igmp_leaves[BR_MCAST_DIR_SIZE]; + __u64 igmp_v1reports[BR_MCAST_DIR_SIZE]; + __u64 igmp_v2reports[BR_MCAST_DIR_SIZE]; + __u64 igmp_v3reports[BR_MCAST_DIR_SIZE]; + __u64 igmp_parse_errors; + + __u64 mld_v1queries[BR_MCAST_DIR_SIZE]; + __u64 mld_v2queries[BR_MCAST_DIR_SIZE]; + __u64 mld_leaves[BR_MCAST_DIR_SIZE]; + __u64 mld_v1reports[BR_MCAST_DIR_SIZE]; + __u64 mld_v2reports[BR_MCAST_DIR_SIZE]; + __u64 mld_parse_errors; + + __u64 mcast_bytes[BR_MCAST_DIR_SIZE]; + __u64 mcast_packets[BR_MCAST_DIR_SIZE]; +}; +#endif /* _LINUX_IF_BRIDGE_H */ diff --git a/libnl/include/linux-private/linux/if_ether.h b/libnl/include/linux-private/linux/if_ether.h new file mode 100644 index 0000000..8c36f63 --- /dev/null +++ b/libnl/include/linux-private/linux/if_ether.h @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Global definitions for the Ethernet IEEE 802.3 interface. + * + * Version: @(#)if_ether.h 1.0.1a 02/08/94 + * + * Author: Fred N. van Kempen, + * Donald Becker, + * Alan Cox, + * Steve Whitehouse, + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_IF_ETHER_H +#define _LINUX_IF_ETHER_H + +#include + +/* + * IEEE 802.3 Ethernet magic constants. The frame sizes omit the preamble + * and FCS/CRC (frame check sequence). + */ + +#define ETH_ALEN 6 /* Octets in one ethernet addr */ +#define ETH_TLEN 2 /* Octets in ethernet type field */ +#define ETH_HLEN 14 /* Total octets in header. */ +#define ETH_ZLEN 60 /* Min. octets in frame sans FCS */ +#define ETH_DATA_LEN 1500 /* Max. octets in payload */ +#define ETH_FRAME_LEN 1514 /* Max. octets in frame sans FCS */ +#define ETH_FCS_LEN 4 /* Octets in the FCS */ + +#define ETH_MIN_MTU 68 /* Min IPv4 MTU per RFC791 */ +#define ETH_MAX_MTU 0xFFFFU /* 65535, same as IP_MAX_MTU */ + +/* + * These are the defined Ethernet Protocol ID's. + */ + +#define ETH_P_LOOP 0x0060 /* Ethernet Loopback packet */ +#define ETH_P_PUP 0x0200 /* Xerox PUP packet */ +#define ETH_P_PUPAT 0x0201 /* Xerox PUP Addr Trans packet */ +#define ETH_P_TSN 0x22F0 /* TSN (IEEE 1722) packet */ +#define ETH_P_ERSPAN2 0x22EB /* ERSPAN version 2 (type III) */ +#define ETH_P_IP 0x0800 /* Internet Protocol packet */ +#define ETH_P_X25 0x0805 /* CCITT X.25 */ +#define ETH_P_ARP 0x0806 /* Address Resolution packet */ +#define ETH_P_BPQ 0x08FF /* G8BPQ AX.25 Ethernet Packet [ NOT AN OFFICIALLY REGISTERED ID ] */ +#define ETH_P_IEEEPUP 0x0a00 /* Xerox IEEE802.3 PUP packet */ +#define ETH_P_IEEEPUPAT 0x0a01 /* Xerox IEEE802.3 PUP Addr Trans packet */ +#define ETH_P_BATMAN 0x4305 /* B.A.T.M.A.N.-Advanced packet [ NOT AN OFFICIALLY REGISTERED ID ] */ +#define ETH_P_DEC 0x6000 /* DEC Assigned proto */ +#define ETH_P_DNA_DL 0x6001 /* DEC DNA Dump/Load */ +#define ETH_P_DNA_RC 0x6002 /* DEC DNA Remote Console */ +#define ETH_P_DNA_RT 0x6003 /* DEC DNA Routing */ +#define ETH_P_LAT 0x6004 /* DEC LAT */ +#define ETH_P_DIAG 0x6005 /* DEC Diagnostics */ +#define ETH_P_CUST 0x6006 /* DEC Customer use */ +#define ETH_P_SCA 0x6007 /* DEC Systems Comms Arch */ +#define ETH_P_TEB 0x6558 /* Trans Ether Bridging */ +#define ETH_P_RARP 0x8035 /* Reverse Addr Res packet */ +#define ETH_P_ATALK 0x809B /* Appletalk DDP */ +#define ETH_P_AARP 0x80F3 /* Appletalk AARP */ +#define ETH_P_8021Q 0x8100 /* 802.1Q VLAN Extended Header */ +#define ETH_P_ERSPAN 0x88BE /* ERSPAN type II */ +#define ETH_P_IPX 0x8137 /* IPX over DIX */ +#define ETH_P_IPV6 0x86DD /* IPv6 over bluebook */ +#define ETH_P_PAUSE 0x8808 /* IEEE Pause frames. See 802.3 31B */ +#define ETH_P_SLOW 0x8809 /* Slow Protocol. See 802.3ad 43B */ +#define ETH_P_WCCP 0x883E /* Web-cache coordination protocol + * defined in draft-wilson-wrec-wccp-v2-00.txt */ +#define ETH_P_MPLS_UC 0x8847 /* MPLS Unicast traffic */ +#define ETH_P_MPLS_MC 0x8848 /* MPLS Multicast traffic */ +#define ETH_P_ATMMPOA 0x884c /* MultiProtocol Over ATM */ +#define ETH_P_PPP_DISC 0x8863 /* PPPoE discovery messages */ +#define ETH_P_PPP_SES 0x8864 /* PPPoE session messages */ +#define ETH_P_LINK_CTL 0x886c /* HPNA, wlan link local tunnel */ +#define ETH_P_ATMFATE 0x8884 /* Frame-based ATM Transport + * over Ethernet + */ +#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ +#define ETH_P_AOE 0x88A2 /* ATA over Ethernet */ +#define ETH_P_8021AD 0x88A8 /* 802.1ad Service VLAN */ +#define ETH_P_802_EX1 0x88B5 /* 802.1 Local Experimental 1. */ +#define ETH_P_PREAUTH 0x88C7 /* 802.11 Preauthentication */ +#define ETH_P_TIPC 0x88CA /* TIPC */ +#define ETH_P_MACSEC 0x88E5 /* 802.1ae MACsec */ +#define ETH_P_8021AH 0x88E7 /* 802.1ah Backbone Service Tag */ +#define ETH_P_MVRP 0x88F5 /* 802.1Q MVRP */ +#define ETH_P_1588 0x88F7 /* IEEE 1588 Timesync */ +#define ETH_P_NCSI 0x88F8 /* NCSI protocol */ +#define ETH_P_PRP 0x88FB /* IEC 62439-3 PRP/HSRv0 */ +#define ETH_P_FCOE 0x8906 /* Fibre Channel over Ethernet */ +#define ETH_P_IBOE 0x8915 /* Infiniband over Ethernet */ +#define ETH_P_TDLS 0x890D /* TDLS */ +#define ETH_P_FIP 0x8914 /* FCoE Initialization Protocol */ +#define ETH_P_80221 0x8917 /* IEEE 802.21 Media Independent Handover Protocol */ +#define ETH_P_HSR 0x892F /* IEC 62439-3 HSRv1 */ +#define ETH_P_NSH 0x894F /* Network Service Header */ +#define ETH_P_LOOPBACK 0x9000 /* Ethernet loopback packet, per IEEE 802.3 */ +#define ETH_P_QINQ1 0x9100 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */ +#define ETH_P_QINQ2 0x9200 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */ +#define ETH_P_QINQ3 0x9300 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */ +#define ETH_P_EDSA 0xDADA /* Ethertype DSA [ NOT AN OFFICIALLY REGISTERED ID ] */ +#define ETH_P_IFE 0xED3E /* ForCES inter-FE LFB type */ +#define ETH_P_AF_IUCV 0xFBFB /* IBM af_iucv [ NOT AN OFFICIALLY REGISTERED ID ] */ + +#define ETH_P_802_3_MIN 0x0600 /* If the value in the ethernet type is less than this value + * then the frame is Ethernet II. Else it is 802.3 */ + +/* + * Non DIX types. Won't clash for 1500 types. + */ + +#define ETH_P_802_3 0x0001 /* Dummy type for 802.3 frames */ +#define ETH_P_AX25 0x0002 /* Dummy protocol id for AX.25 */ +#define ETH_P_ALL 0x0003 /* Every packet (be careful!!!) */ +#define ETH_P_802_2 0x0004 /* 802.2 frames */ +#define ETH_P_SNAP 0x0005 /* Internal only */ +#define ETH_P_DDCMP 0x0006 /* DEC DDCMP: Internal only */ +#define ETH_P_WAN_PPP 0x0007 /* Dummy type for WAN PPP frames*/ +#define ETH_P_PPP_MP 0x0008 /* Dummy type for PPP MP frames */ +#define ETH_P_LOCALTALK 0x0009 /* Localtalk pseudo type */ +#define ETH_P_CAN 0x000C /* CAN: Controller Area Network */ +#define ETH_P_CANFD 0x000D /* CANFD: CAN flexible data rate*/ +#define ETH_P_PPPTALK 0x0010 /* Dummy type for Atalk over PPP*/ +#define ETH_P_TR_802_2 0x0011 /* 802.2 frames */ +#define ETH_P_MOBITEX 0x0015 /* Mobitex (kaz@cafe.net) */ +#define ETH_P_CONTROL 0x0016 /* Card specific control frames */ +#define ETH_P_IRDA 0x0017 /* Linux-IrDA */ +#define ETH_P_ECONET 0x0018 /* Acorn Econet */ +#define ETH_P_HDLC 0x0019 /* HDLC frames */ +#define ETH_P_ARCNET 0x001A /* 1A for ArcNet :-) */ +#define ETH_P_DSA 0x001B /* Distributed Switch Arch. */ +#define ETH_P_TRAILER 0x001C /* Trailer switch tagging */ +#define ETH_P_PHONET 0x00F5 /* Nokia Phonet frames */ +#define ETH_P_IEEE802154 0x00F6 /* IEEE802.15.4 frame */ +#define ETH_P_CAIF 0x00F7 /* ST-Ericsson CAIF protocol */ +#define ETH_P_XDSA 0x00F8 /* Multiplexed DSA protocol */ +#define ETH_P_MAP 0x00F9 /* Qualcomm multiplexing and + * aggregation protocol + */ + +/* + * This is an Ethernet frame header. + */ + +/* allow libcs like musl to deactivate this, glibc does not implement this. */ +#ifndef __UAPI_DEF_ETHHDR +#define __UAPI_DEF_ETHHDR 1 +#endif + +#if __UAPI_DEF_ETHHDR +struct ethhdr { + unsigned char h_dest[ETH_ALEN]; /* destination eth addr */ + unsigned char h_source[ETH_ALEN]; /* source ether addr */ + __be16 h_proto; /* packet type ID field */ +} __attribute__((packed)); +#endif + + +#endif /* _LINUX_IF_ETHER_H */ diff --git a/libnl/include/linux-private/linux/if_link.h b/libnl/include/linux-private/linux/if_link.h new file mode 100644 index 0000000..f4a9715 --- /dev/null +++ b/libnl/include/linux-private/linux/if_link.h @@ -0,0 +1,1000 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_IF_LINK_H +#define _LINUX_IF_LINK_H + +#include +#include + +/* This struct should be in sync with struct rtnl_link_stats64 */ +struct rtnl_link_stats { + __u32 rx_packets; /* total packets received */ + __u32 tx_packets; /* total packets transmitted */ + __u32 rx_bytes; /* total bytes received */ + __u32 tx_bytes; /* total bytes transmitted */ + __u32 rx_errors; /* bad packets received */ + __u32 tx_errors; /* packet transmit problems */ + __u32 rx_dropped; /* no space in linux buffers */ + __u32 tx_dropped; /* no space available in linux */ + __u32 multicast; /* multicast packets received */ + __u32 collisions; + + /* detailed rx_errors: */ + __u32 rx_length_errors; + __u32 rx_over_errors; /* receiver ring buff overflow */ + __u32 rx_crc_errors; /* recved pkt with crc error */ + __u32 rx_frame_errors; /* recv'd frame alignment error */ + __u32 rx_fifo_errors; /* recv'r fifo overrun */ + __u32 rx_missed_errors; /* receiver missed packet */ + + /* detailed tx_errors */ + __u32 tx_aborted_errors; + __u32 tx_carrier_errors; + __u32 tx_fifo_errors; + __u32 tx_heartbeat_errors; + __u32 tx_window_errors; + + /* for cslip etc */ + __u32 rx_compressed; + __u32 tx_compressed; + + __u32 rx_nohandler; /* dropped, no handler found */ +}; + +/* The main device statistics structure */ +struct rtnl_link_stats64 { + __u64 rx_packets; /* total packets received */ + __u64 tx_packets; /* total packets transmitted */ + __u64 rx_bytes; /* total bytes received */ + __u64 tx_bytes; /* total bytes transmitted */ + __u64 rx_errors; /* bad packets received */ + __u64 tx_errors; /* packet transmit problems */ + __u64 rx_dropped; /* no space in linux buffers */ + __u64 tx_dropped; /* no space available in linux */ + __u64 multicast; /* multicast packets received */ + __u64 collisions; + + /* detailed rx_errors: */ + __u64 rx_length_errors; + __u64 rx_over_errors; /* receiver ring buff overflow */ + __u64 rx_crc_errors; /* recved pkt with crc error */ + __u64 rx_frame_errors; /* recv'd frame alignment error */ + __u64 rx_fifo_errors; /* recv'r fifo overrun */ + __u64 rx_missed_errors; /* receiver missed packet */ + + /* detailed tx_errors */ + __u64 tx_aborted_errors; + __u64 tx_carrier_errors; + __u64 tx_fifo_errors; + __u64 tx_heartbeat_errors; + __u64 tx_window_errors; + + /* for cslip etc */ + __u64 rx_compressed; + __u64 tx_compressed; + + __u64 rx_nohandler; /* dropped, no handler found */ +}; + +/* The struct should be in sync with struct ifmap */ +struct rtnl_link_ifmap { + __u64 mem_start; + __u64 mem_end; + __u64 base_addr; + __u16 irq; + __u8 dma; + __u8 port; +}; + +/* + * IFLA_AF_SPEC + * Contains nested attributes for address family specific attributes. + * Each address family may create a attribute with the address family + * number as type and create its own attribute structure in it. + * + * Example: + * [IFLA_AF_SPEC] = { + * [AF_INET] = { + * [IFLA_INET_CONF] = ..., + * }, + * [AF_INET6] = { + * [IFLA_INET6_FLAGS] = ..., + * [IFLA_INET6_CONF] = ..., + * } + * } + */ + +enum { + IFLA_UNSPEC, + IFLA_ADDRESS, + IFLA_BROADCAST, + IFLA_IFNAME, + IFLA_MTU, + IFLA_LINK, + IFLA_QDISC, + IFLA_STATS, + IFLA_COST, +#define IFLA_COST IFLA_COST + IFLA_PRIORITY, +#define IFLA_PRIORITY IFLA_PRIORITY + IFLA_MASTER, +#define IFLA_MASTER IFLA_MASTER + IFLA_WIRELESS, /* Wireless Extension event - see wireless.h */ +#define IFLA_WIRELESS IFLA_WIRELESS + IFLA_PROTINFO, /* Protocol specific information for a link */ +#define IFLA_PROTINFO IFLA_PROTINFO + IFLA_TXQLEN, +#define IFLA_TXQLEN IFLA_TXQLEN + IFLA_MAP, +#define IFLA_MAP IFLA_MAP + IFLA_WEIGHT, +#define IFLA_WEIGHT IFLA_WEIGHT + IFLA_OPERSTATE, + IFLA_LINKMODE, + IFLA_LINKINFO, +#define IFLA_LINKINFO IFLA_LINKINFO + IFLA_NET_NS_PID, + IFLA_IFALIAS, + IFLA_NUM_VF, /* Number of VFs if device is SR-IOV PF */ + IFLA_VFINFO_LIST, + IFLA_STATS64, + IFLA_VF_PORTS, + IFLA_PORT_SELF, + IFLA_AF_SPEC, + IFLA_GROUP, /* Group the device belongs to */ + IFLA_NET_NS_FD, + IFLA_EXT_MASK, /* Extended info mask, VFs, etc */ + IFLA_PROMISCUITY, /* Promiscuity count: > 0 means acts PROMISC */ +#define IFLA_PROMISCUITY IFLA_PROMISCUITY + IFLA_NUM_TX_QUEUES, + IFLA_NUM_RX_QUEUES, + IFLA_CARRIER, + IFLA_PHYS_PORT_ID, + IFLA_CARRIER_CHANGES, + IFLA_PHYS_SWITCH_ID, + IFLA_LINK_NETNSID, + IFLA_PHYS_PORT_NAME, + IFLA_PROTO_DOWN, + IFLA_GSO_MAX_SEGS, + IFLA_GSO_MAX_SIZE, + IFLA_PAD, + IFLA_XDP, + IFLA_EVENT, + IFLA_NEW_NETNSID, + IFLA_IF_NETNSID, + IFLA_CARRIER_UP_COUNT, + IFLA_CARRIER_DOWN_COUNT, + IFLA_NEW_IFINDEX, + IFLA_MIN_MTU, + IFLA_MAX_MTU, + __IFLA_MAX +}; + + +#define IFLA_MAX (__IFLA_MAX - 1) + +/* backwards compatibility for userspace */ +#define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg)))) +#define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg)) + +enum { + IFLA_INET_UNSPEC, + IFLA_INET_CONF, + __IFLA_INET_MAX, +}; + +#define IFLA_INET_MAX (__IFLA_INET_MAX - 1) + +/* ifi_flags. + + IFF_* flags. + + The only change is: + IFF_LOOPBACK, IFF_BROADCAST and IFF_POINTOPOINT are + more not changeable by user. They describe link media + characteristics and set by device driver. + + Comments: + - Combination IFF_BROADCAST|IFF_POINTOPOINT is invalid + - If neither of these three flags are set; + the interface is NBMA. + + - IFF_MULTICAST does not mean anything special: + multicasts can be used on all not-NBMA links. + IFF_MULTICAST means that this media uses special encapsulation + for multicast frames. Apparently, all IFF_POINTOPOINT and + IFF_BROADCAST devices are able to use multicasts too. + */ + +/* IFLA_LINK. + For usual devices it is equal ifi_index. + If it is a "virtual interface" (f.e. tunnel), ifi_link + can point to real physical interface (f.e. for bandwidth calculations), + or maybe 0, what means, that real media is unknown (usual + for IPIP tunnels, when route to endpoint is allowed to change) + */ + +/* Subtype attributes for IFLA_PROTINFO */ +enum { + IFLA_INET6_UNSPEC, + IFLA_INET6_FLAGS, /* link flags */ + IFLA_INET6_CONF, /* sysctl parameters */ + IFLA_INET6_STATS, /* statistics */ + IFLA_INET6_MCAST, /* MC things. What of them? */ + IFLA_INET6_CACHEINFO, /* time values and max reasm size */ + IFLA_INET6_ICMP6STATS, /* statistics (icmpv6) */ + IFLA_INET6_TOKEN, /* device token */ + IFLA_INET6_ADDR_GEN_MODE, /* implicit address generator mode */ + __IFLA_INET6_MAX +}; + +#define IFLA_INET6_MAX (__IFLA_INET6_MAX - 1) + +enum in6_addr_gen_mode { + IN6_ADDR_GEN_MODE_EUI64, + IN6_ADDR_GEN_MODE_NONE, + IN6_ADDR_GEN_MODE_STABLE_PRIVACY, + IN6_ADDR_GEN_MODE_RANDOM, +}; + +/* Bridge section */ + +enum { + IFLA_BR_UNSPEC, + IFLA_BR_FORWARD_DELAY, + IFLA_BR_HELLO_TIME, + IFLA_BR_MAX_AGE, + IFLA_BR_AGEING_TIME, + IFLA_BR_STP_STATE, + IFLA_BR_PRIORITY, + IFLA_BR_VLAN_FILTERING, + IFLA_BR_VLAN_PROTOCOL, + IFLA_BR_GROUP_FWD_MASK, + IFLA_BR_ROOT_ID, + IFLA_BR_BRIDGE_ID, + IFLA_BR_ROOT_PORT, + IFLA_BR_ROOT_PATH_COST, + IFLA_BR_TOPOLOGY_CHANGE, + IFLA_BR_TOPOLOGY_CHANGE_DETECTED, + IFLA_BR_HELLO_TIMER, + IFLA_BR_TCN_TIMER, + IFLA_BR_TOPOLOGY_CHANGE_TIMER, + IFLA_BR_GC_TIMER, + IFLA_BR_GROUP_ADDR, + IFLA_BR_FDB_FLUSH, + IFLA_BR_MCAST_ROUTER, + IFLA_BR_MCAST_SNOOPING, + IFLA_BR_MCAST_QUERY_USE_IFADDR, + IFLA_BR_MCAST_QUERIER, + IFLA_BR_MCAST_HASH_ELASTICITY, + IFLA_BR_MCAST_HASH_MAX, + IFLA_BR_MCAST_LAST_MEMBER_CNT, + IFLA_BR_MCAST_STARTUP_QUERY_CNT, + IFLA_BR_MCAST_LAST_MEMBER_INTVL, + IFLA_BR_MCAST_MEMBERSHIP_INTVL, + IFLA_BR_MCAST_QUERIER_INTVL, + IFLA_BR_MCAST_QUERY_INTVL, + IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, + IFLA_BR_MCAST_STARTUP_QUERY_INTVL, + IFLA_BR_NF_CALL_IPTABLES, + IFLA_BR_NF_CALL_IP6TABLES, + IFLA_BR_NF_CALL_ARPTABLES, + IFLA_BR_VLAN_DEFAULT_PVID, + IFLA_BR_PAD, + IFLA_BR_VLAN_STATS_ENABLED, + IFLA_BR_MCAST_STATS_ENABLED, + IFLA_BR_MCAST_IGMP_VERSION, + IFLA_BR_MCAST_MLD_VERSION, + __IFLA_BR_MAX, +}; + +#define IFLA_BR_MAX (__IFLA_BR_MAX - 1) + +struct ifla_bridge_id { + __u8 prio[2]; + __u8 addr[6]; /* ETH_ALEN */ +}; + +enum { + BRIDGE_MODE_UNSPEC, + BRIDGE_MODE_HAIRPIN, +}; + +enum { + IFLA_BRPORT_UNSPEC, + IFLA_BRPORT_STATE, /* Spanning tree state */ + IFLA_BRPORT_PRIORITY, /* " priority */ + IFLA_BRPORT_COST, /* " cost */ + IFLA_BRPORT_MODE, /* mode (hairpin) */ + IFLA_BRPORT_GUARD, /* bpdu guard */ + IFLA_BRPORT_PROTECT, /* root port protection */ + IFLA_BRPORT_FAST_LEAVE, /* multicast fast leave */ + IFLA_BRPORT_LEARNING, /* mac learning */ + IFLA_BRPORT_UNICAST_FLOOD, /* flood unicast traffic */ + IFLA_BRPORT_PROXYARP, /* proxy ARP */ + IFLA_BRPORT_LEARNING_SYNC, /* mac learning sync from device */ + IFLA_BRPORT_PROXYARP_WIFI, /* proxy ARP for Wi-Fi */ + IFLA_BRPORT_ROOT_ID, /* designated root */ + IFLA_BRPORT_BRIDGE_ID, /* designated bridge */ + IFLA_BRPORT_DESIGNATED_PORT, + IFLA_BRPORT_DESIGNATED_COST, + IFLA_BRPORT_ID, + IFLA_BRPORT_NO, + IFLA_BRPORT_TOPOLOGY_CHANGE_ACK, + IFLA_BRPORT_CONFIG_PENDING, + IFLA_BRPORT_MESSAGE_AGE_TIMER, + IFLA_BRPORT_FORWARD_DELAY_TIMER, + IFLA_BRPORT_HOLD_TIMER, + IFLA_BRPORT_FLUSH, + IFLA_BRPORT_MULTICAST_ROUTER, + IFLA_BRPORT_PAD, + IFLA_BRPORT_MCAST_FLOOD, + IFLA_BRPORT_MCAST_TO_UCAST, + IFLA_BRPORT_VLAN_TUNNEL, + IFLA_BRPORT_BCAST_FLOOD, + IFLA_BRPORT_GROUP_FWD_MASK, + IFLA_BRPORT_NEIGH_SUPPRESS, + IFLA_BRPORT_ISOLATED, + IFLA_BRPORT_BACKUP_PORT, + __IFLA_BRPORT_MAX +}; +#define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) + +struct ifla_cacheinfo { + __u32 max_reasm_len; + __u32 tstamp; /* ipv6InterfaceTable updated timestamp */ + __u32 reachable_time; + __u32 retrans_time; +}; + +enum { + IFLA_INFO_UNSPEC, + IFLA_INFO_KIND, + IFLA_INFO_DATA, + IFLA_INFO_XSTATS, + IFLA_INFO_SLAVE_KIND, + IFLA_INFO_SLAVE_DATA, + __IFLA_INFO_MAX, +}; + +#define IFLA_INFO_MAX (__IFLA_INFO_MAX - 1) + +/* VLAN section */ + +enum { + IFLA_VLAN_UNSPEC, + IFLA_VLAN_ID, + IFLA_VLAN_FLAGS, + IFLA_VLAN_EGRESS_QOS, + IFLA_VLAN_INGRESS_QOS, + IFLA_VLAN_PROTOCOL, + __IFLA_VLAN_MAX, +}; + +#define IFLA_VLAN_MAX (__IFLA_VLAN_MAX - 1) + +struct ifla_vlan_flags { + __u32 flags; + __u32 mask; +}; + +enum { + IFLA_VLAN_QOS_UNSPEC, + IFLA_VLAN_QOS_MAPPING, + __IFLA_VLAN_QOS_MAX +}; + +#define IFLA_VLAN_QOS_MAX (__IFLA_VLAN_QOS_MAX - 1) + +struct ifla_vlan_qos_mapping { + __u32 from; + __u32 to; +}; + +/* MACVLAN section */ +enum { + IFLA_MACVLAN_UNSPEC, + IFLA_MACVLAN_MODE, + IFLA_MACVLAN_FLAGS, + IFLA_MACVLAN_MACADDR_MODE, + IFLA_MACVLAN_MACADDR, + IFLA_MACVLAN_MACADDR_DATA, + IFLA_MACVLAN_MACADDR_COUNT, + __IFLA_MACVLAN_MAX, +}; + +#define IFLA_MACVLAN_MAX (__IFLA_MACVLAN_MAX - 1) + +enum macvlan_mode { + MACVLAN_MODE_PRIVATE = 1, /* don't talk to other macvlans */ + MACVLAN_MODE_VEPA = 2, /* talk to other ports through ext bridge */ + MACVLAN_MODE_BRIDGE = 4, /* talk to bridge ports directly */ + MACVLAN_MODE_PASSTHRU = 8,/* take over the underlying device */ + MACVLAN_MODE_SOURCE = 16,/* use source MAC address list to assign */ +}; + +enum macvlan_macaddr_mode { + MACVLAN_MACADDR_ADD, + MACVLAN_MACADDR_DEL, + MACVLAN_MACADDR_FLUSH, + MACVLAN_MACADDR_SET, +}; + +#define MACVLAN_FLAG_NOPROMISC 1 + +/* VRF section */ +enum { + IFLA_VRF_UNSPEC, + IFLA_VRF_TABLE, + __IFLA_VRF_MAX +}; + +#define IFLA_VRF_MAX (__IFLA_VRF_MAX - 1) + +enum { + IFLA_VRF_PORT_UNSPEC, + IFLA_VRF_PORT_TABLE, + __IFLA_VRF_PORT_MAX +}; + +#define IFLA_VRF_PORT_MAX (__IFLA_VRF_PORT_MAX - 1) + +/* MACSEC section */ +enum { + IFLA_MACSEC_UNSPEC, + IFLA_MACSEC_SCI, + IFLA_MACSEC_PORT, + IFLA_MACSEC_ICV_LEN, + IFLA_MACSEC_CIPHER_SUITE, + IFLA_MACSEC_WINDOW, + IFLA_MACSEC_ENCODING_SA, + IFLA_MACSEC_ENCRYPT, + IFLA_MACSEC_PROTECT, + IFLA_MACSEC_INC_SCI, + IFLA_MACSEC_ES, + IFLA_MACSEC_SCB, + IFLA_MACSEC_REPLAY_PROTECT, + IFLA_MACSEC_VALIDATION, + IFLA_MACSEC_PAD, + __IFLA_MACSEC_MAX, +}; + +#define IFLA_MACSEC_MAX (__IFLA_MACSEC_MAX - 1) + +/* XFRM section */ +enum { + IFLA_XFRM_UNSPEC, + IFLA_XFRM_LINK, + IFLA_XFRM_IF_ID, + __IFLA_XFRM_MAX +}; + +#define IFLA_XFRM_MAX (__IFLA_XFRM_MAX - 1) + +enum macsec_validation_type { + MACSEC_VALIDATE_DISABLED = 0, + MACSEC_VALIDATE_CHECK = 1, + MACSEC_VALIDATE_STRICT = 2, + __MACSEC_VALIDATE_END, + MACSEC_VALIDATE_MAX = __MACSEC_VALIDATE_END - 1, +}; + +/* IPVLAN section */ +enum { + IFLA_IPVLAN_UNSPEC, + IFLA_IPVLAN_MODE, + IFLA_IPVLAN_FLAGS, + __IFLA_IPVLAN_MAX +}; + +#define IFLA_IPVLAN_MAX (__IFLA_IPVLAN_MAX - 1) + +enum ipvlan_mode { + IPVLAN_MODE_L2 = 0, + IPVLAN_MODE_L3, + IPVLAN_MODE_L3S, + IPVLAN_MODE_MAX +}; + +#define IPVLAN_F_PRIVATE 0x01 +#define IPVLAN_F_VEPA 0x02 + +/* VXLAN section */ +enum { + IFLA_VXLAN_UNSPEC, + IFLA_VXLAN_ID, + IFLA_VXLAN_GROUP, /* group or remote address */ + IFLA_VXLAN_LINK, + IFLA_VXLAN_LOCAL, + IFLA_VXLAN_TTL, + IFLA_VXLAN_TOS, + IFLA_VXLAN_LEARNING, + IFLA_VXLAN_AGEING, + IFLA_VXLAN_LIMIT, + IFLA_VXLAN_PORT_RANGE, /* source port */ + IFLA_VXLAN_PROXY, + IFLA_VXLAN_RSC, + IFLA_VXLAN_L2MISS, + IFLA_VXLAN_L3MISS, + IFLA_VXLAN_PORT, /* destination port */ + IFLA_VXLAN_GROUP6, + IFLA_VXLAN_LOCAL6, + IFLA_VXLAN_UDP_CSUM, + IFLA_VXLAN_UDP_ZERO_CSUM6_TX, + IFLA_VXLAN_UDP_ZERO_CSUM6_RX, + IFLA_VXLAN_REMCSUM_TX, + IFLA_VXLAN_REMCSUM_RX, + IFLA_VXLAN_GBP, + IFLA_VXLAN_REMCSUM_NOPARTIAL, + IFLA_VXLAN_COLLECT_METADATA, + IFLA_VXLAN_LABEL, + IFLA_VXLAN_GPE, + IFLA_VXLAN_TTL_INHERIT, + __IFLA_VXLAN_MAX +}; +#define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1) + +struct ifla_vxlan_port_range { + __be16 low; + __be16 high; +}; + +/* GENEVE section */ +enum { + IFLA_GENEVE_UNSPEC, + IFLA_GENEVE_ID, + IFLA_GENEVE_REMOTE, + IFLA_GENEVE_TTL, + IFLA_GENEVE_TOS, + IFLA_GENEVE_PORT, /* destination port */ + IFLA_GENEVE_COLLECT_METADATA, + IFLA_GENEVE_REMOTE6, + IFLA_GENEVE_UDP_CSUM, + IFLA_GENEVE_UDP_ZERO_CSUM6_TX, + IFLA_GENEVE_UDP_ZERO_CSUM6_RX, + IFLA_GENEVE_LABEL, + __IFLA_GENEVE_MAX +}; +#define IFLA_GENEVE_MAX (__IFLA_GENEVE_MAX - 1) + +/* PPP section */ +enum { + IFLA_PPP_UNSPEC, + IFLA_PPP_DEV_FD, + __IFLA_PPP_MAX +}; +#define IFLA_PPP_MAX (__IFLA_PPP_MAX - 1) + +/* GTP section */ + +enum ifla_gtp_role { + GTP_ROLE_GGSN = 0, + GTP_ROLE_SGSN, +}; + +enum { + IFLA_GTP_UNSPEC, + IFLA_GTP_FD0, + IFLA_GTP_FD1, + IFLA_GTP_PDP_HASHSIZE, + IFLA_GTP_ROLE, + __IFLA_GTP_MAX, +}; +#define IFLA_GTP_MAX (__IFLA_GTP_MAX - 1) + +/* Bonding section */ + +enum { + IFLA_BOND_UNSPEC, + IFLA_BOND_MODE, + IFLA_BOND_ACTIVE_SLAVE, + IFLA_BOND_MIIMON, + IFLA_BOND_UPDELAY, + IFLA_BOND_DOWNDELAY, + IFLA_BOND_USE_CARRIER, + IFLA_BOND_ARP_INTERVAL, + IFLA_BOND_ARP_IP_TARGET, + IFLA_BOND_ARP_VALIDATE, + IFLA_BOND_ARP_ALL_TARGETS, + IFLA_BOND_PRIMARY, + IFLA_BOND_PRIMARY_RESELECT, + IFLA_BOND_FAIL_OVER_MAC, + IFLA_BOND_XMIT_HASH_POLICY, + IFLA_BOND_RESEND_IGMP, + IFLA_BOND_NUM_PEER_NOTIF, + IFLA_BOND_ALL_SLAVES_ACTIVE, + IFLA_BOND_MIN_LINKS, + IFLA_BOND_LP_INTERVAL, + IFLA_BOND_PACKETS_PER_SLAVE, + IFLA_BOND_AD_LACP_RATE, + IFLA_BOND_AD_SELECT, + IFLA_BOND_AD_INFO, + IFLA_BOND_AD_ACTOR_SYS_PRIO, + IFLA_BOND_AD_USER_PORT_KEY, + IFLA_BOND_AD_ACTOR_SYSTEM, + IFLA_BOND_TLB_DYNAMIC_LB, + __IFLA_BOND_MAX, +}; + +#define IFLA_BOND_MAX (__IFLA_BOND_MAX - 1) + +enum { + IFLA_BOND_AD_INFO_UNSPEC, + IFLA_BOND_AD_INFO_AGGREGATOR, + IFLA_BOND_AD_INFO_NUM_PORTS, + IFLA_BOND_AD_INFO_ACTOR_KEY, + IFLA_BOND_AD_INFO_PARTNER_KEY, + IFLA_BOND_AD_INFO_PARTNER_MAC, + __IFLA_BOND_AD_INFO_MAX, +}; + +#define IFLA_BOND_AD_INFO_MAX (__IFLA_BOND_AD_INFO_MAX - 1) + +enum { + IFLA_BOND_SLAVE_UNSPEC, + IFLA_BOND_SLAVE_STATE, + IFLA_BOND_SLAVE_MII_STATUS, + IFLA_BOND_SLAVE_LINK_FAILURE_COUNT, + IFLA_BOND_SLAVE_PERM_HWADDR, + IFLA_BOND_SLAVE_QUEUE_ID, + IFLA_BOND_SLAVE_AD_AGGREGATOR_ID, + IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE, + IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE, + __IFLA_BOND_SLAVE_MAX, +}; + +#define IFLA_BOND_SLAVE_MAX (__IFLA_BOND_SLAVE_MAX - 1) + +/* SR-IOV virtual function management section */ + +enum { + IFLA_VF_INFO_UNSPEC, + IFLA_VF_INFO, + __IFLA_VF_INFO_MAX, +}; + +#define IFLA_VF_INFO_MAX (__IFLA_VF_INFO_MAX - 1) + +enum { + IFLA_VF_UNSPEC, + IFLA_VF_MAC, /* Hardware queue specific attributes */ + IFLA_VF_VLAN, /* VLAN ID and QoS */ + IFLA_VF_TX_RATE, /* Max TX Bandwidth Allocation */ + IFLA_VF_SPOOFCHK, /* Spoof Checking on/off switch */ + IFLA_VF_LINK_STATE, /* link state enable/disable/auto switch */ + IFLA_VF_RATE, /* Min and Max TX Bandwidth Allocation */ + IFLA_VF_RSS_QUERY_EN, /* RSS Redirection Table and Hash Key query + * on/off switch + */ + IFLA_VF_STATS, /* network device statistics */ + IFLA_VF_TRUST, /* Trust VF */ + IFLA_VF_IB_NODE_GUID, /* VF Infiniband node GUID */ + IFLA_VF_IB_PORT_GUID, /* VF Infiniband port GUID */ + IFLA_VF_VLAN_LIST, /* nested list of vlans, option for QinQ */ + __IFLA_VF_MAX, +}; + +#define IFLA_VF_MAX (__IFLA_VF_MAX - 1) + +struct ifla_vf_mac { + __u32 vf; + __u8 mac[32]; /* MAX_ADDR_LEN */ +}; + +struct ifla_vf_vlan { + __u32 vf; + __u32 vlan; /* 0 - 4095, 0 disables VLAN filter */ + __u32 qos; +}; + +enum { + IFLA_VF_VLAN_INFO_UNSPEC, + IFLA_VF_VLAN_INFO, /* VLAN ID, QoS and VLAN protocol */ + __IFLA_VF_VLAN_INFO_MAX, +}; + +#define IFLA_VF_VLAN_INFO_MAX (__IFLA_VF_VLAN_INFO_MAX - 1) +#define MAX_VLAN_LIST_LEN 1 + +struct ifla_vf_vlan_info { + __u32 vf; + __u32 vlan; /* 0 - 4095, 0 disables VLAN filter */ + __u32 qos; + __be16 vlan_proto; /* VLAN protocol either 802.1Q or 802.1ad */ +}; + +struct ifla_vf_tx_rate { + __u32 vf; + __u32 rate; /* Max TX bandwidth in Mbps, 0 disables throttling */ +}; + +struct ifla_vf_rate { + __u32 vf; + __u32 min_tx_rate; /* Min Bandwidth in Mbps */ + __u32 max_tx_rate; /* Max Bandwidth in Mbps */ +}; + +struct ifla_vf_spoofchk { + __u32 vf; + __u32 setting; +}; + +struct ifla_vf_guid { + __u32 vf; + __u64 guid; +}; + +enum { + IFLA_VF_LINK_STATE_AUTO, /* link state of the uplink */ + IFLA_VF_LINK_STATE_ENABLE, /* link always up */ + IFLA_VF_LINK_STATE_DISABLE, /* link always down */ + __IFLA_VF_LINK_STATE_MAX, +}; + +struct ifla_vf_link_state { + __u32 vf; + __u32 link_state; +}; + +struct ifla_vf_rss_query_en { + __u32 vf; + __u32 setting; +}; + +enum { + IFLA_VF_STATS_RX_PACKETS, + IFLA_VF_STATS_TX_PACKETS, + IFLA_VF_STATS_RX_BYTES, + IFLA_VF_STATS_TX_BYTES, + IFLA_VF_STATS_BROADCAST, + IFLA_VF_STATS_MULTICAST, + IFLA_VF_STATS_PAD, + IFLA_VF_STATS_RX_DROPPED, + IFLA_VF_STATS_TX_DROPPED, + __IFLA_VF_STATS_MAX, +}; + +#define IFLA_VF_STATS_MAX (__IFLA_VF_STATS_MAX - 1) + +struct ifla_vf_trust { + __u32 vf; + __u32 setting; +}; + +/* VF ports management section + * + * Nested layout of set/get msg is: + * + * [IFLA_NUM_VF] + * [IFLA_VF_PORTS] + * [IFLA_VF_PORT] + * [IFLA_PORT_*], ... + * [IFLA_VF_PORT] + * [IFLA_PORT_*], ... + * ... + * [IFLA_PORT_SELF] + * [IFLA_PORT_*], ... + */ + +enum { + IFLA_VF_PORT_UNSPEC, + IFLA_VF_PORT, /* nest */ + __IFLA_VF_PORT_MAX, +}; + +#define IFLA_VF_PORT_MAX (__IFLA_VF_PORT_MAX - 1) + +enum { + IFLA_PORT_UNSPEC, + IFLA_PORT_VF, /* __u32 */ + IFLA_PORT_PROFILE, /* string */ + IFLA_PORT_VSI_TYPE, /* 802.1Qbg (pre-)standard VDP */ + IFLA_PORT_INSTANCE_UUID, /* binary UUID */ + IFLA_PORT_HOST_UUID, /* binary UUID */ + IFLA_PORT_REQUEST, /* __u8 */ + IFLA_PORT_RESPONSE, /* __u16, output only */ + __IFLA_PORT_MAX, +}; + +#define IFLA_PORT_MAX (__IFLA_PORT_MAX - 1) + +#define PORT_PROFILE_MAX 40 +#define PORT_UUID_MAX 16 +#define PORT_SELF_VF -1 + +enum { + PORT_REQUEST_PREASSOCIATE = 0, + PORT_REQUEST_PREASSOCIATE_RR, + PORT_REQUEST_ASSOCIATE, + PORT_REQUEST_DISASSOCIATE, +}; + +enum { + PORT_VDP_RESPONSE_SUCCESS = 0, + PORT_VDP_RESPONSE_INVALID_FORMAT, + PORT_VDP_RESPONSE_INSUFFICIENT_RESOURCES, + PORT_VDP_RESPONSE_UNUSED_VTID, + PORT_VDP_RESPONSE_VTID_VIOLATION, + PORT_VDP_RESPONSE_VTID_VERSION_VIOALTION, + PORT_VDP_RESPONSE_OUT_OF_SYNC, + /* 0x08-0xFF reserved for future VDP use */ + PORT_PROFILE_RESPONSE_SUCCESS = 0x100, + PORT_PROFILE_RESPONSE_INPROGRESS, + PORT_PROFILE_RESPONSE_INVALID, + PORT_PROFILE_RESPONSE_BADSTATE, + PORT_PROFILE_RESPONSE_INSUFFICIENT_RESOURCES, + PORT_PROFILE_RESPONSE_ERROR, +}; + +struct ifla_port_vsi { + __u8 vsi_mgr_id; + __u8 vsi_type_id[3]; + __u8 vsi_type_version; + __u8 pad[3]; +}; + + +/* IPoIB section */ + +enum { + IFLA_IPOIB_UNSPEC, + IFLA_IPOIB_PKEY, + IFLA_IPOIB_MODE, + IFLA_IPOIB_UMCAST, + __IFLA_IPOIB_MAX +}; + +enum { + IPOIB_MODE_DATAGRAM = 0, /* using unreliable datagram QPs */ + IPOIB_MODE_CONNECTED = 1, /* using connected QPs */ +}; + +#define IFLA_IPOIB_MAX (__IFLA_IPOIB_MAX - 1) + + +/* HSR section */ + +enum { + IFLA_HSR_UNSPEC, + IFLA_HSR_SLAVE1, + IFLA_HSR_SLAVE2, + IFLA_HSR_MULTICAST_SPEC, /* Last byte of supervision addr */ + IFLA_HSR_SUPERVISION_ADDR, /* Supervision frame multicast addr */ + IFLA_HSR_SEQ_NR, + IFLA_HSR_VERSION, /* HSR version */ + __IFLA_HSR_MAX, +}; + +#define IFLA_HSR_MAX (__IFLA_HSR_MAX - 1) + +/* STATS section */ + +struct if_stats_msg { + __u8 family; + __u8 pad1; + __u16 pad2; + __u32 ifindex; + __u32 filter_mask; +}; + +/* A stats attribute can be netdev specific or a global stat. + * For netdev stats, lets use the prefix IFLA_STATS_LINK_* + */ +enum { + IFLA_STATS_UNSPEC, /* also used as 64bit pad attribute */ + IFLA_STATS_LINK_64, + IFLA_STATS_LINK_XSTATS, + IFLA_STATS_LINK_XSTATS_SLAVE, + IFLA_STATS_LINK_OFFLOAD_XSTATS, + IFLA_STATS_AF_SPEC, + __IFLA_STATS_MAX, +}; + +#define IFLA_STATS_MAX (__IFLA_STATS_MAX - 1) + +#define IFLA_STATS_FILTER_BIT(ATTR) (1 << (ATTR - 1)) + +/* These are embedded into IFLA_STATS_LINK_XSTATS: + * [IFLA_STATS_LINK_XSTATS] + * -> [LINK_XSTATS_TYPE_xxx] + * -> [rtnl link type specific attributes] + */ +enum { + LINK_XSTATS_TYPE_UNSPEC, + LINK_XSTATS_TYPE_BRIDGE, + __LINK_XSTATS_TYPE_MAX +}; +#define LINK_XSTATS_TYPE_MAX (__LINK_XSTATS_TYPE_MAX - 1) + +/* These are stats embedded into IFLA_STATS_LINK_OFFLOAD_XSTATS */ +enum { + IFLA_OFFLOAD_XSTATS_UNSPEC, + IFLA_OFFLOAD_XSTATS_CPU_HIT, /* struct rtnl_link_stats64 */ + __IFLA_OFFLOAD_XSTATS_MAX +}; +#define IFLA_OFFLOAD_XSTATS_MAX (__IFLA_OFFLOAD_XSTATS_MAX - 1) + +/* XDP section */ + +#define XDP_FLAGS_UPDATE_IF_NOEXIST (1U << 0) +#define XDP_FLAGS_SKB_MODE (1U << 1) +#define XDP_FLAGS_DRV_MODE (1U << 2) +#define XDP_FLAGS_HW_MODE (1U << 3) +#define XDP_FLAGS_MODES (XDP_FLAGS_SKB_MODE | \ + XDP_FLAGS_DRV_MODE | \ + XDP_FLAGS_HW_MODE) +#define XDP_FLAGS_MASK (XDP_FLAGS_UPDATE_IF_NOEXIST | \ + XDP_FLAGS_MODES) + +/* These are stored into IFLA_XDP_ATTACHED on dump. */ +enum { + XDP_ATTACHED_NONE = 0, + XDP_ATTACHED_DRV, + XDP_ATTACHED_SKB, + XDP_ATTACHED_HW, + XDP_ATTACHED_MULTI, +}; + +enum { + IFLA_XDP_UNSPEC, + IFLA_XDP_FD, + IFLA_XDP_ATTACHED, + IFLA_XDP_FLAGS, + IFLA_XDP_PROG_ID, + IFLA_XDP_DRV_PROG_ID, + IFLA_XDP_SKB_PROG_ID, + IFLA_XDP_HW_PROG_ID, + __IFLA_XDP_MAX, +}; + +#define IFLA_XDP_MAX (__IFLA_XDP_MAX - 1) + +enum { + IFLA_EVENT_NONE, + IFLA_EVENT_REBOOT, /* internal reset / reboot */ + IFLA_EVENT_FEATURES, /* change in offload features */ + IFLA_EVENT_BONDING_FAILOVER, /* change in active slave */ + IFLA_EVENT_NOTIFY_PEERS, /* re-sent grat. arp/ndisc */ + IFLA_EVENT_IGMP_RESEND, /* re-sent IGMP JOIN */ + IFLA_EVENT_BONDING_OPTIONS, /* change in bonding options */ +}; + +/* tun section */ + +enum { + IFLA_TUN_UNSPEC, + IFLA_TUN_OWNER, + IFLA_TUN_GROUP, + IFLA_TUN_TYPE, + IFLA_TUN_PI, + IFLA_TUN_VNET_HDR, + IFLA_TUN_PERSIST, + IFLA_TUN_MULTI_QUEUE, + IFLA_TUN_NUM_QUEUES, + IFLA_TUN_NUM_DISABLED_QUEUES, + __IFLA_TUN_MAX, +}; + +#define IFLA_TUN_MAX (__IFLA_TUN_MAX - 1) + +/* rmnet section */ + +#define RMNET_FLAGS_INGRESS_DEAGGREGATION (1U << 0) +#define RMNET_FLAGS_INGRESS_MAP_COMMANDS (1U << 1) +#define RMNET_FLAGS_INGRESS_MAP_CKSUMV4 (1U << 2) +#define RMNET_FLAGS_EGRESS_MAP_CKSUMV4 (1U << 3) + +enum { + IFLA_RMNET_UNSPEC, + IFLA_RMNET_MUX_ID, + IFLA_RMNET_FLAGS, + __IFLA_RMNET_MAX, +}; + +#define IFLA_RMNET_MAX (__IFLA_RMNET_MAX - 1) + +struct ifla_rmnet_flags { + __u32 flags; + __u32 mask; +}; + +#endif /* _LINUX_IF_LINK_H */ diff --git a/libnl/include/linux-private/linux/if_macsec.h b/libnl/include/linux-private/linux/if_macsec.h new file mode 100644 index 0000000..7743993 --- /dev/null +++ b/libnl/include/linux-private/linux/if_macsec.h @@ -0,0 +1,177 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * include/uapi/linux/if_macsec.h - MACsec device + * + * Copyright (c) 2015 Sabrina Dubroca + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _MACSEC_H +#define _MACSEC_H + +#include + +#define MACSEC_GENL_NAME "macsec" +#define MACSEC_GENL_VERSION 1 + +#define MACSEC_MAX_KEY_LEN 128 + +#define MACSEC_KEYID_LEN 16 + +/* cipher IDs as per IEEE802.1AEbn-2011 */ +#define MACSEC_CIPHER_ID_GCM_AES_128 0x0080C20001000001ULL +#define MACSEC_CIPHER_ID_GCM_AES_256 0x0080C20001000002ULL + +/* deprecated cipher ID for GCM-AES-128 */ +#define MACSEC_DEFAULT_CIPHER_ID 0x0080020001000001ULL +#define MACSEC_DEFAULT_CIPHER_ALT MACSEC_CIPHER_ID_GCM_AES_128 + +#define MACSEC_MIN_ICV_LEN 8 +#define MACSEC_MAX_ICV_LEN 32 +/* upper limit for ICV length as recommended by IEEE802.1AE-2006 */ +#define MACSEC_STD_ICV_LEN 16 + +enum macsec_attrs { + MACSEC_ATTR_UNSPEC, + MACSEC_ATTR_IFINDEX, /* u32, ifindex of the MACsec netdevice */ + MACSEC_ATTR_RXSC_CONFIG, /* config, nested macsec_rxsc_attrs */ + MACSEC_ATTR_SA_CONFIG, /* config, nested macsec_sa_attrs */ + MACSEC_ATTR_SECY, /* dump, nested macsec_secy_attrs */ + MACSEC_ATTR_TXSA_LIST, /* dump, nested, macsec_sa_attrs for each TXSA */ + MACSEC_ATTR_RXSC_LIST, /* dump, nested, macsec_rxsc_attrs for each RXSC */ + MACSEC_ATTR_TXSC_STATS, /* dump, nested, macsec_txsc_stats_attr */ + MACSEC_ATTR_SECY_STATS, /* dump, nested, macsec_secy_stats_attr */ + __MACSEC_ATTR_END, + NUM_MACSEC_ATTR = __MACSEC_ATTR_END, + MACSEC_ATTR_MAX = __MACSEC_ATTR_END - 1, +}; + +enum macsec_secy_attrs { + MACSEC_SECY_ATTR_UNSPEC, + MACSEC_SECY_ATTR_SCI, + MACSEC_SECY_ATTR_ENCODING_SA, + MACSEC_SECY_ATTR_WINDOW, + MACSEC_SECY_ATTR_CIPHER_SUITE, + MACSEC_SECY_ATTR_ICV_LEN, + MACSEC_SECY_ATTR_PROTECT, + MACSEC_SECY_ATTR_REPLAY, + MACSEC_SECY_ATTR_OPER, + MACSEC_SECY_ATTR_VALIDATE, + MACSEC_SECY_ATTR_ENCRYPT, + MACSEC_SECY_ATTR_INC_SCI, + MACSEC_SECY_ATTR_ES, + MACSEC_SECY_ATTR_SCB, + MACSEC_SECY_ATTR_PAD, + __MACSEC_SECY_ATTR_END, + NUM_MACSEC_SECY_ATTR = __MACSEC_SECY_ATTR_END, + MACSEC_SECY_ATTR_MAX = __MACSEC_SECY_ATTR_END - 1, +}; + +enum macsec_rxsc_attrs { + MACSEC_RXSC_ATTR_UNSPEC, + MACSEC_RXSC_ATTR_SCI, /* config/dump, u64 */ + MACSEC_RXSC_ATTR_ACTIVE, /* config/dump, u8 0..1 */ + MACSEC_RXSC_ATTR_SA_LIST, /* dump, nested */ + MACSEC_RXSC_ATTR_STATS, /* dump, nested, macsec_rxsc_stats_attr */ + MACSEC_RXSC_ATTR_PAD, + __MACSEC_RXSC_ATTR_END, + NUM_MACSEC_RXSC_ATTR = __MACSEC_RXSC_ATTR_END, + MACSEC_RXSC_ATTR_MAX = __MACSEC_RXSC_ATTR_END - 1, +}; + +enum macsec_sa_attrs { + MACSEC_SA_ATTR_UNSPEC, + MACSEC_SA_ATTR_AN, /* config/dump, u8 0..3 */ + MACSEC_SA_ATTR_ACTIVE, /* config/dump, u8 0..1 */ + MACSEC_SA_ATTR_PN, /* config/dump, u32 */ + MACSEC_SA_ATTR_KEY, /* config, data */ + MACSEC_SA_ATTR_KEYID, /* config/dump, 128-bit */ + MACSEC_SA_ATTR_STATS, /* dump, nested, macsec_sa_stats_attr */ + MACSEC_SA_ATTR_PAD, + __MACSEC_SA_ATTR_END, + NUM_MACSEC_SA_ATTR = __MACSEC_SA_ATTR_END, + MACSEC_SA_ATTR_MAX = __MACSEC_SA_ATTR_END - 1, +}; + +enum macsec_nl_commands { + MACSEC_CMD_GET_TXSC, + MACSEC_CMD_ADD_RXSC, + MACSEC_CMD_DEL_RXSC, + MACSEC_CMD_UPD_RXSC, + MACSEC_CMD_ADD_TXSA, + MACSEC_CMD_DEL_TXSA, + MACSEC_CMD_UPD_TXSA, + MACSEC_CMD_ADD_RXSA, + MACSEC_CMD_DEL_RXSA, + MACSEC_CMD_UPD_RXSA, +}; + +/* u64 per-RXSC stats */ +enum macsec_rxsc_stats_attr { + MACSEC_RXSC_STATS_ATTR_UNSPEC, + MACSEC_RXSC_STATS_ATTR_IN_OCTETS_VALIDATED, + MACSEC_RXSC_STATS_ATTR_IN_OCTETS_DECRYPTED, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_UNCHECKED, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_DELAYED, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_OK, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_INVALID, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_LATE, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_NOT_VALID, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_NOT_USING_SA, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_UNUSED_SA, + MACSEC_RXSC_STATS_ATTR_PAD, + __MACSEC_RXSC_STATS_ATTR_END, + NUM_MACSEC_RXSC_STATS_ATTR = __MACSEC_RXSC_STATS_ATTR_END, + MACSEC_RXSC_STATS_ATTR_MAX = __MACSEC_RXSC_STATS_ATTR_END - 1, +}; + +/* u32 per-{RX,TX}SA stats */ +enum macsec_sa_stats_attr { + MACSEC_SA_STATS_ATTR_UNSPEC, + MACSEC_SA_STATS_ATTR_IN_PKTS_OK, + MACSEC_SA_STATS_ATTR_IN_PKTS_INVALID, + MACSEC_SA_STATS_ATTR_IN_PKTS_NOT_VALID, + MACSEC_SA_STATS_ATTR_IN_PKTS_NOT_USING_SA, + MACSEC_SA_STATS_ATTR_IN_PKTS_UNUSED_SA, + MACSEC_SA_STATS_ATTR_OUT_PKTS_PROTECTED, + MACSEC_SA_STATS_ATTR_OUT_PKTS_ENCRYPTED, + __MACSEC_SA_STATS_ATTR_END, + NUM_MACSEC_SA_STATS_ATTR = __MACSEC_SA_STATS_ATTR_END, + MACSEC_SA_STATS_ATTR_MAX = __MACSEC_SA_STATS_ATTR_END - 1, +}; + +/* u64 per-TXSC stats */ +enum macsec_txsc_stats_attr { + MACSEC_TXSC_STATS_ATTR_UNSPEC, + MACSEC_TXSC_STATS_ATTR_OUT_PKTS_PROTECTED, + MACSEC_TXSC_STATS_ATTR_OUT_PKTS_ENCRYPTED, + MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_PROTECTED, + MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_ENCRYPTED, + MACSEC_TXSC_STATS_ATTR_PAD, + __MACSEC_TXSC_STATS_ATTR_END, + NUM_MACSEC_TXSC_STATS_ATTR = __MACSEC_TXSC_STATS_ATTR_END, + MACSEC_TXSC_STATS_ATTR_MAX = __MACSEC_TXSC_STATS_ATTR_END - 1, +}; + +/* u64 per-SecY stats */ +enum macsec_secy_stats_attr { + MACSEC_SECY_STATS_ATTR_UNSPEC, + MACSEC_SECY_STATS_ATTR_OUT_PKTS_UNTAGGED, + MACSEC_SECY_STATS_ATTR_IN_PKTS_UNTAGGED, + MACSEC_SECY_STATS_ATTR_OUT_PKTS_TOO_LONG, + MACSEC_SECY_STATS_ATTR_IN_PKTS_NO_TAG, + MACSEC_SECY_STATS_ATTR_IN_PKTS_BAD_TAG, + MACSEC_SECY_STATS_ATTR_IN_PKTS_UNKNOWN_SCI, + MACSEC_SECY_STATS_ATTR_IN_PKTS_NO_SCI, + MACSEC_SECY_STATS_ATTR_IN_PKTS_OVERRUN, + MACSEC_SECY_STATS_ATTR_PAD, + __MACSEC_SECY_STATS_ATTR_END, + NUM_MACSEC_SECY_STATS_ATTR = __MACSEC_SECY_STATS_ATTR_END, + MACSEC_SECY_STATS_ATTR_MAX = __MACSEC_SECY_STATS_ATTR_END - 1, +}; + +#endif /* _MACSEC_H */ diff --git a/libnl/include/linux-private/linux/if_tunnel.h b/libnl/include/linux-private/linux/if_tunnel.h new file mode 100644 index 0000000..ecdc766 --- /dev/null +++ b/libnl/include/linux-private/linux/if_tunnel.h @@ -0,0 +1,163 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _IF_TUNNEL_H_ +#define _IF_TUNNEL_H_ + +#include +#include +#include +#include +#include + + +#define SIOCGETTUNNEL (SIOCDEVPRIVATE + 0) +#define SIOCADDTUNNEL (SIOCDEVPRIVATE + 1) +#define SIOCDELTUNNEL (SIOCDEVPRIVATE + 2) +#define SIOCCHGTUNNEL (SIOCDEVPRIVATE + 3) +#define SIOCGETPRL (SIOCDEVPRIVATE + 4) +#define SIOCADDPRL (SIOCDEVPRIVATE + 5) +#define SIOCDELPRL (SIOCDEVPRIVATE + 6) +#define SIOCCHGPRL (SIOCDEVPRIVATE + 7) +#define SIOCGET6RD (SIOCDEVPRIVATE + 8) +#define SIOCADD6RD (SIOCDEVPRIVATE + 9) +#define SIOCDEL6RD (SIOCDEVPRIVATE + 10) +#define SIOCCHG6RD (SIOCDEVPRIVATE + 11) + +#define GRE_CSUM __cpu_to_be16(0x8000) +#define GRE_ROUTING __cpu_to_be16(0x4000) +#define GRE_KEY __cpu_to_be16(0x2000) +#define GRE_SEQ __cpu_to_be16(0x1000) +#define GRE_STRICT __cpu_to_be16(0x0800) +#define GRE_REC __cpu_to_be16(0x0700) +#define GRE_ACK __cpu_to_be16(0x0080) +#define GRE_FLAGS __cpu_to_be16(0x0078) +#define GRE_VERSION __cpu_to_be16(0x0007) + +#define GRE_IS_CSUM(f) ((f) & GRE_CSUM) +#define GRE_IS_ROUTING(f) ((f) & GRE_ROUTING) +#define GRE_IS_KEY(f) ((f) & GRE_KEY) +#define GRE_IS_SEQ(f) ((f) & GRE_SEQ) +#define GRE_IS_STRICT(f) ((f) & GRE_STRICT) +#define GRE_IS_REC(f) ((f) & GRE_REC) +#define GRE_IS_ACK(f) ((f) & GRE_ACK) + +#define GRE_VERSION_0 __cpu_to_be16(0x0000) +#define GRE_VERSION_1 __cpu_to_be16(0x0001) +#define GRE_PROTO_PPP __cpu_to_be16(0x880b) +#define GRE_PPTP_KEY_MASK __cpu_to_be32(0xffff) + +struct ip_tunnel_parm { + char name[IFNAMSIZ]; + int link; + __be16 i_flags; + __be16 o_flags; + __be32 i_key; + __be32 o_key; + struct iphdr iph; +}; + +enum { + IFLA_IPTUN_UNSPEC, + IFLA_IPTUN_LINK, + IFLA_IPTUN_LOCAL, + IFLA_IPTUN_REMOTE, + IFLA_IPTUN_TTL, + IFLA_IPTUN_TOS, + IFLA_IPTUN_ENCAP_LIMIT, + IFLA_IPTUN_FLOWINFO, + IFLA_IPTUN_FLAGS, + IFLA_IPTUN_PROTO, + IFLA_IPTUN_PMTUDISC, + IFLA_IPTUN_6RD_PREFIX, + IFLA_IPTUN_6RD_RELAY_PREFIX, + IFLA_IPTUN_6RD_PREFIXLEN, + IFLA_IPTUN_6RD_RELAY_PREFIXLEN, + IFLA_IPTUN_ENCAP_TYPE, + IFLA_IPTUN_ENCAP_FLAGS, + IFLA_IPTUN_ENCAP_SPORT, + IFLA_IPTUN_ENCAP_DPORT, + IFLA_IPTUN_COLLECT_METADATA, + IFLA_IPTUN_FWMARK, + __IFLA_IPTUN_MAX, +}; +#define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1) + +enum tunnel_encap_types { + TUNNEL_ENCAP_NONE, + TUNNEL_ENCAP_FOU, + TUNNEL_ENCAP_GUE, + TUNNEL_ENCAP_MPLS, +}; + +#define TUNNEL_ENCAP_FLAG_CSUM (1<<0) +#define TUNNEL_ENCAP_FLAG_CSUM6 (1<<1) +#define TUNNEL_ENCAP_FLAG_REMCSUM (1<<2) + +/* SIT-mode i_flags */ +#define SIT_ISATAP 0x0001 + +struct ip_tunnel_prl { + __be32 addr; + __u16 flags; + __u16 __reserved; + __u32 datalen; + __u32 __reserved2; + /* data follows */ +}; + +/* PRL flags */ +#define PRL_DEFAULT 0x0001 + +struct ip_tunnel_6rd { + struct in6_addr prefix; + __be32 relay_prefix; + __u16 prefixlen; + __u16 relay_prefixlen; +}; + +enum { + IFLA_GRE_UNSPEC, + IFLA_GRE_LINK, + IFLA_GRE_IFLAGS, + IFLA_GRE_OFLAGS, + IFLA_GRE_IKEY, + IFLA_GRE_OKEY, + IFLA_GRE_LOCAL, + IFLA_GRE_REMOTE, + IFLA_GRE_TTL, + IFLA_GRE_TOS, + IFLA_GRE_PMTUDISC, + IFLA_GRE_ENCAP_LIMIT, + IFLA_GRE_FLOWINFO, + IFLA_GRE_FLAGS, + IFLA_GRE_ENCAP_TYPE, + IFLA_GRE_ENCAP_FLAGS, + IFLA_GRE_ENCAP_SPORT, + IFLA_GRE_ENCAP_DPORT, + IFLA_GRE_COLLECT_METADATA, + IFLA_GRE_IGNORE_DF, + IFLA_GRE_FWMARK, + IFLA_GRE_ERSPAN_INDEX, + IFLA_GRE_ERSPAN_VER, + IFLA_GRE_ERSPAN_DIR, + IFLA_GRE_ERSPAN_HWID, + __IFLA_GRE_MAX, +}; + +#define IFLA_GRE_MAX (__IFLA_GRE_MAX - 1) + +/* VTI-mode i_flags */ +#define VTI_ISVTI ((__be16)0x0001) + +enum { + IFLA_VTI_UNSPEC, + IFLA_VTI_LINK, + IFLA_VTI_IKEY, + IFLA_VTI_OKEY, + IFLA_VTI_LOCAL, + IFLA_VTI_REMOTE, + IFLA_VTI_FWMARK, + __IFLA_VTI_MAX, +}; + +#define IFLA_VTI_MAX (__IFLA_VTI_MAX - 1) +#endif /* _IF_TUNNEL_H_ */ diff --git a/libnl/include/linux-private/linux/if_vlan.h b/libnl/include/linux-private/linux/if_vlan.h new file mode 100644 index 0000000..18a15da --- /dev/null +++ b/libnl/include/linux-private/linux/if_vlan.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * VLAN An implementation of 802.1Q VLAN tagging. + * + * Authors: Ben Greear + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#ifndef _LINUX_IF_VLAN_H_ +#define _LINUX_IF_VLAN_H_ + + +/* VLAN IOCTLs are found in sockios.h */ + +/* Passed in vlan_ioctl_args structure to determine behaviour. */ +enum vlan_ioctl_cmds { + ADD_VLAN_CMD, + DEL_VLAN_CMD, + SET_VLAN_INGRESS_PRIORITY_CMD, + SET_VLAN_EGRESS_PRIORITY_CMD, + GET_VLAN_INGRESS_PRIORITY_CMD, + GET_VLAN_EGRESS_PRIORITY_CMD, + SET_VLAN_NAME_TYPE_CMD, + SET_VLAN_FLAG_CMD, + GET_VLAN_REALDEV_NAME_CMD, /* If this works, you know it's a VLAN device, btw */ + GET_VLAN_VID_CMD /* Get the VID of this VLAN (specified by name) */ +}; + +enum vlan_flags { + VLAN_FLAG_REORDER_HDR = 0x1, + VLAN_FLAG_GVRP = 0x2, + VLAN_FLAG_LOOSE_BINDING = 0x4, + VLAN_FLAG_MVRP = 0x8, +}; + +enum vlan_name_types { + VLAN_NAME_TYPE_PLUS_VID, /* Name will look like: vlan0005 */ + VLAN_NAME_TYPE_RAW_PLUS_VID, /* name will look like: eth1.0005 */ + VLAN_NAME_TYPE_PLUS_VID_NO_PAD, /* Name will look like: vlan5 */ + VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD, /* Name will look like: eth0.5 */ + VLAN_NAME_TYPE_HIGHEST +}; + +struct vlan_ioctl_args { + int cmd; /* Should be one of the vlan_ioctl_cmds enum above. */ + char device1[24]; + + union { + char device2[24]; + int VID; + unsigned int skb_priority; + unsigned int name_type; + unsigned int bind_type; + unsigned int flag; /* Matches vlan_dev_priv flags */ + } u; + + short vlan_qos; +}; + +#endif /* _LINUX_IF_VLAN_H_ */ diff --git a/libnl/include/linux-private/linux/in.h b/libnl/include/linux-private/linux/in.h new file mode 100644 index 0000000..a4f143b --- /dev/null +++ b/libnl/include/linux-private/linux/in.h @@ -0,0 +1,301 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Definitions of the Internet Protocol. + * + * Version: @(#)in.h 1.0.1 04/21/93 + * + * Authors: Original taken from the GNU Project file. + * Fred N. van Kempen, + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#ifndef _LINUX_IN_H +#define _LINUX_IN_H + +#include +#include +#include + +#if __UAPI_DEF_IN_IPPROTO +/* Standard well-defined IP protocols. */ +enum { + IPPROTO_IP = 0, /* Dummy protocol for TCP */ +#define IPPROTO_IP IPPROTO_IP + IPPROTO_ICMP = 1, /* Internet Control Message Protocol */ +#define IPPROTO_ICMP IPPROTO_ICMP + IPPROTO_IGMP = 2, /* Internet Group Management Protocol */ +#define IPPROTO_IGMP IPPROTO_IGMP + IPPROTO_IPIP = 4, /* IPIP tunnels (older KA9Q tunnels use 94) */ +#define IPPROTO_IPIP IPPROTO_IPIP + IPPROTO_TCP = 6, /* Transmission Control Protocol */ +#define IPPROTO_TCP IPPROTO_TCP + IPPROTO_EGP = 8, /* Exterior Gateway Protocol */ +#define IPPROTO_EGP IPPROTO_EGP + IPPROTO_PUP = 12, /* PUP protocol */ +#define IPPROTO_PUP IPPROTO_PUP + IPPROTO_UDP = 17, /* User Datagram Protocol */ +#define IPPROTO_UDP IPPROTO_UDP + IPPROTO_IDP = 22, /* XNS IDP protocol */ +#define IPPROTO_IDP IPPROTO_IDP + IPPROTO_TP = 29, /* SO Transport Protocol Class 4 */ +#define IPPROTO_TP IPPROTO_TP + IPPROTO_DCCP = 33, /* Datagram Congestion Control Protocol */ +#define IPPROTO_DCCP IPPROTO_DCCP + IPPROTO_IPV6 = 41, /* IPv6-in-IPv4 tunnelling */ +#define IPPROTO_IPV6 IPPROTO_IPV6 + IPPROTO_RSVP = 46, /* RSVP Protocol */ +#define IPPROTO_RSVP IPPROTO_RSVP + IPPROTO_GRE = 47, /* Cisco GRE tunnels (rfc 1701,1702) */ +#define IPPROTO_GRE IPPROTO_GRE + IPPROTO_ESP = 50, /* Encapsulation Security Payload protocol */ +#define IPPROTO_ESP IPPROTO_ESP + IPPROTO_AH = 51, /* Authentication Header protocol */ +#define IPPROTO_AH IPPROTO_AH + IPPROTO_MTP = 92, /* Multicast Transport Protocol */ +#define IPPROTO_MTP IPPROTO_MTP + IPPROTO_BEETPH = 94, /* IP option pseudo header for BEET */ +#define IPPROTO_BEETPH IPPROTO_BEETPH + IPPROTO_ENCAP = 98, /* Encapsulation Header */ +#define IPPROTO_ENCAP IPPROTO_ENCAP + IPPROTO_PIM = 103, /* Protocol Independent Multicast */ +#define IPPROTO_PIM IPPROTO_PIM + IPPROTO_COMP = 108, /* Compression Header Protocol */ +#define IPPROTO_COMP IPPROTO_COMP + IPPROTO_SCTP = 132, /* Stream Control Transport Protocol */ +#define IPPROTO_SCTP IPPROTO_SCTP + IPPROTO_UDPLITE = 136, /* UDP-Lite (RFC 3828) */ +#define IPPROTO_UDPLITE IPPROTO_UDPLITE + IPPROTO_MPLS = 137, /* MPLS in IP (RFC 4023) */ +#define IPPROTO_MPLS IPPROTO_MPLS + IPPROTO_RAW = 255, /* Raw IP packets */ +#define IPPROTO_RAW IPPROTO_RAW + IPPROTO_MAX +}; +#endif + +#if __UAPI_DEF_IN_ADDR +/* Internet address. */ +struct in_addr { + __be32 s_addr; +}; +#endif + +#define IP_TOS 1 +#define IP_TTL 2 +#define IP_HDRINCL 3 +#define IP_OPTIONS 4 +#define IP_ROUTER_ALERT 5 +#define IP_RECVOPTS 6 +#define IP_RETOPTS 7 +#define IP_PKTINFO 8 +#define IP_PKTOPTIONS 9 +#define IP_MTU_DISCOVER 10 +#define IP_RECVERR 11 +#define IP_RECVTTL 12 +#define IP_RECVTOS 13 +#define IP_MTU 14 +#define IP_FREEBIND 15 +#define IP_IPSEC_POLICY 16 +#define IP_XFRM_POLICY 17 +#define IP_PASSSEC 18 +#define IP_TRANSPARENT 19 + +/* BSD compatibility */ +#define IP_RECVRETOPTS IP_RETOPTS + +/* TProxy original addresses */ +#define IP_ORIGDSTADDR 20 +#define IP_RECVORIGDSTADDR IP_ORIGDSTADDR + +#define IP_MINTTL 21 +#define IP_NODEFRAG 22 +#define IP_CHECKSUM 23 +#define IP_BIND_ADDRESS_NO_PORT 24 +#define IP_RECVFRAGSIZE 25 + +/* IP_MTU_DISCOVER values */ +#define IP_PMTUDISC_DONT 0 /* Never send DF frames */ +#define IP_PMTUDISC_WANT 1 /* Use per route hints */ +#define IP_PMTUDISC_DO 2 /* Always DF */ +#define IP_PMTUDISC_PROBE 3 /* Ignore dst pmtu */ +/* Always use interface mtu (ignores dst pmtu) but don't set DF flag. + * Also incoming ICMP frag_needed notifications will be ignored on + * this socket to prevent accepting spoofed ones. + */ +#define IP_PMTUDISC_INTERFACE 4 +/* weaker version of IP_PMTUDISC_INTERFACE, which allos packets to get + * fragmented if they exeed the interface mtu + */ +#define IP_PMTUDISC_OMIT 5 + +#define IP_MULTICAST_IF 32 +#define IP_MULTICAST_TTL 33 +#define IP_MULTICAST_LOOP 34 +#define IP_ADD_MEMBERSHIP 35 +#define IP_DROP_MEMBERSHIP 36 +#define IP_UNBLOCK_SOURCE 37 +#define IP_BLOCK_SOURCE 38 +#define IP_ADD_SOURCE_MEMBERSHIP 39 +#define IP_DROP_SOURCE_MEMBERSHIP 40 +#define IP_MSFILTER 41 +#define MCAST_JOIN_GROUP 42 +#define MCAST_BLOCK_SOURCE 43 +#define MCAST_UNBLOCK_SOURCE 44 +#define MCAST_LEAVE_GROUP 45 +#define MCAST_JOIN_SOURCE_GROUP 46 +#define MCAST_LEAVE_SOURCE_GROUP 47 +#define MCAST_MSFILTER 48 +#define IP_MULTICAST_ALL 49 +#define IP_UNICAST_IF 50 + +#define MCAST_EXCLUDE 0 +#define MCAST_INCLUDE 1 + +/* These need to appear somewhere around here */ +#define IP_DEFAULT_MULTICAST_TTL 1 +#define IP_DEFAULT_MULTICAST_LOOP 1 + +/* Request struct for multicast socket ops */ + +#if __UAPI_DEF_IP_MREQ +struct ip_mreq { + struct in_addr imr_multiaddr; /* IP multicast address of group */ + struct in_addr imr_interface; /* local IP address of interface */ +}; + +struct ip_mreqn { + struct in_addr imr_multiaddr; /* IP multicast address of group */ + struct in_addr imr_address; /* local IP address of interface */ + int imr_ifindex; /* Interface index */ +}; + +struct ip_mreq_source { + __be32 imr_multiaddr; + __be32 imr_interface; + __be32 imr_sourceaddr; +}; + +struct ip_msfilter { + __be32 imsf_multiaddr; + __be32 imsf_interface; + __u32 imsf_fmode; + __u32 imsf_numsrc; + __be32 imsf_slist[1]; +}; + +#define IP_MSFILTER_SIZE(numsrc) \ + (sizeof(struct ip_msfilter) - sizeof(__u32) \ + + (numsrc) * sizeof(__u32)) + +struct group_req { + __u32 gr_interface; /* interface index */ + struct __kernel_sockaddr_storage gr_group; /* group address */ +}; + +struct group_source_req { + __u32 gsr_interface; /* interface index */ + struct __kernel_sockaddr_storage gsr_group; /* group address */ + struct __kernel_sockaddr_storage gsr_source; /* source address */ +}; + +struct group_filter { + __u32 gf_interface; /* interface index */ + struct __kernel_sockaddr_storage gf_group; /* multicast address */ + __u32 gf_fmode; /* filter mode */ + __u32 gf_numsrc; /* number of sources */ + struct __kernel_sockaddr_storage gf_slist[1]; /* interface index */ +}; + +#define GROUP_FILTER_SIZE(numsrc) \ + (sizeof(struct group_filter) - sizeof(struct __kernel_sockaddr_storage) \ + + (numsrc) * sizeof(struct __kernel_sockaddr_storage)) +#endif + +#if __UAPI_DEF_IN_PKTINFO +struct in_pktinfo { + int ipi_ifindex; + struct in_addr ipi_spec_dst; + struct in_addr ipi_addr; +}; +#endif + +/* Structure describing an Internet (IP) socket address. */ +#if __UAPI_DEF_SOCKADDR_IN +#define __SOCK_SIZE__ 16 /* sizeof(struct sockaddr) */ +struct sockaddr_in { + __kernel_sa_family_t sin_family; /* Address family */ + __be16 sin_port; /* Port number */ + struct in_addr sin_addr; /* Internet address */ + + /* Pad to size of `struct sockaddr'. */ + unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) - + sizeof(unsigned short int) - sizeof(struct in_addr)]; +}; +#define sin_zero __pad /* for BSD UNIX comp. -FvK */ +#endif + +#if __UAPI_DEF_IN_CLASS +/* + * Definitions of the bits in an Internet address integer. + * On subnets, host and network parts are found according + * to the subnet mask, not these masks. + */ +#define IN_CLASSA(a) ((((long int) (a)) & 0x80000000) == 0) +#define IN_CLASSA_NET 0xff000000 +#define IN_CLASSA_NSHIFT 24 +#define IN_CLASSA_HOST (0xffffffff & ~IN_CLASSA_NET) +#define IN_CLASSA_MAX 128 + +#define IN_CLASSB(a) ((((long int) (a)) & 0xc0000000) == 0x80000000) +#define IN_CLASSB_NET 0xffff0000 +#define IN_CLASSB_NSHIFT 16 +#define IN_CLASSB_HOST (0xffffffff & ~IN_CLASSB_NET) +#define IN_CLASSB_MAX 65536 + +#define IN_CLASSC(a) ((((long int) (a)) & 0xe0000000) == 0xc0000000) +#define IN_CLASSC_NET 0xffffff00 +#define IN_CLASSC_NSHIFT 8 +#define IN_CLASSC_HOST (0xffffffff & ~IN_CLASSC_NET) + +#define IN_CLASSD(a) ((((long int) (a)) & 0xf0000000) == 0xe0000000) +#define IN_MULTICAST(a) IN_CLASSD(a) +#define IN_MULTICAST_NET 0xF0000000 + +#define IN_EXPERIMENTAL(a) ((((long int) (a)) & 0xf0000000) == 0xf0000000) +#define IN_BADCLASS(a) IN_EXPERIMENTAL((a)) + +/* Address to accept any incoming messages. */ +#define INADDR_ANY ((unsigned long int) 0x00000000) + +/* Address to send to all hosts. */ +#define INADDR_BROADCAST ((unsigned long int) 0xffffffff) + +/* Address indicating an error return. */ +#define INADDR_NONE ((unsigned long int) 0xffffffff) + +/* Network number for local host loopback. */ +#define IN_LOOPBACKNET 127 + +/* Address to loopback in software to local host. */ +#define INADDR_LOOPBACK 0x7f000001 /* 127.0.0.1 */ +#define IN_LOOPBACK(a) ((((long int) (a)) & 0xff000000) == 0x7f000000) + +/* Defines for Multicast INADDR */ +#define INADDR_UNSPEC_GROUP 0xe0000000U /* 224.0.0.0 */ +#define INADDR_ALLHOSTS_GROUP 0xe0000001U /* 224.0.0.1 */ +#define INADDR_ALLRTRS_GROUP 0xe0000002U /* 224.0.0.2 */ +#define INADDR_MAX_LOCAL_GROUP 0xe00000ffU /* 224.0.0.255 */ +#endif + +/* contains the htonl type stuff.. */ +#include + + +#endif /* _LINUX_IN_H */ diff --git a/libnl/include/linux-private/linux/in6.h b/libnl/include/linux-private/linux/in6.h new file mode 100644 index 0000000..409bb3f --- /dev/null +++ b/libnl/include/linux-private/linux/in6.h @@ -0,0 +1,298 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * Types and definitions for AF_INET6 + * Linux INET6 implementation + * + * Authors: + * Pedro Roque + * + * Sources: + * IPv6 Program Interfaces for BSD Systems + * + * + * Advanced Sockets API for IPv6 + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_IN6_H +#define _LINUX_IN6_H + +#include +#include + +/* + * IPv6 address structure + */ + +#if __UAPI_DEF_IN6_ADDR +struct in6_addr { + union { + __u8 u6_addr8[16]; +#if __UAPI_DEF_IN6_ADDR_ALT + __be16 u6_addr16[8]; + __be32 u6_addr32[4]; +#endif + } in6_u; +#define s6_addr in6_u.u6_addr8 +#if __UAPI_DEF_IN6_ADDR_ALT +#define s6_addr16 in6_u.u6_addr16 +#define s6_addr32 in6_u.u6_addr32 +#endif +}; +#endif /* __UAPI_DEF_IN6_ADDR */ + +#if __UAPI_DEF_SOCKADDR_IN6 +struct sockaddr_in6 { + unsigned short int sin6_family; /* AF_INET6 */ + __be16 sin6_port; /* Transport layer port # */ + __be32 sin6_flowinfo; /* IPv6 flow information */ + struct in6_addr sin6_addr; /* IPv6 address */ + __u32 sin6_scope_id; /* scope id (new in RFC2553) */ +}; +#endif /* __UAPI_DEF_SOCKADDR_IN6 */ + +#if __UAPI_DEF_IPV6_MREQ +struct ipv6_mreq { + /* IPv6 multicast address of group */ + struct in6_addr ipv6mr_multiaddr; + + /* local IPv6 address of interface */ + int ipv6mr_ifindex; +}; +#endif /* __UAPI_DEF_IVP6_MREQ */ + +#define ipv6mr_acaddr ipv6mr_multiaddr + +struct in6_flowlabel_req { + struct in6_addr flr_dst; + __be32 flr_label; + __u8 flr_action; + __u8 flr_share; + __u16 flr_flags; + __u16 flr_expires; + __u16 flr_linger; + __u32 __flr_pad; + /* Options in format of IPV6_PKTOPTIONS */ +}; + +#define IPV6_FL_A_GET 0 +#define IPV6_FL_A_PUT 1 +#define IPV6_FL_A_RENEW 2 + +#define IPV6_FL_F_CREATE 1 +#define IPV6_FL_F_EXCL 2 +#define IPV6_FL_F_REFLECT 4 +#define IPV6_FL_F_REMOTE 8 + +#define IPV6_FL_S_NONE 0 +#define IPV6_FL_S_EXCL 1 +#define IPV6_FL_S_PROCESS 2 +#define IPV6_FL_S_USER 3 +#define IPV6_FL_S_ANY 255 + + +/* + * Bitmask constant declarations to help applications select out the + * flow label and priority fields. + * + * Note that this are in host byte order while the flowinfo field of + * sockaddr_in6 is in network byte order. + */ + +#define IPV6_FLOWINFO_FLOWLABEL 0x000fffff +#define IPV6_FLOWINFO_PRIORITY 0x0ff00000 + +/* These definitions are obsolete */ +#define IPV6_PRIORITY_UNCHARACTERIZED 0x0000 +#define IPV6_PRIORITY_FILLER 0x0100 +#define IPV6_PRIORITY_UNATTENDED 0x0200 +#define IPV6_PRIORITY_RESERVED1 0x0300 +#define IPV6_PRIORITY_BULK 0x0400 +#define IPV6_PRIORITY_RESERVED2 0x0500 +#define IPV6_PRIORITY_INTERACTIVE 0x0600 +#define IPV6_PRIORITY_CONTROL 0x0700 +#define IPV6_PRIORITY_8 0x0800 +#define IPV6_PRIORITY_9 0x0900 +#define IPV6_PRIORITY_10 0x0a00 +#define IPV6_PRIORITY_11 0x0b00 +#define IPV6_PRIORITY_12 0x0c00 +#define IPV6_PRIORITY_13 0x0d00 +#define IPV6_PRIORITY_14 0x0e00 +#define IPV6_PRIORITY_15 0x0f00 + +/* + * IPV6 extension headers + */ +#if __UAPI_DEF_IPPROTO_V6 +#define IPPROTO_HOPOPTS 0 /* IPv6 hop-by-hop options */ +#define IPPROTO_ROUTING 43 /* IPv6 routing header */ +#define IPPROTO_FRAGMENT 44 /* IPv6 fragmentation header */ +#define IPPROTO_ICMPV6 58 /* ICMPv6 */ +#define IPPROTO_NONE 59 /* IPv6 no next header */ +#define IPPROTO_DSTOPTS 60 /* IPv6 destination options */ +#define IPPROTO_MH 135 /* IPv6 mobility header */ +#endif /* __UAPI_DEF_IPPROTO_V6 */ + +/* + * IPv6 TLV options. + */ +#define IPV6_TLV_PAD1 0 +#define IPV6_TLV_PADN 1 +#define IPV6_TLV_ROUTERALERT 5 +#define IPV6_TLV_CALIPSO 7 /* RFC 5570 */ +#define IPV6_TLV_JUMBO 194 +#define IPV6_TLV_HAO 201 /* home address option */ + +/* + * IPV6 socket options + */ +#if __UAPI_DEF_IPV6_OPTIONS +#define IPV6_ADDRFORM 1 +#define IPV6_2292PKTINFO 2 +#define IPV6_2292HOPOPTS 3 +#define IPV6_2292DSTOPTS 4 +#define IPV6_2292RTHDR 5 +#define IPV6_2292PKTOPTIONS 6 +#define IPV6_CHECKSUM 7 +#define IPV6_2292HOPLIMIT 8 +#define IPV6_NEXTHOP 9 +#define IPV6_AUTHHDR 10 /* obsolete */ +#define IPV6_FLOWINFO 11 + +#define IPV6_UNICAST_HOPS 16 +#define IPV6_MULTICAST_IF 17 +#define IPV6_MULTICAST_HOPS 18 +#define IPV6_MULTICAST_LOOP 19 +#define IPV6_ADD_MEMBERSHIP 20 +#define IPV6_DROP_MEMBERSHIP 21 +#define IPV6_ROUTER_ALERT 22 +#define IPV6_MTU_DISCOVER 23 +#define IPV6_MTU 24 +#define IPV6_RECVERR 25 +#define IPV6_V6ONLY 26 +#define IPV6_JOIN_ANYCAST 27 +#define IPV6_LEAVE_ANYCAST 28 + +/* IPV6_MTU_DISCOVER values */ +#define IPV6_PMTUDISC_DONT 0 +#define IPV6_PMTUDISC_WANT 1 +#define IPV6_PMTUDISC_DO 2 +#define IPV6_PMTUDISC_PROBE 3 +/* same as IPV6_PMTUDISC_PROBE, provided for symetry with IPv4 + * also see comments on IP_PMTUDISC_INTERFACE + */ +#define IPV6_PMTUDISC_INTERFACE 4 +/* weaker version of IPV6_PMTUDISC_INTERFACE, which allows packets to + * get fragmented if they exceed the interface mtu + */ +#define IPV6_PMTUDISC_OMIT 5 + +/* Flowlabel */ +#define IPV6_FLOWLABEL_MGR 32 +#define IPV6_FLOWINFO_SEND 33 + +#define IPV6_IPSEC_POLICY 34 +#define IPV6_XFRM_POLICY 35 +#define IPV6_HDRINCL 36 +#endif + +/* + * Multicast: + * Following socket options are shared between IPv4 and IPv6. + * + * MCAST_JOIN_GROUP 42 + * MCAST_BLOCK_SOURCE 43 + * MCAST_UNBLOCK_SOURCE 44 + * MCAST_LEAVE_GROUP 45 + * MCAST_JOIN_SOURCE_GROUP 46 + * MCAST_LEAVE_SOURCE_GROUP 47 + * MCAST_MSFILTER 48 + */ + +/* + * Advanced API (RFC3542) (1) + * + * Note: IPV6_RECVRTHDRDSTOPTS does not exist. see net/ipv6/datagram.c. + */ + +#define IPV6_RECVPKTINFO 49 +#define IPV6_PKTINFO 50 +#define IPV6_RECVHOPLIMIT 51 +#define IPV6_HOPLIMIT 52 +#define IPV6_RECVHOPOPTS 53 +#define IPV6_HOPOPTS 54 +#define IPV6_RTHDRDSTOPTS 55 +#define IPV6_RECVRTHDR 56 +#define IPV6_RTHDR 57 +#define IPV6_RECVDSTOPTS 58 +#define IPV6_DSTOPTS 59 +#define IPV6_RECVPATHMTU 60 +#define IPV6_PATHMTU 61 +#define IPV6_DONTFRAG 62 +#if 0 /* not yet */ +#define IPV6_USE_MIN_MTU 63 +#endif + +/* + * Netfilter (1) + * + * Following socket options are used in ip6_tables; + * see include/linux/netfilter_ipv6/ip6_tables.h. + * + * IP6T_SO_SET_REPLACE / IP6T_SO_GET_INFO 64 + * IP6T_SO_SET_ADD_COUNTERS / IP6T_SO_GET_ENTRIES 65 + */ + +/* + * Advanced API (RFC3542) (2) + */ +#define IPV6_RECVTCLASS 66 +#define IPV6_TCLASS 67 + +/* + * Netfilter (2) + * + * Following socket options are used in ip6_tables; + * see include/linux/netfilter_ipv6/ip6_tables.h. + * + * IP6T_SO_GET_REVISION_MATCH 68 + * IP6T_SO_GET_REVISION_TARGET 69 + * IP6T_SO_ORIGINAL_DST 80 + */ + +#define IPV6_AUTOFLOWLABEL 70 +/* RFC5014: Source address selection */ +#define IPV6_ADDR_PREFERENCES 72 + +#define IPV6_PREFER_SRC_TMP 0x0001 +#define IPV6_PREFER_SRC_PUBLIC 0x0002 +#define IPV6_PREFER_SRC_PUBTMP_DEFAULT 0x0100 +#define IPV6_PREFER_SRC_COA 0x0004 +#define IPV6_PREFER_SRC_HOME 0x0400 +#define IPV6_PREFER_SRC_CGA 0x0008 +#define IPV6_PREFER_SRC_NONCGA 0x0800 + +/* RFC5082: Generalized Ttl Security Mechanism */ +#define IPV6_MINHOPCOUNT 73 + +#define IPV6_ORIGDSTADDR 74 +#define IPV6_RECVORIGDSTADDR IPV6_ORIGDSTADDR +#define IPV6_TRANSPARENT 75 +#define IPV6_UNICAST_IF 76 +#define IPV6_RECVFRAGSIZE 77 +#define IPV6_FREEBIND 78 + +/* + * Multicast Routing: + * see include/uapi/linux/mroute6.h. + * + * MRT6_BASE 200 + * ... + * MRT6_MAX + */ +#endif /* _LINUX_IN6_H */ diff --git a/libnl/include/linux-private/linux/inet_diag.h b/libnl/include/linux-private/linux/inet_diag.h new file mode 100644 index 0000000..f3bcd7e --- /dev/null +++ b/libnl/include/linux-private/linux/inet_diag.h @@ -0,0 +1,205 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _INET_DIAG_H_ +#define _INET_DIAG_H_ + +#include + +/* Just some random number */ +#define TCPDIAG_GETSOCK 18 +#define DCCPDIAG_GETSOCK 19 + +#define INET_DIAG_GETSOCK_MAX 24 + +/* Socket identity */ +struct inet_diag_sockid { + __be16 idiag_sport; + __be16 idiag_dport; + __be32 idiag_src[4]; + __be32 idiag_dst[4]; + __u32 idiag_if; + __u32 idiag_cookie[2]; +#define INET_DIAG_NOCOOKIE (~0U) +}; + +/* Request structure */ + +struct inet_diag_req { + __u8 idiag_family; /* Family of addresses. */ + __u8 idiag_src_len; + __u8 idiag_dst_len; + __u8 idiag_ext; /* Query extended information */ + + struct inet_diag_sockid id; + + __u32 idiag_states; /* States to dump */ + __u32 idiag_dbs; /* Tables to dump (NI) */ +}; + +struct inet_diag_req_v2 { + __u8 sdiag_family; + __u8 sdiag_protocol; + __u8 idiag_ext; + __u8 pad; + __u32 idiag_states; + struct inet_diag_sockid id; +}; + +/* + * SOCK_RAW sockets require the underlied protocol to be + * additionally specified so we can use @pad member for + * this, but we can't rename it because userspace programs + * still may depend on this name. Instead lets use another + * structure definition as an alias for struct + * @inet_diag_req_v2. + */ +struct inet_diag_req_raw { + __u8 sdiag_family; + __u8 sdiag_protocol; + __u8 idiag_ext; + __u8 sdiag_raw_protocol; + __u32 idiag_states; + struct inet_diag_sockid id; +}; + +enum { + INET_DIAG_REQ_NONE, + INET_DIAG_REQ_BYTECODE, +}; + +#define INET_DIAG_REQ_MAX INET_DIAG_REQ_BYTECODE + +/* Bytecode is sequence of 4 byte commands followed by variable arguments. + * All the commands identified by "code" are conditional jumps forward: + * to offset cc+"yes" or to offset cc+"no". "yes" is supposed to be + * length of the command and its arguments. + */ + +struct inet_diag_bc_op { + unsigned char code; + unsigned char yes; + unsigned short no; +}; + +enum { + INET_DIAG_BC_NOP, + INET_DIAG_BC_JMP, + INET_DIAG_BC_S_GE, + INET_DIAG_BC_S_LE, + INET_DIAG_BC_D_GE, + INET_DIAG_BC_D_LE, + INET_DIAG_BC_AUTO, + INET_DIAG_BC_S_COND, + INET_DIAG_BC_D_COND, + INET_DIAG_BC_DEV_COND, /* u32 ifindex */ + INET_DIAG_BC_MARK_COND, + INET_DIAG_BC_S_EQ, + INET_DIAG_BC_D_EQ, +}; + +struct inet_diag_hostcond { + __u8 family; + __u8 prefix_len; + int port; + __be32 addr[0]; +}; + +struct inet_diag_markcond { + __u32 mark; + __u32 mask; +}; + +/* Base info structure. It contains socket identity (addrs/ports/cookie) + * and, alas, the information shown by netstat. */ +struct inet_diag_msg { + __u8 idiag_family; + __u8 idiag_state; + __u8 idiag_timer; + __u8 idiag_retrans; + + struct inet_diag_sockid id; + + __u32 idiag_expires; + __u32 idiag_rqueue; + __u32 idiag_wqueue; + __u32 idiag_uid; + __u32 idiag_inode; +}; + +/* Extensions */ + +enum { + INET_DIAG_NONE, + INET_DIAG_MEMINFO, + INET_DIAG_INFO, + INET_DIAG_VEGASINFO, + INET_DIAG_CONG, + INET_DIAG_TOS, + INET_DIAG_TCLASS, + INET_DIAG_SKMEMINFO, + INET_DIAG_SHUTDOWN, + + /* + * Next extenstions cannot be requested in struct inet_diag_req_v2: + * its field idiag_ext has only 8 bits. + */ + + INET_DIAG_DCTCPINFO, /* request as INET_DIAG_VEGASINFO */ + INET_DIAG_PROTOCOL, /* response attribute only */ + INET_DIAG_SKV6ONLY, + INET_DIAG_LOCALS, + INET_DIAG_PEERS, + INET_DIAG_PAD, + INET_DIAG_MARK, /* only with CAP_NET_ADMIN */ + INET_DIAG_BBRINFO, /* request as INET_DIAG_VEGASINFO */ + INET_DIAG_CLASS_ID, /* request as INET_DIAG_TCLASS */ + INET_DIAG_MD5SIG, + __INET_DIAG_MAX, +}; + +#define INET_DIAG_MAX (__INET_DIAG_MAX - 1) + +/* INET_DIAG_MEM */ + +struct inet_diag_meminfo { + __u32 idiag_rmem; + __u32 idiag_wmem; + __u32 idiag_fmem; + __u32 idiag_tmem; +}; + +/* INET_DIAG_VEGASINFO */ + +struct tcpvegas_info { + __u32 tcpv_enabled; + __u32 tcpv_rttcnt; + __u32 tcpv_rtt; + __u32 tcpv_minrtt; +}; + +/* INET_DIAG_DCTCPINFO */ + +struct tcp_dctcp_info { + __u16 dctcp_enabled; + __u16 dctcp_ce_state; + __u32 dctcp_alpha; + __u32 dctcp_ab_ecn; + __u32 dctcp_ab_tot; +}; + +/* INET_DIAG_BBRINFO */ + +struct tcp_bbr_info { + /* u64 bw: max-filtered BW (app throughput) estimate in Byte per sec: */ + __u32 bbr_bw_lo; /* lower 32 bits of bw */ + __u32 bbr_bw_hi; /* upper 32 bits of bw */ + __u32 bbr_min_rtt; /* min-filtered RTT in uSec */ + __u32 bbr_pacing_gain; /* pacing gain shifted left 8 bits */ + __u32 bbr_cwnd_gain; /* cwnd gain shifted left 8 bits */ +}; + +union tcp_cc_info { + struct tcpvegas_info vegas; + struct tcp_dctcp_info dctcp; + struct tcp_bbr_info bbr; +}; +#endif /* _INET_DIAG_H_ */ diff --git a/libnl/include/linux-private/linux/ip.h b/libnl/include/linux-private/linux/ip.h new file mode 100644 index 0000000..f4ecd2f --- /dev/null +++ b/libnl/include/linux-private/linux/ip.h @@ -0,0 +1,177 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Definitions for the IP protocol. + * + * Version: @(#)ip.h 1.0.2 04/28/93 + * + * Authors: Fred N. van Kempen, + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#ifndef _LINUX_IP_H +#define _LINUX_IP_H +#include +#include + +#define IPTOS_TOS_MASK 0x1E +#define IPTOS_TOS(tos) ((tos)&IPTOS_TOS_MASK) +#define IPTOS_LOWDELAY 0x10 +#define IPTOS_THROUGHPUT 0x08 +#define IPTOS_RELIABILITY 0x04 +#define IPTOS_MINCOST 0x02 + +#define IPTOS_PREC_MASK 0xE0 +#define IPTOS_PREC(tos) ((tos)&IPTOS_PREC_MASK) +#define IPTOS_PREC_NETCONTROL 0xe0 +#define IPTOS_PREC_INTERNETCONTROL 0xc0 +#define IPTOS_PREC_CRITIC_ECP 0xa0 +#define IPTOS_PREC_FLASHOVERRIDE 0x80 +#define IPTOS_PREC_FLASH 0x60 +#define IPTOS_PREC_IMMEDIATE 0x40 +#define IPTOS_PREC_PRIORITY 0x20 +#define IPTOS_PREC_ROUTINE 0x00 + + +/* IP options */ +#define IPOPT_COPY 0x80 +#define IPOPT_CLASS_MASK 0x60 +#define IPOPT_NUMBER_MASK 0x1f + +#define IPOPT_COPIED(o) ((o)&IPOPT_COPY) +#define IPOPT_CLASS(o) ((o)&IPOPT_CLASS_MASK) +#define IPOPT_NUMBER(o) ((o)&IPOPT_NUMBER_MASK) + +#define IPOPT_CONTROL 0x00 +#define IPOPT_RESERVED1 0x20 +#define IPOPT_MEASUREMENT 0x40 +#define IPOPT_RESERVED2 0x60 + +#define IPOPT_END (0 |IPOPT_CONTROL) +#define IPOPT_NOOP (1 |IPOPT_CONTROL) +#define IPOPT_SEC (2 |IPOPT_CONTROL|IPOPT_COPY) +#define IPOPT_LSRR (3 |IPOPT_CONTROL|IPOPT_COPY) +#define IPOPT_TIMESTAMP (4 |IPOPT_MEASUREMENT) +#define IPOPT_CIPSO (6 |IPOPT_CONTROL|IPOPT_COPY) +#define IPOPT_RR (7 |IPOPT_CONTROL) +#define IPOPT_SID (8 |IPOPT_CONTROL|IPOPT_COPY) +#define IPOPT_SSRR (9 |IPOPT_CONTROL|IPOPT_COPY) +#define IPOPT_RA (20|IPOPT_CONTROL|IPOPT_COPY) + +#define IPVERSION 4 +#define MAXTTL 255 +#define IPDEFTTL 64 + +#define IPOPT_OPTVAL 0 +#define IPOPT_OLEN 1 +#define IPOPT_OFFSET 2 +#define IPOPT_MINOFF 4 +#define MAX_IPOPTLEN 40 +#define IPOPT_NOP IPOPT_NOOP +#define IPOPT_EOL IPOPT_END +#define IPOPT_TS IPOPT_TIMESTAMP + +#define IPOPT_TS_TSONLY 0 /* timestamps only */ +#define IPOPT_TS_TSANDADDR 1 /* timestamps and addresses */ +#define IPOPT_TS_PRESPEC 3 /* specified modules only */ + +#define IPV4_BEET_PHMAXLEN 8 + +struct iphdr { +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u8 ihl:4, + version:4; +#elif defined (__BIG_ENDIAN_BITFIELD) + __u8 version:4, + ihl:4; +#else +#error "Please fix " +#endif + __u8 tos; + __be16 tot_len; + __be16 id; + __be16 frag_off; + __u8 ttl; + __u8 protocol; + __sum16 check; + __be32 saddr; + __be32 daddr; + /*The options start here. */ +}; + + +struct ip_auth_hdr { + __u8 nexthdr; + __u8 hdrlen; /* This one is measured in 32 bit units! */ + __be16 reserved; + __be32 spi; + __be32 seq_no; /* Sequence number */ + __u8 auth_data[0]; /* Variable len but >=4. Mind the 64 bit alignment! */ +}; + +struct ip_esp_hdr { + __be32 spi; + __be32 seq_no; /* Sequence number */ + __u8 enc_data[0]; /* Variable len but >=8. Mind the 64 bit alignment! */ +}; + +struct ip_comp_hdr { + __u8 nexthdr; + __u8 flags; + __be16 cpi; +}; + +struct ip_beet_phdr { + __u8 nexthdr; + __u8 hdrlen; + __u8 padlen; + __u8 reserved; +}; + +/* index values for the variables in ipv4_devconf */ +enum +{ + IPV4_DEVCONF_FORWARDING=1, + IPV4_DEVCONF_MC_FORWARDING, + IPV4_DEVCONF_PROXY_ARP, + IPV4_DEVCONF_ACCEPT_REDIRECTS, + IPV4_DEVCONF_SECURE_REDIRECTS, + IPV4_DEVCONF_SEND_REDIRECTS, + IPV4_DEVCONF_SHARED_MEDIA, + IPV4_DEVCONF_RP_FILTER, + IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE, + IPV4_DEVCONF_BOOTP_RELAY, + IPV4_DEVCONF_LOG_MARTIANS, + IPV4_DEVCONF_TAG, + IPV4_DEVCONF_ARPFILTER, + IPV4_DEVCONF_MEDIUM_ID, + IPV4_DEVCONF_NOXFRM, + IPV4_DEVCONF_NOPOLICY, + IPV4_DEVCONF_FORCE_IGMP_VERSION, + IPV4_DEVCONF_ARP_ANNOUNCE, + IPV4_DEVCONF_ARP_IGNORE, + IPV4_DEVCONF_PROMOTE_SECONDARIES, + IPV4_DEVCONF_ARP_ACCEPT, + IPV4_DEVCONF_ARP_NOTIFY, + IPV4_DEVCONF_ACCEPT_LOCAL, + IPV4_DEVCONF_SRC_VMARK, + IPV4_DEVCONF_PROXY_ARP_PVLAN, + IPV4_DEVCONF_ROUTE_LOCALNET, + IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL, + IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL, + IPV4_DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN, + IPV4_DEVCONF_DROP_UNICAST_IN_L2_MULTICAST, + IPV4_DEVCONF_DROP_GRATUITOUS_ARP, + IPV4_DEVCONF_BC_FORWARDING, + __IPV4_DEVCONF_MAX +}; + +#define IPV4_DEVCONF_MAX (__IPV4_DEVCONF_MAX - 1) + +#endif /* _LINUX_IP_H */ diff --git a/libnl/include/linux-private/linux/ipv6.h b/libnl/include/linux-private/linux/ipv6.h new file mode 100644 index 0000000..769b4a3 --- /dev/null +++ b/libnl/include/linux-private/linux/ipv6.h @@ -0,0 +1,194 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _IPV6_H +#define _IPV6_H + +#include +#include +#include +#include + +/* The latest drafts declared increase in minimal mtu up to 1280. */ + +#define IPV6_MIN_MTU 1280 + +/* + * Advanced API + * source interface/address selection, source routing, etc... + * *under construction* + */ + +#if __UAPI_DEF_IN6_PKTINFO +struct in6_pktinfo { + struct in6_addr ipi6_addr; + int ipi6_ifindex; +}; +#endif + +#if __UAPI_DEF_IP6_MTUINFO +struct ip6_mtuinfo { + struct sockaddr_in6 ip6m_addr; + __u32 ip6m_mtu; +}; +#endif + +struct in6_ifreq { + struct in6_addr ifr6_addr; + __u32 ifr6_prefixlen; + int ifr6_ifindex; +}; + +#define IPV6_SRCRT_STRICT 0x01 /* Deprecated; will be removed */ +#define IPV6_SRCRT_TYPE_0 0 /* Deprecated; will be removed */ +#define IPV6_SRCRT_TYPE_2 2 /* IPv6 type 2 Routing Header */ +#define IPV6_SRCRT_TYPE_4 4 /* Segment Routing with IPv6 */ + +/* + * routing header + */ +struct ipv6_rt_hdr { + __u8 nexthdr; + __u8 hdrlen; + __u8 type; + __u8 segments_left; + + /* + * type specific data + * variable length field + */ +}; + + +struct ipv6_opt_hdr { + __u8 nexthdr; + __u8 hdrlen; + /* + * TLV encoded option data follows. + */ +} __attribute__((packed)); /* required for some archs */ + +#define ipv6_destopt_hdr ipv6_opt_hdr +#define ipv6_hopopt_hdr ipv6_opt_hdr + +/* Router Alert option values (RFC2711) */ +#define IPV6_OPT_ROUTERALERT_MLD 0x0000 /* MLD(RFC2710) */ + +/* + * routing header type 0 (used in cmsghdr struct) + */ + +struct rt0_hdr { + struct ipv6_rt_hdr rt_hdr; + __u32 reserved; + struct in6_addr addr[0]; + +#define rt0_type rt_hdr.type +}; + +/* + * routing header type 2 + */ + +struct rt2_hdr { + struct ipv6_rt_hdr rt_hdr; + __u32 reserved; + struct in6_addr addr; + +#define rt2_type rt_hdr.type +}; + +/* + * home address option in destination options header + */ + +struct ipv6_destopt_hao { + __u8 type; + __u8 length; + struct in6_addr addr; +} __attribute__((packed)); + +/* + * IPv6 fixed header + * + * BEWARE, it is incorrect. The first 4 bits of flow_lbl + * are glued to priority now, forming "class". + */ + +struct ipv6hdr { +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u8 priority:4, + version:4; +#elif defined(__BIG_ENDIAN_BITFIELD) + __u8 version:4, + priority:4; +#else +#error "Please fix " +#endif + __u8 flow_lbl[3]; + + __be16 payload_len; + __u8 nexthdr; + __u8 hop_limit; + + struct in6_addr saddr; + struct in6_addr daddr; +}; + + +/* index values for the variables in ipv6_devconf */ +enum { + DEVCONF_FORWARDING = 0, + DEVCONF_HOPLIMIT, + DEVCONF_MTU6, + DEVCONF_ACCEPT_RA, + DEVCONF_ACCEPT_REDIRECTS, + DEVCONF_AUTOCONF, + DEVCONF_DAD_TRANSMITS, + DEVCONF_RTR_SOLICITS, + DEVCONF_RTR_SOLICIT_INTERVAL, + DEVCONF_RTR_SOLICIT_DELAY, + DEVCONF_USE_TEMPADDR, + DEVCONF_TEMP_VALID_LFT, + DEVCONF_TEMP_PREFERED_LFT, + DEVCONF_REGEN_MAX_RETRY, + DEVCONF_MAX_DESYNC_FACTOR, + DEVCONF_MAX_ADDRESSES, + DEVCONF_FORCE_MLD_VERSION, + DEVCONF_ACCEPT_RA_DEFRTR, + DEVCONF_ACCEPT_RA_PINFO, + DEVCONF_ACCEPT_RA_RTR_PREF, + DEVCONF_RTR_PROBE_INTERVAL, + DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN, + DEVCONF_PROXY_NDP, + DEVCONF_OPTIMISTIC_DAD, + DEVCONF_ACCEPT_SOURCE_ROUTE, + DEVCONF_MC_FORWARDING, + DEVCONF_DISABLE_IPV6, + DEVCONF_ACCEPT_DAD, + DEVCONF_FORCE_TLLAO, + DEVCONF_NDISC_NOTIFY, + DEVCONF_MLDV1_UNSOLICITED_REPORT_INTERVAL, + DEVCONF_MLDV2_UNSOLICITED_REPORT_INTERVAL, + DEVCONF_SUPPRESS_FRAG_NDISC, + DEVCONF_ACCEPT_RA_FROM_LOCAL, + DEVCONF_USE_OPTIMISTIC, + DEVCONF_ACCEPT_RA_MTU, + DEVCONF_STABLE_SECRET, + DEVCONF_USE_OIF_ADDRS_ONLY, + DEVCONF_ACCEPT_RA_MIN_HOP_LIMIT, + DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN, + DEVCONF_DROP_UNICAST_IN_L2_MULTICAST, + DEVCONF_DROP_UNSOLICITED_NA, + DEVCONF_KEEP_ADDR_ON_DOWN, + DEVCONF_RTR_SOLICIT_MAX_INTERVAL, + DEVCONF_SEG6_ENABLED, + DEVCONF_SEG6_REQUIRE_HMAC, + DEVCONF_ENHANCED_DAD, + DEVCONF_ADDR_GEN_MODE, + DEVCONF_DISABLE_POLICY, + DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN, + DEVCONF_NDISC_TCLASS, + DEVCONF_MAX +}; + + +#endif /* _IPV6_H */ diff --git a/libnl/include/linux-private/linux/libc-compat.h b/libnl/include/linux-private/linux/libc-compat.h new file mode 100644 index 0000000..a159991 --- /dev/null +++ b/libnl/include/linux-private/linux/libc-compat.h @@ -0,0 +1,267 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Compatibility interface for userspace libc header coordination: + * + * Define compatibility macros that are used to control the inclusion or + * exclusion of UAPI structures and definitions in coordination with another + * userspace C library. + * + * This header is intended to solve the problem of UAPI definitions that + * conflict with userspace definitions. If a UAPI header has such conflicting + * definitions then the solution is as follows: + * + * * Synchronize the UAPI header and the libc headers so either one can be + * used and such that the ABI is preserved. If this is not possible then + * no simple compatibility interface exists (you need to write translating + * wrappers and rename things) and you can't use this interface. + * + * Then follow this process: + * + * (a) Include libc-compat.h in the UAPI header. + * e.g. #include + * This include must be as early as possible. + * + * (b) In libc-compat.h add enough code to detect that the comflicting + * userspace libc header has been included first. + * + * (c) If the userspace libc header has been included first define a set of + * guard macros of the form __UAPI_DEF_FOO and set their values to 1, else + * set their values to 0. + * + * (d) Back in the UAPI header with the conflicting definitions, guard the + * definitions with: + * #if __UAPI_DEF_FOO + * ... + * #endif + * + * This fixes the situation where the linux headers are included *after* the + * libc headers. To fix the problem with the inclusion in the other order the + * userspace libc headers must be fixed like this: + * + * * For all definitions that conflict with kernel definitions wrap those + * defines in the following: + * #if !__UAPI_DEF_FOO + * ... + * #endif + * + * This prevents the redefinition of a construct already defined by the kernel. + */ +#ifndef _LIBC_COMPAT_H +#define _LIBC_COMPAT_H + +/* We have included glibc headers... */ +#if defined(__GLIBC__) + +/* Coordinate with glibc net/if.h header. */ +#if defined(_NET_IF_H) && defined(__USE_MISC) + +/* GLIBC headers included first so don't define anything + * that would already be defined. */ + +#define __UAPI_DEF_IF_IFCONF 0 +#define __UAPI_DEF_IF_IFMAP 0 +#define __UAPI_DEF_IF_IFNAMSIZ 0 +#define __UAPI_DEF_IF_IFREQ 0 +/* Everything up to IFF_DYNAMIC, matches net/if.h until glibc 2.23 */ +#define __UAPI_DEF_IF_NET_DEVICE_FLAGS 0 +/* For the future if glibc adds IFF_LOWER_UP, IFF_DORMANT and IFF_ECHO */ +#ifndef __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO +#define __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO 1 +#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO */ + +#else /* _NET_IF_H */ + +/* Linux headers included first, and we must define everything + * we need. The expectation is that glibc will check the + * __UAPI_DEF_* defines and adjust appropriately. */ + +#define __UAPI_DEF_IF_IFCONF 1 +#define __UAPI_DEF_IF_IFMAP 1 +#define __UAPI_DEF_IF_IFNAMSIZ 1 +#define __UAPI_DEF_IF_IFREQ 1 +/* Everything up to IFF_DYNAMIC, matches net/if.h until glibc 2.23 */ +#define __UAPI_DEF_IF_NET_DEVICE_FLAGS 1 +/* For the future if glibc adds IFF_LOWER_UP, IFF_DORMANT and IFF_ECHO */ +#define __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO 1 + +#endif /* _NET_IF_H */ + +/* Coordinate with glibc netinet/in.h header. */ +#if defined(_NETINET_IN_H) + +/* GLIBC headers included first so don't define anything + * that would already be defined. */ +#define __UAPI_DEF_IN_ADDR 0 +#define __UAPI_DEF_IN_IPPROTO 0 +#define __UAPI_DEF_IN_PKTINFO 0 +#define __UAPI_DEF_IP_MREQ 0 +#define __UAPI_DEF_SOCKADDR_IN 0 +#define __UAPI_DEF_IN_CLASS 0 + +#define __UAPI_DEF_IN6_ADDR 0 +/* The exception is the in6_addr macros which must be defined + * if the glibc code didn't define them. This guard matches + * the guard in glibc/inet/netinet/in.h which defines the + * additional in6_addr macros e.g. s6_addr16, and s6_addr32. */ +#if defined(__USE_MISC) || defined (__USE_GNU) +#define __UAPI_DEF_IN6_ADDR_ALT 0 +#else +#define __UAPI_DEF_IN6_ADDR_ALT 1 +#endif +#define __UAPI_DEF_SOCKADDR_IN6 0 +#define __UAPI_DEF_IPV6_MREQ 0 +#define __UAPI_DEF_IPPROTO_V6 0 +#define __UAPI_DEF_IPV6_OPTIONS 0 +#define __UAPI_DEF_IN6_PKTINFO 0 +#define __UAPI_DEF_IP6_MTUINFO 0 + +#else + +/* Linux headers included first, and we must define everything + * we need. The expectation is that glibc will check the + * __UAPI_DEF_* defines and adjust appropriately. */ +#define __UAPI_DEF_IN_ADDR 1 +#define __UAPI_DEF_IN_IPPROTO 1 +#define __UAPI_DEF_IN_PKTINFO 1 +#define __UAPI_DEF_IP_MREQ 1 +#define __UAPI_DEF_SOCKADDR_IN 1 +#define __UAPI_DEF_IN_CLASS 1 + +#define __UAPI_DEF_IN6_ADDR 1 +/* We unconditionally define the in6_addr macros and glibc must + * coordinate. */ +#define __UAPI_DEF_IN6_ADDR_ALT 1 +#define __UAPI_DEF_SOCKADDR_IN6 1 +#define __UAPI_DEF_IPV6_MREQ 1 +#define __UAPI_DEF_IPPROTO_V6 1 +#define __UAPI_DEF_IPV6_OPTIONS 1 +#define __UAPI_DEF_IN6_PKTINFO 1 +#define __UAPI_DEF_IP6_MTUINFO 1 + +#endif /* _NETINET_IN_H */ + +/* Coordinate with glibc netipx/ipx.h header. */ +#if defined(__NETIPX_IPX_H) + +#define __UAPI_DEF_SOCKADDR_IPX 0 +#define __UAPI_DEF_IPX_ROUTE_DEFINITION 0 +#define __UAPI_DEF_IPX_INTERFACE_DEFINITION 0 +#define __UAPI_DEF_IPX_CONFIG_DATA 0 +#define __UAPI_DEF_IPX_ROUTE_DEF 0 + +#else /* defined(__NETIPX_IPX_H) */ + +#define __UAPI_DEF_SOCKADDR_IPX 1 +#define __UAPI_DEF_IPX_ROUTE_DEFINITION 1 +#define __UAPI_DEF_IPX_INTERFACE_DEFINITION 1 +#define __UAPI_DEF_IPX_CONFIG_DATA 1 +#define __UAPI_DEF_IPX_ROUTE_DEF 1 + +#endif /* defined(__NETIPX_IPX_H) */ + +/* Definitions for xattr.h */ +#if defined(_SYS_XATTR_H) +#define __UAPI_DEF_XATTR 0 +#else +#define __UAPI_DEF_XATTR 1 +#endif + +/* If we did not see any headers from any supported C libraries, + * or we are being included in the kernel, then define everything + * that we need. Check for previous __UAPI_* definitions to give + * unsupported C libraries a way to opt out of any kernel definition. */ +#else /* !defined(__GLIBC__) */ + +/* Definitions for if.h */ +#ifndef __UAPI_DEF_IF_IFCONF +#define __UAPI_DEF_IF_IFCONF 1 +#endif +#ifndef __UAPI_DEF_IF_IFMAP +#define __UAPI_DEF_IF_IFMAP 1 +#endif +#ifndef __UAPI_DEF_IF_IFNAMSIZ +#define __UAPI_DEF_IF_IFNAMSIZ 1 +#endif +#ifndef __UAPI_DEF_IF_IFREQ +#define __UAPI_DEF_IF_IFREQ 1 +#endif +/* Everything up to IFF_DYNAMIC, matches net/if.h until glibc 2.23 */ +#ifndef __UAPI_DEF_IF_NET_DEVICE_FLAGS +#define __UAPI_DEF_IF_NET_DEVICE_FLAGS 1 +#endif +/* For the future if glibc adds IFF_LOWER_UP, IFF_DORMANT and IFF_ECHO */ +#ifndef __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO +#define __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO 1 +#endif + +/* Definitions for in.h */ +#ifndef __UAPI_DEF_IN_ADDR +#define __UAPI_DEF_IN_ADDR 1 +#endif +#ifndef __UAPI_DEF_IN_IPPROTO +#define __UAPI_DEF_IN_IPPROTO 1 +#endif +#ifndef __UAPI_DEF_IN_PKTINFO +#define __UAPI_DEF_IN_PKTINFO 1 +#endif +#ifndef __UAPI_DEF_IP_MREQ +#define __UAPI_DEF_IP_MREQ 1 +#endif +#ifndef __UAPI_DEF_SOCKADDR_IN +#define __UAPI_DEF_SOCKADDR_IN 1 +#endif +#ifndef __UAPI_DEF_IN_CLASS +#define __UAPI_DEF_IN_CLASS 1 +#endif + +/* Definitions for in6.h */ +#ifndef __UAPI_DEF_IN6_ADDR +#define __UAPI_DEF_IN6_ADDR 1 +#endif +#ifndef __UAPI_DEF_IN6_ADDR_ALT +#define __UAPI_DEF_IN6_ADDR_ALT 1 +#endif +#ifndef __UAPI_DEF_SOCKADDR_IN6 +#define __UAPI_DEF_SOCKADDR_IN6 1 +#endif +#ifndef __UAPI_DEF_IPV6_MREQ +#define __UAPI_DEF_IPV6_MREQ 1 +#endif +#ifndef __UAPI_DEF_IPPROTO_V6 +#define __UAPI_DEF_IPPROTO_V6 1 +#endif +#ifndef __UAPI_DEF_IPV6_OPTIONS +#define __UAPI_DEF_IPV6_OPTIONS 1 +#endif +#ifndef __UAPI_DEF_IN6_PKTINFO +#define __UAPI_DEF_IN6_PKTINFO 1 +#endif +#ifndef __UAPI_DEF_IP6_MTUINFO +#define __UAPI_DEF_IP6_MTUINFO 1 +#endif + +/* Definitions for ipx.h */ +#ifndef __UAPI_DEF_SOCKADDR_IPX +#define __UAPI_DEF_SOCKADDR_IPX 1 +#endif +#ifndef __UAPI_DEF_IPX_ROUTE_DEFINITION +#define __UAPI_DEF_IPX_ROUTE_DEFINITION 1 +#endif +#ifndef __UAPI_DEF_IPX_INTERFACE_DEFINITION +#define __UAPI_DEF_IPX_INTERFACE_DEFINITION 1 +#endif +#ifndef __UAPI_DEF_IPX_CONFIG_DATA +#define __UAPI_DEF_IPX_CONFIG_DATA 1 +#endif +#ifndef __UAPI_DEF_IPX_ROUTE_DEF +#define __UAPI_DEF_IPX_ROUTE_DEF 1 +#endif + +/* Definitions for xattr.h */ +#ifndef __UAPI_DEF_XATTR +#define __UAPI_DEF_XATTR 1 +#endif + +#endif /* __GLIBC__ */ + +#endif /* _LIBC_COMPAT_H */ diff --git a/libnl/include/linux-private/linux/lwtunnel.h b/libnl/include/linux-private/linux/lwtunnel.h new file mode 100644 index 0000000..3f3fe6f --- /dev/null +++ b/libnl/include/linux-private/linux/lwtunnel.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LWTUNNEL_H_ +#define _LWTUNNEL_H_ + +#include + +enum lwtunnel_encap_types { + LWTUNNEL_ENCAP_NONE, + LWTUNNEL_ENCAP_MPLS, + LWTUNNEL_ENCAP_IP, + LWTUNNEL_ENCAP_ILA, + LWTUNNEL_ENCAP_IP6, + LWTUNNEL_ENCAP_SEG6, + LWTUNNEL_ENCAP_BPF, + LWTUNNEL_ENCAP_SEG6_LOCAL, + __LWTUNNEL_ENCAP_MAX, +}; + +#define LWTUNNEL_ENCAP_MAX (__LWTUNNEL_ENCAP_MAX - 1) + +enum lwtunnel_ip_t { + LWTUNNEL_IP_UNSPEC, + LWTUNNEL_IP_ID, + LWTUNNEL_IP_DST, + LWTUNNEL_IP_SRC, + LWTUNNEL_IP_TTL, + LWTUNNEL_IP_TOS, + LWTUNNEL_IP_FLAGS, + LWTUNNEL_IP_PAD, + __LWTUNNEL_IP_MAX, +}; + +#define LWTUNNEL_IP_MAX (__LWTUNNEL_IP_MAX - 1) + +enum lwtunnel_ip6_t { + LWTUNNEL_IP6_UNSPEC, + LWTUNNEL_IP6_ID, + LWTUNNEL_IP6_DST, + LWTUNNEL_IP6_SRC, + LWTUNNEL_IP6_HOPLIMIT, + LWTUNNEL_IP6_TC, + LWTUNNEL_IP6_FLAGS, + LWTUNNEL_IP6_PAD, + __LWTUNNEL_IP6_MAX, +}; + +#define LWTUNNEL_IP6_MAX (__LWTUNNEL_IP6_MAX - 1) + +enum { + LWT_BPF_PROG_UNSPEC, + LWT_BPF_PROG_FD, + LWT_BPF_PROG_NAME, + __LWT_BPF_PROG_MAX, +}; + +#define LWT_BPF_PROG_MAX (__LWT_BPF_PROG_MAX - 1) + +enum { + LWT_BPF_UNSPEC, + LWT_BPF_IN, + LWT_BPF_OUT, + LWT_BPF_XMIT, + LWT_BPF_XMIT_HEADROOM, + __LWT_BPF_MAX, +}; + +#define LWT_BPF_MAX (__LWT_BPF_MAX - 1) + +#define LWT_BPF_MAX_HEADROOM 256 + +#endif /* _LWTUNNEL_H_ */ diff --git a/libnl/include/linux-private/linux/mpls.h b/libnl/include/linux-private/linux/mpls.h new file mode 100644 index 0000000..9effbf9 --- /dev/null +++ b/libnl/include/linux-private/linux/mpls.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _MPLS_H +#define _MPLS_H + +#include +#include + +/* Reference: RFC 5462, RFC 3032 + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Label | TC |S| TTL | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Label: Label Value, 20 bits + * TC: Traffic Class field, 3 bits + * S: Bottom of Stack, 1 bit + * TTL: Time to Live, 8 bits + */ + +struct mpls_label { + __be32 entry; +}; + +#define MPLS_LS_LABEL_MASK 0xFFFFF000 +#define MPLS_LS_LABEL_SHIFT 12 +#define MPLS_LS_TC_MASK 0x00000E00 +#define MPLS_LS_TC_SHIFT 9 +#define MPLS_LS_S_MASK 0x00000100 +#define MPLS_LS_S_SHIFT 8 +#define MPLS_LS_TTL_MASK 0x000000FF +#define MPLS_LS_TTL_SHIFT 0 + +/* Reserved labels */ +#define MPLS_LABEL_IPV4NULL 0 /* RFC3032 */ +#define MPLS_LABEL_RTALERT 1 /* RFC3032 */ +#define MPLS_LABEL_IPV6NULL 2 /* RFC3032 */ +#define MPLS_LABEL_IMPLNULL 3 /* RFC3032 */ +#define MPLS_LABEL_ENTROPY 7 /* RFC6790 */ +#define MPLS_LABEL_GAL 13 /* RFC5586 */ +#define MPLS_LABEL_OAMALERT 14 /* RFC3429 */ +#define MPLS_LABEL_EXTENSION 15 /* RFC7274 */ + +#define MPLS_LABEL_FIRST_UNRESERVED 16 /* RFC3032 */ + +/* These are embedded into IFLA_STATS_AF_SPEC: + * [IFLA_STATS_AF_SPEC] + * -> [AF_MPLS] + * -> [MPLS_STATS_xxx] + * + * Attributes: + * [MPLS_STATS_LINK] = { + * struct mpls_link_stats + * } + */ +enum { + MPLS_STATS_UNSPEC, /* also used as 64bit pad attribute */ + MPLS_STATS_LINK, + __MPLS_STATS_MAX, +}; + +#define MPLS_STATS_MAX (__MPLS_STATS_MAX - 1) + +struct mpls_link_stats { + __u64 rx_packets; /* total packets received */ + __u64 tx_packets; /* total packets transmitted */ + __u64 rx_bytes; /* total bytes received */ + __u64 tx_bytes; /* total bytes transmitted */ + __u64 rx_errors; /* bad packets received */ + __u64 tx_errors; /* packet transmit problems */ + __u64 rx_dropped; /* packet dropped on receive */ + __u64 tx_dropped; /* packet dropped on transmit */ + __u64 rx_noroute; /* no route for packet dest */ +}; + +#endif /* _MPLS_H */ diff --git a/libnl/include/linux-private/linux/mpls_iptunnel.h b/libnl/include/linux-private/linux/mpls_iptunnel.h new file mode 100644 index 0000000..2c69b7d --- /dev/null +++ b/libnl/include/linux-private/linux/mpls_iptunnel.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * mpls tunnel api + * + * Authors: + * Roopa Prabhu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_MPLS_IPTUNNEL_H +#define _LINUX_MPLS_IPTUNNEL_H + +/* MPLS tunnel attributes + * [RTA_ENCAP] = { + * [MPLS_IPTUNNEL_DST] + * [MPLS_IPTUNNEL_TTL] + * } + */ +enum { + MPLS_IPTUNNEL_UNSPEC, + MPLS_IPTUNNEL_DST, + MPLS_IPTUNNEL_TTL, + __MPLS_IPTUNNEL_MAX, +}; +#define MPLS_IPTUNNEL_MAX (__MPLS_IPTUNNEL_MAX - 1) + +#endif /* _LINUX_MPLS_IPTUNNEL_H */ diff --git a/libnl/include/linux-private/linux/neighbour.h b/libnl/include/linux-private/linux/neighbour.h new file mode 100644 index 0000000..904db61 --- /dev/null +++ b/libnl/include/linux-private/linux/neighbour.h @@ -0,0 +1,172 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_NEIGHBOUR_H +#define __LINUX_NEIGHBOUR_H + +#include +#include + +struct ndmsg { + __u8 ndm_family; + __u8 ndm_pad1; + __u16 ndm_pad2; + __s32 ndm_ifindex; + __u16 ndm_state; + __u8 ndm_flags; + __u8 ndm_type; +}; + +enum { + NDA_UNSPEC, + NDA_DST, + NDA_LLADDR, + NDA_CACHEINFO, + NDA_PROBES, + NDA_VLAN, + NDA_PORT, + NDA_VNI, + NDA_IFINDEX, + NDA_MASTER, + NDA_LINK_NETNSID, + NDA_SRC_VNI, + __NDA_MAX +}; + +#define NDA_MAX (__NDA_MAX - 1) + +/* + * Neighbor Cache Entry Flags + */ + +#define NTF_USE 0x01 +#define NTF_SELF 0x02 +#define NTF_MASTER 0x04 +#define NTF_PROXY 0x08 /* == ATF_PUBL */ +#define NTF_EXT_LEARNED 0x10 +#define NTF_OFFLOADED 0x20 +#define NTF_ROUTER 0x80 + +/* + * Neighbor Cache Entry States. + */ + +#define NUD_INCOMPLETE 0x01 +#define NUD_REACHABLE 0x02 +#define NUD_STALE 0x04 +#define NUD_DELAY 0x08 +#define NUD_PROBE 0x10 +#define NUD_FAILED 0x20 + +/* Dummy states */ +#define NUD_NOARP 0x40 +#define NUD_PERMANENT 0x80 +#define NUD_NONE 0x00 + +/* NUD_NOARP & NUD_PERMANENT are pseudostates, they never change + and make no address resolution or NUD. + NUD_PERMANENT also cannot be deleted by garbage collectors. + */ + +struct nda_cacheinfo { + __u32 ndm_confirmed; + __u32 ndm_used; + __u32 ndm_updated; + __u32 ndm_refcnt; +}; + +/***************************************************************** + * Neighbour tables specific messages. + * + * To retrieve the neighbour tables send RTM_GETNEIGHTBL with the + * NLM_F_DUMP flag set. Every neighbour table configuration is + * spread over multiple messages to avoid running into message + * size limits on systems with many interfaces. The first message + * in the sequence transports all not device specific data such as + * statistics, configuration, and the default parameter set. + * This message is followed by 0..n messages carrying device + * specific parameter sets. + * Although the ordering should be sufficient, NDTA_NAME can be + * used to identify sequences. The initial message can be identified + * by checking for NDTA_CONFIG. The device specific messages do + * not contain this TLV but have NDTPA_IFINDEX set to the + * corresponding interface index. + * + * To change neighbour table attributes, send RTM_SETNEIGHTBL + * with NDTA_NAME set. Changeable attribute include NDTA_THRESH[1-3], + * NDTA_GC_INTERVAL, and all TLVs in NDTA_PARMS unless marked + * otherwise. Device specific parameter sets can be changed by + * setting NDTPA_IFINDEX to the interface index of the corresponding + * device. + ****/ + +struct ndt_stats { + __u64 ndts_allocs; + __u64 ndts_destroys; + __u64 ndts_hash_grows; + __u64 ndts_res_failed; + __u64 ndts_lookups; + __u64 ndts_hits; + __u64 ndts_rcv_probes_mcast; + __u64 ndts_rcv_probes_ucast; + __u64 ndts_periodic_gc_runs; + __u64 ndts_forced_gc_runs; + __u64 ndts_table_fulls; +}; + +enum { + NDTPA_UNSPEC, + NDTPA_IFINDEX, /* u32, unchangeable */ + NDTPA_REFCNT, /* u32, read-only */ + NDTPA_REACHABLE_TIME, /* u64, read-only, msecs */ + NDTPA_BASE_REACHABLE_TIME, /* u64, msecs */ + NDTPA_RETRANS_TIME, /* u64, msecs */ + NDTPA_GC_STALETIME, /* u64, msecs */ + NDTPA_DELAY_PROBE_TIME, /* u64, msecs */ + NDTPA_QUEUE_LEN, /* u32 */ + NDTPA_APP_PROBES, /* u32 */ + NDTPA_UCAST_PROBES, /* u32 */ + NDTPA_MCAST_PROBES, /* u32 */ + NDTPA_ANYCAST_DELAY, /* u64, msecs */ + NDTPA_PROXY_DELAY, /* u64, msecs */ + NDTPA_PROXY_QLEN, /* u32 */ + NDTPA_LOCKTIME, /* u64, msecs */ + NDTPA_QUEUE_LENBYTES, /* u32 */ + NDTPA_MCAST_REPROBES, /* u32 */ + NDTPA_PAD, + __NDTPA_MAX +}; +#define NDTPA_MAX (__NDTPA_MAX - 1) + +struct ndtmsg { + __u8 ndtm_family; + __u8 ndtm_pad1; + __u16 ndtm_pad2; +}; + +struct ndt_config { + __u16 ndtc_key_len; + __u16 ndtc_entry_size; + __u32 ndtc_entries; + __u32 ndtc_last_flush; /* delta to now in msecs */ + __u32 ndtc_last_rand; /* delta to now in msecs */ + __u32 ndtc_hash_rnd; + __u32 ndtc_hash_mask; + __u32 ndtc_hash_chain_gc; + __u32 ndtc_proxy_qlen; +}; + +enum { + NDTA_UNSPEC, + NDTA_NAME, /* char *, unchangeable */ + NDTA_THRESH1, /* u32 */ + NDTA_THRESH2, /* u32 */ + NDTA_THRESH3, /* u32 */ + NDTA_CONFIG, /* struct ndt_config, read-only */ + NDTA_PARMS, /* nested TLV NDTPA_* */ + NDTA_STATS, /* struct ndt_stats, read-only */ + NDTA_GC_INTERVAL, /* u64, msecs */ + NDTA_PAD, + __NDTA_MAX +}; +#define NDTA_MAX (__NDTA_MAX - 1) + +#endif diff --git a/libnl/include/linux-private/linux/netconf.h b/libnl/include/linux-private/linux/netconf.h new file mode 100644 index 0000000..229e885 --- /dev/null +++ b/libnl/include/linux-private/linux/netconf.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_NETCONF_H_ +#define _LINUX_NETCONF_H_ + +#include +#include + +struct netconfmsg { + __u8 ncm_family; +}; + +enum { + NETCONFA_UNSPEC, + NETCONFA_IFINDEX, + NETCONFA_FORWARDING, + NETCONFA_RP_FILTER, + NETCONFA_MC_FORWARDING, + NETCONFA_PROXY_NEIGH, + NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, + NETCONFA_INPUT, + NETCONFA_BC_FORWARDING, + __NETCONFA_MAX +}; +#define NETCONFA_MAX (__NETCONFA_MAX - 1) +#define NETCONFA_ALL -1 + +#define NETCONFA_IFINDEX_ALL -1 +#define NETCONFA_IFINDEX_DEFAULT -2 + +#endif /* _LINUX_NETCONF_H_ */ diff --git a/libnl/include/linux-private/linux/netfilter.h b/libnl/include/linux-private/linux/netfilter.h new file mode 100644 index 0000000..36378a0 --- /dev/null +++ b/libnl/include/linux-private/linux/netfilter.h @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_NETFILTER_H +#define __LINUX_NETFILTER_H + +#include + +#include +#include + +/* Responses from hook functions. */ +#define NF_DROP 0 +#define NF_ACCEPT 1 +#define NF_STOLEN 2 +#define NF_QUEUE 3 +#define NF_REPEAT 4 +#define NF_STOP 5 /* Deprecated, for userspace nf_queue compatibility. */ +#define NF_MAX_VERDICT NF_STOP + +/* we overload the higher bits for encoding auxiliary data such as the queue + * number or errno values. Not nice, but better than additional function + * arguments. */ +#define NF_VERDICT_MASK 0x000000ff + +/* extra verdict flags have mask 0x0000ff00 */ +#define NF_VERDICT_FLAG_QUEUE_BYPASS 0x00008000 + +/* queue number (NF_QUEUE) or errno (NF_DROP) */ +#define NF_VERDICT_QMASK 0xffff0000 +#define NF_VERDICT_QBITS 16 + +#define NF_QUEUE_NR(x) ((((x) << 16) & NF_VERDICT_QMASK) | NF_QUEUE) + +#define NF_DROP_ERR(x) (((-x) << 16) | NF_DROP) + +/* only for userspace compatibility */ +/* Generic cache responses from hook functions. + <= 0x2000 is used for protocol-flags. */ +#define NFC_UNKNOWN 0x4000 +#define NFC_ALTERED 0x8000 + +/* NF_VERDICT_BITS should be 8 now, but userspace might break if this changes */ +#define NF_VERDICT_BITS 16 + +enum nf_inet_hooks { + NF_INET_PRE_ROUTING, + NF_INET_LOCAL_IN, + NF_INET_FORWARD, + NF_INET_LOCAL_OUT, + NF_INET_POST_ROUTING, + NF_INET_NUMHOOKS +}; + +enum nf_dev_hooks { + NF_NETDEV_INGRESS, + NF_NETDEV_NUMHOOKS +}; + +enum { + NFPROTO_UNSPEC = 0, + NFPROTO_INET = 1, + NFPROTO_IPV4 = 2, + NFPROTO_ARP = 3, + NFPROTO_NETDEV = 5, + NFPROTO_BRIDGE = 7, + NFPROTO_IPV6 = 10, + NFPROTO_DECNET = 12, + NFPROTO_NUMPROTO, +}; + +union nf_inet_addr { + __u32 all[4]; + __be32 ip; + __be32 ip6[4]; + struct in_addr in; + struct in6_addr in6; +}; + +#endif /* __LINUX_NETFILTER_H */ diff --git a/libnl/include/linux-private/linux/netfilter/nf_conntrack_common.h b/libnl/include/linux-private/linux/netfilter/nf_conntrack_common.h new file mode 100644 index 0000000..dc374c9 --- /dev/null +++ b/libnl/include/linux-private/linux/netfilter/nf_conntrack_common.h @@ -0,0 +1,142 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _NF_CONNTRACK_COMMON_H +#define _NF_CONNTRACK_COMMON_H +/* Connection state tracking for netfilter. This is separated from, + but required by, the NAT layer; it can also be used by an iptables + extension. */ +enum ip_conntrack_info { + /* Part of an established connection (either direction). */ + IP_CT_ESTABLISHED, + + /* Like NEW, but related to an existing connection, or ICMP error + (in either direction). */ + IP_CT_RELATED, + + /* Started a new connection to track (only + IP_CT_DIR_ORIGINAL); may be a retransmission. */ + IP_CT_NEW, + + /* >= this indicates reply direction */ + IP_CT_IS_REPLY, + + IP_CT_ESTABLISHED_REPLY = IP_CT_ESTABLISHED + IP_CT_IS_REPLY, + IP_CT_RELATED_REPLY = IP_CT_RELATED + IP_CT_IS_REPLY, + /* No NEW in reply direction. */ + + /* Number of distinct IP_CT types. */ + IP_CT_NUMBER, + + /* only for userspace compatibility */ + IP_CT_NEW_REPLY = IP_CT_NUMBER, +}; + +#define NF_CT_STATE_INVALID_BIT (1 << 0) +#define NF_CT_STATE_BIT(ctinfo) (1 << ((ctinfo) % IP_CT_IS_REPLY + 1)) +#define NF_CT_STATE_UNTRACKED_BIT (1 << 6) + +/* Bitset representing status of connection. */ +enum ip_conntrack_status { + /* It's an expected connection: bit 0 set. This bit never changed */ + IPS_EXPECTED_BIT = 0, + IPS_EXPECTED = (1 << IPS_EXPECTED_BIT), + + /* We've seen packets both ways: bit 1 set. Can be set, not unset. */ + IPS_SEEN_REPLY_BIT = 1, + IPS_SEEN_REPLY = (1 << IPS_SEEN_REPLY_BIT), + + /* Conntrack should never be early-expired. */ + IPS_ASSURED_BIT = 2, + IPS_ASSURED = (1 << IPS_ASSURED_BIT), + + /* Connection is confirmed: originating packet has left box */ + IPS_CONFIRMED_BIT = 3, + IPS_CONFIRMED = (1 << IPS_CONFIRMED_BIT), + + /* Connection needs src nat in orig dir. This bit never changed. */ + IPS_SRC_NAT_BIT = 4, + IPS_SRC_NAT = (1 << IPS_SRC_NAT_BIT), + + /* Connection needs dst nat in orig dir. This bit never changed. */ + IPS_DST_NAT_BIT = 5, + IPS_DST_NAT = (1 << IPS_DST_NAT_BIT), + + /* Both together. */ + IPS_NAT_MASK = (IPS_DST_NAT | IPS_SRC_NAT), + + /* Connection needs TCP sequence adjusted. */ + IPS_SEQ_ADJUST_BIT = 6, + IPS_SEQ_ADJUST = (1 << IPS_SEQ_ADJUST_BIT), + + /* NAT initialization bits. */ + IPS_SRC_NAT_DONE_BIT = 7, + IPS_SRC_NAT_DONE = (1 << IPS_SRC_NAT_DONE_BIT), + + IPS_DST_NAT_DONE_BIT = 8, + IPS_DST_NAT_DONE = (1 << IPS_DST_NAT_DONE_BIT), + + /* Both together */ + IPS_NAT_DONE_MASK = (IPS_DST_NAT_DONE | IPS_SRC_NAT_DONE), + + /* Connection is dying (removed from lists), can not be unset. */ + IPS_DYING_BIT = 9, + IPS_DYING = (1 << IPS_DYING_BIT), + + /* Connection has fixed timeout. */ + IPS_FIXED_TIMEOUT_BIT = 10, + IPS_FIXED_TIMEOUT = (1 << IPS_FIXED_TIMEOUT_BIT), + + /* Conntrack is a template */ + IPS_TEMPLATE_BIT = 11, + IPS_TEMPLATE = (1 << IPS_TEMPLATE_BIT), + + /* Conntrack is a fake untracked entry. Obsolete and not used anymore */ + IPS_UNTRACKED_BIT = 12, + IPS_UNTRACKED = (1 << IPS_UNTRACKED_BIT), + + /* Conntrack got a helper explicitly attached via CT target. */ + IPS_HELPER_BIT = 13, + IPS_HELPER = (1 << IPS_HELPER_BIT), + + /* Conntrack has been offloaded to flow table. */ + IPS_OFFLOAD_BIT = 14, + IPS_OFFLOAD = (1 << IPS_OFFLOAD_BIT), + + /* Be careful here, modifying these bits can make things messy, + * so don't let users modify them directly. + */ + IPS_UNCHANGEABLE_MASK = (IPS_NAT_DONE_MASK | IPS_NAT_MASK | + IPS_EXPECTED | IPS_CONFIRMED | IPS_DYING | + IPS_SEQ_ADJUST | IPS_TEMPLATE | IPS_OFFLOAD), + + __IPS_MAX_BIT = 15, +}; + +/* Connection tracking event types */ +enum ip_conntrack_events { + IPCT_NEW, /* new conntrack */ + IPCT_RELATED, /* related conntrack */ + IPCT_DESTROY, /* destroyed conntrack */ + IPCT_REPLY, /* connection has seen two-way traffic */ + IPCT_ASSURED, /* connection status has changed to assured */ + IPCT_PROTOINFO, /* protocol information has changed */ + IPCT_HELPER, /* new helper has been set */ + IPCT_MARK, /* new mark has been set */ + IPCT_SEQADJ, /* sequence adjustment has changed */ + IPCT_NATSEQADJ = IPCT_SEQADJ, + IPCT_SECMARK, /* new security mark has been set */ + IPCT_LABEL, /* new connlabel has been set */ + IPCT_SYNPROXY, /* synproxy has been set */ +}; + +enum ip_conntrack_expect_events { + IPEXP_NEW, /* new expectation */ + IPEXP_DESTROY, /* destroyed expectation */ +}; + +/* expectation flags */ +#define NF_CT_EXPECT_PERMANENT 0x1 +#define NF_CT_EXPECT_INACTIVE 0x2 +#define NF_CT_EXPECT_USERSPACE 0x4 + + +#endif /* _NF_CONNTRACK_COMMON_H */ diff --git a/libnl/include/linux-private/linux/netfilter/nfnetlink.h b/libnl/include/linux-private/linux/netfilter/nfnetlink.h new file mode 100644 index 0000000..a89f3a5 --- /dev/null +++ b/libnl/include/linux-private/linux/netfilter/nfnetlink.h @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _NFNETLINK_H +#define _NFNETLINK_H +#include +#include + +enum nfnetlink_groups { + NFNLGRP_NONE, +#define NFNLGRP_NONE NFNLGRP_NONE + NFNLGRP_CONNTRACK_NEW, +#define NFNLGRP_CONNTRACK_NEW NFNLGRP_CONNTRACK_NEW + NFNLGRP_CONNTRACK_UPDATE, +#define NFNLGRP_CONNTRACK_UPDATE NFNLGRP_CONNTRACK_UPDATE + NFNLGRP_CONNTRACK_DESTROY, +#define NFNLGRP_CONNTRACK_DESTROY NFNLGRP_CONNTRACK_DESTROY + NFNLGRP_CONNTRACK_EXP_NEW, +#define NFNLGRP_CONNTRACK_EXP_NEW NFNLGRP_CONNTRACK_EXP_NEW + NFNLGRP_CONNTRACK_EXP_UPDATE, +#define NFNLGRP_CONNTRACK_EXP_UPDATE NFNLGRP_CONNTRACK_EXP_UPDATE + NFNLGRP_CONNTRACK_EXP_DESTROY, +#define NFNLGRP_CONNTRACK_EXP_DESTROY NFNLGRP_CONNTRACK_EXP_DESTROY + NFNLGRP_NFTABLES, +#define NFNLGRP_NFTABLES NFNLGRP_NFTABLES + NFNLGRP_ACCT_QUOTA, +#define NFNLGRP_ACCT_QUOTA NFNLGRP_ACCT_QUOTA + NFNLGRP_NFTRACE, +#define NFNLGRP_NFTRACE NFNLGRP_NFTRACE + __NFNLGRP_MAX, +}; +#define NFNLGRP_MAX (__NFNLGRP_MAX - 1) + +/* General form of address family dependent message. + */ +struct nfgenmsg { + __u8 nfgen_family; /* AF_xxx */ + __u8 version; /* nfnetlink version */ + __be16 res_id; /* resource id */ +}; + +#define NFNETLINK_V0 0 + +/* netfilter netlink message types are split in two pieces: + * 8 bit subsystem, 8bit operation. + */ + +#define NFNL_SUBSYS_ID(x) ((x & 0xff00) >> 8) +#define NFNL_MSG_TYPE(x) (x & 0x00ff) + +/* No enum here, otherwise __stringify() trick of MODULE_ALIAS_NFNL_SUBSYS() + * won't work anymore */ +#define NFNL_SUBSYS_NONE 0 +#define NFNL_SUBSYS_CTNETLINK 1 +#define NFNL_SUBSYS_CTNETLINK_EXP 2 +#define NFNL_SUBSYS_QUEUE 3 +#define NFNL_SUBSYS_ULOG 4 +#define NFNL_SUBSYS_OSF 5 +#define NFNL_SUBSYS_IPSET 6 +#define NFNL_SUBSYS_ACCT 7 +#define NFNL_SUBSYS_CTNETLINK_TIMEOUT 8 +#define NFNL_SUBSYS_CTHELPER 9 +#define NFNL_SUBSYS_NFTABLES 10 +#define NFNL_SUBSYS_NFT_COMPAT 11 +#define NFNL_SUBSYS_COUNT 12 + +/* Reserved control nfnetlink messages */ +#define NFNL_MSG_BATCH_BEGIN NLMSG_MIN_TYPE +#define NFNL_MSG_BATCH_END NLMSG_MIN_TYPE+1 + +/** + * enum nfnl_batch_attributes - nfnetlink batch netlink attributes + * + * @NFNL_BATCH_GENID: generation ID for this changeset (NLA_U32) + */ +enum nfnl_batch_attributes { + NFNL_BATCH_UNSPEC, + NFNL_BATCH_GENID, + __NFNL_BATCH_MAX +}; +#define NFNL_BATCH_MAX (__NFNL_BATCH_MAX - 1) + +#endif /* _NFNETLINK_H */ diff --git a/libnl/include/linux-private/linux/netfilter/nfnetlink_compat.h b/libnl/include/linux-private/linux/netfilter/nfnetlink_compat.h new file mode 100644 index 0000000..ead7161 --- /dev/null +++ b/libnl/include/linux-private/linux/netfilter/nfnetlink_compat.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _NFNETLINK_COMPAT_H +#define _NFNETLINK_COMPAT_H + +#include + +/* Old nfnetlink macros for userspace */ + +/* nfnetlink groups: Up to 32 maximum */ +#define NF_NETLINK_CONNTRACK_NEW 0x00000001 +#define NF_NETLINK_CONNTRACK_UPDATE 0x00000002 +#define NF_NETLINK_CONNTRACK_DESTROY 0x00000004 +#define NF_NETLINK_CONNTRACK_EXP_NEW 0x00000008 +#define NF_NETLINK_CONNTRACK_EXP_UPDATE 0x00000010 +#define NF_NETLINK_CONNTRACK_EXP_DESTROY 0x00000020 + +/* Generic structure for encapsulation optional netfilter information. + * It is reminiscent of sockaddr, but with sa_family replaced + * with attribute type. + * ! This should someday be put somewhere generic as now rtnetlink and + * ! nfnetlink use the same attributes methods. - J. Schulist. + */ + +struct nfattr { + __u16 nfa_len; + __u16 nfa_type; /* we use 15 bits for the type, and the highest + * bit to indicate whether the payload is nested */ +}; + +/* FIXME: Apart from NFNL_NFA_NESTED shamelessly copy and pasted from + * rtnetlink.h, it's time to put this in a generic file */ + +#define NFNL_NFA_NEST 0x8000 +#define NFA_TYPE(attr) ((attr)->nfa_type & 0x7fff) + +#define NFA_ALIGNTO 4 +#define NFA_ALIGN(len) (((len) + NFA_ALIGNTO - 1) & ~(NFA_ALIGNTO - 1)) +#define NFA_OK(nfa,len) ((len) > 0 && (nfa)->nfa_len >= sizeof(struct nfattr) \ + && (nfa)->nfa_len <= (len)) +#define NFA_NEXT(nfa,attrlen) ((attrlen) -= NFA_ALIGN((nfa)->nfa_len), \ + (struct nfattr *)(((char *)(nfa)) + NFA_ALIGN((nfa)->nfa_len))) +#define NFA_LENGTH(len) (NFA_ALIGN(sizeof(struct nfattr)) + (len)) +#define NFA_SPACE(len) NFA_ALIGN(NFA_LENGTH(len)) +#define NFA_DATA(nfa) ((void *)(((char *)(nfa)) + NFA_LENGTH(0))) +#define NFA_PAYLOAD(nfa) ((int)((nfa)->nfa_len) - NFA_LENGTH(0)) +#define NFA_NEST(skb, type) \ +({ struct nfattr *__start = (struct nfattr *)skb_tail_pointer(skb); \ + NFA_PUT(skb, (NFNL_NFA_NEST | type), 0, NULL); \ + __start; }) +#define NFA_NEST_END(skb, start) \ +({ (start)->nfa_len = skb_tail_pointer(skb) - (unsigned char *)(start); \ + (skb)->len; }) +#define NFA_NEST_CANCEL(skb, start) \ +({ if (start) \ + skb_trim(skb, (unsigned char *) (start) - (skb)->data); \ + -1; }) + +#define NFM_NFA(n) ((struct nfattr *)(((char *)(n)) \ + + NLMSG_ALIGN(sizeof(struct nfgenmsg)))) +#define NFM_PAYLOAD(n) NLMSG_PAYLOAD(n, sizeof(struct nfgenmsg)) + +#endif /* _NFNETLINK_COMPAT_H */ diff --git a/libnl/include/linux-private/linux/netfilter/nfnetlink_conntrack.h b/libnl/include/linux-private/linux/netfilter/nfnetlink_conntrack.h new file mode 100644 index 0000000..1d41810 --- /dev/null +++ b/libnl/include/linux-private/linux/netfilter/nfnetlink_conntrack.h @@ -0,0 +1,279 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _IPCONNTRACK_NETLINK_H +#define _IPCONNTRACK_NETLINK_H +#include + +enum cntl_msg_types { + IPCTNL_MSG_CT_NEW, + IPCTNL_MSG_CT_GET, + IPCTNL_MSG_CT_DELETE, + IPCTNL_MSG_CT_GET_CTRZERO, + IPCTNL_MSG_CT_GET_STATS_CPU, + IPCTNL_MSG_CT_GET_STATS, + IPCTNL_MSG_CT_GET_DYING, + IPCTNL_MSG_CT_GET_UNCONFIRMED, + + IPCTNL_MSG_MAX +}; + +enum ctnl_exp_msg_types { + IPCTNL_MSG_EXP_NEW, + IPCTNL_MSG_EXP_GET, + IPCTNL_MSG_EXP_DELETE, + IPCTNL_MSG_EXP_GET_STATS_CPU, + + IPCTNL_MSG_EXP_MAX +}; + + +enum ctattr_type { + CTA_UNSPEC, + CTA_TUPLE_ORIG, + CTA_TUPLE_REPLY, + CTA_STATUS, + CTA_PROTOINFO, + CTA_HELP, + CTA_NAT_SRC, +#define CTA_NAT CTA_NAT_SRC /* backwards compatibility */ + CTA_TIMEOUT, + CTA_MARK, + CTA_COUNTERS_ORIG, + CTA_COUNTERS_REPLY, + CTA_USE, + CTA_ID, + CTA_NAT_DST, + CTA_TUPLE_MASTER, + CTA_SEQ_ADJ_ORIG, + CTA_NAT_SEQ_ADJ_ORIG = CTA_SEQ_ADJ_ORIG, + CTA_SEQ_ADJ_REPLY, + CTA_NAT_SEQ_ADJ_REPLY = CTA_SEQ_ADJ_REPLY, + CTA_SECMARK, /* obsolete */ + CTA_ZONE, + CTA_SECCTX, + CTA_TIMESTAMP, + CTA_MARK_MASK, + CTA_LABELS, + CTA_LABELS_MASK, + CTA_SYNPROXY, + __CTA_MAX +}; +#define CTA_MAX (__CTA_MAX - 1) + +enum ctattr_tuple { + CTA_TUPLE_UNSPEC, + CTA_TUPLE_IP, + CTA_TUPLE_PROTO, + CTA_TUPLE_ZONE, + __CTA_TUPLE_MAX +}; +#define CTA_TUPLE_MAX (__CTA_TUPLE_MAX - 1) + +enum ctattr_ip { + CTA_IP_UNSPEC, + CTA_IP_V4_SRC, + CTA_IP_V4_DST, + CTA_IP_V6_SRC, + CTA_IP_V6_DST, + __CTA_IP_MAX +}; +#define CTA_IP_MAX (__CTA_IP_MAX - 1) + +enum ctattr_l4proto { + CTA_PROTO_UNSPEC, + CTA_PROTO_NUM, + CTA_PROTO_SRC_PORT, + CTA_PROTO_DST_PORT, + CTA_PROTO_ICMP_ID, + CTA_PROTO_ICMP_TYPE, + CTA_PROTO_ICMP_CODE, + CTA_PROTO_ICMPV6_ID, + CTA_PROTO_ICMPV6_TYPE, + CTA_PROTO_ICMPV6_CODE, + __CTA_PROTO_MAX +}; +#define CTA_PROTO_MAX (__CTA_PROTO_MAX - 1) + +enum ctattr_protoinfo { + CTA_PROTOINFO_UNSPEC, + CTA_PROTOINFO_TCP, + CTA_PROTOINFO_DCCP, + CTA_PROTOINFO_SCTP, + __CTA_PROTOINFO_MAX +}; +#define CTA_PROTOINFO_MAX (__CTA_PROTOINFO_MAX - 1) + +enum ctattr_protoinfo_tcp { + CTA_PROTOINFO_TCP_UNSPEC, + CTA_PROTOINFO_TCP_STATE, + CTA_PROTOINFO_TCP_WSCALE_ORIGINAL, + CTA_PROTOINFO_TCP_WSCALE_REPLY, + CTA_PROTOINFO_TCP_FLAGS_ORIGINAL, + CTA_PROTOINFO_TCP_FLAGS_REPLY, + __CTA_PROTOINFO_TCP_MAX +}; +#define CTA_PROTOINFO_TCP_MAX (__CTA_PROTOINFO_TCP_MAX - 1) + +enum ctattr_protoinfo_dccp { + CTA_PROTOINFO_DCCP_UNSPEC, + CTA_PROTOINFO_DCCP_STATE, + CTA_PROTOINFO_DCCP_ROLE, + CTA_PROTOINFO_DCCP_HANDSHAKE_SEQ, + CTA_PROTOINFO_DCCP_PAD, + __CTA_PROTOINFO_DCCP_MAX, +}; +#define CTA_PROTOINFO_DCCP_MAX (__CTA_PROTOINFO_DCCP_MAX - 1) + +enum ctattr_protoinfo_sctp { + CTA_PROTOINFO_SCTP_UNSPEC, + CTA_PROTOINFO_SCTP_STATE, + CTA_PROTOINFO_SCTP_VTAG_ORIGINAL, + CTA_PROTOINFO_SCTP_VTAG_REPLY, + __CTA_PROTOINFO_SCTP_MAX +}; +#define CTA_PROTOINFO_SCTP_MAX (__CTA_PROTOINFO_SCTP_MAX - 1) + +enum ctattr_counters { + CTA_COUNTERS_UNSPEC, + CTA_COUNTERS_PACKETS, /* 64bit counters */ + CTA_COUNTERS_BYTES, /* 64bit counters */ + CTA_COUNTERS32_PACKETS, /* old 32bit counters, unused */ + CTA_COUNTERS32_BYTES, /* old 32bit counters, unused */ + CTA_COUNTERS_PAD, + __CTA_COUNTERS_MAX +}; +#define CTA_COUNTERS_MAX (__CTA_COUNTERS_MAX - 1) + +enum ctattr_tstamp { + CTA_TIMESTAMP_UNSPEC, + CTA_TIMESTAMP_START, + CTA_TIMESTAMP_STOP, + CTA_TIMESTAMP_PAD, + __CTA_TIMESTAMP_MAX +}; +#define CTA_TIMESTAMP_MAX (__CTA_TIMESTAMP_MAX - 1) + +enum ctattr_nat { + CTA_NAT_UNSPEC, + CTA_NAT_V4_MINIP, +#define CTA_NAT_MINIP CTA_NAT_V4_MINIP + CTA_NAT_V4_MAXIP, +#define CTA_NAT_MAXIP CTA_NAT_V4_MAXIP + CTA_NAT_PROTO, + CTA_NAT_V6_MINIP, + CTA_NAT_V6_MAXIP, + __CTA_NAT_MAX +}; +#define CTA_NAT_MAX (__CTA_NAT_MAX - 1) + +enum ctattr_protonat { + CTA_PROTONAT_UNSPEC, + CTA_PROTONAT_PORT_MIN, + CTA_PROTONAT_PORT_MAX, + __CTA_PROTONAT_MAX +}; +#define CTA_PROTONAT_MAX (__CTA_PROTONAT_MAX - 1) + +enum ctattr_seqadj { + CTA_SEQADJ_UNSPEC, + CTA_SEQADJ_CORRECTION_POS, + CTA_SEQADJ_OFFSET_BEFORE, + CTA_SEQADJ_OFFSET_AFTER, + __CTA_SEQADJ_MAX +}; +#define CTA_SEQADJ_MAX (__CTA_SEQADJ_MAX - 1) + +enum ctattr_natseq { + CTA_NAT_SEQ_UNSPEC, + CTA_NAT_SEQ_CORRECTION_POS, + CTA_NAT_SEQ_OFFSET_BEFORE, + CTA_NAT_SEQ_OFFSET_AFTER, + __CTA_NAT_SEQ_MAX +}; +#define CTA_NAT_SEQ_MAX (__CTA_NAT_SEQ_MAX - 1) + +enum ctattr_synproxy { + CTA_SYNPROXY_UNSPEC, + CTA_SYNPROXY_ISN, + CTA_SYNPROXY_ITS, + CTA_SYNPROXY_TSOFF, + __CTA_SYNPROXY_MAX, +}; +#define CTA_SYNPROXY_MAX (__CTA_SYNPROXY_MAX - 1) + +enum ctattr_expect { + CTA_EXPECT_UNSPEC, + CTA_EXPECT_MASTER, + CTA_EXPECT_TUPLE, + CTA_EXPECT_MASK, + CTA_EXPECT_TIMEOUT, + CTA_EXPECT_ID, + CTA_EXPECT_HELP_NAME, + CTA_EXPECT_ZONE, + CTA_EXPECT_FLAGS, + CTA_EXPECT_CLASS, + CTA_EXPECT_NAT, + CTA_EXPECT_FN, + __CTA_EXPECT_MAX +}; +#define CTA_EXPECT_MAX (__CTA_EXPECT_MAX - 1) + +enum ctattr_expect_nat { + CTA_EXPECT_NAT_UNSPEC, + CTA_EXPECT_NAT_DIR, + CTA_EXPECT_NAT_TUPLE, + __CTA_EXPECT_NAT_MAX +}; +#define CTA_EXPECT_NAT_MAX (__CTA_EXPECT_NAT_MAX - 1) + +enum ctattr_help { + CTA_HELP_UNSPEC, + CTA_HELP_NAME, + CTA_HELP_INFO, + __CTA_HELP_MAX +}; +#define CTA_HELP_MAX (__CTA_HELP_MAX - 1) + +enum ctattr_secctx { + CTA_SECCTX_UNSPEC, + CTA_SECCTX_NAME, + __CTA_SECCTX_MAX +}; +#define CTA_SECCTX_MAX (__CTA_SECCTX_MAX - 1) + +enum ctattr_stats_cpu { + CTA_STATS_UNSPEC, + CTA_STATS_SEARCHED, /* no longer used */ + CTA_STATS_FOUND, + CTA_STATS_NEW, /* no longer used */ + CTA_STATS_INVALID, + CTA_STATS_IGNORE, + CTA_STATS_DELETE, /* no longer used */ + CTA_STATS_DELETE_LIST, /* no longer used */ + CTA_STATS_INSERT, + CTA_STATS_INSERT_FAILED, + CTA_STATS_DROP, + CTA_STATS_EARLY_DROP, + CTA_STATS_ERROR, + CTA_STATS_SEARCH_RESTART, + __CTA_STATS_MAX, +}; +#define CTA_STATS_MAX (__CTA_STATS_MAX - 1) + +enum ctattr_stats_global { + CTA_STATS_GLOBAL_UNSPEC, + CTA_STATS_GLOBAL_ENTRIES, + CTA_STATS_GLOBAL_MAX_ENTRIES, + __CTA_STATS_GLOBAL_MAX, +}; +#define CTA_STATS_GLOBAL_MAX (__CTA_STATS_GLOBAL_MAX - 1) + +enum ctattr_expect_stats { + CTA_STATS_EXP_UNSPEC, + CTA_STATS_EXP_NEW, + CTA_STATS_EXP_CREATE, + CTA_STATS_EXP_DELETE, + __CTA_STATS_EXP_MAX, +}; +#define CTA_STATS_EXP_MAX (__CTA_STATS_EXP_MAX - 1) + +#endif /* _IPCONNTRACK_NETLINK_H */ diff --git a/libnl/include/linux-private/linux/netfilter/nfnetlink_log.h b/libnl/include/linux-private/linux/netfilter/nfnetlink_log.h new file mode 100644 index 0000000..20983cb --- /dev/null +++ b/libnl/include/linux-private/linux/netfilter/nfnetlink_log.h @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _NFNETLINK_LOG_H +#define _NFNETLINK_LOG_H + +/* This file describes the netlink messages (i.e. 'protocol packets'), + * and not any kind of function definitions. It is shared between kernel and + * userspace. Don't put kernel specific stuff in here */ + +#include +#include + +enum nfulnl_msg_types { + NFULNL_MSG_PACKET, /* packet from kernel to userspace */ + NFULNL_MSG_CONFIG, /* connect to a particular queue */ + + NFULNL_MSG_MAX +}; + +struct nfulnl_msg_packet_hdr { + __be16 hw_protocol; /* hw protocol (network order) */ + __u8 hook; /* netfilter hook */ + __u8 _pad; +}; + +struct nfulnl_msg_packet_hw { + __be16 hw_addrlen; + __u16 _pad; + __u8 hw_addr[8]; +}; + +struct nfulnl_msg_packet_timestamp { + __aligned_be64 sec; + __aligned_be64 usec; +}; + +enum nfulnl_attr_type { + NFULA_UNSPEC, + NFULA_PACKET_HDR, + NFULA_MARK, /* __u32 nfmark */ + NFULA_TIMESTAMP, /* nfulnl_msg_packet_timestamp */ + NFULA_IFINDEX_INDEV, /* __u32 ifindex */ + NFULA_IFINDEX_OUTDEV, /* __u32 ifindex */ + NFULA_IFINDEX_PHYSINDEV, /* __u32 ifindex */ + NFULA_IFINDEX_PHYSOUTDEV, /* __u32 ifindex */ + NFULA_HWADDR, /* nfulnl_msg_packet_hw */ + NFULA_PAYLOAD, /* opaque data payload */ + NFULA_PREFIX, /* string prefix */ + NFULA_UID, /* user id of socket */ + NFULA_SEQ, /* instance-local sequence number */ + NFULA_SEQ_GLOBAL, /* global sequence number */ + NFULA_GID, /* group id of socket */ + NFULA_HWTYPE, /* hardware type */ + NFULA_HWHEADER, /* hardware header */ + NFULA_HWLEN, /* hardware header length */ + NFULA_CT, /* nf_conntrack_netlink.h */ + NFULA_CT_INFO, /* enum ip_conntrack_info */ + + __NFULA_MAX +}; +#define NFULA_MAX (__NFULA_MAX - 1) + +enum nfulnl_msg_config_cmds { + NFULNL_CFG_CMD_NONE, + NFULNL_CFG_CMD_BIND, + NFULNL_CFG_CMD_UNBIND, + NFULNL_CFG_CMD_PF_BIND, + NFULNL_CFG_CMD_PF_UNBIND, +}; + +struct nfulnl_msg_config_cmd { + __u8 command; /* nfulnl_msg_config_cmds */ +} __attribute__ ((packed)); + +struct nfulnl_msg_config_mode { + __be32 copy_range; + __u8 copy_mode; + __u8 _pad; +} __attribute__ ((packed)); + +enum nfulnl_attr_config { + NFULA_CFG_UNSPEC, + NFULA_CFG_CMD, /* nfulnl_msg_config_cmd */ + NFULA_CFG_MODE, /* nfulnl_msg_config_mode */ + NFULA_CFG_NLBUFSIZ, /* __u32 buffer size */ + NFULA_CFG_TIMEOUT, /* __u32 in 1/100 s */ + NFULA_CFG_QTHRESH, /* __u32 */ + NFULA_CFG_FLAGS, /* __u16 */ + __NFULA_CFG_MAX +}; +#define NFULA_CFG_MAX (__NFULA_CFG_MAX -1) + +#define NFULNL_COPY_NONE 0x00 +#define NFULNL_COPY_META 0x01 +#define NFULNL_COPY_PACKET 0x02 +/* 0xff is reserved, don't use it for new copy modes. */ + +#define NFULNL_CFG_F_SEQ 0x0001 +#define NFULNL_CFG_F_SEQ_GLOBAL 0x0002 +#define NFULNL_CFG_F_CONNTRACK 0x0004 + +#endif /* _NFNETLINK_LOG_H */ diff --git a/libnl/include/linux-private/linux/netfilter/nfnetlink_queue.h b/libnl/include/linux-private/linux/netfilter/nfnetlink_queue.h new file mode 100644 index 0000000..bcb2cb5 --- /dev/null +++ b/libnl/include/linux-private/linux/netfilter/nfnetlink_queue.h @@ -0,0 +1,128 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _NFNETLINK_QUEUE_H +#define _NFNETLINK_QUEUE_H + +#include +#include + +enum nfqnl_msg_types { + NFQNL_MSG_PACKET, /* packet from kernel to userspace */ + NFQNL_MSG_VERDICT, /* verdict from userspace to kernel */ + NFQNL_MSG_CONFIG, /* connect to a particular queue */ + NFQNL_MSG_VERDICT_BATCH, /* batchv from userspace to kernel */ + + NFQNL_MSG_MAX +}; + +struct nfqnl_msg_packet_hdr { + __be32 packet_id; /* unique ID of packet in queue */ + __be16 hw_protocol; /* hw protocol (network order) */ + __u8 hook; /* netfilter hook */ +} __attribute__ ((packed)); + +struct nfqnl_msg_packet_hw { + __be16 hw_addrlen; + __u16 _pad; + __u8 hw_addr[8]; +}; + +struct nfqnl_msg_packet_timestamp { + __aligned_be64 sec; + __aligned_be64 usec; +}; + +enum nfqnl_vlan_attr { + NFQA_VLAN_UNSPEC, + NFQA_VLAN_PROTO, /* __be16 skb vlan_proto */ + NFQA_VLAN_TCI, /* __be16 skb htons(vlan_tci) */ + __NFQA_VLAN_MAX, +}; +#define NFQA_VLAN_MAX (__NFQA_VLAN_MAX - 1) + +enum nfqnl_attr_type { + NFQA_UNSPEC, + NFQA_PACKET_HDR, + NFQA_VERDICT_HDR, /* nfqnl_msg_verdict_hrd */ + NFQA_MARK, /* __u32 nfmark */ + NFQA_TIMESTAMP, /* nfqnl_msg_packet_timestamp */ + NFQA_IFINDEX_INDEV, /* __u32 ifindex */ + NFQA_IFINDEX_OUTDEV, /* __u32 ifindex */ + NFQA_IFINDEX_PHYSINDEV, /* __u32 ifindex */ + NFQA_IFINDEX_PHYSOUTDEV, /* __u32 ifindex */ + NFQA_HWADDR, /* nfqnl_msg_packet_hw */ + NFQA_PAYLOAD, /* opaque data payload */ + NFQA_CT, /* nf_conntrack_netlink.h */ + NFQA_CT_INFO, /* enum ip_conntrack_info */ + NFQA_CAP_LEN, /* __u32 length of captured packet */ + NFQA_SKB_INFO, /* __u32 skb meta information */ + NFQA_EXP, /* nf_conntrack_netlink.h */ + NFQA_UID, /* __u32 sk uid */ + NFQA_GID, /* __u32 sk gid */ + NFQA_SECCTX, /* security context string */ + NFQA_VLAN, /* nested attribute: packet vlan info */ + NFQA_L2HDR, /* full L2 header */ + + __NFQA_MAX +}; +#define NFQA_MAX (__NFQA_MAX - 1) + +struct nfqnl_msg_verdict_hdr { + __be32 verdict; + __be32 id; +}; + + +enum nfqnl_msg_config_cmds { + NFQNL_CFG_CMD_NONE, + NFQNL_CFG_CMD_BIND, + NFQNL_CFG_CMD_UNBIND, + NFQNL_CFG_CMD_PF_BIND, + NFQNL_CFG_CMD_PF_UNBIND, +}; + +struct nfqnl_msg_config_cmd { + __u8 command; /* nfqnl_msg_config_cmds */ + __u8 _pad; + __be16 pf; /* AF_xxx for PF_[UN]BIND */ +}; + +enum nfqnl_config_mode { + NFQNL_COPY_NONE, + NFQNL_COPY_META, + NFQNL_COPY_PACKET, +}; + +struct nfqnl_msg_config_params { + __be32 copy_range; + __u8 copy_mode; /* enum nfqnl_config_mode */ +} __attribute__ ((packed)); + + +enum nfqnl_attr_config { + NFQA_CFG_UNSPEC, + NFQA_CFG_CMD, /* nfqnl_msg_config_cmd */ + NFQA_CFG_PARAMS, /* nfqnl_msg_config_params */ + NFQA_CFG_QUEUE_MAXLEN, /* __u32 */ + NFQA_CFG_MASK, /* identify which flags to change */ + NFQA_CFG_FLAGS, /* value of these flags (__u32) */ + __NFQA_CFG_MAX +}; +#define NFQA_CFG_MAX (__NFQA_CFG_MAX-1) + +/* Flags for NFQA_CFG_FLAGS */ +#define NFQA_CFG_F_FAIL_OPEN (1 << 0) +#define NFQA_CFG_F_CONNTRACK (1 << 1) +#define NFQA_CFG_F_GSO (1 << 2) +#define NFQA_CFG_F_UID_GID (1 << 3) +#define NFQA_CFG_F_SECCTX (1 << 4) +#define NFQA_CFG_F_MAX (1 << 5) + +/* flags for NFQA_SKB_INFO */ +/* packet appears to have wrong checksums, but they are ok */ +#define NFQA_SKB_CSUMNOTREADY (1 << 0) +/* packet is GSO (i.e., exceeds device mtu) */ +#define NFQA_SKB_GSO (1 << 1) +/* csum not validated (incoming device doesn't support hw checksum, etc.) */ +#define NFQA_SKB_CSUM_NOTVERIFIED (1 << 2) + +#endif /* _NFNETLINK_QUEUE_H */ diff --git a/libnl/include/linux-private/linux/netlink.h b/libnl/include/linux-private/linux/netlink.h new file mode 100644 index 0000000..0b2c29b --- /dev/null +++ b/libnl/include/linux-private/linux/netlink.h @@ -0,0 +1,247 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_NETLINK_H +#define __LINUX_NETLINK_H + +#include +#include /* for __kernel_sa_family_t */ +#include + +#define NETLINK_ROUTE 0 /* Routing/device hook */ +#define NETLINK_UNUSED 1 /* Unused number */ +#define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */ +#define NETLINK_FIREWALL 3 /* Unused number, formerly ip_queue */ +#define NETLINK_SOCK_DIAG 4 /* socket monitoring */ +#define NETLINK_NFLOG 5 /* netfilter/iptables ULOG */ +#define NETLINK_XFRM 6 /* ipsec */ +#define NETLINK_SELINUX 7 /* SELinux event notifications */ +#define NETLINK_ISCSI 8 /* Open-iSCSI */ +#define NETLINK_AUDIT 9 /* auditing */ +#define NETLINK_FIB_LOOKUP 10 +#define NETLINK_CONNECTOR 11 +#define NETLINK_NETFILTER 12 /* netfilter subsystem */ +#define NETLINK_IP6_FW 13 +#define NETLINK_DNRTMSG 14 /* DECnet routing messages */ +#define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace */ +#define NETLINK_GENERIC 16 +/* leave room for NETLINK_DM (DM Events) */ +#define NETLINK_SCSITRANSPORT 18 /* SCSI Transports */ +#define NETLINK_ECRYPTFS 19 +#define NETLINK_RDMA 20 +#define NETLINK_CRYPTO 21 /* Crypto layer */ +#define NETLINK_SMC 22 /* SMC monitoring */ + +#define NETLINK_INET_DIAG NETLINK_SOCK_DIAG + +#define MAX_LINKS 32 + +struct sockaddr_nl { + __kernel_sa_family_t nl_family; /* AF_NETLINK */ + unsigned short nl_pad; /* zero */ + __u32 nl_pid; /* port ID */ + __u32 nl_groups; /* multicast groups mask */ +}; + +struct nlmsghdr { + __u32 nlmsg_len; /* Length of message including header */ + __u16 nlmsg_type; /* Message content */ + __u16 nlmsg_flags; /* Additional flags */ + __u32 nlmsg_seq; /* Sequence number */ + __u32 nlmsg_pid; /* Sending process port ID */ +}; + +/* Flags values */ + +#define NLM_F_REQUEST 0x01 /* It is request message. */ +#define NLM_F_MULTI 0x02 /* Multipart message, terminated by NLMSG_DONE */ +#define NLM_F_ACK 0x04 /* Reply with ack, with zero or error code */ +#define NLM_F_ECHO 0x08 /* Echo this request */ +#define NLM_F_DUMP_INTR 0x10 /* Dump was inconsistent due to sequence change */ +#define NLM_F_DUMP_FILTERED 0x20 /* Dump was filtered as requested */ + +/* Modifiers to GET request */ +#define NLM_F_ROOT 0x100 /* specify tree root */ +#define NLM_F_MATCH 0x200 /* return all matching */ +#define NLM_F_ATOMIC 0x400 /* atomic GET */ +#define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH) + +/* Modifiers to NEW request */ +#define NLM_F_REPLACE 0x100 /* Override existing */ +#define NLM_F_EXCL 0x200 /* Do not touch, if it exists */ +#define NLM_F_CREATE 0x400 /* Create, if it does not exist */ +#define NLM_F_APPEND 0x800 /* Add to end of list */ + +/* Modifiers to DELETE request */ +#define NLM_F_NONREC 0x100 /* Do not delete recursively */ + +/* Flags for ACK message */ +#define NLM_F_CAPPED 0x100 /* request was capped */ +#define NLM_F_ACK_TLVS 0x200 /* extended ACK TVLs were included */ + +/* + 4.4BSD ADD NLM_F_CREATE|NLM_F_EXCL + 4.4BSD CHANGE NLM_F_REPLACE + + True CHANGE NLM_F_CREATE|NLM_F_REPLACE + Append NLM_F_CREATE + Check NLM_F_EXCL + */ + +#define NLMSG_ALIGNTO 4U +#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) ) +#define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr))) +#define NLMSG_LENGTH(len) ((len) + NLMSG_HDRLEN) +#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len)) +#define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0))) +#define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \ + (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len))) +#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \ + (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \ + (nlh)->nlmsg_len <= (len)) +#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len))) + +#define NLMSG_NOOP 0x1 /* Nothing. */ +#define NLMSG_ERROR 0x2 /* Error */ +#define NLMSG_DONE 0x3 /* End of a dump */ +#define NLMSG_OVERRUN 0x4 /* Data lost */ + +#define NLMSG_MIN_TYPE 0x10 /* < 0x10: reserved control messages */ + +struct nlmsgerr { + int error; + struct nlmsghdr msg; + /* + * followed by the message contents unless NETLINK_CAP_ACK was set + * or the ACK indicates success (error == 0) + * message length is aligned with NLMSG_ALIGN() + */ + /* + * followed by TLVs defined in enum nlmsgerr_attrs + * if NETLINK_EXT_ACK was set + */ +}; + +/** + * enum nlmsgerr_attrs - nlmsgerr attributes + * @NLMSGERR_ATTR_UNUSED: unused + * @NLMSGERR_ATTR_MSG: error message string (string) + * @NLMSGERR_ATTR_OFFS: offset of the invalid attribute in the original + * message, counting from the beginning of the header (u32) + * @NLMSGERR_ATTR_COOKIE: arbitrary subsystem specific cookie to + * be used - in the success case - to identify a created + * object or operation or similar (binary) + * @__NLMSGERR_ATTR_MAX: number of attributes + * @NLMSGERR_ATTR_MAX: highest attribute number + */ +enum nlmsgerr_attrs { + NLMSGERR_ATTR_UNUSED, + NLMSGERR_ATTR_MSG, + NLMSGERR_ATTR_OFFS, + NLMSGERR_ATTR_COOKIE, + + __NLMSGERR_ATTR_MAX, + NLMSGERR_ATTR_MAX = __NLMSGERR_ATTR_MAX - 1 +}; + +#define NETLINK_ADD_MEMBERSHIP 1 +#define NETLINK_DROP_MEMBERSHIP 2 +#define NETLINK_PKTINFO 3 +#define NETLINK_BROADCAST_ERROR 4 +#define NETLINK_NO_ENOBUFS 5 +#define NETLINK_RX_RING 6 +#define NETLINK_TX_RING 7 +#define NETLINK_LISTEN_ALL_NSID 8 +#define NETLINK_LIST_MEMBERSHIPS 9 +#define NETLINK_CAP_ACK 10 +#define NETLINK_EXT_ACK 11 + +struct nl_pktinfo { + __u32 group; +}; + +struct nl_mmap_req { + unsigned int nm_block_size; + unsigned int nm_block_nr; + unsigned int nm_frame_size; + unsigned int nm_frame_nr; +}; + +struct nl_mmap_hdr { + unsigned int nm_status; + unsigned int nm_len; + __u32 nm_group; + /* credentials */ + __u32 nm_pid; + __u32 nm_uid; + __u32 nm_gid; +}; + +enum nl_mmap_status { + NL_MMAP_STATUS_UNUSED, + NL_MMAP_STATUS_RESERVED, + NL_MMAP_STATUS_VALID, + NL_MMAP_STATUS_COPY, + NL_MMAP_STATUS_SKIP, +}; + +#define NL_MMAP_MSG_ALIGNMENT NLMSG_ALIGNTO +#define NL_MMAP_MSG_ALIGN(sz) __ALIGN_KERNEL(sz, NL_MMAP_MSG_ALIGNMENT) +#define NL_MMAP_HDRLEN NL_MMAP_MSG_ALIGN(sizeof(struct nl_mmap_hdr)) + +#define NET_MAJOR 36 /* Major 36 is reserved for networking */ + +enum { + NETLINK_UNCONNECTED = 0, + NETLINK_CONNECTED, +}; + +/* + * <------- NLA_HDRLEN ------> <-- NLA_ALIGN(payload)--> + * +---------------------+- - -+- - - - - - - - - -+- - -+ + * | Header | Pad | Payload | Pad | + * | (struct nlattr) | ing | | ing | + * +---------------------+- - -+- - - - - - - - - -+- - -+ + * <-------------- nlattr->nla_len --------------> + */ + +struct nlattr { + __u16 nla_len; + __u16 nla_type; +}; + +/* + * nla_type (16 bits) + * +---+---+-------------------------------+ + * | N | O | Attribute Type | + * +---+---+-------------------------------+ + * N := Carries nested attributes + * O := Payload stored in network byte order + * + * Note: The N and O flag are mutually exclusive. + */ +#define NLA_F_NESTED (1 << 15) +#define NLA_F_NET_BYTEORDER (1 << 14) +#define NLA_TYPE_MASK ~(NLA_F_NESTED | NLA_F_NET_BYTEORDER) + +#define NLA_ALIGNTO 4 +#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1)) +#define NLA_HDRLEN ((int) NLA_ALIGN(sizeof(struct nlattr))) + +/* Generic 32 bitflags attribute content sent to the kernel. + * + * The value is a bitmap that defines the values being set + * The selector is a bitmask that defines which value is legit + * + * Examples: + * value = 0x0, and selector = 0x1 + * implies we are selecting bit 1 and we want to set its value to 0. + * + * value = 0x2, and selector = 0x2 + * implies we are selecting bit 2 and we want to set its value to 1. + * + */ +struct nla_bitfield32 { + __u32 value; + __u32 selector; +}; + +#endif /* __LINUX_NETLINK_H */ diff --git a/libnl/include/linux-private/linux/pkt_cls.h b/libnl/include/linux-private/linux/pkt_cls.h new file mode 100644 index 0000000..be382fb --- /dev/null +++ b/libnl/include/linux-private/linux/pkt_cls.h @@ -0,0 +1,610 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_PKT_CLS_H +#define __LINUX_PKT_CLS_H + +#include +#include + +#define TC_COOKIE_MAX_SIZE 16 + +/* Action attributes */ +enum { + TCA_ACT_UNSPEC, + TCA_ACT_KIND, + TCA_ACT_OPTIONS, + TCA_ACT_INDEX, + TCA_ACT_STATS, + TCA_ACT_PAD, + TCA_ACT_COOKIE, + __TCA_ACT_MAX +}; + +#define TCA_ACT_MAX __TCA_ACT_MAX +#define TCA_OLD_COMPAT (TCA_ACT_MAX+1) +#define TCA_ACT_MAX_PRIO 32 +#define TCA_ACT_BIND 1 +#define TCA_ACT_NOBIND 0 +#define TCA_ACT_UNBIND 1 +#define TCA_ACT_NOUNBIND 0 +#define TCA_ACT_REPLACE 1 +#define TCA_ACT_NOREPLACE 0 + +#define TC_ACT_UNSPEC (-1) +#define TC_ACT_OK 0 +#define TC_ACT_RECLASSIFY 1 +#define TC_ACT_SHOT 2 +#define TC_ACT_PIPE 3 +#define TC_ACT_STOLEN 4 +#define TC_ACT_QUEUED 5 +#define TC_ACT_REPEAT 6 +#define TC_ACT_REDIRECT 7 +#define TC_ACT_TRAP 8 /* For hw path, this means "trap to cpu" + * and don't further process the frame + * in hardware. For sw path, this is + * equivalent of TC_ACT_STOLEN - drop + * the skb and act like everything + * is alright. + */ +#define TC_ACT_VALUE_MAX TC_ACT_TRAP + +/* There is a special kind of actions called "extended actions", + * which need a value parameter. These have a local opcode located in + * the highest nibble, starting from 1. The rest of the bits + * are used to carry the value. These two parts together make + * a combined opcode. + */ +#define __TC_ACT_EXT_SHIFT 28 +#define __TC_ACT_EXT(local) ((local) << __TC_ACT_EXT_SHIFT) +#define TC_ACT_EXT_VAL_MASK ((1 << __TC_ACT_EXT_SHIFT) - 1) +#define TC_ACT_EXT_OPCODE(combined) ((combined) & (~TC_ACT_EXT_VAL_MASK)) +#define TC_ACT_EXT_CMP(combined, opcode) (TC_ACT_EXT_OPCODE(combined) == opcode) + +#define TC_ACT_JUMP __TC_ACT_EXT(1) +#define TC_ACT_GOTO_CHAIN __TC_ACT_EXT(2) +#define TC_ACT_EXT_OPCODE_MAX TC_ACT_GOTO_CHAIN + +/* Action type identifiers*/ +enum { + TCA_ID_UNSPEC=0, + TCA_ID_POLICE=1, + /* other actions go here */ + __TCA_ID_MAX=255 +}; + +#define TCA_ID_MAX __TCA_ID_MAX + +struct tc_police { + __u32 index; + int action; +#define TC_POLICE_UNSPEC TC_ACT_UNSPEC +#define TC_POLICE_OK TC_ACT_OK +#define TC_POLICE_RECLASSIFY TC_ACT_RECLASSIFY +#define TC_POLICE_SHOT TC_ACT_SHOT +#define TC_POLICE_PIPE TC_ACT_PIPE + + __u32 limit; + __u32 burst; + __u32 mtu; + struct tc_ratespec rate; + struct tc_ratespec peakrate; + int refcnt; + int bindcnt; + __u32 capab; +}; + +struct tcf_t { + __u64 install; + __u64 lastuse; + __u64 expires; + __u64 firstuse; +}; + +struct tc_cnt { + int refcnt; + int bindcnt; +}; + +#define tc_gen \ + __u32 index; \ + __u32 capab; \ + int action; \ + int refcnt; \ + int bindcnt + +enum { + TCA_POLICE_UNSPEC, + TCA_POLICE_TBF, + TCA_POLICE_RATE, + TCA_POLICE_PEAKRATE, + TCA_POLICE_AVRATE, + TCA_POLICE_RESULT, + TCA_POLICE_TM, + TCA_POLICE_PAD, + __TCA_POLICE_MAX +#define TCA_POLICE_RESULT TCA_POLICE_RESULT +}; + +#define TCA_POLICE_MAX (__TCA_POLICE_MAX - 1) + +/* tca flags definitions */ +#define TCA_CLS_FLAGS_SKIP_HW (1 << 0) /* don't offload filter to HW */ +#define TCA_CLS_FLAGS_SKIP_SW (1 << 1) /* don't use filter in SW */ +#define TCA_CLS_FLAGS_IN_HW (1 << 2) /* filter is offloaded to HW */ +#define TCA_CLS_FLAGS_NOT_IN_HW (1 << 3) /* filter isn't offloaded to HW */ +#define TCA_CLS_FLAGS_VERBOSE (1 << 4) /* verbose logging */ + +/* U32 filters */ + +#define TC_U32_HTID(h) ((h)&0xFFF00000) +#define TC_U32_USERHTID(h) (TC_U32_HTID(h)>>20) +#define TC_U32_HASH(h) (((h)>>12)&0xFF) +#define TC_U32_NODE(h) ((h)&0xFFF) +#define TC_U32_KEY(h) ((h)&0xFFFFF) +#define TC_U32_UNSPEC 0 +#define TC_U32_ROOT (0xFFF00000) + +enum { + TCA_U32_UNSPEC, + TCA_U32_CLASSID, + TCA_U32_HASH, + TCA_U32_LINK, + TCA_U32_DIVISOR, + TCA_U32_SEL, + TCA_U32_POLICE, + TCA_U32_ACT, + TCA_U32_INDEV, + TCA_U32_PCNT, + TCA_U32_MARK, + TCA_U32_FLAGS, + TCA_U32_PAD, + __TCA_U32_MAX +}; + +#define TCA_U32_MAX (__TCA_U32_MAX - 1) + +struct tc_u32_key { + __be32 mask; + __be32 val; + int off; + int offmask; +}; + +struct tc_u32_sel { + unsigned char flags; + unsigned char offshift; + unsigned char nkeys; + + __be16 offmask; + __u16 off; + short offoff; + + short hoff; + __be32 hmask; + struct tc_u32_key keys[0]; +}; + +struct tc_u32_mark { + __u32 val; + __u32 mask; + __u32 success; +}; + +struct tc_u32_pcnt { + __u64 rcnt; + __u64 rhit; + __u64 kcnts[0]; +}; + +/* Flags */ + +#define TC_U32_TERMINAL 1 +#define TC_U32_OFFSET 2 +#define TC_U32_VAROFFSET 4 +#define TC_U32_EAT 8 + +#define TC_U32_MAXDEPTH 8 + + +/* RSVP filter */ + +enum { + TCA_RSVP_UNSPEC, + TCA_RSVP_CLASSID, + TCA_RSVP_DST, + TCA_RSVP_SRC, + TCA_RSVP_PINFO, + TCA_RSVP_POLICE, + TCA_RSVP_ACT, + __TCA_RSVP_MAX +}; + +#define TCA_RSVP_MAX (__TCA_RSVP_MAX - 1 ) + +struct tc_rsvp_gpi { + __u32 key; + __u32 mask; + int offset; +}; + +struct tc_rsvp_pinfo { + struct tc_rsvp_gpi dpi; + struct tc_rsvp_gpi spi; + __u8 protocol; + __u8 tunnelid; + __u8 tunnelhdr; + __u8 pad; +}; + +/* ROUTE filter */ + +enum { + TCA_ROUTE4_UNSPEC, + TCA_ROUTE4_CLASSID, + TCA_ROUTE4_TO, + TCA_ROUTE4_FROM, + TCA_ROUTE4_IIF, + TCA_ROUTE4_POLICE, + TCA_ROUTE4_ACT, + __TCA_ROUTE4_MAX +}; + +#define TCA_ROUTE4_MAX (__TCA_ROUTE4_MAX - 1) + + +/* FW filter */ + +enum { + TCA_FW_UNSPEC, + TCA_FW_CLASSID, + TCA_FW_POLICE, + TCA_FW_INDEV, /* used by CONFIG_NET_CLS_IND */ + TCA_FW_ACT, /* used by CONFIG_NET_CLS_ACT */ + TCA_FW_MASK, + __TCA_FW_MAX +}; + +#define TCA_FW_MAX (__TCA_FW_MAX - 1) + +/* TC index filter */ + +enum { + TCA_TCINDEX_UNSPEC, + TCA_TCINDEX_HASH, + TCA_TCINDEX_MASK, + TCA_TCINDEX_SHIFT, + TCA_TCINDEX_FALL_THROUGH, + TCA_TCINDEX_CLASSID, + TCA_TCINDEX_POLICE, + TCA_TCINDEX_ACT, + __TCA_TCINDEX_MAX +}; + +#define TCA_TCINDEX_MAX (__TCA_TCINDEX_MAX - 1) + +/* Flow filter */ + +enum { + FLOW_KEY_SRC, + FLOW_KEY_DST, + FLOW_KEY_PROTO, + FLOW_KEY_PROTO_SRC, + FLOW_KEY_PROTO_DST, + FLOW_KEY_IIF, + FLOW_KEY_PRIORITY, + FLOW_KEY_MARK, + FLOW_KEY_NFCT, + FLOW_KEY_NFCT_SRC, + FLOW_KEY_NFCT_DST, + FLOW_KEY_NFCT_PROTO_SRC, + FLOW_KEY_NFCT_PROTO_DST, + FLOW_KEY_RTCLASSID, + FLOW_KEY_SKUID, + FLOW_KEY_SKGID, + FLOW_KEY_VLAN_TAG, + FLOW_KEY_RXHASH, + __FLOW_KEY_MAX, +}; + +#define FLOW_KEY_MAX (__FLOW_KEY_MAX - 1) + +enum { + FLOW_MODE_MAP, + FLOW_MODE_HASH, +}; + +enum { + TCA_FLOW_UNSPEC, + TCA_FLOW_KEYS, + TCA_FLOW_MODE, + TCA_FLOW_BASECLASS, + TCA_FLOW_RSHIFT, + TCA_FLOW_ADDEND, + TCA_FLOW_MASK, + TCA_FLOW_XOR, + TCA_FLOW_DIVISOR, + TCA_FLOW_ACT, + TCA_FLOW_POLICE, + TCA_FLOW_EMATCHES, + TCA_FLOW_PERTURB, + __TCA_FLOW_MAX +}; + +#define TCA_FLOW_MAX (__TCA_FLOW_MAX - 1) + +/* Basic filter */ + +enum { + TCA_BASIC_UNSPEC, + TCA_BASIC_CLASSID, + TCA_BASIC_EMATCHES, + TCA_BASIC_ACT, + TCA_BASIC_POLICE, + __TCA_BASIC_MAX +}; + +#define TCA_BASIC_MAX (__TCA_BASIC_MAX - 1) + + +/* Cgroup classifier */ + +enum { + TCA_CGROUP_UNSPEC, + TCA_CGROUP_ACT, + TCA_CGROUP_POLICE, + TCA_CGROUP_EMATCHES, + __TCA_CGROUP_MAX, +}; + +#define TCA_CGROUP_MAX (__TCA_CGROUP_MAX - 1) + +/* BPF classifier */ + +#define TCA_BPF_FLAG_ACT_DIRECT (1 << 0) + +enum { + TCA_BPF_UNSPEC, + TCA_BPF_ACT, + TCA_BPF_POLICE, + TCA_BPF_CLASSID, + TCA_BPF_OPS_LEN, + TCA_BPF_OPS, + TCA_BPF_FD, + TCA_BPF_NAME, + TCA_BPF_FLAGS, + TCA_BPF_FLAGS_GEN, + TCA_BPF_TAG, + TCA_BPF_ID, + __TCA_BPF_MAX, +}; + +#define TCA_BPF_MAX (__TCA_BPF_MAX - 1) + +/* Flower classifier */ + +enum { + TCA_FLOWER_UNSPEC, + TCA_FLOWER_CLASSID, + TCA_FLOWER_INDEV, + TCA_FLOWER_ACT, + TCA_FLOWER_KEY_ETH_DST, /* ETH_ALEN */ + TCA_FLOWER_KEY_ETH_DST_MASK, /* ETH_ALEN */ + TCA_FLOWER_KEY_ETH_SRC, /* ETH_ALEN */ + TCA_FLOWER_KEY_ETH_SRC_MASK, /* ETH_ALEN */ + TCA_FLOWER_KEY_ETH_TYPE, /* be16 */ + TCA_FLOWER_KEY_IP_PROTO, /* u8 */ + TCA_FLOWER_KEY_IPV4_SRC, /* be32 */ + TCA_FLOWER_KEY_IPV4_SRC_MASK, /* be32 */ + TCA_FLOWER_KEY_IPV4_DST, /* be32 */ + TCA_FLOWER_KEY_IPV4_DST_MASK, /* be32 */ + TCA_FLOWER_KEY_IPV6_SRC, /* struct in6_addr */ + TCA_FLOWER_KEY_IPV6_SRC_MASK, /* struct in6_addr */ + TCA_FLOWER_KEY_IPV6_DST, /* struct in6_addr */ + TCA_FLOWER_KEY_IPV6_DST_MASK, /* struct in6_addr */ + TCA_FLOWER_KEY_TCP_SRC, /* be16 */ + TCA_FLOWER_KEY_TCP_DST, /* be16 */ + TCA_FLOWER_KEY_UDP_SRC, /* be16 */ + TCA_FLOWER_KEY_UDP_DST, /* be16 */ + + TCA_FLOWER_FLAGS, + TCA_FLOWER_KEY_VLAN_ID, /* be16 */ + TCA_FLOWER_KEY_VLAN_PRIO, /* u8 */ + TCA_FLOWER_KEY_VLAN_ETH_TYPE, /* be16 */ + + TCA_FLOWER_KEY_ENC_KEY_ID, /* be32 */ + TCA_FLOWER_KEY_ENC_IPV4_SRC, /* be32 */ + TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK,/* be32 */ + TCA_FLOWER_KEY_ENC_IPV4_DST, /* be32 */ + TCA_FLOWER_KEY_ENC_IPV4_DST_MASK,/* be32 */ + TCA_FLOWER_KEY_ENC_IPV6_SRC, /* struct in6_addr */ + TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK,/* struct in6_addr */ + TCA_FLOWER_KEY_ENC_IPV6_DST, /* struct in6_addr */ + TCA_FLOWER_KEY_ENC_IPV6_DST_MASK,/* struct in6_addr */ + + TCA_FLOWER_KEY_TCP_SRC_MASK, /* be16 */ + TCA_FLOWER_KEY_TCP_DST_MASK, /* be16 */ + TCA_FLOWER_KEY_UDP_SRC_MASK, /* be16 */ + TCA_FLOWER_KEY_UDP_DST_MASK, /* be16 */ + TCA_FLOWER_KEY_SCTP_SRC_MASK, /* be16 */ + TCA_FLOWER_KEY_SCTP_DST_MASK, /* be16 */ + + TCA_FLOWER_KEY_SCTP_SRC, /* be16 */ + TCA_FLOWER_KEY_SCTP_DST, /* be16 */ + + TCA_FLOWER_KEY_ENC_UDP_SRC_PORT, /* be16 */ + TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK, /* be16 */ + TCA_FLOWER_KEY_ENC_UDP_DST_PORT, /* be16 */ + TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK, /* be16 */ + + TCA_FLOWER_KEY_FLAGS, /* be32 */ + TCA_FLOWER_KEY_FLAGS_MASK, /* be32 */ + + TCA_FLOWER_KEY_ICMPV4_CODE, /* u8 */ + TCA_FLOWER_KEY_ICMPV4_CODE_MASK,/* u8 */ + TCA_FLOWER_KEY_ICMPV4_TYPE, /* u8 */ + TCA_FLOWER_KEY_ICMPV4_TYPE_MASK,/* u8 */ + TCA_FLOWER_KEY_ICMPV6_CODE, /* u8 */ + TCA_FLOWER_KEY_ICMPV6_CODE_MASK,/* u8 */ + TCA_FLOWER_KEY_ICMPV6_TYPE, /* u8 */ + TCA_FLOWER_KEY_ICMPV6_TYPE_MASK,/* u8 */ + + TCA_FLOWER_KEY_ARP_SIP, /* be32 */ + TCA_FLOWER_KEY_ARP_SIP_MASK, /* be32 */ + TCA_FLOWER_KEY_ARP_TIP, /* be32 */ + TCA_FLOWER_KEY_ARP_TIP_MASK, /* be32 */ + TCA_FLOWER_KEY_ARP_OP, /* u8 */ + TCA_FLOWER_KEY_ARP_OP_MASK, /* u8 */ + TCA_FLOWER_KEY_ARP_SHA, /* ETH_ALEN */ + TCA_FLOWER_KEY_ARP_SHA_MASK, /* ETH_ALEN */ + TCA_FLOWER_KEY_ARP_THA, /* ETH_ALEN */ + TCA_FLOWER_KEY_ARP_THA_MASK, /* ETH_ALEN */ + + TCA_FLOWER_KEY_MPLS_TTL, /* u8 - 8 bits */ + TCA_FLOWER_KEY_MPLS_BOS, /* u8 - 1 bit */ + TCA_FLOWER_KEY_MPLS_TC, /* u8 - 3 bits */ + TCA_FLOWER_KEY_MPLS_LABEL, /* be32 - 20 bits */ + + TCA_FLOWER_KEY_TCP_FLAGS, /* be16 */ + TCA_FLOWER_KEY_TCP_FLAGS_MASK, /* be16 */ + + TCA_FLOWER_KEY_IP_TOS, /* u8 */ + TCA_FLOWER_KEY_IP_TOS_MASK, /* u8 */ + TCA_FLOWER_KEY_IP_TTL, /* u8 */ + TCA_FLOWER_KEY_IP_TTL_MASK, /* u8 */ + + TCA_FLOWER_KEY_CVLAN_ID, /* be16 */ + TCA_FLOWER_KEY_CVLAN_PRIO, /* u8 */ + TCA_FLOWER_KEY_CVLAN_ETH_TYPE, /* be16 */ + + TCA_FLOWER_KEY_ENC_IP_TOS, /* u8 */ + TCA_FLOWER_KEY_ENC_IP_TOS_MASK, /* u8 */ + TCA_FLOWER_KEY_ENC_IP_TTL, /* u8 */ + TCA_FLOWER_KEY_ENC_IP_TTL_MASK, /* u8 */ + + TCA_FLOWER_KEY_ENC_OPTS, + TCA_FLOWER_KEY_ENC_OPTS_MASK, + + __TCA_FLOWER_MAX, +}; + +#define TCA_FLOWER_MAX (__TCA_FLOWER_MAX - 1) + +enum { + TCA_FLOWER_KEY_ENC_OPTS_UNSPEC, + TCA_FLOWER_KEY_ENC_OPTS_GENEVE, /* Nested + * TCA_FLOWER_KEY_ENC_OPT_GENEVE_ + * attributes + */ + __TCA_FLOWER_KEY_ENC_OPTS_MAX, +}; + +#define TCA_FLOWER_KEY_ENC_OPTS_MAX (__TCA_FLOWER_KEY_ENC_OPTS_MAX - 1) + +enum { + TCA_FLOWER_KEY_ENC_OPT_GENEVE_UNSPEC, + TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS, /* u16 */ + TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE, /* u8 */ + TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA, /* 4 to 128 bytes */ + + __TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX, +}; + +#define TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX \ + (__TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX - 1) + +enum { + TCA_FLOWER_KEY_FLAGS_IS_FRAGMENT = (1 << 0), + TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST = (1 << 1), +}; + +/* Match-all classifier */ + +enum { + TCA_MATCHALL_UNSPEC, + TCA_MATCHALL_CLASSID, + TCA_MATCHALL_ACT, + TCA_MATCHALL_FLAGS, + __TCA_MATCHALL_MAX, +}; + +#define TCA_MATCHALL_MAX (__TCA_MATCHALL_MAX - 1) + +/* Extended Matches */ + +struct tcf_ematch_tree_hdr { + __u16 nmatches; + __u16 progid; +}; + +enum { + TCA_EMATCH_TREE_UNSPEC, + TCA_EMATCH_TREE_HDR, + TCA_EMATCH_TREE_LIST, + __TCA_EMATCH_TREE_MAX +}; +#define TCA_EMATCH_TREE_MAX (__TCA_EMATCH_TREE_MAX - 1) + +struct tcf_ematch_hdr { + __u16 matchid; + __u16 kind; + __u16 flags; + __u16 pad; /* currently unused */ +}; + +/* 0 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +-----------------------+-+-+---+ + * | Unused |S|I| R | + * +-----------------------+-+-+---+ + * + * R(2) ::= relation to next ematch + * where: 0 0 END (last ematch) + * 0 1 AND + * 1 0 OR + * 1 1 Unused (invalid) + * I(1) ::= invert result + * S(1) ::= simple payload + */ +#define TCF_EM_REL_END 0 +#define TCF_EM_REL_AND (1<<0) +#define TCF_EM_REL_OR (1<<1) +#define TCF_EM_INVERT (1<<2) +#define TCF_EM_SIMPLE (1<<3) + +#define TCF_EM_REL_MASK 3 +#define TCF_EM_REL_VALID(v) (((v) & TCF_EM_REL_MASK) != TCF_EM_REL_MASK) + +enum { + TCF_LAYER_LINK, + TCF_LAYER_NETWORK, + TCF_LAYER_TRANSPORT, + __TCF_LAYER_MAX +}; +#define TCF_LAYER_MAX (__TCF_LAYER_MAX - 1) + +/* Ematch type assignments + * 1..32767 Reserved for ematches inside kernel tree + * 32768..65535 Free to use, not reliable + */ +#define TCF_EM_CONTAINER 0 +#define TCF_EM_CMP 1 +#define TCF_EM_NBYTE 2 +#define TCF_EM_U32 3 +#define TCF_EM_META 4 +#define TCF_EM_TEXT 5 +#define TCF_EM_VLAN 6 +#define TCF_EM_CANID 7 +#define TCF_EM_IPSET 8 +#define TCF_EM_IPT 9 +#define TCF_EM_MAX 9 + +enum { + TCF_EM_PROG_TC +}; + +enum { + TCF_EM_OPND_EQ, + TCF_EM_OPND_GT, + TCF_EM_OPND_LT +}; + +#endif diff --git a/libnl/include/linux-private/linux/pkt_sched.h b/libnl/include/linux-private/linux/pkt_sched.h new file mode 100644 index 0000000..8975fd1 --- /dev/null +++ b/libnl/include/linux-private/linux/pkt_sched.h @@ -0,0 +1,1087 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_PKT_SCHED_H +#define __LINUX_PKT_SCHED_H + +#include + +/* Logical priority bands not depending on specific packet scheduler. + Every scheduler will map them to real traffic classes, if it has + no more precise mechanism to classify packets. + + These numbers have no special meaning, though their coincidence + with obsolete IPv6 values is not occasional :-). New IPv6 drafts + preferred full anarchy inspired by diffserv group. + + Note: TC_PRIO_BESTEFFORT does not mean that it is the most unhappy + class, actually, as rule it will be handled with more care than + filler or even bulk. + */ + +#define TC_PRIO_BESTEFFORT 0 +#define TC_PRIO_FILLER 1 +#define TC_PRIO_BULK 2 +#define TC_PRIO_INTERACTIVE_BULK 4 +#define TC_PRIO_INTERACTIVE 6 +#define TC_PRIO_CONTROL 7 + +#define TC_PRIO_MAX 15 + +/* Generic queue statistics, available for all the elements. + Particular schedulers may have also their private records. + */ + +struct tc_stats { + __u64 bytes; /* Number of enqueued bytes */ + __u32 packets; /* Number of enqueued packets */ + __u32 drops; /* Packets dropped because of lack of resources */ + __u32 overlimits; /* Number of throttle events when this + * flow goes out of allocated bandwidth */ + __u32 bps; /* Current flow byte rate */ + __u32 pps; /* Current flow packet rate */ + __u32 qlen; + __u32 backlog; +}; + +struct tc_estimator { + signed char interval; + unsigned char ewma_log; +}; + +/* "Handles" + --------- + + All the traffic control objects have 32bit identifiers, or "handles". + + They can be considered as opaque numbers from user API viewpoint, + but actually they always consist of two fields: major and + minor numbers, which are interpreted by kernel specially, + that may be used by applications, though not recommended. + + F.e. qdisc handles always have minor number equal to zero, + classes (or flows) have major equal to parent qdisc major, and + minor uniquely identifying class inside qdisc. + + Macros to manipulate handles: + */ + +#define TC_H_MAJ_MASK (0xFFFF0000U) +#define TC_H_MIN_MASK (0x0000FFFFU) +#define TC_H_MAJ(h) ((h)&TC_H_MAJ_MASK) +#define TC_H_MIN(h) ((h)&TC_H_MIN_MASK) +#define TC_H_MAKE(maj,min) (((maj)&TC_H_MAJ_MASK)|((min)&TC_H_MIN_MASK)) + +#define TC_H_UNSPEC (0U) +#define TC_H_ROOT (0xFFFFFFFFU) +#define TC_H_INGRESS (0xFFFFFFF1U) +#define TC_H_CLSACT TC_H_INGRESS + +#define TC_H_MIN_PRIORITY 0xFFE0U +#define TC_H_MIN_INGRESS 0xFFF2U +#define TC_H_MIN_EGRESS 0xFFF3U + +/* Need to corrospond to iproute2 tc/tc_core.h "enum link_layer" */ +enum tc_link_layer { + TC_LINKLAYER_UNAWARE, /* Indicate unaware old iproute2 util */ + TC_LINKLAYER_ETHERNET, + TC_LINKLAYER_ATM, +}; +#define TC_LINKLAYER_MASK 0x0F /* limit use to lower 4 bits */ + +struct tc_ratespec { + unsigned char cell_log; + __u8 linklayer; /* lower 4 bits */ + unsigned short overhead; + short cell_align; + unsigned short mpu; + __u32 rate; +}; + +#define TC_RTAB_SIZE 1024 + +struct tc_sizespec { + unsigned char cell_log; + unsigned char size_log; + short cell_align; + int overhead; + unsigned int linklayer; + unsigned int mpu; + unsigned int mtu; + unsigned int tsize; +}; + +enum { + TCA_STAB_UNSPEC, + TCA_STAB_BASE, + TCA_STAB_DATA, + __TCA_STAB_MAX +}; + +#define TCA_STAB_MAX (__TCA_STAB_MAX - 1) + +/* FIFO section */ + +struct tc_fifo_qopt { + __u32 limit; /* Queue length: bytes for bfifo, packets for pfifo */ +}; + +/* SKBPRIO section */ + +/* + * Priorities go from zero to (SKBPRIO_MAX_PRIORITY - 1). + * SKBPRIO_MAX_PRIORITY should be at least 64 in order for skbprio to be able + * to map one to one the DS field of IPV4 and IPV6 headers. + * Memory allocation grows linearly with SKBPRIO_MAX_PRIORITY. + */ + +#define SKBPRIO_MAX_PRIORITY 64 + +struct tc_skbprio_qopt { + __u32 limit; /* Queue length in packets. */ +}; + +/* PRIO section */ + +#define TCQ_PRIO_BANDS 16 +#define TCQ_MIN_PRIO_BANDS 2 + +struct tc_prio_qopt { + int bands; /* Number of bands */ + __u8 priomap[TC_PRIO_MAX+1]; /* Map: logical priority -> PRIO band */ +}; + +/* MULTIQ section */ + +struct tc_multiq_qopt { + __u16 bands; /* Number of bands */ + __u16 max_bands; /* Maximum number of queues */ +}; + +/* PLUG section */ + +#define TCQ_PLUG_BUFFER 0 +#define TCQ_PLUG_RELEASE_ONE 1 +#define TCQ_PLUG_RELEASE_INDEFINITE 2 +#define TCQ_PLUG_LIMIT 3 + +struct tc_plug_qopt { + /* TCQ_PLUG_BUFFER: Inset a plug into the queue and + * buffer any incoming packets + * TCQ_PLUG_RELEASE_ONE: Dequeue packets from queue head + * to beginning of the next plug. + * TCQ_PLUG_RELEASE_INDEFINITE: Dequeue all packets from queue. + * Stop buffering packets until the next TCQ_PLUG_BUFFER + * command is received (just act as a pass-thru queue). + * TCQ_PLUG_LIMIT: Increase/decrease queue size + */ + int action; + __u32 limit; +}; + +/* TBF section */ + +struct tc_tbf_qopt { + struct tc_ratespec rate; + struct tc_ratespec peakrate; + __u32 limit; + __u32 buffer; + __u32 mtu; +}; + +enum { + TCA_TBF_UNSPEC, + TCA_TBF_PARMS, + TCA_TBF_RTAB, + TCA_TBF_PTAB, + TCA_TBF_RATE64, + TCA_TBF_PRATE64, + TCA_TBF_BURST, + TCA_TBF_PBURST, + TCA_TBF_PAD, + __TCA_TBF_MAX, +}; + +#define TCA_TBF_MAX (__TCA_TBF_MAX - 1) + + +/* TEQL section */ + +/* TEQL does not require any parameters */ + +/* SFQ section */ + +struct tc_sfq_qopt { + unsigned quantum; /* Bytes per round allocated to flow */ + int perturb_period; /* Period of hash perturbation */ + __u32 limit; /* Maximal packets in queue */ + unsigned divisor; /* Hash divisor */ + unsigned flows; /* Maximal number of flows */ +}; + +struct tc_sfqred_stats { + __u32 prob_drop; /* Early drops, below max threshold */ + __u32 forced_drop; /* Early drops, after max threshold */ + __u32 prob_mark; /* Marked packets, below max threshold */ + __u32 forced_mark; /* Marked packets, after max threshold */ + __u32 prob_mark_head; /* Marked packets, below max threshold */ + __u32 forced_mark_head;/* Marked packets, after max threshold */ +}; + +struct tc_sfq_qopt_v1 { + struct tc_sfq_qopt v0; + unsigned int depth; /* max number of packets per flow */ + unsigned int headdrop; +/* SFQRED parameters */ + __u32 limit; /* HARD maximal flow queue length (bytes) */ + __u32 qth_min; /* Min average length threshold (bytes) */ + __u32 qth_max; /* Max average length threshold (bytes) */ + unsigned char Wlog; /* log(W) */ + unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */ + unsigned char Scell_log; /* cell size for idle damping */ + unsigned char flags; + __u32 max_P; /* probability, high resolution */ +/* SFQRED stats */ + struct tc_sfqred_stats stats; +}; + + +struct tc_sfq_xstats { + __s32 allot; +}; + +/* RED section */ + +enum { + TCA_RED_UNSPEC, + TCA_RED_PARMS, + TCA_RED_STAB, + TCA_RED_MAX_P, + __TCA_RED_MAX, +}; + +#define TCA_RED_MAX (__TCA_RED_MAX - 1) + +struct tc_red_qopt { + __u32 limit; /* HARD maximal queue length (bytes) */ + __u32 qth_min; /* Min average length threshold (bytes) */ + __u32 qth_max; /* Max average length threshold (bytes) */ + unsigned char Wlog; /* log(W) */ + unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */ + unsigned char Scell_log; /* cell size for idle damping */ + unsigned char flags; +#define TC_RED_ECN 1 +#define TC_RED_HARDDROP 2 +#define TC_RED_ADAPTATIVE 4 +}; + +struct tc_red_xstats { + __u32 early; /* Early drops */ + __u32 pdrop; /* Drops due to queue limits */ + __u32 other; /* Drops due to drop() calls */ + __u32 marked; /* Marked packets */ +}; + +/* GRED section */ + +#define MAX_DPs 16 + +enum { + TCA_GRED_UNSPEC, + TCA_GRED_PARMS, + TCA_GRED_STAB, + TCA_GRED_DPS, + TCA_GRED_MAX_P, + TCA_GRED_LIMIT, + __TCA_GRED_MAX, +}; + +#define TCA_GRED_MAX (__TCA_GRED_MAX - 1) + +struct tc_gred_qopt { + __u32 limit; /* HARD maximal queue length (bytes) */ + __u32 qth_min; /* Min average length threshold (bytes) */ + __u32 qth_max; /* Max average length threshold (bytes) */ + __u32 DP; /* up to 2^32 DPs */ + __u32 backlog; + __u32 qave; + __u32 forced; + __u32 early; + __u32 other; + __u32 pdrop; + __u8 Wlog; /* log(W) */ + __u8 Plog; /* log(P_max/(qth_max-qth_min)) */ + __u8 Scell_log; /* cell size for idle damping */ + __u8 prio; /* prio of this VQ */ + __u32 packets; + __u32 bytesin; +}; + +/* gred setup */ +struct tc_gred_sopt { + __u32 DPs; + __u32 def_DP; + __u8 grio; + __u8 flags; + __u16 pad1; +}; + +/* CHOKe section */ + +enum { + TCA_CHOKE_UNSPEC, + TCA_CHOKE_PARMS, + TCA_CHOKE_STAB, + TCA_CHOKE_MAX_P, + __TCA_CHOKE_MAX, +}; + +#define TCA_CHOKE_MAX (__TCA_CHOKE_MAX - 1) + +struct tc_choke_qopt { + __u32 limit; /* Hard queue length (packets) */ + __u32 qth_min; /* Min average threshold (packets) */ + __u32 qth_max; /* Max average threshold (packets) */ + unsigned char Wlog; /* log(W) */ + unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */ + unsigned char Scell_log; /* cell size for idle damping */ + unsigned char flags; /* see RED flags */ +}; + +struct tc_choke_xstats { + __u32 early; /* Early drops */ + __u32 pdrop; /* Drops due to queue limits */ + __u32 other; /* Drops due to drop() calls */ + __u32 marked; /* Marked packets */ + __u32 matched; /* Drops due to flow match */ +}; + +/* HTB section */ +#define TC_HTB_NUMPRIO 8 +#define TC_HTB_MAXDEPTH 8 +#define TC_HTB_PROTOVER 3 /* the same as HTB and TC's major */ + +struct tc_htb_opt { + struct tc_ratespec rate; + struct tc_ratespec ceil; + __u32 buffer; + __u32 cbuffer; + __u32 quantum; + __u32 level; /* out only */ + __u32 prio; +}; +struct tc_htb_glob { + __u32 version; /* to match HTB/TC */ + __u32 rate2quantum; /* bps->quantum divisor */ + __u32 defcls; /* default class number */ + __u32 debug; /* debug flags */ + + /* stats */ + __u32 direct_pkts; /* count of non shaped packets */ +}; +enum { + TCA_HTB_UNSPEC, + TCA_HTB_PARMS, + TCA_HTB_INIT, + TCA_HTB_CTAB, + TCA_HTB_RTAB, + TCA_HTB_DIRECT_QLEN, + TCA_HTB_RATE64, + TCA_HTB_CEIL64, + TCA_HTB_PAD, + __TCA_HTB_MAX, +}; + +#define TCA_HTB_MAX (__TCA_HTB_MAX - 1) + +struct tc_htb_xstats { + __u32 lends; + __u32 borrows; + __u32 giants; /* too big packets (rate will not be accurate) */ + __u32 tokens; + __u32 ctokens; +}; + +/* HFSC section */ + +struct tc_hfsc_qopt { + __u16 defcls; /* default class */ +}; + +struct tc_service_curve { + __u32 m1; /* slope of the first segment in bps */ + __u32 d; /* x-projection of the first segment in us */ + __u32 m2; /* slope of the second segment in bps */ +}; + +struct tc_hfsc_stats { + __u64 work; /* total work done */ + __u64 rtwork; /* work done by real-time criteria */ + __u32 period; /* current period */ + __u32 level; /* class level in hierarchy */ +}; + +enum { + TCA_HFSC_UNSPEC, + TCA_HFSC_RSC, + TCA_HFSC_FSC, + TCA_HFSC_USC, + __TCA_HFSC_MAX, +}; + +#define TCA_HFSC_MAX (__TCA_HFSC_MAX - 1) + + +/* CBQ section */ + +#define TC_CBQ_MAXPRIO 8 +#define TC_CBQ_MAXLEVEL 8 +#define TC_CBQ_DEF_EWMA 5 + +struct tc_cbq_lssopt { + unsigned char change; + unsigned char flags; +#define TCF_CBQ_LSS_BOUNDED 1 +#define TCF_CBQ_LSS_ISOLATED 2 + unsigned char ewma_log; + unsigned char level; +#define TCF_CBQ_LSS_FLAGS 1 +#define TCF_CBQ_LSS_EWMA 2 +#define TCF_CBQ_LSS_MAXIDLE 4 +#define TCF_CBQ_LSS_MINIDLE 8 +#define TCF_CBQ_LSS_OFFTIME 0x10 +#define TCF_CBQ_LSS_AVPKT 0x20 + __u32 maxidle; + __u32 minidle; + __u32 offtime; + __u32 avpkt; +}; + +struct tc_cbq_wrropt { + unsigned char flags; + unsigned char priority; + unsigned char cpriority; + unsigned char __reserved; + __u32 allot; + __u32 weight; +}; + +struct tc_cbq_ovl { + unsigned char strategy; +#define TC_CBQ_OVL_CLASSIC 0 +#define TC_CBQ_OVL_DELAY 1 +#define TC_CBQ_OVL_LOWPRIO 2 +#define TC_CBQ_OVL_DROP 3 +#define TC_CBQ_OVL_RCLASSIC 4 + unsigned char priority2; + __u16 pad; + __u32 penalty; +}; + +struct tc_cbq_police { + unsigned char police; + unsigned char __res1; + unsigned short __res2; +}; + +struct tc_cbq_fopt { + __u32 split; + __u32 defmap; + __u32 defchange; +}; + +struct tc_cbq_xstats { + __u32 borrows; + __u32 overactions; + __s32 avgidle; + __s32 undertime; +}; + +enum { + TCA_CBQ_UNSPEC, + TCA_CBQ_LSSOPT, + TCA_CBQ_WRROPT, + TCA_CBQ_FOPT, + TCA_CBQ_OVL_STRATEGY, + TCA_CBQ_RATE, + TCA_CBQ_RTAB, + TCA_CBQ_POLICE, + __TCA_CBQ_MAX, +}; + +#define TCA_CBQ_MAX (__TCA_CBQ_MAX - 1) + +/* dsmark section */ + +enum { + TCA_DSMARK_UNSPEC, + TCA_DSMARK_INDICES, + TCA_DSMARK_DEFAULT_INDEX, + TCA_DSMARK_SET_TC_INDEX, + TCA_DSMARK_MASK, + TCA_DSMARK_VALUE, + __TCA_DSMARK_MAX, +}; + +#define TCA_DSMARK_MAX (__TCA_DSMARK_MAX - 1) + +/* ATM section */ + +enum { + TCA_ATM_UNSPEC, + TCA_ATM_FD, /* file/socket descriptor */ + TCA_ATM_PTR, /* pointer to descriptor - later */ + TCA_ATM_HDR, /* LL header */ + TCA_ATM_EXCESS, /* excess traffic class (0 for CLP) */ + TCA_ATM_ADDR, /* PVC address (for output only) */ + TCA_ATM_STATE, /* VC state (ATM_VS_*; for output only) */ + __TCA_ATM_MAX, +}; + +#define TCA_ATM_MAX (__TCA_ATM_MAX - 1) + +/* Network emulator */ + +enum { + TCA_NETEM_UNSPEC, + TCA_NETEM_CORR, + TCA_NETEM_DELAY_DIST, + TCA_NETEM_REORDER, + TCA_NETEM_CORRUPT, + TCA_NETEM_LOSS, + TCA_NETEM_RATE, + TCA_NETEM_ECN, + TCA_NETEM_RATE64, + TCA_NETEM_PAD, + TCA_NETEM_LATENCY64, + TCA_NETEM_JITTER64, + TCA_NETEM_SLOT, + TCA_NETEM_SLOT_DIST, + __TCA_NETEM_MAX, +}; + +#define TCA_NETEM_MAX (__TCA_NETEM_MAX - 1) + +struct tc_netem_qopt { + __u32 latency; /* added delay (us) */ + __u32 limit; /* fifo limit (packets) */ + __u32 loss; /* random packet loss (0=none ~0=100%) */ + __u32 gap; /* re-ordering gap (0 for none) */ + __u32 duplicate; /* random packet dup (0=none ~0=100%) */ + __u32 jitter; /* random jitter in latency (us) */ +}; + +struct tc_netem_corr { + __u32 delay_corr; /* delay correlation */ + __u32 loss_corr; /* packet loss correlation */ + __u32 dup_corr; /* duplicate correlation */ +}; + +struct tc_netem_reorder { + __u32 probability; + __u32 correlation; +}; + +struct tc_netem_corrupt { + __u32 probability; + __u32 correlation; +}; + +struct tc_netem_rate { + __u32 rate; /* byte/s */ + __s32 packet_overhead; + __u32 cell_size; + __s32 cell_overhead; +}; + +struct tc_netem_slot { + __s64 min_delay; /* nsec */ + __s64 max_delay; + __s32 max_packets; + __s32 max_bytes; + __s64 dist_delay; /* nsec */ + __s64 dist_jitter; /* nsec */ +}; + +enum { + NETEM_LOSS_UNSPEC, + NETEM_LOSS_GI, /* General Intuitive - 4 state model */ + NETEM_LOSS_GE, /* Gilbert Elliot models */ + __NETEM_LOSS_MAX +}; +#define NETEM_LOSS_MAX (__NETEM_LOSS_MAX - 1) + +/* State transition probabilities for 4 state model */ +struct tc_netem_gimodel { + __u32 p13; + __u32 p31; + __u32 p32; + __u32 p14; + __u32 p23; +}; + +/* Gilbert-Elliot models */ +struct tc_netem_gemodel { + __u32 p; + __u32 r; + __u32 h; + __u32 k1; +}; + +#define NETEM_DIST_SCALE 8192 +#define NETEM_DIST_MAX 16384 + +/* DRR */ + +enum { + TCA_DRR_UNSPEC, + TCA_DRR_QUANTUM, + __TCA_DRR_MAX +}; + +#define TCA_DRR_MAX (__TCA_DRR_MAX - 1) + +struct tc_drr_stats { + __u32 deficit; +}; + +/* MQPRIO */ +#define TC_QOPT_BITMASK 15 +#define TC_QOPT_MAX_QUEUE 16 + +enum { + TC_MQPRIO_HW_OFFLOAD_NONE, /* no offload requested */ + TC_MQPRIO_HW_OFFLOAD_TCS, /* offload TCs, no queue counts */ + __TC_MQPRIO_HW_OFFLOAD_MAX +}; + +#define TC_MQPRIO_HW_OFFLOAD_MAX (__TC_MQPRIO_HW_OFFLOAD_MAX - 1) + +enum { + TC_MQPRIO_MODE_DCB, + TC_MQPRIO_MODE_CHANNEL, + __TC_MQPRIO_MODE_MAX +}; + +#define __TC_MQPRIO_MODE_MAX (__TC_MQPRIO_MODE_MAX - 1) + +enum { + TC_MQPRIO_SHAPER_DCB, + TC_MQPRIO_SHAPER_BW_RATE, /* Add new shapers below */ + __TC_MQPRIO_SHAPER_MAX +}; + +#define __TC_MQPRIO_SHAPER_MAX (__TC_MQPRIO_SHAPER_MAX - 1) + +struct tc_mqprio_qopt { + __u8 num_tc; + __u8 prio_tc_map[TC_QOPT_BITMASK + 1]; + __u8 hw; + __u16 count[TC_QOPT_MAX_QUEUE]; + __u16 offset[TC_QOPT_MAX_QUEUE]; +}; + +#define TC_MQPRIO_F_MODE 0x1 +#define TC_MQPRIO_F_SHAPER 0x2 +#define TC_MQPRIO_F_MIN_RATE 0x4 +#define TC_MQPRIO_F_MAX_RATE 0x8 + +enum { + TCA_MQPRIO_UNSPEC, + TCA_MQPRIO_MODE, + TCA_MQPRIO_SHAPER, + TCA_MQPRIO_MIN_RATE64, + TCA_MQPRIO_MAX_RATE64, + __TCA_MQPRIO_MAX, +}; + +#define TCA_MQPRIO_MAX (__TCA_MQPRIO_MAX - 1) + +/* SFB */ + +enum { + TCA_SFB_UNSPEC, + TCA_SFB_PARMS, + __TCA_SFB_MAX, +}; + +#define TCA_SFB_MAX (__TCA_SFB_MAX - 1) + +/* + * Note: increment, decrement are Q0.16 fixed-point values. + */ +struct tc_sfb_qopt { + __u32 rehash_interval; /* delay between hash move, in ms */ + __u32 warmup_time; /* double buffering warmup time in ms (warmup_time < rehash_interval) */ + __u32 max; /* max len of qlen_min */ + __u32 bin_size; /* maximum queue length per bin */ + __u32 increment; /* probability increment, (d1 in Blue) */ + __u32 decrement; /* probability decrement, (d2 in Blue) */ + __u32 limit; /* max SFB queue length */ + __u32 penalty_rate; /* inelastic flows are rate limited to 'rate' pps */ + __u32 penalty_burst; +}; + +struct tc_sfb_xstats { + __u32 earlydrop; + __u32 penaltydrop; + __u32 bucketdrop; + __u32 queuedrop; + __u32 childdrop; /* drops in child qdisc */ + __u32 marked; + __u32 maxqlen; + __u32 maxprob; + __u32 avgprob; +}; + +#define SFB_MAX_PROB 0xFFFF + +/* QFQ */ +enum { + TCA_QFQ_UNSPEC, + TCA_QFQ_WEIGHT, + TCA_QFQ_LMAX, + __TCA_QFQ_MAX +}; + +#define TCA_QFQ_MAX (__TCA_QFQ_MAX - 1) + +struct tc_qfq_stats { + __u32 weight; + __u32 lmax; +}; + +/* CODEL */ + +enum { + TCA_CODEL_UNSPEC, + TCA_CODEL_TARGET, + TCA_CODEL_LIMIT, + TCA_CODEL_INTERVAL, + TCA_CODEL_ECN, + TCA_CODEL_CE_THRESHOLD, + __TCA_CODEL_MAX +}; + +#define TCA_CODEL_MAX (__TCA_CODEL_MAX - 1) + +struct tc_codel_xstats { + __u32 maxpacket; /* largest packet we've seen so far */ + __u32 count; /* how many drops we've done since the last time we + * entered dropping state + */ + __u32 lastcount; /* count at entry to dropping state */ + __u32 ldelay; /* in-queue delay seen by most recently dequeued packet */ + __s32 drop_next; /* time to drop next packet */ + __u32 drop_overlimit; /* number of time max qdisc packet limit was hit */ + __u32 ecn_mark; /* number of packets we ECN marked instead of dropped */ + __u32 dropping; /* are we in dropping state ? */ + __u32 ce_mark; /* number of CE marked packets because of ce_threshold */ +}; + +/* FQ_CODEL */ + +enum { + TCA_FQ_CODEL_UNSPEC, + TCA_FQ_CODEL_TARGET, + TCA_FQ_CODEL_LIMIT, + TCA_FQ_CODEL_INTERVAL, + TCA_FQ_CODEL_ECN, + TCA_FQ_CODEL_FLOWS, + TCA_FQ_CODEL_QUANTUM, + TCA_FQ_CODEL_CE_THRESHOLD, + TCA_FQ_CODEL_DROP_BATCH_SIZE, + TCA_FQ_CODEL_MEMORY_LIMIT, + __TCA_FQ_CODEL_MAX +}; + +#define TCA_FQ_CODEL_MAX (__TCA_FQ_CODEL_MAX - 1) + +enum { + TCA_FQ_CODEL_XSTATS_QDISC, + TCA_FQ_CODEL_XSTATS_CLASS, +}; + +struct tc_fq_codel_qd_stats { + __u32 maxpacket; /* largest packet we've seen so far */ + __u32 drop_overlimit; /* number of time max qdisc + * packet limit was hit + */ + __u32 ecn_mark; /* number of packets we ECN marked + * instead of being dropped + */ + __u32 new_flow_count; /* number of time packets + * created a 'new flow' + */ + __u32 new_flows_len; /* count of flows in new list */ + __u32 old_flows_len; /* count of flows in old list */ + __u32 ce_mark; /* packets above ce_threshold */ + __u32 memory_usage; /* in bytes */ + __u32 drop_overmemory; +}; + +struct tc_fq_codel_cl_stats { + __s32 deficit; + __u32 ldelay; /* in-queue delay seen by most recently + * dequeued packet + */ + __u32 count; + __u32 lastcount; + __u32 dropping; + __s32 drop_next; +}; + +struct tc_fq_codel_xstats { + __u32 type; + union { + struct tc_fq_codel_qd_stats qdisc_stats; + struct tc_fq_codel_cl_stats class_stats; + }; +}; + +/* FQ */ + +enum { + TCA_FQ_UNSPEC, + + TCA_FQ_PLIMIT, /* limit of total number of packets in queue */ + + TCA_FQ_FLOW_PLIMIT, /* limit of packets per flow */ + + TCA_FQ_QUANTUM, /* RR quantum */ + + TCA_FQ_INITIAL_QUANTUM, /* RR quantum for new flow */ + + TCA_FQ_RATE_ENABLE, /* enable/disable rate limiting */ + + TCA_FQ_FLOW_DEFAULT_RATE,/* obsolete, do not use */ + + TCA_FQ_FLOW_MAX_RATE, /* per flow max rate */ + + TCA_FQ_BUCKETS_LOG, /* log2(number of buckets) */ + + TCA_FQ_FLOW_REFILL_DELAY, /* flow credit refill delay in usec */ + + TCA_FQ_ORPHAN_MASK, /* mask applied to orphaned skb hashes */ + + TCA_FQ_LOW_RATE_THRESHOLD, /* per packet delay under this rate */ + + __TCA_FQ_MAX +}; + +#define TCA_FQ_MAX (__TCA_FQ_MAX - 1) + +struct tc_fq_qd_stats { + __u64 gc_flows; + __u64 highprio_packets; + __u64 tcp_retrans; + __u64 throttled; + __u64 flows_plimit; + __u64 pkts_too_long; + __u64 allocation_errors; + __s64 time_next_delayed_flow; + __u32 flows; + __u32 inactive_flows; + __u32 throttled_flows; + __u32 unthrottle_latency_ns; +}; + +/* Heavy-Hitter Filter */ + +enum { + TCA_HHF_UNSPEC, + TCA_HHF_BACKLOG_LIMIT, + TCA_HHF_QUANTUM, + TCA_HHF_HH_FLOWS_LIMIT, + TCA_HHF_RESET_TIMEOUT, + TCA_HHF_ADMIT_BYTES, + TCA_HHF_EVICT_TIMEOUT, + TCA_HHF_NON_HH_WEIGHT, + __TCA_HHF_MAX +}; + +#define TCA_HHF_MAX (__TCA_HHF_MAX - 1) + +struct tc_hhf_xstats { + __u32 drop_overlimit; /* number of times max qdisc packet limit + * was hit + */ + __u32 hh_overlimit; /* number of times max heavy-hitters was hit */ + __u32 hh_tot_count; /* number of captured heavy-hitters so far */ + __u32 hh_cur_count; /* number of current heavy-hitters */ +}; + +/* PIE */ +enum { + TCA_PIE_UNSPEC, + TCA_PIE_TARGET, + TCA_PIE_LIMIT, + TCA_PIE_TUPDATE, + TCA_PIE_ALPHA, + TCA_PIE_BETA, + TCA_PIE_ECN, + TCA_PIE_BYTEMODE, + __TCA_PIE_MAX +}; +#define TCA_PIE_MAX (__TCA_PIE_MAX - 1) + +struct tc_pie_xstats { + __u32 prob; /* current probability */ + __u32 delay; /* current delay in ms */ + __u32 avg_dq_rate; /* current average dq_rate in bits/pie_time */ + __u32 packets_in; /* total number of packets enqueued */ + __u32 dropped; /* packets dropped due to pie_action */ + __u32 overlimit; /* dropped due to lack of space in queue */ + __u32 maxq; /* maximum queue size */ + __u32 ecn_mark; /* packets marked with ecn*/ +}; + +/* CBS */ +struct tc_cbs_qopt { + __u8 offload; + __u8 _pad[3]; + __s32 hicredit; + __s32 locredit; + __s32 idleslope; + __s32 sendslope; +}; + +enum { + TCA_CBS_UNSPEC, + TCA_CBS_PARMS, + __TCA_CBS_MAX, +}; + +#define TCA_CBS_MAX (__TCA_CBS_MAX - 1) + + +/* ETF */ +struct tc_etf_qopt { + __s32 delta; + __s32 clockid; + __u32 flags; +#define TC_ETF_DEADLINE_MODE_ON BIT(0) +#define TC_ETF_OFFLOAD_ON BIT(1) +}; + +enum { + TCA_ETF_UNSPEC, + TCA_ETF_PARMS, + __TCA_ETF_MAX, +}; + +#define TCA_ETF_MAX (__TCA_ETF_MAX - 1) + + +/* CAKE */ +enum { + TCA_CAKE_UNSPEC, + TCA_CAKE_PAD, + TCA_CAKE_BASE_RATE64, + TCA_CAKE_DIFFSERV_MODE, + TCA_CAKE_ATM, + TCA_CAKE_FLOW_MODE, + TCA_CAKE_OVERHEAD, + TCA_CAKE_RTT, + TCA_CAKE_TARGET, + TCA_CAKE_AUTORATE, + TCA_CAKE_MEMORY, + TCA_CAKE_NAT, + TCA_CAKE_RAW, + TCA_CAKE_WASH, + TCA_CAKE_MPU, + TCA_CAKE_INGRESS, + TCA_CAKE_ACK_FILTER, + TCA_CAKE_SPLIT_GSO, + __TCA_CAKE_MAX +}; +#define TCA_CAKE_MAX (__TCA_CAKE_MAX - 1) + +enum { + __TCA_CAKE_STATS_INVALID, + TCA_CAKE_STATS_PAD, + TCA_CAKE_STATS_CAPACITY_ESTIMATE64, + TCA_CAKE_STATS_MEMORY_LIMIT, + TCA_CAKE_STATS_MEMORY_USED, + TCA_CAKE_STATS_AVG_NETOFF, + TCA_CAKE_STATS_MIN_NETLEN, + TCA_CAKE_STATS_MAX_NETLEN, + TCA_CAKE_STATS_MIN_ADJLEN, + TCA_CAKE_STATS_MAX_ADJLEN, + TCA_CAKE_STATS_TIN_STATS, + TCA_CAKE_STATS_DEFICIT, + TCA_CAKE_STATS_COBALT_COUNT, + TCA_CAKE_STATS_DROPPING, + TCA_CAKE_STATS_DROP_NEXT_US, + TCA_CAKE_STATS_P_DROP, + TCA_CAKE_STATS_BLUE_TIMER_US, + __TCA_CAKE_STATS_MAX +}; +#define TCA_CAKE_STATS_MAX (__TCA_CAKE_STATS_MAX - 1) + +enum { + __TCA_CAKE_TIN_STATS_INVALID, + TCA_CAKE_TIN_STATS_PAD, + TCA_CAKE_TIN_STATS_SENT_PACKETS, + TCA_CAKE_TIN_STATS_SENT_BYTES64, + TCA_CAKE_TIN_STATS_DROPPED_PACKETS, + TCA_CAKE_TIN_STATS_DROPPED_BYTES64, + TCA_CAKE_TIN_STATS_ACKS_DROPPED_PACKETS, + TCA_CAKE_TIN_STATS_ACKS_DROPPED_BYTES64, + TCA_CAKE_TIN_STATS_ECN_MARKED_PACKETS, + TCA_CAKE_TIN_STATS_ECN_MARKED_BYTES64, + TCA_CAKE_TIN_STATS_BACKLOG_PACKETS, + TCA_CAKE_TIN_STATS_BACKLOG_BYTES, + TCA_CAKE_TIN_STATS_THRESHOLD_RATE64, + TCA_CAKE_TIN_STATS_TARGET_US, + TCA_CAKE_TIN_STATS_INTERVAL_US, + TCA_CAKE_TIN_STATS_WAY_INDIRECT_HITS, + TCA_CAKE_TIN_STATS_WAY_MISSES, + TCA_CAKE_TIN_STATS_WAY_COLLISIONS, + TCA_CAKE_TIN_STATS_PEAK_DELAY_US, + TCA_CAKE_TIN_STATS_AVG_DELAY_US, + TCA_CAKE_TIN_STATS_BASE_DELAY_US, + TCA_CAKE_TIN_STATS_SPARSE_FLOWS, + TCA_CAKE_TIN_STATS_BULK_FLOWS, + TCA_CAKE_TIN_STATS_UNRESPONSIVE_FLOWS, + TCA_CAKE_TIN_STATS_MAX_SKBLEN, + TCA_CAKE_TIN_STATS_FLOW_QUANTUM, + __TCA_CAKE_TIN_STATS_MAX +}; +#define TCA_CAKE_TIN_STATS_MAX (__TCA_CAKE_TIN_STATS_MAX - 1) +#define TC_CAKE_MAX_TINS (8) + +enum { + CAKE_FLOW_NONE = 0, + CAKE_FLOW_SRC_IP, + CAKE_FLOW_DST_IP, + CAKE_FLOW_HOSTS, /* = CAKE_FLOW_SRC_IP | CAKE_FLOW_DST_IP */ + CAKE_FLOW_FLOWS, + CAKE_FLOW_DUAL_SRC, /* = CAKE_FLOW_SRC_IP | CAKE_FLOW_FLOWS */ + CAKE_FLOW_DUAL_DST, /* = CAKE_FLOW_DST_IP | CAKE_FLOW_FLOWS */ + CAKE_FLOW_TRIPLE, /* = CAKE_FLOW_HOSTS | CAKE_FLOW_FLOWS */ + CAKE_FLOW_MAX, +}; + +enum { + CAKE_DIFFSERV_DIFFSERV3 = 0, + CAKE_DIFFSERV_DIFFSERV4, + CAKE_DIFFSERV_DIFFSERV8, + CAKE_DIFFSERV_BESTEFFORT, + CAKE_DIFFSERV_PRECEDENCE, + CAKE_DIFFSERV_MAX +}; + +enum { + CAKE_ACK_NONE = 0, + CAKE_ACK_FILTER, + CAKE_ACK_AGGRESSIVE, + CAKE_ACK_MAX +}; + +enum { + CAKE_ATM_NONE = 0, + CAKE_ATM_ATM, + CAKE_ATM_PTM, + CAKE_ATM_MAX +}; + +#endif diff --git a/libnl/include/linux-private/linux/rtnetlink.h b/libnl/include/linux-private/linux/rtnetlink.h new file mode 100644 index 0000000..8c1d600 --- /dev/null +++ b/libnl/include/linux-private/linux/rtnetlink.h @@ -0,0 +1,749 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_RTNETLINK_H +#define __LINUX_RTNETLINK_H + +#include +#include +#include +#include +#include + +/* rtnetlink families. Values up to 127 are reserved for real address + * families, values above 128 may be used arbitrarily. + */ +#define RTNL_FAMILY_IPMR 128 +#define RTNL_FAMILY_IP6MR 129 +#define RTNL_FAMILY_MAX 129 + +/**** + * Routing/neighbour discovery messages. + ****/ + +/* Types of messages */ + +enum { + RTM_BASE = 16, +#define RTM_BASE RTM_BASE + + RTM_NEWLINK = 16, +#define RTM_NEWLINK RTM_NEWLINK + RTM_DELLINK, +#define RTM_DELLINK RTM_DELLINK + RTM_GETLINK, +#define RTM_GETLINK RTM_GETLINK + RTM_SETLINK, +#define RTM_SETLINK RTM_SETLINK + + RTM_NEWADDR = 20, +#define RTM_NEWADDR RTM_NEWADDR + RTM_DELADDR, +#define RTM_DELADDR RTM_DELADDR + RTM_GETADDR, +#define RTM_GETADDR RTM_GETADDR + + RTM_NEWROUTE = 24, +#define RTM_NEWROUTE RTM_NEWROUTE + RTM_DELROUTE, +#define RTM_DELROUTE RTM_DELROUTE + RTM_GETROUTE, +#define RTM_GETROUTE RTM_GETROUTE + + RTM_NEWNEIGH = 28, +#define RTM_NEWNEIGH RTM_NEWNEIGH + RTM_DELNEIGH, +#define RTM_DELNEIGH RTM_DELNEIGH + RTM_GETNEIGH, +#define RTM_GETNEIGH RTM_GETNEIGH + + RTM_NEWRULE = 32, +#define RTM_NEWRULE RTM_NEWRULE + RTM_DELRULE, +#define RTM_DELRULE RTM_DELRULE + RTM_GETRULE, +#define RTM_GETRULE RTM_GETRULE + + RTM_NEWQDISC = 36, +#define RTM_NEWQDISC RTM_NEWQDISC + RTM_DELQDISC, +#define RTM_DELQDISC RTM_DELQDISC + RTM_GETQDISC, +#define RTM_GETQDISC RTM_GETQDISC + + RTM_NEWTCLASS = 40, +#define RTM_NEWTCLASS RTM_NEWTCLASS + RTM_DELTCLASS, +#define RTM_DELTCLASS RTM_DELTCLASS + RTM_GETTCLASS, +#define RTM_GETTCLASS RTM_GETTCLASS + + RTM_NEWTFILTER = 44, +#define RTM_NEWTFILTER RTM_NEWTFILTER + RTM_DELTFILTER, +#define RTM_DELTFILTER RTM_DELTFILTER + RTM_GETTFILTER, +#define RTM_GETTFILTER RTM_GETTFILTER + + RTM_NEWACTION = 48, +#define RTM_NEWACTION RTM_NEWACTION + RTM_DELACTION, +#define RTM_DELACTION RTM_DELACTION + RTM_GETACTION, +#define RTM_GETACTION RTM_GETACTION + + RTM_NEWPREFIX = 52, +#define RTM_NEWPREFIX RTM_NEWPREFIX + + RTM_GETMULTICAST = 58, +#define RTM_GETMULTICAST RTM_GETMULTICAST + + RTM_GETANYCAST = 62, +#define RTM_GETANYCAST RTM_GETANYCAST + + RTM_NEWNEIGHTBL = 64, +#define RTM_NEWNEIGHTBL RTM_NEWNEIGHTBL + RTM_GETNEIGHTBL = 66, +#define RTM_GETNEIGHTBL RTM_GETNEIGHTBL + RTM_SETNEIGHTBL, +#define RTM_SETNEIGHTBL RTM_SETNEIGHTBL + + RTM_NEWNDUSEROPT = 68, +#define RTM_NEWNDUSEROPT RTM_NEWNDUSEROPT + + RTM_NEWADDRLABEL = 72, +#define RTM_NEWADDRLABEL RTM_NEWADDRLABEL + RTM_DELADDRLABEL, +#define RTM_DELADDRLABEL RTM_DELADDRLABEL + RTM_GETADDRLABEL, +#define RTM_GETADDRLABEL RTM_GETADDRLABEL + + RTM_GETDCB = 78, +#define RTM_GETDCB RTM_GETDCB + RTM_SETDCB, +#define RTM_SETDCB RTM_SETDCB + + RTM_NEWNETCONF = 80, +#define RTM_NEWNETCONF RTM_NEWNETCONF + RTM_DELNETCONF, +#define RTM_DELNETCONF RTM_DELNETCONF + RTM_GETNETCONF = 82, +#define RTM_GETNETCONF RTM_GETNETCONF + + RTM_NEWMDB = 84, +#define RTM_NEWMDB RTM_NEWMDB + RTM_DELMDB = 85, +#define RTM_DELMDB RTM_DELMDB + RTM_GETMDB = 86, +#define RTM_GETMDB RTM_GETMDB + + RTM_NEWNSID = 88, +#define RTM_NEWNSID RTM_NEWNSID + RTM_DELNSID = 89, +#define RTM_DELNSID RTM_DELNSID + RTM_GETNSID = 90, +#define RTM_GETNSID RTM_GETNSID + + RTM_NEWSTATS = 92, +#define RTM_NEWSTATS RTM_NEWSTATS + RTM_GETSTATS = 94, +#define RTM_GETSTATS RTM_GETSTATS + + RTM_NEWCACHEREPORT = 96, +#define RTM_NEWCACHEREPORT RTM_NEWCACHEREPORT + + RTM_NEWCHAIN = 100, +#define RTM_NEWCHAIN RTM_NEWCHAIN + RTM_DELCHAIN, +#define RTM_DELCHAIN RTM_DELCHAIN + RTM_GETCHAIN, +#define RTM_GETCHAIN RTM_GETCHAIN + + __RTM_MAX, +#define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1) +}; + +#define RTM_NR_MSGTYPES (RTM_MAX + 1 - RTM_BASE) +#define RTM_NR_FAMILIES (RTM_NR_MSGTYPES >> 2) +#define RTM_FAM(cmd) (((cmd) - RTM_BASE) >> 2) + +/* + Generic structure for encapsulation of optional route information. + It is reminiscent of sockaddr, but with sa_family replaced + with attribute type. + */ + +struct rtattr { + unsigned short rta_len; + unsigned short rta_type; +}; + +/* Macros to handle rtattributes */ + +#define RTA_ALIGNTO 4U +#define RTA_ALIGN(len) ( ((len)+RTA_ALIGNTO-1) & ~(RTA_ALIGNTO-1) ) +#define RTA_OK(rta,len) ((len) >= (int)sizeof(struct rtattr) && \ + (rta)->rta_len >= sizeof(struct rtattr) && \ + (rta)->rta_len <= (len)) +#define RTA_NEXT(rta,attrlen) ((attrlen) -= RTA_ALIGN((rta)->rta_len), \ + (struct rtattr*)(((char*)(rta)) + RTA_ALIGN((rta)->rta_len))) +#define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len)) +#define RTA_SPACE(len) RTA_ALIGN(RTA_LENGTH(len)) +#define RTA_DATA(rta) ((void*)(((char*)(rta)) + RTA_LENGTH(0))) +#define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0)) + + + + +/****************************************************************************** + * Definitions used in routing table administration. + ****/ + +struct rtmsg { + unsigned char rtm_family; + unsigned char rtm_dst_len; + unsigned char rtm_src_len; + unsigned char rtm_tos; + + unsigned char rtm_table; /* Routing table id */ + unsigned char rtm_protocol; /* Routing protocol; see below */ + unsigned char rtm_scope; /* See below */ + unsigned char rtm_type; /* See below */ + + unsigned rtm_flags; +}; + +/* rtm_type */ + +enum { + RTN_UNSPEC, + RTN_UNICAST, /* Gateway or direct route */ + RTN_LOCAL, /* Accept locally */ + RTN_BROADCAST, /* Accept locally as broadcast, + send as broadcast */ + RTN_ANYCAST, /* Accept locally as broadcast, + but send as unicast */ + RTN_MULTICAST, /* Multicast route */ + RTN_BLACKHOLE, /* Drop */ + RTN_UNREACHABLE, /* Destination is unreachable */ + RTN_PROHIBIT, /* Administratively prohibited */ + RTN_THROW, /* Not in this table */ + RTN_NAT, /* Translate this address */ + RTN_XRESOLVE, /* Use external resolver */ + __RTN_MAX +}; + +#define RTN_MAX (__RTN_MAX - 1) + + +/* rtm_protocol */ + +#define RTPROT_UNSPEC 0 +#define RTPROT_REDIRECT 1 /* Route installed by ICMP redirects; + not used by current IPv4 */ +#define RTPROT_KERNEL 2 /* Route installed by kernel */ +#define RTPROT_BOOT 3 /* Route installed during boot */ +#define RTPROT_STATIC 4 /* Route installed by administrator */ + +/* Values of protocol >= RTPROT_STATIC are not interpreted by kernel; + they are just passed from user and back as is. + It will be used by hypothetical multiple routing daemons. + Note that protocol values should be standardized in order to + avoid conflicts. + */ + +#define RTPROT_GATED 8 /* Apparently, GateD */ +#define RTPROT_RA 9 /* RDISC/ND router advertisements */ +#define RTPROT_MRT 10 /* Merit MRT */ +#define RTPROT_ZEBRA 11 /* Zebra */ +#define RTPROT_BIRD 12 /* BIRD */ +#define RTPROT_DNROUTED 13 /* DECnet routing daemon */ +#define RTPROT_XORP 14 /* XORP */ +#define RTPROT_NTK 15 /* Netsukuku */ +#define RTPROT_DHCP 16 /* DHCP client */ +#define RTPROT_MROUTED 17 /* Multicast daemon */ +#define RTPROT_BABEL 42 /* Babel daemon */ +#define RTPROT_BGP 186 /* BGP Routes */ +#define RTPROT_ISIS 187 /* ISIS Routes */ +#define RTPROT_OSPF 188 /* OSPF Routes */ +#define RTPROT_RIP 189 /* RIP Routes */ +#define RTPROT_EIGRP 192 /* EIGRP Routes */ + +/* rtm_scope + + Really it is not scope, but sort of distance to the destination. + NOWHERE are reserved for not existing destinations, HOST is our + local addresses, LINK are destinations, located on directly attached + link and UNIVERSE is everywhere in the Universe. + + Intermediate values are also possible f.e. interior routes + could be assigned a value between UNIVERSE and LINK. +*/ + +enum rt_scope_t { + RT_SCOPE_UNIVERSE=0, +/* User defined values */ + RT_SCOPE_SITE=200, + RT_SCOPE_LINK=253, + RT_SCOPE_HOST=254, + RT_SCOPE_NOWHERE=255 +}; + +/* rtm_flags */ + +#define RTM_F_NOTIFY 0x100 /* Notify user of route change */ +#define RTM_F_CLONED 0x200 /* This route is cloned */ +#define RTM_F_EQUALIZE 0x400 /* Multipath equalizer: NI */ +#define RTM_F_PREFIX 0x800 /* Prefix addresses */ +#define RTM_F_LOOKUP_TABLE 0x1000 /* set rtm_table to FIB lookup result */ +#define RTM_F_FIB_MATCH 0x2000 /* return full fib lookup match */ + +/* Reserved table identifiers */ + +enum rt_class_t { + RT_TABLE_UNSPEC=0, +/* User defined values */ + RT_TABLE_COMPAT=252, + RT_TABLE_DEFAULT=253, + RT_TABLE_MAIN=254, + RT_TABLE_LOCAL=255, + RT_TABLE_MAX=0xFFFFFFFF +}; + + +/* Routing message attributes */ + +enum rtattr_type_t { + RTA_UNSPEC, + RTA_DST, + RTA_SRC, + RTA_IIF, + RTA_OIF, + RTA_GATEWAY, + RTA_PRIORITY, + RTA_PREFSRC, + RTA_METRICS, + RTA_MULTIPATH, + RTA_PROTOINFO, /* no longer used */ + RTA_FLOW, + RTA_CACHEINFO, + RTA_SESSION, /* no longer used */ + RTA_MP_ALGO, /* no longer used */ + RTA_TABLE, + RTA_MARK, + RTA_MFC_STATS, + RTA_VIA, + RTA_NEWDST, + RTA_PREF, + RTA_ENCAP_TYPE, + RTA_ENCAP, + RTA_EXPIRES, + RTA_PAD, + RTA_UID, + RTA_TTL_PROPAGATE, + RTA_IP_PROTO, + RTA_SPORT, + RTA_DPORT, + __RTA_MAX +}; + +#define RTA_MAX (__RTA_MAX - 1) + +#define RTM_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct rtmsg)))) +#define RTM_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct rtmsg)) + +/* RTM_MULTIPATH --- array of struct rtnexthop. + * + * "struct rtnexthop" describes all necessary nexthop information, + * i.e. parameters of path to a destination via this nexthop. + * + * At the moment it is impossible to set different prefsrc, mtu, window + * and rtt for different paths from multipath. + */ + +struct rtnexthop { + unsigned short rtnh_len; + unsigned char rtnh_flags; + unsigned char rtnh_hops; + int rtnh_ifindex; +}; + +/* rtnh_flags */ + +#define RTNH_F_DEAD 1 /* Nexthop is dead (used by multipath) */ +#define RTNH_F_PERVASIVE 2 /* Do recursive gateway lookup */ +#define RTNH_F_ONLINK 4 /* Gateway is forced on link */ +#define RTNH_F_OFFLOAD 8 /* offloaded route */ +#define RTNH_F_LINKDOWN 16 /* carrier-down on nexthop */ +#define RTNH_F_UNRESOLVED 32 /* The entry is unresolved (ipmr) */ + +#define RTNH_COMPARE_MASK (RTNH_F_DEAD | RTNH_F_LINKDOWN | RTNH_F_OFFLOAD) + +/* Macros to handle hexthops */ + +#define RTNH_ALIGNTO 4 +#define RTNH_ALIGN(len) ( ((len)+RTNH_ALIGNTO-1) & ~(RTNH_ALIGNTO-1) ) +#define RTNH_OK(rtnh,len) ((rtnh)->rtnh_len >= sizeof(struct rtnexthop) && \ + ((int)(rtnh)->rtnh_len) <= (len)) +#define RTNH_NEXT(rtnh) ((struct rtnexthop*)(((char*)(rtnh)) + RTNH_ALIGN((rtnh)->rtnh_len))) +#define RTNH_LENGTH(len) (RTNH_ALIGN(sizeof(struct rtnexthop)) + (len)) +#define RTNH_SPACE(len) RTNH_ALIGN(RTNH_LENGTH(len)) +#define RTNH_DATA(rtnh) ((struct rtattr*)(((char*)(rtnh)) + RTNH_LENGTH(0))) + +/* RTA_VIA */ +struct rtvia { + __kernel_sa_family_t rtvia_family; + __u8 rtvia_addr[0]; +}; + +/* RTM_CACHEINFO */ + +struct rta_cacheinfo { + __u32 rta_clntref; + __u32 rta_lastuse; + __s32 rta_expires; + __u32 rta_error; + __u32 rta_used; + +#define RTNETLINK_HAVE_PEERINFO 1 + __u32 rta_id; + __u32 rta_ts; + __u32 rta_tsage; +}; + +/* RTM_METRICS --- array of struct rtattr with types of RTAX_* */ + +enum { + RTAX_UNSPEC, +#define RTAX_UNSPEC RTAX_UNSPEC + RTAX_LOCK, +#define RTAX_LOCK RTAX_LOCK + RTAX_MTU, +#define RTAX_MTU RTAX_MTU + RTAX_WINDOW, +#define RTAX_WINDOW RTAX_WINDOW + RTAX_RTT, +#define RTAX_RTT RTAX_RTT + RTAX_RTTVAR, +#define RTAX_RTTVAR RTAX_RTTVAR + RTAX_SSTHRESH, +#define RTAX_SSTHRESH RTAX_SSTHRESH + RTAX_CWND, +#define RTAX_CWND RTAX_CWND + RTAX_ADVMSS, +#define RTAX_ADVMSS RTAX_ADVMSS + RTAX_REORDERING, +#define RTAX_REORDERING RTAX_REORDERING + RTAX_HOPLIMIT, +#define RTAX_HOPLIMIT RTAX_HOPLIMIT + RTAX_INITCWND, +#define RTAX_INITCWND RTAX_INITCWND + RTAX_FEATURES, +#define RTAX_FEATURES RTAX_FEATURES + RTAX_RTO_MIN, +#define RTAX_RTO_MIN RTAX_RTO_MIN + RTAX_INITRWND, +#define RTAX_INITRWND RTAX_INITRWND + RTAX_QUICKACK, +#define RTAX_QUICKACK RTAX_QUICKACK + RTAX_CC_ALGO, +#define RTAX_CC_ALGO RTAX_CC_ALGO + RTAX_FASTOPEN_NO_COOKIE, +#define RTAX_FASTOPEN_NO_COOKIE RTAX_FASTOPEN_NO_COOKIE + __RTAX_MAX +}; + +#define RTAX_MAX (__RTAX_MAX - 1) + +#define RTAX_FEATURE_ECN (1 << 0) +#define RTAX_FEATURE_SACK (1 << 1) +#define RTAX_FEATURE_TIMESTAMP (1 << 2) +#define RTAX_FEATURE_ALLFRAG (1 << 3) + +#define RTAX_FEATURE_MASK (RTAX_FEATURE_ECN | RTAX_FEATURE_SACK | \ + RTAX_FEATURE_TIMESTAMP | RTAX_FEATURE_ALLFRAG) + +struct rta_session { + __u8 proto; + __u8 pad1; + __u16 pad2; + + union { + struct { + __u16 sport; + __u16 dport; + } ports; + + struct { + __u8 type; + __u8 code; + __u16 ident; + } icmpt; + + __u32 spi; + } u; +}; + +struct rta_mfc_stats { + __u64 mfcs_packets; + __u64 mfcs_bytes; + __u64 mfcs_wrong_if; +}; + +/**** + * General form of address family dependent message. + ****/ + +struct rtgenmsg { + unsigned char rtgen_family; +}; + +/***************************************************************** + * Link layer specific messages. + ****/ + +/* struct ifinfomsg + * passes link level specific information, not dependent + * on network protocol. + */ + +struct ifinfomsg { + unsigned char ifi_family; + unsigned char __ifi_pad; + unsigned short ifi_type; /* ARPHRD_* */ + int ifi_index; /* Link index */ + unsigned ifi_flags; /* IFF_* flags */ + unsigned ifi_change; /* IFF_* change mask */ +}; + +/******************************************************************** + * prefix information + ****/ + +struct prefixmsg { + unsigned char prefix_family; + unsigned char prefix_pad1; + unsigned short prefix_pad2; + int prefix_ifindex; + unsigned char prefix_type; + unsigned char prefix_len; + unsigned char prefix_flags; + unsigned char prefix_pad3; +}; + +enum +{ + PREFIX_UNSPEC, + PREFIX_ADDRESS, + PREFIX_CACHEINFO, + __PREFIX_MAX +}; + +#define PREFIX_MAX (__PREFIX_MAX - 1) + +struct prefix_cacheinfo { + __u32 preferred_time; + __u32 valid_time; +}; + + +/***************************************************************** + * Traffic control messages. + ****/ + +struct tcmsg { + unsigned char tcm_family; + unsigned char tcm__pad1; + unsigned short tcm__pad2; + int tcm_ifindex; + __u32 tcm_handle; + __u32 tcm_parent; +/* tcm_block_index is used instead of tcm_parent + * in case tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK + */ +#define tcm_block_index tcm_parent + __u32 tcm_info; +}; + +/* For manipulation of filters in shared block, tcm_ifindex is set to + * TCM_IFINDEX_MAGIC_BLOCK, and tcm_parent is aliased to tcm_block_index + * which is the block index. + */ +#define TCM_IFINDEX_MAGIC_BLOCK (0xFFFFFFFFU) + +enum { + TCA_UNSPEC, + TCA_KIND, + TCA_OPTIONS, + TCA_STATS, + TCA_XSTATS, + TCA_RATE, + TCA_FCNT, + TCA_STATS2, + TCA_STAB, + TCA_PAD, + TCA_DUMP_INVISIBLE, + TCA_CHAIN, + TCA_HW_OFFLOAD, + TCA_INGRESS_BLOCK, + TCA_EGRESS_BLOCK, + __TCA_MAX +}; + +#define TCA_MAX (__TCA_MAX - 1) + +#define TCA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcmsg)))) +#define TCA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcmsg)) + +/******************************************************************** + * Neighbor Discovery userland options + ****/ + +struct nduseroptmsg { + unsigned char nduseropt_family; + unsigned char nduseropt_pad1; + unsigned short nduseropt_opts_len; /* Total length of options */ + int nduseropt_ifindex; + __u8 nduseropt_icmp_type; + __u8 nduseropt_icmp_code; + unsigned short nduseropt_pad2; + unsigned int nduseropt_pad3; + /* Followed by one or more ND options */ +}; + +enum { + NDUSEROPT_UNSPEC, + NDUSEROPT_SRCADDR, + __NDUSEROPT_MAX +}; + +#define NDUSEROPT_MAX (__NDUSEROPT_MAX - 1) + +/* RTnetlink multicast groups - backwards compatibility for userspace */ +#define RTMGRP_LINK 1 +#define RTMGRP_NOTIFY 2 +#define RTMGRP_NEIGH 4 +#define RTMGRP_TC 8 + +#define RTMGRP_IPV4_IFADDR 0x10 +#define RTMGRP_IPV4_MROUTE 0x20 +#define RTMGRP_IPV4_ROUTE 0x40 +#define RTMGRP_IPV4_RULE 0x80 + +#define RTMGRP_IPV6_IFADDR 0x100 +#define RTMGRP_IPV6_MROUTE 0x200 +#define RTMGRP_IPV6_ROUTE 0x400 +#define RTMGRP_IPV6_IFINFO 0x800 + +#define RTMGRP_DECnet_IFADDR 0x1000 +#define RTMGRP_DECnet_ROUTE 0x4000 + +#define RTMGRP_IPV6_PREFIX 0x20000 + +/* RTnetlink multicast groups */ +enum rtnetlink_groups { + RTNLGRP_NONE, +#define RTNLGRP_NONE RTNLGRP_NONE + RTNLGRP_LINK, +#define RTNLGRP_LINK RTNLGRP_LINK + RTNLGRP_NOTIFY, +#define RTNLGRP_NOTIFY RTNLGRP_NOTIFY + RTNLGRP_NEIGH, +#define RTNLGRP_NEIGH RTNLGRP_NEIGH + RTNLGRP_TC, +#define RTNLGRP_TC RTNLGRP_TC + RTNLGRP_IPV4_IFADDR, +#define RTNLGRP_IPV4_IFADDR RTNLGRP_IPV4_IFADDR + RTNLGRP_IPV4_MROUTE, +#define RTNLGRP_IPV4_MROUTE RTNLGRP_IPV4_MROUTE + RTNLGRP_IPV4_ROUTE, +#define RTNLGRP_IPV4_ROUTE RTNLGRP_IPV4_ROUTE + RTNLGRP_IPV4_RULE, +#define RTNLGRP_IPV4_RULE RTNLGRP_IPV4_RULE + RTNLGRP_IPV6_IFADDR, +#define RTNLGRP_IPV6_IFADDR RTNLGRP_IPV6_IFADDR + RTNLGRP_IPV6_MROUTE, +#define RTNLGRP_IPV6_MROUTE RTNLGRP_IPV6_MROUTE + RTNLGRP_IPV6_ROUTE, +#define RTNLGRP_IPV6_ROUTE RTNLGRP_IPV6_ROUTE + RTNLGRP_IPV6_IFINFO, +#define RTNLGRP_IPV6_IFINFO RTNLGRP_IPV6_IFINFO + RTNLGRP_DECnet_IFADDR, +#define RTNLGRP_DECnet_IFADDR RTNLGRP_DECnet_IFADDR + RTNLGRP_NOP2, + RTNLGRP_DECnet_ROUTE, +#define RTNLGRP_DECnet_ROUTE RTNLGRP_DECnet_ROUTE + RTNLGRP_DECnet_RULE, +#define RTNLGRP_DECnet_RULE RTNLGRP_DECnet_RULE + RTNLGRP_NOP4, + RTNLGRP_IPV6_PREFIX, +#define RTNLGRP_IPV6_PREFIX RTNLGRP_IPV6_PREFIX + RTNLGRP_IPV6_RULE, +#define RTNLGRP_IPV6_RULE RTNLGRP_IPV6_RULE + RTNLGRP_ND_USEROPT, +#define RTNLGRP_ND_USEROPT RTNLGRP_ND_USEROPT + RTNLGRP_PHONET_IFADDR, +#define RTNLGRP_PHONET_IFADDR RTNLGRP_PHONET_IFADDR + RTNLGRP_PHONET_ROUTE, +#define RTNLGRP_PHONET_ROUTE RTNLGRP_PHONET_ROUTE + RTNLGRP_DCB, +#define RTNLGRP_DCB RTNLGRP_DCB + RTNLGRP_IPV4_NETCONF, +#define RTNLGRP_IPV4_NETCONF RTNLGRP_IPV4_NETCONF + RTNLGRP_IPV6_NETCONF, +#define RTNLGRP_IPV6_NETCONF RTNLGRP_IPV6_NETCONF + RTNLGRP_MDB, +#define RTNLGRP_MDB RTNLGRP_MDB + RTNLGRP_MPLS_ROUTE, +#define RTNLGRP_MPLS_ROUTE RTNLGRP_MPLS_ROUTE + RTNLGRP_NSID, +#define RTNLGRP_NSID RTNLGRP_NSID + RTNLGRP_MPLS_NETCONF, +#define RTNLGRP_MPLS_NETCONF RTNLGRP_MPLS_NETCONF + RTNLGRP_IPV4_MROUTE_R, +#define RTNLGRP_IPV4_MROUTE_R RTNLGRP_IPV4_MROUTE_R + RTNLGRP_IPV6_MROUTE_R, +#define RTNLGRP_IPV6_MROUTE_R RTNLGRP_IPV6_MROUTE_R + __RTNLGRP_MAX +}; +#define RTNLGRP_MAX (__RTNLGRP_MAX - 1) + +/* TC action piece */ +struct tcamsg { + unsigned char tca_family; + unsigned char tca__pad1; + unsigned short tca__pad2; +}; + +enum { + TCA_ROOT_UNSPEC, + TCA_ROOT_TAB, +#define TCA_ACT_TAB TCA_ROOT_TAB +#define TCAA_MAX TCA_ROOT_TAB + TCA_ROOT_FLAGS, + TCA_ROOT_COUNT, + TCA_ROOT_TIME_DELTA, /* in msecs */ + __TCA_ROOT_MAX, +#define TCA_ROOT_MAX (__TCA_ROOT_MAX - 1) +}; + +#define TA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcamsg)))) +#define TA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcamsg)) +/* tcamsg flags stored in attribute TCA_ROOT_FLAGS + * + * TCA_FLAG_LARGE_DUMP_ON user->kernel to request for larger than TCA_ACT_MAX_PRIO + * actions in a dump. All dump responses will contain the number of actions + * being dumped stored in for user app's consumption in TCA_ROOT_COUNT + * + */ +#define TCA_FLAG_LARGE_DUMP_ON (1 << 0) + +/* New extended info filters for IFLA_EXT_MASK */ +#define RTEXT_FILTER_VF (1 << 0) +#define RTEXT_FILTER_BRVLAN (1 << 1) +#define RTEXT_FILTER_BRVLAN_COMPRESSED (1 << 2) +#define RTEXT_FILTER_SKIP_STATS (1 << 3) + +/* End of information exported to user level */ + + + +#endif /* __LINUX_RTNETLINK_H */ diff --git a/libnl/include/linux-private/linux/snmp.h b/libnl/include/linux-private/linux/snmp.h new file mode 100644 index 0000000..abae27c --- /dev/null +++ b/libnl/include/linux-private/linux/snmp.h @@ -0,0 +1,324 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Definitions for MIBs + * + * Author: Hideaki YOSHIFUJI + */ + +#ifndef _LINUX_SNMP_H +#define _LINUX_SNMP_H + +/* ipstats mib definitions */ +/* + * RFC 1213: MIB-II + * RFC 2011 (updates 1213): SNMPv2-MIB-IP + * RFC 2863: Interfaces Group MIB + * RFC 2465: IPv6 MIB: General Group + * draft-ietf-ipv6-rfc2011-update-10.txt: MIB for IP: IP Statistics Tables + */ +enum +{ + IPSTATS_MIB_NUM = 0, +/* frequently written fields in fast path, kept in same cache line */ + IPSTATS_MIB_INPKTS, /* InReceives */ + IPSTATS_MIB_INOCTETS, /* InOctets */ + IPSTATS_MIB_INDELIVERS, /* InDelivers */ + IPSTATS_MIB_OUTFORWDATAGRAMS, /* OutForwDatagrams */ + IPSTATS_MIB_OUTPKTS, /* OutRequests */ + IPSTATS_MIB_OUTOCTETS, /* OutOctets */ +/* other fields */ + IPSTATS_MIB_INHDRERRORS, /* InHdrErrors */ + IPSTATS_MIB_INTOOBIGERRORS, /* InTooBigErrors */ + IPSTATS_MIB_INNOROUTES, /* InNoRoutes */ + IPSTATS_MIB_INADDRERRORS, /* InAddrErrors */ + IPSTATS_MIB_INUNKNOWNPROTOS, /* InUnknownProtos */ + IPSTATS_MIB_INTRUNCATEDPKTS, /* InTruncatedPkts */ + IPSTATS_MIB_INDISCARDS, /* InDiscards */ + IPSTATS_MIB_OUTDISCARDS, /* OutDiscards */ + IPSTATS_MIB_OUTNOROUTES, /* OutNoRoutes */ + IPSTATS_MIB_REASMTIMEOUT, /* ReasmTimeout */ + IPSTATS_MIB_REASMREQDS, /* ReasmReqds */ + IPSTATS_MIB_REASMOKS, /* ReasmOKs */ + IPSTATS_MIB_REASMFAILS, /* ReasmFails */ + IPSTATS_MIB_FRAGOKS, /* FragOKs */ + IPSTATS_MIB_FRAGFAILS, /* FragFails */ + IPSTATS_MIB_FRAGCREATES, /* FragCreates */ + IPSTATS_MIB_INMCASTPKTS, /* InMcastPkts */ + IPSTATS_MIB_OUTMCASTPKTS, /* OutMcastPkts */ + IPSTATS_MIB_INBCASTPKTS, /* InBcastPkts */ + IPSTATS_MIB_OUTBCASTPKTS, /* OutBcastPkts */ + IPSTATS_MIB_INMCASTOCTETS, /* InMcastOctets */ + IPSTATS_MIB_OUTMCASTOCTETS, /* OutMcastOctets */ + IPSTATS_MIB_INBCASTOCTETS, /* InBcastOctets */ + IPSTATS_MIB_OUTBCASTOCTETS, /* OutBcastOctets */ + IPSTATS_MIB_CSUMERRORS, /* InCsumErrors */ + IPSTATS_MIB_NOECTPKTS, /* InNoECTPkts */ + IPSTATS_MIB_ECT1PKTS, /* InECT1Pkts */ + IPSTATS_MIB_ECT0PKTS, /* InECT0Pkts */ + IPSTATS_MIB_CEPKTS, /* InCEPkts */ + IPSTATS_MIB_REASM_OVERLAPS, /* ReasmOverlaps */ + __IPSTATS_MIB_MAX +}; + +/* icmp mib definitions */ +/* + * RFC 1213: MIB-II ICMP Group + * RFC 2011 (updates 1213): SNMPv2 MIB for IP: ICMP group + */ +enum +{ + ICMP_MIB_NUM = 0, + ICMP_MIB_INMSGS, /* InMsgs */ + ICMP_MIB_INERRORS, /* InErrors */ + ICMP_MIB_INDESTUNREACHS, /* InDestUnreachs */ + ICMP_MIB_INTIMEEXCDS, /* InTimeExcds */ + ICMP_MIB_INPARMPROBS, /* InParmProbs */ + ICMP_MIB_INSRCQUENCHS, /* InSrcQuenchs */ + ICMP_MIB_INREDIRECTS, /* InRedirects */ + ICMP_MIB_INECHOS, /* InEchos */ + ICMP_MIB_INECHOREPS, /* InEchoReps */ + ICMP_MIB_INTIMESTAMPS, /* InTimestamps */ + ICMP_MIB_INTIMESTAMPREPS, /* InTimestampReps */ + ICMP_MIB_INADDRMASKS, /* InAddrMasks */ + ICMP_MIB_INADDRMASKREPS, /* InAddrMaskReps */ + ICMP_MIB_OUTMSGS, /* OutMsgs */ + ICMP_MIB_OUTERRORS, /* OutErrors */ + ICMP_MIB_OUTDESTUNREACHS, /* OutDestUnreachs */ + ICMP_MIB_OUTTIMEEXCDS, /* OutTimeExcds */ + ICMP_MIB_OUTPARMPROBS, /* OutParmProbs */ + ICMP_MIB_OUTSRCQUENCHS, /* OutSrcQuenchs */ + ICMP_MIB_OUTREDIRECTS, /* OutRedirects */ + ICMP_MIB_OUTECHOS, /* OutEchos */ + ICMP_MIB_OUTECHOREPS, /* OutEchoReps */ + ICMP_MIB_OUTTIMESTAMPS, /* OutTimestamps */ + ICMP_MIB_OUTTIMESTAMPREPS, /* OutTimestampReps */ + ICMP_MIB_OUTADDRMASKS, /* OutAddrMasks */ + ICMP_MIB_OUTADDRMASKREPS, /* OutAddrMaskReps */ + ICMP_MIB_CSUMERRORS, /* InCsumErrors */ + __ICMP_MIB_MAX +}; + +#define __ICMPMSG_MIB_MAX 512 /* Out+In for all 8-bit ICMP types */ + +/* icmp6 mib definitions */ +/* + * RFC 2466: ICMPv6-MIB + */ +enum +{ + ICMP6_MIB_NUM = 0, + ICMP6_MIB_INMSGS, /* InMsgs */ + ICMP6_MIB_INERRORS, /* InErrors */ + ICMP6_MIB_OUTMSGS, /* OutMsgs */ + ICMP6_MIB_OUTERRORS, /* OutErrors */ + ICMP6_MIB_CSUMERRORS, /* InCsumErrors */ + __ICMP6_MIB_MAX +}; + +#define __ICMP6MSG_MIB_MAX 512 /* Out+In for all 8-bit ICMPv6 types */ + +/* tcp mib definitions */ +/* + * RFC 1213: MIB-II TCP group + * RFC 2012 (updates 1213): SNMPv2-MIB-TCP + */ +enum +{ + TCP_MIB_NUM = 0, + TCP_MIB_RTOALGORITHM, /* RtoAlgorithm */ + TCP_MIB_RTOMIN, /* RtoMin */ + TCP_MIB_RTOMAX, /* RtoMax */ + TCP_MIB_MAXCONN, /* MaxConn */ + TCP_MIB_ACTIVEOPENS, /* ActiveOpens */ + TCP_MIB_PASSIVEOPENS, /* PassiveOpens */ + TCP_MIB_ATTEMPTFAILS, /* AttemptFails */ + TCP_MIB_ESTABRESETS, /* EstabResets */ + TCP_MIB_CURRESTAB, /* CurrEstab */ + TCP_MIB_INSEGS, /* InSegs */ + TCP_MIB_OUTSEGS, /* OutSegs */ + TCP_MIB_RETRANSSEGS, /* RetransSegs */ + TCP_MIB_INERRS, /* InErrs */ + TCP_MIB_OUTRSTS, /* OutRsts */ + TCP_MIB_CSUMERRORS, /* InCsumErrors */ + __TCP_MIB_MAX +}; + +/* udp mib definitions */ +/* + * RFC 1213: MIB-II UDP group + * RFC 2013 (updates 1213): SNMPv2-MIB-UDP + */ +enum +{ + UDP_MIB_NUM = 0, + UDP_MIB_INDATAGRAMS, /* InDatagrams */ + UDP_MIB_NOPORTS, /* NoPorts */ + UDP_MIB_INERRORS, /* InErrors */ + UDP_MIB_OUTDATAGRAMS, /* OutDatagrams */ + UDP_MIB_RCVBUFERRORS, /* RcvbufErrors */ + UDP_MIB_SNDBUFERRORS, /* SndbufErrors */ + UDP_MIB_CSUMERRORS, /* InCsumErrors */ + UDP_MIB_IGNOREDMULTI, /* IgnoredMulti */ + __UDP_MIB_MAX +}; + +/* linux mib definitions */ +enum +{ + LINUX_MIB_NUM = 0, + LINUX_MIB_SYNCOOKIESSENT, /* SyncookiesSent */ + LINUX_MIB_SYNCOOKIESRECV, /* SyncookiesRecv */ + LINUX_MIB_SYNCOOKIESFAILED, /* SyncookiesFailed */ + LINUX_MIB_EMBRYONICRSTS, /* EmbryonicRsts */ + LINUX_MIB_PRUNECALLED, /* PruneCalled */ + LINUX_MIB_RCVPRUNED, /* RcvPruned */ + LINUX_MIB_OFOPRUNED, /* OfoPruned */ + LINUX_MIB_OUTOFWINDOWICMPS, /* OutOfWindowIcmps */ + LINUX_MIB_LOCKDROPPEDICMPS, /* LockDroppedIcmps */ + LINUX_MIB_ARPFILTER, /* ArpFilter */ + LINUX_MIB_TIMEWAITED, /* TimeWaited */ + LINUX_MIB_TIMEWAITRECYCLED, /* TimeWaitRecycled */ + LINUX_MIB_TIMEWAITKILLED, /* TimeWaitKilled */ + LINUX_MIB_PAWSACTIVEREJECTED, /* PAWSActiveRejected */ + LINUX_MIB_PAWSESTABREJECTED, /* PAWSEstabRejected */ + LINUX_MIB_DELAYEDACKS, /* DelayedACKs */ + LINUX_MIB_DELAYEDACKLOCKED, /* DelayedACKLocked */ + LINUX_MIB_DELAYEDACKLOST, /* DelayedACKLost */ + LINUX_MIB_LISTENOVERFLOWS, /* ListenOverflows */ + LINUX_MIB_LISTENDROPS, /* ListenDrops */ + LINUX_MIB_TCPHPHITS, /* TCPHPHits */ + LINUX_MIB_TCPPUREACKS, /* TCPPureAcks */ + LINUX_MIB_TCPHPACKS, /* TCPHPAcks */ + LINUX_MIB_TCPRENORECOVERY, /* TCPRenoRecovery */ + LINUX_MIB_TCPSACKRECOVERY, /* TCPSackRecovery */ + LINUX_MIB_TCPSACKRENEGING, /* TCPSACKReneging */ + LINUX_MIB_TCPSACKREORDER, /* TCPSACKReorder */ + LINUX_MIB_TCPRENOREORDER, /* TCPRenoReorder */ + LINUX_MIB_TCPTSREORDER, /* TCPTSReorder */ + LINUX_MIB_TCPFULLUNDO, /* TCPFullUndo */ + LINUX_MIB_TCPPARTIALUNDO, /* TCPPartialUndo */ + LINUX_MIB_TCPDSACKUNDO, /* TCPDSACKUndo */ + LINUX_MIB_TCPLOSSUNDO, /* TCPLossUndo */ + LINUX_MIB_TCPLOSTRETRANSMIT, /* TCPLostRetransmit */ + LINUX_MIB_TCPRENOFAILURES, /* TCPRenoFailures */ + LINUX_MIB_TCPSACKFAILURES, /* TCPSackFailures */ + LINUX_MIB_TCPLOSSFAILURES, /* TCPLossFailures */ + LINUX_MIB_TCPFASTRETRANS, /* TCPFastRetrans */ + LINUX_MIB_TCPSLOWSTARTRETRANS, /* TCPSlowStartRetrans */ + LINUX_MIB_TCPTIMEOUTS, /* TCPTimeouts */ + LINUX_MIB_TCPLOSSPROBES, /* TCPLossProbes */ + LINUX_MIB_TCPLOSSPROBERECOVERY, /* TCPLossProbeRecovery */ + LINUX_MIB_TCPRENORECOVERYFAIL, /* TCPRenoRecoveryFail */ + LINUX_MIB_TCPSACKRECOVERYFAIL, /* TCPSackRecoveryFail */ + LINUX_MIB_TCPRCVCOLLAPSED, /* TCPRcvCollapsed */ + LINUX_MIB_TCPDSACKOLDSENT, /* TCPDSACKOldSent */ + LINUX_MIB_TCPDSACKOFOSENT, /* TCPDSACKOfoSent */ + LINUX_MIB_TCPDSACKRECV, /* TCPDSACKRecv */ + LINUX_MIB_TCPDSACKOFORECV, /* TCPDSACKOfoRecv */ + LINUX_MIB_TCPABORTONDATA, /* TCPAbortOnData */ + LINUX_MIB_TCPABORTONCLOSE, /* TCPAbortOnClose */ + LINUX_MIB_TCPABORTONMEMORY, /* TCPAbortOnMemory */ + LINUX_MIB_TCPABORTONTIMEOUT, /* TCPAbortOnTimeout */ + LINUX_MIB_TCPABORTONLINGER, /* TCPAbortOnLinger */ + LINUX_MIB_TCPABORTFAILED, /* TCPAbortFailed */ + LINUX_MIB_TCPMEMORYPRESSURES, /* TCPMemoryPressures */ + LINUX_MIB_TCPMEMORYPRESSURESCHRONO, /* TCPMemoryPressuresChrono */ + LINUX_MIB_TCPSACKDISCARD, /* TCPSACKDiscard */ + LINUX_MIB_TCPDSACKIGNOREDOLD, /* TCPSACKIgnoredOld */ + LINUX_MIB_TCPDSACKIGNOREDNOUNDO, /* TCPSACKIgnoredNoUndo */ + LINUX_MIB_TCPSPURIOUSRTOS, /* TCPSpuriousRTOs */ + LINUX_MIB_TCPMD5NOTFOUND, /* TCPMD5NotFound */ + LINUX_MIB_TCPMD5UNEXPECTED, /* TCPMD5Unexpected */ + LINUX_MIB_TCPMD5FAILURE, /* TCPMD5Failure */ + LINUX_MIB_SACKSHIFTED, + LINUX_MIB_SACKMERGED, + LINUX_MIB_SACKSHIFTFALLBACK, + LINUX_MIB_TCPBACKLOGDROP, + LINUX_MIB_PFMEMALLOCDROP, + LINUX_MIB_TCPMINTTLDROP, /* RFC 5082 */ + LINUX_MIB_TCPDEFERACCEPTDROP, + LINUX_MIB_IPRPFILTER, /* IP Reverse Path Filter (rp_filter) */ + LINUX_MIB_TCPTIMEWAITOVERFLOW, /* TCPTimeWaitOverflow */ + LINUX_MIB_TCPREQQFULLDOCOOKIES, /* TCPReqQFullDoCookies */ + LINUX_MIB_TCPREQQFULLDROP, /* TCPReqQFullDrop */ + LINUX_MIB_TCPRETRANSFAIL, /* TCPRetransFail */ + LINUX_MIB_TCPRCVCOALESCE, /* TCPRcvCoalesce */ + LINUX_MIB_TCPOFOQUEUE, /* TCPOFOQueue */ + LINUX_MIB_TCPOFODROP, /* TCPOFODrop */ + LINUX_MIB_TCPOFOMERGE, /* TCPOFOMerge */ + LINUX_MIB_TCPCHALLENGEACK, /* TCPChallengeACK */ + LINUX_MIB_TCPSYNCHALLENGE, /* TCPSYNChallenge */ + LINUX_MIB_TCPFASTOPENACTIVE, /* TCPFastOpenActive */ + LINUX_MIB_TCPFASTOPENACTIVEFAIL, /* TCPFastOpenActiveFail */ + LINUX_MIB_TCPFASTOPENPASSIVE, /* TCPFastOpenPassive*/ + LINUX_MIB_TCPFASTOPENPASSIVEFAIL, /* TCPFastOpenPassiveFail */ + LINUX_MIB_TCPFASTOPENLISTENOVERFLOW, /* TCPFastOpenListenOverflow */ + LINUX_MIB_TCPFASTOPENCOOKIEREQD, /* TCPFastOpenCookieReqd */ + LINUX_MIB_TCPFASTOPENBLACKHOLE, /* TCPFastOpenBlackholeDetect */ + LINUX_MIB_TCPSPURIOUS_RTX_HOSTQUEUES, /* TCPSpuriousRtxHostQueues */ + LINUX_MIB_BUSYPOLLRXPACKETS, /* BusyPollRxPackets */ + LINUX_MIB_TCPAUTOCORKING, /* TCPAutoCorking */ + LINUX_MIB_TCPFROMZEROWINDOWADV, /* TCPFromZeroWindowAdv */ + LINUX_MIB_TCPTOZEROWINDOWADV, /* TCPToZeroWindowAdv */ + LINUX_MIB_TCPWANTZEROWINDOWADV, /* TCPWantZeroWindowAdv */ + LINUX_MIB_TCPSYNRETRANS, /* TCPSynRetrans */ + LINUX_MIB_TCPORIGDATASENT, /* TCPOrigDataSent */ + LINUX_MIB_TCPHYSTARTTRAINDETECT, /* TCPHystartTrainDetect */ + LINUX_MIB_TCPHYSTARTTRAINCWND, /* TCPHystartTrainCwnd */ + LINUX_MIB_TCPHYSTARTDELAYDETECT, /* TCPHystartDelayDetect */ + LINUX_MIB_TCPHYSTARTDELAYCWND, /* TCPHystartDelayCwnd */ + LINUX_MIB_TCPACKSKIPPEDSYNRECV, /* TCPACKSkippedSynRecv */ + LINUX_MIB_TCPACKSKIPPEDPAWS, /* TCPACKSkippedPAWS */ + LINUX_MIB_TCPACKSKIPPEDSEQ, /* TCPACKSkippedSeq */ + LINUX_MIB_TCPACKSKIPPEDFINWAIT2, /* TCPACKSkippedFinWait2 */ + LINUX_MIB_TCPACKSKIPPEDTIMEWAIT, /* TCPACKSkippedTimeWait */ + LINUX_MIB_TCPACKSKIPPEDCHALLENGE, /* TCPACKSkippedChallenge */ + LINUX_MIB_TCPWINPROBE, /* TCPWinProbe */ + LINUX_MIB_TCPKEEPALIVE, /* TCPKeepAlive */ + LINUX_MIB_TCPMTUPFAIL, /* TCPMTUPFail */ + LINUX_MIB_TCPMTUPSUCCESS, /* TCPMTUPSuccess */ + LINUX_MIB_TCPDELIVERED, /* TCPDelivered */ + LINUX_MIB_TCPDELIVEREDCE, /* TCPDeliveredCE */ + LINUX_MIB_TCPACKCOMPRESSED, /* TCPAckCompressed */ + LINUX_MIB_TCPZEROWINDOWDROP, /* TCPZeroWindowDrop */ + LINUX_MIB_TCPRCVQDROP, /* TCPRcvQDrop */ + LINUX_MIB_TCPWQUEUETOOBIG, /* TCPWqueueTooBig */ + __LINUX_MIB_MAX +}; + +/* linux Xfrm mib definitions */ +enum +{ + LINUX_MIB_XFRMNUM = 0, + LINUX_MIB_XFRMINERROR, /* XfrmInError */ + LINUX_MIB_XFRMINBUFFERERROR, /* XfrmInBufferError */ + LINUX_MIB_XFRMINHDRERROR, /* XfrmInHdrError */ + LINUX_MIB_XFRMINNOSTATES, /* XfrmInNoStates */ + LINUX_MIB_XFRMINSTATEPROTOERROR, /* XfrmInStateProtoError */ + LINUX_MIB_XFRMINSTATEMODEERROR, /* XfrmInStateModeError */ + LINUX_MIB_XFRMINSTATESEQERROR, /* XfrmInStateSeqError */ + LINUX_MIB_XFRMINSTATEEXPIRED, /* XfrmInStateExpired */ + LINUX_MIB_XFRMINSTATEMISMATCH, /* XfrmInStateMismatch */ + LINUX_MIB_XFRMINSTATEINVALID, /* XfrmInStateInvalid */ + LINUX_MIB_XFRMINTMPLMISMATCH, /* XfrmInTmplMismatch */ + LINUX_MIB_XFRMINNOPOLS, /* XfrmInNoPols */ + LINUX_MIB_XFRMINPOLBLOCK, /* XfrmInPolBlock */ + LINUX_MIB_XFRMINPOLERROR, /* XfrmInPolError */ + LINUX_MIB_XFRMOUTERROR, /* XfrmOutError */ + LINUX_MIB_XFRMOUTBUNDLEGENERROR, /* XfrmOutBundleGenError */ + LINUX_MIB_XFRMOUTBUNDLECHECKERROR, /* XfrmOutBundleCheckError */ + LINUX_MIB_XFRMOUTNOSTATES, /* XfrmOutNoStates */ + LINUX_MIB_XFRMOUTSTATEPROTOERROR, /* XfrmOutStateProtoError */ + LINUX_MIB_XFRMOUTSTATEMODEERROR, /* XfrmOutStateModeError */ + LINUX_MIB_XFRMOUTSTATESEQERROR, /* XfrmOutStateSeqError */ + LINUX_MIB_XFRMOUTSTATEEXPIRED, /* XfrmOutStateExpired */ + LINUX_MIB_XFRMOUTPOLBLOCK, /* XfrmOutPolBlock */ + LINUX_MIB_XFRMOUTPOLDEAD, /* XfrmOutPolDead */ + LINUX_MIB_XFRMOUTPOLERROR, /* XfrmOutPolError */ + LINUX_MIB_XFRMFWDHDRERROR, /* XfrmFwdHdrError*/ + LINUX_MIB_XFRMOUTSTATEINVALID, /* XfrmOutStateInvalid */ + LINUX_MIB_XFRMACQUIREERROR, /* XfrmAcquireError */ + __LINUX_MIB_XFRMMAX +}; + +#endif /* _LINUX_SNMP_H */ diff --git a/libnl/include/linux-private/linux/sock_diag.h b/libnl/include/linux-private/linux/sock_diag.h new file mode 100644 index 0000000..a69cf20 --- /dev/null +++ b/libnl/include/linux-private/linux/sock_diag.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __SOCK_DIAG_H__ +#define __SOCK_DIAG_H__ + +#include + +#define SOCK_DIAG_BY_FAMILY 20 +#define SOCK_DESTROY 21 + +struct sock_diag_req { + __u8 sdiag_family; + __u8 sdiag_protocol; +}; + +enum { + SK_MEMINFO_RMEM_ALLOC, + SK_MEMINFO_RCVBUF, + SK_MEMINFO_WMEM_ALLOC, + SK_MEMINFO_SNDBUF, + SK_MEMINFO_FWD_ALLOC, + SK_MEMINFO_WMEM_QUEUED, + SK_MEMINFO_OPTMEM, + SK_MEMINFO_BACKLOG, + SK_MEMINFO_DROPS, + + SK_MEMINFO_VARS, +}; + +enum sknetlink_groups { + SKNLGRP_NONE, + SKNLGRP_INET_TCP_DESTROY, + SKNLGRP_INET_UDP_DESTROY, + SKNLGRP_INET6_TCP_DESTROY, + SKNLGRP_INET6_UDP_DESTROY, + __SKNLGRP_MAX, +}; +#define SKNLGRP_MAX (__SKNLGRP_MAX - 1) + +#endif /* __SOCK_DIAG_H__ */ diff --git a/libnl/include/linux-private/linux/socket.h b/libnl/include/linux-private/linux/socket.h new file mode 100644 index 0000000..268b948 --- /dev/null +++ b/libnl/include/linux-private/linux/socket.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_SOCKET_H +#define _LINUX_SOCKET_H + +/* + * Desired design of maximum size and alignment (see RFC2553) + */ +#define _K_SS_MAXSIZE 128 /* Implementation specific max size */ +#define _K_SS_ALIGNSIZE (__alignof__ (struct sockaddr *)) + /* Implementation specific desired alignment */ + +typedef unsigned short __kernel_sa_family_t; + +struct __kernel_sockaddr_storage { + __kernel_sa_family_t ss_family; /* address family */ + /* Following field(s) are implementation specific */ + char __data[_K_SS_MAXSIZE - sizeof(unsigned short)]; + /* space to achieve desired size, */ + /* _SS_MAXSIZE value minus size of ss_family */ +} __attribute__ ((aligned(_K_SS_ALIGNSIZE))); /* force desired alignment */ + +#endif /* _LINUX_SOCKET_H */ diff --git a/libnl/include/linux-private/linux/tc_act/tc_gact.h b/libnl/include/linux-private/linux/tc_act/tc_gact.h new file mode 100644 index 0000000..94273c3 --- /dev/null +++ b/libnl/include/linux-private/linux/tc_act/tc_gact.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_TC_GACT_H +#define __LINUX_TC_GACT_H + +#include +#include + +#define TCA_ACT_GACT 5 +struct tc_gact { + tc_gen; + +}; + +struct tc_gact_p { +#define PGACT_NONE 0 +#define PGACT_NETRAND 1 +#define PGACT_DETERM 2 +#define MAX_RAND (PGACT_DETERM + 1 ) + __u16 ptype; + __u16 pval; + int paction; +}; + +enum { + TCA_GACT_UNSPEC, + TCA_GACT_TM, + TCA_GACT_PARMS, + TCA_GACT_PROB, + TCA_GACT_PAD, + __TCA_GACT_MAX +}; +#define TCA_GACT_MAX (__TCA_GACT_MAX - 1) + +#endif diff --git a/libnl/include/linux-private/linux/tc_act/tc_mirred.h b/libnl/include/linux-private/linux/tc_act/tc_mirred.h new file mode 100644 index 0000000..5dd671c --- /dev/null +++ b/libnl/include/linux-private/linux/tc_act/tc_mirred.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_TC_MIR_H +#define __LINUX_TC_MIR_H + +#include +#include + +#define TCA_ACT_MIRRED 8 +#define TCA_EGRESS_REDIR 1 /* packet redirect to EGRESS*/ +#define TCA_EGRESS_MIRROR 2 /* mirror packet to EGRESS */ +#define TCA_INGRESS_REDIR 3 /* packet redirect to INGRESS*/ +#define TCA_INGRESS_MIRROR 4 /* mirror packet to INGRESS */ + +struct tc_mirred { + tc_gen; + int eaction; /* one of IN/EGRESS_MIRROR/REDIR */ + __u32 ifindex; /* ifindex of egress port */ +}; + +enum { + TCA_MIRRED_UNSPEC, + TCA_MIRRED_TM, + TCA_MIRRED_PARMS, + TCA_MIRRED_PAD, + __TCA_MIRRED_MAX +}; +#define TCA_MIRRED_MAX (__TCA_MIRRED_MAX - 1) + +#endif diff --git a/libnl/include/linux-private/linux/tc_act/tc_skbedit.h b/libnl/include/linux-private/linux/tc_act/tc_skbedit.h new file mode 100644 index 0000000..6de6071 --- /dev/null +++ b/libnl/include/linux-private/linux/tc_act/tc_skbedit.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Copyright (c) 2008, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + * Author: Alexander Duyck + */ + +#ifndef __LINUX_TC_SKBEDIT_H +#define __LINUX_TC_SKBEDIT_H + +#include + +#define TCA_ACT_SKBEDIT 11 + +#define SKBEDIT_F_PRIORITY 0x1 +#define SKBEDIT_F_QUEUE_MAPPING 0x2 +#define SKBEDIT_F_MARK 0x4 +#define SKBEDIT_F_PTYPE 0x8 +#define SKBEDIT_F_MASK 0x10 +#define SKBEDIT_F_INHERITDSFIELD 0x20 + +struct tc_skbedit { + tc_gen; +}; + +enum { + TCA_SKBEDIT_UNSPEC, + TCA_SKBEDIT_TM, + TCA_SKBEDIT_PARMS, + TCA_SKBEDIT_PRIORITY, + TCA_SKBEDIT_QUEUE_MAPPING, + TCA_SKBEDIT_MARK, + TCA_SKBEDIT_PAD, + TCA_SKBEDIT_PTYPE, + TCA_SKBEDIT_MASK, + TCA_SKBEDIT_FLAGS, + __TCA_SKBEDIT_MAX +}; +#define TCA_SKBEDIT_MAX (__TCA_SKBEDIT_MAX - 1) + +#endif diff --git a/libnl/include/linux-private/linux/tc_act/tc_vlan.h b/libnl/include/linux-private/linux/tc_act/tc_vlan.h new file mode 100644 index 0000000..0d7b5fd --- /dev/null +++ b/libnl/include/linux-private/linux/tc_act/tc_vlan.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * Copyright (c) 2014 Jiri Pirko + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __LINUX_TC_VLAN_H +#define __LINUX_TC_VLAN_H + +#include + +#define TCA_ACT_VLAN 12 + +#define TCA_VLAN_ACT_POP 1 +#define TCA_VLAN_ACT_PUSH 2 +#define TCA_VLAN_ACT_MODIFY 3 + +struct tc_vlan { + tc_gen; + int v_action; +}; + +enum { + TCA_VLAN_UNSPEC, + TCA_VLAN_TM, + TCA_VLAN_PARMS, + TCA_VLAN_PUSH_VLAN_ID, + TCA_VLAN_PUSH_VLAN_PROTOCOL, + TCA_VLAN_PAD, + TCA_VLAN_PUSH_VLAN_PRIORITY, + __TCA_VLAN_MAX, +}; +#define TCA_VLAN_MAX (__TCA_VLAN_MAX - 1) + +#endif diff --git a/libnl/include/linux-private/linux/tc_ematch/tc_em_meta.h b/libnl/include/linux-private/linux/tc_ematch/tc_em_meta.h new file mode 100644 index 0000000..cf30b5b --- /dev/null +++ b/libnl/include/linux-private/linux/tc_ematch/tc_em_meta.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_TC_EM_META_H +#define __LINUX_TC_EM_META_H + +#include +#include + +enum { + TCA_EM_META_UNSPEC, + TCA_EM_META_HDR, + TCA_EM_META_LVALUE, + TCA_EM_META_RVALUE, + __TCA_EM_META_MAX +}; +#define TCA_EM_META_MAX (__TCA_EM_META_MAX - 1) + +struct tcf_meta_val { + __u16 kind; + __u8 shift; + __u8 op; +}; + +#define TCF_META_TYPE_MASK (0xf << 12) +#define TCF_META_TYPE(kind) (((kind) & TCF_META_TYPE_MASK) >> 12) +#define TCF_META_ID_MASK 0x7ff +#define TCF_META_ID(kind) ((kind) & TCF_META_ID_MASK) + +enum { + TCF_META_TYPE_VAR, + TCF_META_TYPE_INT, + __TCF_META_TYPE_MAX +}; +#define TCF_META_TYPE_MAX (__TCF_META_TYPE_MAX - 1) + +enum { + TCF_META_ID_VALUE, + TCF_META_ID_RANDOM, + TCF_META_ID_LOADAVG_0, + TCF_META_ID_LOADAVG_1, + TCF_META_ID_LOADAVG_2, + TCF_META_ID_DEV, + TCF_META_ID_PRIORITY, + TCF_META_ID_PROTOCOL, + TCF_META_ID_PKTTYPE, + TCF_META_ID_PKTLEN, + TCF_META_ID_DATALEN, + TCF_META_ID_MACLEN, + TCF_META_ID_NFMARK, + TCF_META_ID_TCINDEX, + TCF_META_ID_RTCLASSID, + TCF_META_ID_RTIIF, + TCF_META_ID_SK_FAMILY, + TCF_META_ID_SK_STATE, + TCF_META_ID_SK_REUSE, + TCF_META_ID_SK_BOUND_IF, + TCF_META_ID_SK_REFCNT, + TCF_META_ID_SK_SHUTDOWN, + TCF_META_ID_SK_PROTO, + TCF_META_ID_SK_TYPE, + TCF_META_ID_SK_RCVBUF, + TCF_META_ID_SK_RMEM_ALLOC, + TCF_META_ID_SK_WMEM_ALLOC, + TCF_META_ID_SK_OMEM_ALLOC, + TCF_META_ID_SK_WMEM_QUEUED, + TCF_META_ID_SK_RCV_QLEN, + TCF_META_ID_SK_SND_QLEN, + TCF_META_ID_SK_ERR_QLEN, + TCF_META_ID_SK_FORWARD_ALLOCS, + TCF_META_ID_SK_SNDBUF, + TCF_META_ID_SK_ALLOCS, + __TCF_META_ID_SK_ROUTE_CAPS, /* unimplemented but in ABI already */ + TCF_META_ID_SK_HASH, + TCF_META_ID_SK_LINGERTIME, + TCF_META_ID_SK_ACK_BACKLOG, + TCF_META_ID_SK_MAX_ACK_BACKLOG, + TCF_META_ID_SK_PRIO, + TCF_META_ID_SK_RCVLOWAT, + TCF_META_ID_SK_RCVTIMEO, + TCF_META_ID_SK_SNDTIMEO, + TCF_META_ID_SK_SENDMSG_OFF, + TCF_META_ID_SK_WRITE_PENDING, + TCF_META_ID_VLAN_TAG, + TCF_META_ID_RXHASH, + __TCF_META_ID_MAX +}; +#define TCF_META_ID_MAX (__TCF_META_ID_MAX - 1) + +struct tcf_meta_hdr { + struct tcf_meta_val left; + struct tcf_meta_val right; +}; + +#endif diff --git a/libnl/include/linux-private/linux/veth.h b/libnl/include/linux-private/linux/veth.h new file mode 100644 index 0000000..52b58e5 --- /dev/null +++ b/libnl/include/linux-private/linux/veth.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __NET_VETH_H_ +#define __NET_VETH_H_ + +enum { + VETH_INFO_UNSPEC, + VETH_INFO_PEER, + + __VETH_INFO_MAX +#define VETH_INFO_MAX (__VETH_INFO_MAX - 1) +}; + +#endif diff --git a/libnl/include/linux-private/linux/xfrm.h b/libnl/include/linux-private/linux/xfrm.h new file mode 100644 index 0000000..5cdda9d --- /dev/null +++ b/libnl/include/linux-private/linux/xfrm.h @@ -0,0 +1,540 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_XFRM_H +#define _LINUX_XFRM_H + +#include +#include + +/* All of the structures in this file may not change size as they are + * passed into the kernel from userspace via netlink sockets. + */ + +/* Structure to encapsulate addresses. I do not want to use + * "standard" structure. My apologies. + */ +typedef union { + __be32 a4; + __be32 a6[4]; + struct in6_addr in6; +} xfrm_address_t; + +/* Ident of a specific xfrm_state. It is used on input to lookup + * the state by (spi,daddr,ah/esp) or to store information about + * spi, protocol and tunnel address on output. + */ +struct xfrm_id { + xfrm_address_t daddr; + __be32 spi; + __u8 proto; +}; + +struct xfrm_sec_ctx { + __u8 ctx_doi; + __u8 ctx_alg; + __u16 ctx_len; + __u32 ctx_sid; + char ctx_str[0]; +}; + +/* Security Context Domains of Interpretation */ +#define XFRM_SC_DOI_RESERVED 0 +#define XFRM_SC_DOI_LSM 1 + +/* Security Context Algorithms */ +#define XFRM_SC_ALG_RESERVED 0 +#define XFRM_SC_ALG_SELINUX 1 + +/* Selector, used as selector both on policy rules (SPD) and SAs. */ + +struct xfrm_selector { + xfrm_address_t daddr; + xfrm_address_t saddr; + __be16 dport; + __be16 dport_mask; + __be16 sport; + __be16 sport_mask; + __u16 family; + __u8 prefixlen_d; + __u8 prefixlen_s; + __u8 proto; + int ifindex; + __kernel_uid32_t user; +}; + +#define XFRM_INF (~(__u64)0) + +struct xfrm_lifetime_cfg { + __u64 soft_byte_limit; + __u64 hard_byte_limit; + __u64 soft_packet_limit; + __u64 hard_packet_limit; + __u64 soft_add_expires_seconds; + __u64 hard_add_expires_seconds; + __u64 soft_use_expires_seconds; + __u64 hard_use_expires_seconds; +}; + +struct xfrm_lifetime_cur { + __u64 bytes; + __u64 packets; + __u64 add_time; + __u64 use_time; +}; + +struct xfrm_replay_state { + __u32 oseq; + __u32 seq; + __u32 bitmap; +}; + +#define XFRMA_REPLAY_ESN_MAX 4096 + +struct xfrm_replay_state_esn { + unsigned int bmp_len; + __u32 oseq; + __u32 seq; + __u32 oseq_hi; + __u32 seq_hi; + __u32 replay_window; + __u32 bmp[0]; +}; + +struct xfrm_algo { + char alg_name[64]; + unsigned int alg_key_len; /* in bits */ + char alg_key[0]; +}; + +struct xfrm_algo_auth { + char alg_name[64]; + unsigned int alg_key_len; /* in bits */ + unsigned int alg_trunc_len; /* in bits */ + char alg_key[0]; +}; + +struct xfrm_algo_aead { + char alg_name[64]; + unsigned int alg_key_len; /* in bits */ + unsigned int alg_icv_len; /* in bits */ + char alg_key[0]; +}; + +struct xfrm_stats { + __u32 replay_window; + __u32 replay; + __u32 integrity_failed; +}; + +enum { + XFRM_POLICY_TYPE_MAIN = 0, + XFRM_POLICY_TYPE_SUB = 1, + XFRM_POLICY_TYPE_MAX = 2, + XFRM_POLICY_TYPE_ANY = 255 +}; + +enum { + XFRM_POLICY_IN = 0, + XFRM_POLICY_OUT = 1, + XFRM_POLICY_FWD = 2, + XFRM_POLICY_MASK = 3, + XFRM_POLICY_MAX = 3 +}; + +enum { + XFRM_SHARE_ANY, /* No limitations */ + XFRM_SHARE_SESSION, /* For this session only */ + XFRM_SHARE_USER, /* For this user only */ + XFRM_SHARE_UNIQUE /* Use once */ +}; + +#define XFRM_MODE_TRANSPORT 0 +#define XFRM_MODE_TUNNEL 1 +#define XFRM_MODE_ROUTEOPTIMIZATION 2 +#define XFRM_MODE_IN_TRIGGER 3 +#define XFRM_MODE_BEET 4 +#define XFRM_MODE_MAX 5 + +/* Netlink configuration messages. */ +enum { + XFRM_MSG_BASE = 0x10, + + XFRM_MSG_NEWSA = 0x10, +#define XFRM_MSG_NEWSA XFRM_MSG_NEWSA + XFRM_MSG_DELSA, +#define XFRM_MSG_DELSA XFRM_MSG_DELSA + XFRM_MSG_GETSA, +#define XFRM_MSG_GETSA XFRM_MSG_GETSA + + XFRM_MSG_NEWPOLICY, +#define XFRM_MSG_NEWPOLICY XFRM_MSG_NEWPOLICY + XFRM_MSG_DELPOLICY, +#define XFRM_MSG_DELPOLICY XFRM_MSG_DELPOLICY + XFRM_MSG_GETPOLICY, +#define XFRM_MSG_GETPOLICY XFRM_MSG_GETPOLICY + + XFRM_MSG_ALLOCSPI, +#define XFRM_MSG_ALLOCSPI XFRM_MSG_ALLOCSPI + XFRM_MSG_ACQUIRE, +#define XFRM_MSG_ACQUIRE XFRM_MSG_ACQUIRE + XFRM_MSG_EXPIRE, +#define XFRM_MSG_EXPIRE XFRM_MSG_EXPIRE + + XFRM_MSG_UPDPOLICY, +#define XFRM_MSG_UPDPOLICY XFRM_MSG_UPDPOLICY + XFRM_MSG_UPDSA, +#define XFRM_MSG_UPDSA XFRM_MSG_UPDSA + + XFRM_MSG_POLEXPIRE, +#define XFRM_MSG_POLEXPIRE XFRM_MSG_POLEXPIRE + + XFRM_MSG_FLUSHSA, +#define XFRM_MSG_FLUSHSA XFRM_MSG_FLUSHSA + XFRM_MSG_FLUSHPOLICY, +#define XFRM_MSG_FLUSHPOLICY XFRM_MSG_FLUSHPOLICY + + XFRM_MSG_NEWAE, +#define XFRM_MSG_NEWAE XFRM_MSG_NEWAE + XFRM_MSG_GETAE, +#define XFRM_MSG_GETAE XFRM_MSG_GETAE + + XFRM_MSG_REPORT, +#define XFRM_MSG_REPORT XFRM_MSG_REPORT + + XFRM_MSG_MIGRATE, +#define XFRM_MSG_MIGRATE XFRM_MSG_MIGRATE + + XFRM_MSG_NEWSADINFO, +#define XFRM_MSG_NEWSADINFO XFRM_MSG_NEWSADINFO + XFRM_MSG_GETSADINFO, +#define XFRM_MSG_GETSADINFO XFRM_MSG_GETSADINFO + + XFRM_MSG_NEWSPDINFO, +#define XFRM_MSG_NEWSPDINFO XFRM_MSG_NEWSPDINFO + XFRM_MSG_GETSPDINFO, +#define XFRM_MSG_GETSPDINFO XFRM_MSG_GETSPDINFO + + XFRM_MSG_MAPPING, +#define XFRM_MSG_MAPPING XFRM_MSG_MAPPING + __XFRM_MSG_MAX +}; +#define XFRM_MSG_MAX (__XFRM_MSG_MAX - 1) + +#define XFRM_NR_MSGTYPES (XFRM_MSG_MAX + 1 - XFRM_MSG_BASE) + +/* + * Generic LSM security context for comunicating to user space + * NOTE: Same format as sadb_x_sec_ctx + */ +struct xfrm_user_sec_ctx { + __u16 len; + __u16 exttype; + __u8 ctx_alg; /* LSMs: e.g., selinux == 1 */ + __u8 ctx_doi; + __u16 ctx_len; +}; + +struct xfrm_user_tmpl { + struct xfrm_id id; + __u16 family; + xfrm_address_t saddr; + __u32 reqid; + __u8 mode; + __u8 share; + __u8 optional; + __u32 aalgos; + __u32 ealgos; + __u32 calgos; +}; + +struct xfrm_encap_tmpl { + __u16 encap_type; + __be16 encap_sport; + __be16 encap_dport; + xfrm_address_t encap_oa; +}; + +/* AEVENT flags */ +enum xfrm_ae_ftype_t { + XFRM_AE_UNSPEC, + XFRM_AE_RTHR=1, /* replay threshold*/ + XFRM_AE_RVAL=2, /* replay value */ + XFRM_AE_LVAL=4, /* lifetime value */ + XFRM_AE_ETHR=8, /* expiry timer threshold */ + XFRM_AE_CR=16, /* Event cause is replay update */ + XFRM_AE_CE=32, /* Event cause is timer expiry */ + XFRM_AE_CU=64, /* Event cause is policy update */ + __XFRM_AE_MAX + +#define XFRM_AE_MAX (__XFRM_AE_MAX - 1) +}; + +struct xfrm_userpolicy_type { + __u8 type; + __u16 reserved1; + __u8 reserved2; +}; + +/* Netlink message attributes. */ +enum xfrm_attr_type_t { + XFRMA_UNSPEC, + XFRMA_ALG_AUTH, /* struct xfrm_algo */ + XFRMA_ALG_CRYPT, /* struct xfrm_algo */ + XFRMA_ALG_COMP, /* struct xfrm_algo */ + XFRMA_ENCAP, /* struct xfrm_algo + struct xfrm_encap_tmpl */ + XFRMA_TMPL, /* 1 or more struct xfrm_user_tmpl */ + XFRMA_SA, /* struct xfrm_usersa_info */ + XFRMA_POLICY, /*struct xfrm_userpolicy_info */ + XFRMA_SEC_CTX, /* struct xfrm_sec_ctx */ + XFRMA_LTIME_VAL, + XFRMA_REPLAY_VAL, + XFRMA_REPLAY_THRESH, + XFRMA_ETIMER_THRESH, + XFRMA_SRCADDR, /* xfrm_address_t */ + XFRMA_COADDR, /* xfrm_address_t */ + XFRMA_LASTUSED, /* unsigned long */ + XFRMA_POLICY_TYPE, /* struct xfrm_userpolicy_type */ + XFRMA_MIGRATE, + XFRMA_ALG_AEAD, /* struct xfrm_algo_aead */ + XFRMA_KMADDRESS, /* struct xfrm_user_kmaddress */ + XFRMA_ALG_AUTH_TRUNC, /* struct xfrm_algo_auth */ + XFRMA_MARK, /* struct xfrm_mark */ + XFRMA_TFCPAD, /* __u32 */ + XFRMA_REPLAY_ESN_VAL, /* struct xfrm_replay_state_esn */ + XFRMA_SA_EXTRA_FLAGS, /* __u32 */ + XFRMA_PROTO, /* __u8 */ + XFRMA_ADDRESS_FILTER, /* struct xfrm_address_filter */ + XFRMA_PAD, + XFRMA_OFFLOAD_DEV, /* struct xfrm_state_offload */ + XFRMA_SET_MARK, /* __u32 */ + XFRMA_SET_MARK_MASK, /* __u32 */ + XFRMA_IF_ID, /* __u32 */ + __XFRMA_MAX + +#define XFRMA_OUTPUT_MARK XFRMA_SET_MARK /* Compatibility */ +#define XFRMA_MAX (__XFRMA_MAX - 1) +}; + +struct xfrm_mark { + __u32 v; /* value */ + __u32 m; /* mask */ +}; + +enum xfrm_sadattr_type_t { + XFRMA_SAD_UNSPEC, + XFRMA_SAD_CNT, + XFRMA_SAD_HINFO, + __XFRMA_SAD_MAX + +#define XFRMA_SAD_MAX (__XFRMA_SAD_MAX - 1) +}; + +struct xfrmu_sadhinfo { + __u32 sadhcnt; /* current hash bkts */ + __u32 sadhmcnt; /* max allowed hash bkts */ +}; + +enum xfrm_spdattr_type_t { + XFRMA_SPD_UNSPEC, + XFRMA_SPD_INFO, + XFRMA_SPD_HINFO, + XFRMA_SPD_IPV4_HTHRESH, + XFRMA_SPD_IPV6_HTHRESH, + __XFRMA_SPD_MAX + +#define XFRMA_SPD_MAX (__XFRMA_SPD_MAX - 1) +}; + +struct xfrmu_spdinfo { + __u32 incnt; + __u32 outcnt; + __u32 fwdcnt; + __u32 inscnt; + __u32 outscnt; + __u32 fwdscnt; +}; + +struct xfrmu_spdhinfo { + __u32 spdhcnt; + __u32 spdhmcnt; +}; + +struct xfrmu_spdhthresh { + __u8 lbits; + __u8 rbits; +}; + +struct xfrm_usersa_info { + struct xfrm_selector sel; + struct xfrm_id id; + xfrm_address_t saddr; + struct xfrm_lifetime_cfg lft; + struct xfrm_lifetime_cur curlft; + struct xfrm_stats stats; + __u32 seq; + __u32 reqid; + __u16 family; + __u8 mode; /* XFRM_MODE_xxx */ + __u8 replay_window; + __u8 flags; +#define XFRM_STATE_NOECN 1 +#define XFRM_STATE_DECAP_DSCP 2 +#define XFRM_STATE_NOPMTUDISC 4 +#define XFRM_STATE_WILDRECV 8 +#define XFRM_STATE_ICMP 16 +#define XFRM_STATE_AF_UNSPEC 32 +#define XFRM_STATE_ALIGN4 64 +#define XFRM_STATE_ESN 128 +}; + +#define XFRM_SA_XFLAG_DONT_ENCAP_DSCP 1 + +struct xfrm_usersa_id { + xfrm_address_t daddr; + __be32 spi; + __u16 family; + __u8 proto; +}; + +struct xfrm_aevent_id { + struct xfrm_usersa_id sa_id; + xfrm_address_t saddr; + __u32 flags; + __u32 reqid; +}; + +struct xfrm_userspi_info { + struct xfrm_usersa_info info; + __u32 min; + __u32 max; +}; + +struct xfrm_userpolicy_info { + struct xfrm_selector sel; + struct xfrm_lifetime_cfg lft; + struct xfrm_lifetime_cur curlft; + __u32 priority; + __u32 index; + __u8 dir; + __u8 action; +#define XFRM_POLICY_ALLOW 0 +#define XFRM_POLICY_BLOCK 1 + __u8 flags; +#define XFRM_POLICY_LOCALOK 1 /* Allow user to override global policy */ + /* Automatically expand selector to include matching ICMP payloads. */ +#define XFRM_POLICY_ICMP 2 + __u8 share; +}; + +struct xfrm_userpolicy_id { + struct xfrm_selector sel; + __u32 index; + __u8 dir; +}; + +struct xfrm_user_acquire { + struct xfrm_id id; + xfrm_address_t saddr; + struct xfrm_selector sel; + struct xfrm_userpolicy_info policy; + __u32 aalgos; + __u32 ealgos; + __u32 calgos; + __u32 seq; +}; + +struct xfrm_user_expire { + struct xfrm_usersa_info state; + __u8 hard; +}; + +struct xfrm_user_polexpire { + struct xfrm_userpolicy_info pol; + __u8 hard; +}; + +struct xfrm_usersa_flush { + __u8 proto; +}; + +struct xfrm_user_report { + __u8 proto; + struct xfrm_selector sel; +}; + +/* Used by MIGRATE to pass addresses IKE should use to perform + * SA negotiation with the peer */ +struct xfrm_user_kmaddress { + xfrm_address_t local; + xfrm_address_t remote; + __u32 reserved; + __u16 family; +}; + +struct xfrm_user_migrate { + xfrm_address_t old_daddr; + xfrm_address_t old_saddr; + xfrm_address_t new_daddr; + xfrm_address_t new_saddr; + __u8 proto; + __u8 mode; + __u16 reserved; + __u32 reqid; + __u16 old_family; + __u16 new_family; +}; + +struct xfrm_user_mapping { + struct xfrm_usersa_id id; + __u32 reqid; + xfrm_address_t old_saddr; + xfrm_address_t new_saddr; + __be16 old_sport; + __be16 new_sport; +}; + +struct xfrm_address_filter { + xfrm_address_t saddr; + xfrm_address_t daddr; + __u16 family; + __u8 splen; + __u8 dplen; +}; + +struct xfrm_user_offload { + int ifindex; + __u8 flags; +}; +#define XFRM_OFFLOAD_IPV6 1 +#define XFRM_OFFLOAD_INBOUND 2 + +/* backwards compatibility for userspace */ +#define XFRMGRP_ACQUIRE 1 +#define XFRMGRP_EXPIRE 2 +#define XFRMGRP_SA 4 +#define XFRMGRP_POLICY 8 +#define XFRMGRP_REPORT 0x20 + +enum xfrm_nlgroups { + XFRMNLGRP_NONE, +#define XFRMNLGRP_NONE XFRMNLGRP_NONE + XFRMNLGRP_ACQUIRE, +#define XFRMNLGRP_ACQUIRE XFRMNLGRP_ACQUIRE + XFRMNLGRP_EXPIRE, +#define XFRMNLGRP_EXPIRE XFRMNLGRP_EXPIRE + XFRMNLGRP_SA, +#define XFRMNLGRP_SA XFRMNLGRP_SA + XFRMNLGRP_POLICY, +#define XFRMNLGRP_POLICY XFRMNLGRP_POLICY + XFRMNLGRP_AEVENTS, +#define XFRMNLGRP_AEVENTS XFRMNLGRP_AEVENTS + XFRMNLGRP_REPORT, +#define XFRMNLGRP_REPORT XFRMNLGRP_REPORT + XFRMNLGRP_MIGRATE, +#define XFRMNLGRP_MIGRATE XFRMNLGRP_MIGRATE + XFRMNLGRP_MAPPING, +#define XFRMNLGRP_MAPPING XFRMNLGRP_MAPPING + __XFRMNLGRP_MAX +}; +#define XFRMNLGRP_MAX (__XFRMNLGRP_MAX - 1) + +#endif /* _LINUX_XFRM_H */ diff --git a/libnl/include/netlink-private/cache-api.h b/libnl/include/netlink-private/cache-api.h new file mode 100644 index 0000000..38662b7 --- /dev/null +++ b/libnl/include/netlink-private/cache-api.h @@ -0,0 +1,265 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + */ + +#ifndef NETLINK_CACHE_API_H_ +#define NETLINK_CACHE_API_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @ingroup cache + * @defgroup cache_api Cache Implementation + * @brief + * + * @par 1) Cache Definition + * @code + * struct nl_cache_ops my_cache_ops = { + * .co_name = "route/link", + * .co_protocol = NETLINK_ROUTE, + * .co_hdrsize = sizeof(struct ifinfomsg), + * .co_obj_ops = &my_obj_ops, + * }; + * @endcode + * + * @par 2) + * @code + * // The simplest way to fill a cache is by providing a request-update + * // function which must trigger a complete dump on the kernel-side of + * // whatever the cache covers. + * static int my_request_update(struct nl_cache *cache, + * struct nl_sock *socket) + * { + * // In this example, we request a full dump of the interface table + * return nl_rtgen_request(socket, RTM_GETLINK, AF_UNSPEC, NLM_F_DUMP); + * } + * + * // The resulting netlink messages sent back will be fed into a message + * // parser one at a time. The message parser has to extract all relevant + * // information from the message and create an object reflecting the + * // contents of the message and pass it on to the parser callback function + * // provide which will add the object to the cache. + * static int my_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + * struct nlmsghdr *nlh, struct nl_parser_param *pp) + * { + * struct my_obj *obj; + * + * obj = my_obj_alloc(); + * obj->ce_msgtype = nlh->nlmsg_type; + * + * // Parse the netlink message and continue creating the object. + * + * err = pp->pp_cb((struct nl_object *) obj, pp); + * if (err < 0) + * goto errout; + * } + * + * struct nl_cache_ops my_cache_ops = { + * ... + * .co_request_update = my_request_update, + * .co_msg_parser = my_msg_parser, + * }; + * @endcode + * + * @par 3) Notification based Updates + * @code + * // Caches can be kept up-to-date based on notifications if the kernel + * // sends out notifications whenever an object is added/removed/changed. + * // + * // It is trivial to support this, first a list of groups needs to be + * // defined which are required to join in order to receive all necessary + * // notifications. The groups are separated by address family to support + * // the common situation where a separate group is used for each address + * // family. If there is only one group, simply specify AF_UNSPEC. + * static struct nl_af_group addr_groups[] = { + * { AF_INET, RTNLGRP_IPV4_IFADDR }, + * { AF_INET6, RTNLGRP_IPV6_IFADDR }, + * { END_OF_GROUP_LIST }, + * }; + * + * // In order for the caching system to know the meaning of each message + * // type it requires a table which maps each supported message type to + * // a cache action, e.g. RTM_NEWADDR means address has been added or + * // updated, RTM_DELADDR means address has been removed. + * static struct nl_cache_ops rtnl_addr_ops = { + * ... + * .co_msgtypes = { + * { RTM_NEWADDR, NL_ACT_NEW, "new" }, + * { RTM_DELADDR, NL_ACT_DEL, "del" }, + * { RTM_GETADDR, NL_ACT_GET, "get" }, + * END_OF_MSGTYPES_LIST, + * }, + * .co_groups = addr_groups, + * }; + * + * // It is now possible to keep the cache up-to-date using the cache manager. + * @endcode + * @{ + */ + +#define END_OF_MSGTYPES_LIST { -1, -1, NULL } + +/** + * Message type to cache action association + */ +struct nl_msgtype +{ + /** Netlink message type */ + int mt_id; + + /** Cache action to take */ + int mt_act; + + /** Name of operation for human-readable printing */ + char * mt_name; +}; + +/** + * Address family to netlink group association + */ +struct nl_af_group +{ + /** Address family */ + int ag_family; + + /** Netlink group identifier */ + int ag_group; +}; + +#define END_OF_GROUP_LIST AF_UNSPEC, 0 + +/** + * Parser parameters + * + * This structure is used to configure what kind of parser to use + * when parsing netlink messages to create objects. + */ +struct nl_parser_param +{ + /** Function to parse netlink messages into objects */ + int (*pp_cb)(struct nl_object *, struct nl_parser_param *); + + /** Arbitary argument to be passed to the parser */ + void * pp_arg; +}; + +/** + * Cache Operations + * + * This structure defines the characterstics of a cache type. It contains + * pointers to functions which implement the specifics of the object type + * the cache can hold. + */ +struct nl_cache_ops +{ + /** Name of cache type (must be unique) */ + char * co_name; + + /** Size of family specific netlink header */ + int co_hdrsize; + + /** Netlink protocol */ + int co_protocol; + + /** cache object hash size **/ + int co_hash_size; + + /** cache flags */ + unsigned int co_flags; + + /** Reference counter */ + unsigned int co_refcnt; + + /** Group definition */ + struct nl_af_group * co_groups; + + /** + * Called whenever an update of the cache is required. Must send + * a request message to the kernel requesting a complete dump. + */ + int (*co_request_update)(struct nl_cache *, struct nl_sock *); + + /** + * Called whenever a message was received that needs to be parsed. + * Must parse the message and call the paser callback function + * (nl_parser_param) provided via the argument. + */ + int (*co_msg_parser)(struct nl_cache_ops *, struct sockaddr_nl *, + struct nlmsghdr *, struct nl_parser_param *); + + /** + * The function registered under this callback is called after a + * netlink notification associated with this cache type has been + * parsed into an object and is being considered for inclusio into + * the specified cache. + * + * The purpose of this function is to filter out notifications + * which should be ignored when updating caches. + * + * The function must return NL_SKIP to prevent the object from + * being included, or NL_OK to include it. + * + * @code + * int my_filter(struct nl_cache *cache, struct nl_object *obj) + * { + * if (reason_to_not_include_obj(obj)) + * return NL_SKIP; + * + * return NL_OK; + * } + * @endcode + */ + int (*co_event_filter)(struct nl_cache *, struct nl_object *obj); + + /** + * The function registered under this callback is called when an + * object formed from a notification event needs to be included in + * a cache. + * + * For each modified object, the change callback \c change_cb must + * be called with the \c data argument provided. + * + * If no function is registered, the function nl_cache_include() + * will be used for this purpose. + * + * @see nl_cache_include() + */ + int (*co_include_event)(struct nl_cache *cache, struct nl_object *obj, + change_func_t change_cb, change_func_v2_t change_cb_v2, + void *data); + + void (*reserved_1)(void); + void (*reserved_2)(void); + void (*reserved_3)(void); + void (*reserved_4)(void); + void (*reserved_5)(void); + void (*reserved_6)(void); + void (*reserved_7)(void); + void (*reserved_8)(void); + + /** Object operations */ + struct nl_object_ops * co_obj_ops; + + /** Internal, do not touch! */ + struct nl_cache_ops *co_next; + + struct nl_cache *co_major_cache; + struct genl_ops * co_genl; + + /* Message type definition */ + struct nl_msgtype co_msgtypes[]; +}; + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink-private/genl.h b/libnl/include/netlink-private/genl.h new file mode 100644 index 0000000..ac592bd --- /dev/null +++ b/libnl/include/netlink-private/genl.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + */ + +#ifndef NETLINK_GENL_PRIV_H_ +#define NETLINK_GENL_PRIV_H_ + +#include +#include + +#define GENL_HDRSIZE(hdrlen) (GENL_HDRLEN + (hdrlen)) + +extern int genl_resolve_id(struct genl_ops *ops); + +#endif diff --git a/libnl/include/netlink-private/netlink.h b/libnl/include/netlink-private/netlink.h new file mode 100644 index 0000000..77a1b89 --- /dev/null +++ b/libnl/include/netlink-private/netlink.h @@ -0,0 +1,281 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + */ + +#ifndef NETLINK_LOCAL_H_ +#define NETLINK_LOCAL_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifndef SOL_NETLINK +#define SOL_NETLINK 270 +#endif + +#include + +/* local header copies */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef DISABLE_PTHREADS +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#define NSEC_PER_SEC 1000000000L + +struct trans_tbl { + uint64_t i; + const char *a; +}; + +#define __ADD(id, name) { .i = id, .a = #name } + +struct trans_list { + int i; + char *a; + struct nl_list_head list; +}; + +#ifdef NL_DEBUG +#define NL_DBG(LVL,FMT,ARG...) \ + do { \ + if (LVL <= nl_debug) { \ + int _errsv = errno; \ + fprintf(stderr, \ + "DBG<" #LVL ">%20s:%-4u %s: " FMT, \ + __FILE__, __LINE__, \ + __func__, ##ARG); \ + errno = _errsv; \ + } \ + } while (0) +#else /* NL_DEBUG */ +#define NL_DBG(LVL,FMT,ARG...) do { } while(0) +#endif /* NL_DEBUG */ + +#define BUG() \ + do { \ + fprintf(stderr, "BUG at file position %s:%d:%s\n", \ + __FILE__, __LINE__, __func__); \ + assert(0); \ + } while (0) + +#define BUG_ON(condition) \ + do { \ + if (condition) \ + BUG(); \ + } while (0) + + +#define APPBUG(msg) \ + do { \ + fprintf(stderr, "APPLICATION BUG: %s:%d:%s: %s\n", \ + __FILE__, __LINE__, __func__, msg); \ + assert(0); \ + } while(0) + +extern int __nl_read_num_str_file(const char *path, + int (*cb)(long, const char *)); + +extern int __trans_list_add(int, const char *, struct nl_list_head *); +extern void __trans_list_clear(struct nl_list_head *); + +extern char *__type2str(int, char *, size_t, const struct trans_tbl *, size_t); +extern int __str2type(const char *, const struct trans_tbl *, size_t); + +extern char *__list_type2str(int, char *, size_t, struct nl_list_head *); +extern int __list_str2type(const char *, struct nl_list_head *); + +extern char *__flags2str(int, char *, size_t, const struct trans_tbl *, size_t); +extern int __str2flags(const char *, const struct trans_tbl *, size_t); + +extern void dump_from_ops(struct nl_object *, struct nl_dump_params *); +extern struct rtnl_link *link_lookup(struct nl_cache *cache, int ifindex); + +static inline int nl_cb_call(struct nl_cb *cb, enum nl_cb_type type, struct nl_msg *msg) +{ + int ret; + + cb->cb_active = type; + ret = cb->cb_set[type](msg, cb->cb_args[type]); + cb->cb_active = __NL_CB_TYPE_MAX; + return ret; +} + +#define ARRAY_SIZE(X) (sizeof(X) / sizeof((X)[0])) + +/* This is also defined in stddef.h */ +#ifndef offsetof +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +#define __init __attribute__ ((constructor)) +#define __exit __attribute__ ((destructor)) +#undef __deprecated +#define __deprecated __attribute__ ((deprecated)) + +#define min(x,y) ({ \ + __typeof__(x) _x = (x); \ + __typeof__(y) _y = (y); \ + (void) (&_x == &_y); \ + _x < _y ? _x : _y; }) + +#define max(x,y) ({ \ + __typeof__(x) _x = (x); \ + __typeof__(y) _y = (y); \ + (void) (&_x == &_y); \ + _x > _y ? _x : _y; }) + +#define min_t(type,x,y) \ + ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; }) +#define max_t(type,x,y) \ + ({ type __x = (x); type __y = (y); __x > __y ? __x: __y; }) + +extern int nl_cache_parse(struct nl_cache_ops *, struct sockaddr_nl *, + struct nlmsghdr *, struct nl_parser_param *); + + +static inline void rtnl_copy_ratespec(struct rtnl_ratespec *dst, + struct tc_ratespec *src) +{ + dst->rs_cell_log = src->cell_log; + dst->rs_overhead = src->overhead; + dst->rs_cell_align = src->cell_align; + dst->rs_mpu = src->mpu; + dst->rs_rate64 = src->rate; +} + +static inline void rtnl_rcopy_ratespec(struct tc_ratespec *dst, + struct rtnl_ratespec *src) +{ + dst->cell_log = src->rs_cell_log; + dst->overhead = src->rs_overhead; + dst->cell_align = src->rs_cell_align; + dst->mpu = src->rs_mpu; + dst->rate = src->rs_rate64 > 0xFFFFFFFFull ? 0xFFFFFFFFull : (uint32_t) src->rs_rate64; +} + +static inline const char *nl_cache_name(struct nl_cache *cache) +{ + return cache->c_ops ? cache->c_ops->co_name : "unknown"; +} + +#define GENL_FAMILY(id, name) \ + { \ + { id, NL_ACT_UNSPEC, name }, \ + END_OF_MSGTYPES_LIST, \ + } + +static inline int wait_for_ack(struct nl_sock *sk) +{ + if (sk->s_flags & NL_NO_AUTO_ACK) + return 0; + else + return nl_wait_for_ack(sk); +} + +static inline int build_sysconf_path(char **strp, const char *filename) +{ + char *sysconfdir; + + sysconfdir = getenv("NLSYSCONFDIR"); + + if (!sysconfdir) + sysconfdir = "/etc"; + + return asprintf(strp, "%s/%s", sysconfdir, filename); +} + +#ifndef DISABLE_PTHREADS +#define NL_LOCK(NAME) pthread_mutex_t (NAME) = PTHREAD_MUTEX_INITIALIZER +#define NL_RW_LOCK(NAME) pthread_rwlock_t (NAME) = PTHREAD_RWLOCK_INITIALIZER + +static inline void nl_lock(pthread_mutex_t *lock) +{ + pthread_mutex_lock(lock); +} + +static inline void nl_unlock(pthread_mutex_t *lock) +{ + pthread_mutex_unlock(lock); +} + +static inline void nl_read_lock(pthread_rwlock_t *lock) +{ + pthread_rwlock_rdlock(lock); +} + +static inline void nl_read_unlock(pthread_rwlock_t *lock) +{ + pthread_rwlock_unlock(lock); +} + +static inline void nl_write_lock(pthread_rwlock_t *lock) +{ + pthread_rwlock_wrlock(lock); +} + +static inline void nl_write_unlock(pthread_rwlock_t *lock) +{ + pthread_rwlock_unlock(lock); +} + +#else +#define NL_LOCK(NAME) int __unused_lock_ ##NAME __attribute__((unused)) +#define NL_RW_LOCK(NAME) int __unused_lock_ ##NAME __attribute__((unused)) + +#define nl_lock(LOCK) do { } while(0) +#define nl_unlock(LOCK) do { } while(0) +#define nl_read_lock(LOCK) do { } while(0) +#define nl_read_unlock(LOCK) do { } while(0) +#define nl_write_lock(LOCK) do { } while(0) +#define nl_write_unlock(LOCK) do { } while(0) +#endif + +static inline int rtnl_tc_calc_txtime64(int bufsize, uint64_t rate) +{ + return ((double) bufsize / (double) rate) * 1000000.0; +} + +static inline int rtnl_tc_calc_bufsize64(int txtime, uint64_t rate) +{ + return ((double) txtime * (double) rate) / 1000000.0; +} + +#endif diff --git a/libnl/include/netlink-private/nl-auto.h b/libnl/include/netlink-private/nl-auto.h new file mode 100644 index 0000000..f93de3c --- /dev/null +++ b/libnl/include/netlink-private/nl-auto.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#ifndef NETLINK_NL_AUTO_H_ +#define NETLINK_NL_AUTO_H_ + +#include + +#define _nl_auto(fcn) __attribute__ ((__cleanup__(fcn))) + +#define _NL_AUTO_DEFINE_FCN_VOID0(CastType, name, func) \ +static inline void name(void *v) \ +{ \ + if (*((CastType *) v)) \ + func(*((CastType *) v)); \ +} + +#define _NL_AUTO_DEFINE_FCN_TYPED0(CastType, name, func) \ +static inline void name(CastType *v) \ +{ \ + if (*v) \ + func(*v); \ +} + +#define _nl_auto_free _nl_auto(_nl_auto_free_fcn) +_NL_AUTO_DEFINE_FCN_VOID0(void *, _nl_auto_free_fcn, free) + +struct nl_addr; +void nl_addr_put(struct nl_addr *); +#define _nl_auto_nl_addr _nl_auto(_nl_auto_nl_addr_fcn) +_NL_AUTO_DEFINE_FCN_TYPED0(struct nl_addr *, _nl_auto_nl_addr_fcn, nl_addr_put) + +struct nl_msg; +void nlmsg_free(struct nl_msg *); +#define _nl_auto_nl_msg _nl_auto(_nl_auto_nl_msg_fcn) +_NL_AUTO_DEFINE_FCN_TYPED0(struct nl_msg *, _nl_auto_nl_msg_fcn, nlmsg_free) + +struct rtnl_link; +void rtnl_link_put(struct rtnl_link *); +#define _nl_auto_rtnl_link _nl_auto(_nl_auto_rtnl_link_fcn) +_NL_AUTO_DEFINE_FCN_TYPED0(struct rtnl_link *, _nl_auto_rtnl_link_fcn, rtnl_link_put) + +struct rtnl_route; +void rtnl_route_put(struct rtnl_route *); +#define _nl_auto_rtnl_route _nl_auto(_nl_auto_rtnl_route_fcn) +_NL_AUTO_DEFINE_FCN_TYPED0(struct rtnl_route *, _nl_auto_rtnl_route_fcn, rtnl_route_put) + +struct rtnl_nexthop; +void rtnl_route_nh_free(struct rtnl_nexthop *); +#define _nl_auto_rtnl_nexthop _nl_auto(_nl_auto_rtnl_nexthop_fcn) +_NL_AUTO_DEFINE_FCN_TYPED0(struct rtnl_nexthop *, _nl_auto_rtnl_nexthop_fcn, rtnl_route_nh_free) + +struct nl_cache; +void nl_cache_put(struct nl_cache *); +#define _nl_auto_nl_cache _nl_auto(_nl_auto_nl_cache_fcn) +_NL_AUTO_DEFINE_FCN_TYPED0(struct nl_cache *, _nl_auto_nl_cache_fcn, nl_cache_put) + +struct rtnl_link_af_ops; +void rtnl_link_af_ops_put(struct rtnl_link_af_ops *); +#define _nl_auto_rtnl_link_af_ops _nl_auto(_nl_auto_rtnl_link_af_ops_fcn) +_NL_AUTO_DEFINE_FCN_TYPED0(struct rtnl_link_af_ops *, _nl_auto_rtnl_link_af_ops_fcn, rtnl_link_af_ops_put) + +#endif /* NETLINK_NL_AUTO_H_ */ diff --git a/libnl/include/netlink-private/object-api.h b/libnl/include/netlink-private/object-api.h new file mode 100644 index 0000000..a2838fd --- /dev/null +++ b/libnl/include/netlink-private/object-api.h @@ -0,0 +1,372 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + */ + +#ifndef NETLINK_OBJECT_API_H_ +#define NETLINK_OBJECT_API_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @ingroup object + * @defgroup object_api Object API + * @brief + * + * @par 1) Object Definition + * @code + * // Define your object starting with the common object header + * struct my_obj { + * NLHDR_COMMON + * int my_data; + * }; + * + * // Fill out the object operations structure + * struct nl_object_ops my_ops = { + * .oo_name = "my_obj", + * .oo_size = sizeof(struct my_obj), + * }; + * + * // At this point the object can be allocated, you may want to provide a + * // separate _alloc() function to ease allocting objects of this kind. + * struct nl_object *obj = nl_object_alloc(&my_ops); + * + * // And release it again... + * nl_object_put(obj); + * @endcode + * + * @par 2) Allocating additional data + * @code + * // You may require to allocate additional data and store it inside + * // object, f.e. assuming there is a field `ptr'. + * struct my_obj { + * NLHDR_COMMON + * void * ptr; + * }; + * + * // And at some point you may assign allocated data to this field: + * my_obj->ptr = calloc(1, ...); + * + * // In order to not introduce any memory leaks you have to release + * // this data again when the last reference is given back. + * static void my_obj_free_data(struct nl_object *obj) + * { + * struct my_obj *my_obj = nl_object_priv(obj); + * + * free(my_obj->ptr); + * } + * + * // Also when the object is cloned, you must ensure for your pointer + * // stay valid even if one of the clones is freed by either making + * // a clone as well or increase the reference count. + * static int my_obj_clone(struct nl_object *src, struct nl_object *dst) + * { + * struct my_obj *my_src = nl_object_priv(src); + * struct my_obj *my_dst = nl_object_priv(dst); + * + * if (src->ptr) { + * dst->ptr = calloc(1, ...); + * memcpy(dst->ptr, src->ptr, ...); + * } + * } + * + * struct nl_object_ops my_ops = { + * ... + * .oo_free_data = my_obj_free_data, + * .oo_clone = my_obj_clone, + * }; + * @endcode + * + * @par 3) Object Dumping + * @code + * static int my_obj_dump_detailed(struct nl_object *obj, + * struct nl_dump_params *params) + * { + * struct my_obj *my_obj = nl_object_priv(obj); + * + * // It is absolutely essential to use nl_dump() when printing + * // any text to make sure the dumping parameters are respected. + * nl_dump(params, "Obj Integer: %d\n", my_obj->my_int); + * + * // Before we can dump the next line, make sure to prefix + * // this line correctly. + * nl_new_line(params); + * + * // You may also split a line into multiple nl_dump() calls. + * nl_dump(params, "String: %s ", my_obj->my_string); + * nl_dump(params, "String-2: %s\n", my_obj->another_string); + * } + * + * struct nl_object_ops my_ops = { + * ... + * .oo_dump[NL_DUMP_FULL] = my_obj_dump_detailed, + * }; + * @endcode + * + * @par 4) Object Attributes + * @code + * // The concept of object attributes is optional but can ease the typical + * // case of objects that have optional attributes, e.g. a route may have a + * // nexthop assigned but it is not required to. + * + * // The first step to define your object specific bitmask listing all + * // attributes + * #define MY_ATTR_FOO (1<<0) + * #define MY_ATTR_BAR (1<<1) + * + * // Bit 31 for attributes is reserved for 32-bit API. + * + * // When assigning an optional attribute to the object, make sure + * // to mark its availability. + * my_obj->foo = 123123; + * my_obj->ce_mask |= MY_ATTR_FOO; + * + * // At any time you may use this mask to check for the availability + * // of the attribute, e.g. while dumping + * if (my_obj->ce_mask & MY_ATTR_FOO) + * nl_dump(params, "foo %d ", my_obj->foo); + * + * // One of the big advantages of this concept is that it allows for + * // standardized comparisons which make it trivial for caches to + * // identify unique objects by use of unified comparison functions. + * // In order for it to work, your object implementation must provide + * // a comparison function and define a list of attributes which + * // combined together make an object unique. + * + * static int my_obj_compare(struct nl_object *_a, struct nl_object *_b, + * uint32_t attrs, int flags) + * { + * struct my_obj *a = nl_object_priv(_a): + * struct my_obj *b = nl_object_priv(_b): + * int diff = 0; + * + * // We help ourselves in defining our own DIFF macro which will + * // call ATTR_DIFF() on both objects which will make sure to only + * // compare the attributes if required. + * #define MY_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, MY_ATTR_##ATTR, a, b, EXPR) + * + * // Call our own diff macro for each attribute to build a bitmask + * // representing the attributes which mismatch. + * diff |= MY_DIFF(FOO, a->foo != b->foo) + * diff |= MY_DIFF(BAR, strcmp(a->bar, b->bar)) + * + * return diff; + * } + * + * // In order to identify identical objects with differing attributes + * // you must specify the attributes required to uniquely identify + * // your object. Make sure to not include too many attributes, this + * // list is used when caches look for an old version of an object. + * struct nl_object_ops my_ops = { + * ... + * .oo_id_attrs = MY_ATTR_FOO, + * .oo_compare = my_obj_compare, + * }; + * @endcode + * @{ + */ + +/** + * Common Object Header + * + * This macro must be included as first member in every object + * definition to allow objects to be cached. + */ +#define NLHDR_COMMON \ + int ce_refcnt; \ + struct nl_object_ops * ce_ops; \ + struct nl_cache * ce_cache; \ + struct nl_list_head ce_list; \ + int ce_msgtype; \ + int ce_flags; \ + uint64_t ce_mask; + +struct nl_object +{ + NLHDR_COMMON +}; + + +/** + * Return true if attribute is available in both objects + * @arg A an object + * @arg B another object + * @arg ATTR attribute bit + * + * @return True if the attribute is available, otherwise false is returned. + */ +#define AVAILABLE(A, B, ATTR) (((A)->ce_mask & (B)->ce_mask) & (ATTR)) + +/** + * Return true if attribute is available in only one of both objects + * @arg A an object + * @arg B another object + * @arg ATTR attribute bit + * + * @return True if the attribute is available in only one of both objects, + * otherwise false is returned. + */ +#define AVAILABLE_MISMATCH(A, B, ATTR) (((A)->ce_mask ^ (B)->ce_mask) & (ATTR)) + +/** + * Return true if attributes mismatch + * @arg A an object + * @arg B another object + * @arg ATTR attribute bit + * @arg EXPR Comparison expression + * + * This function will check if the attribute in question is available + * in both objects, if not this will count as a mismatch. + * + * If available the function will execute the expression which must + * return true if the attributes mismatch. + * + * @return True if the attribute mismatch, or false if they match. + */ +#define ATTR_MISMATCH(A, B, ATTR, EXPR) (AVAILABLE_MISMATCH(A, B, ATTR) || \ + (AVAILABLE(A, B, ATTR) && (EXPR))) + +/** + * Return attribute bit if attribute does not match + * @arg LIST list of attributes to be compared + * @arg ATTR attribute bit + * @arg A an object + * @arg B another object + * @arg EXPR Comparison expression + * + * This function will check if the attribute in question is available + * in both objects, if not this will count as a mismatch. + * + * If available the function will execute the expression which must + * return true if the attributes mismatch. + * + * In case the attributes mismatch, the attribute is returned, otherwise + * 0 is returned. + * + * @code + * diff |= ATTR_DIFF(attrs, MY_ATTR_FOO, a, b, a->foo != b->foo); + * @endcode + */ +#define ATTR_DIFF(LIST, ATTR, A, B, EXPR) \ +({ uint64_t diff = 0; \ + if (((LIST) & (ATTR)) && ATTR_MISMATCH(A, B, ATTR, EXPR)) \ + diff = ATTR; \ + diff; }) + +/** + * Object Operations + */ +struct nl_object_ops +{ + /** + * Unique name of object type + * + * Must be in the form family/name, e.g. "route/addr" + */ + char * oo_name; + + /** Size of object including its header */ + size_t oo_size; + + /* List of attributes needed to uniquely identify the object */ + uint32_t oo_id_attrs; + + /** + * Constructor function + * + * Will be called when a new object of this type is allocated. + * Can be used to initialize members such as lists etc. + */ + void (*oo_constructor)(struct nl_object *); + + /** + * Destructor function + * + * Will be called when an object is freed. Must free all + * resources which may have been allocated as part of this + * object. + */ + void (*oo_free_data)(struct nl_object *); + + /** + * Cloning function + * + * Will be called when an object needs to be cloned. Please + * note that the generic object code will make an exact + * copy of the object first, therefore you only need to take + * care of members which require reference counting etc. + * + * May return a negative error code to abort cloning. + */ + int (*oo_clone)(struct nl_object *, struct nl_object *); + + /** + * Dumping functions + * + * Will be called when an object is dumped. The implementations + * have to use nl_dump(), nl_dump_line(), and nl_new_line() to + * dump objects. + * + * The functions must return the number of lines printed. + */ + void (*oo_dump[NL_DUMP_MAX+1])(struct nl_object *, + struct nl_dump_params *); + + /** + * Comparison function + * + * Will be called when two objects of the same type are + * compared. It takes the two objects in question, an object + * specific bitmask defining which attributes should be + * compared and flags to control the behaviour. + * + * The function must return a bitmask with the relevant bit + * set for each attribute that mismatches. + */ + uint64_t (*oo_compare)(struct nl_object *, struct nl_object *, + uint64_t, int); + + + /** + * update function + * + * Will be called when the object given by first argument + * needs to be updated with the contents of the second object + * + * The function must return 0 for success and error for failure + * to update. In case of failure its assumed that the original + * object is not touched + */ + int (*oo_update)(struct nl_object *, struct nl_object *); + + /** + * Hash Key generator function + * + * When called returns a hash key for the object being + * referenced. This key will be used by higher level hash functions + * to build association lists. Each object type gets to specify + * it's own key formulation + */ + void (*oo_keygen)(struct nl_object *, uint32_t *, uint32_t); + + char *(*oo_attrs2str)(int, char *, size_t); + + /** + * Get key attributes by family function + */ + uint32_t (*oo_id_attrs_get)(struct nl_object *); +}; + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink-private/route/link/api.h b/libnl/include/netlink-private/route/link/api.h new file mode 100644 index 0000000..5ce24b1 --- /dev/null +++ b/libnl/include/netlink-private/route/link/api.h @@ -0,0 +1,193 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + */ + +#ifndef NETLINK_LINK_API_H_ +#define NETLINK_LINK_API_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @ingroup link_api + * + * Available operations to modules implementing a link info type. + */ +struct rtnl_link_info_ops +{ + /** Name of link info type, must match name on kernel side */ + char * io_name; + + /** Reference count, DO NOT MODIFY */ + int io_refcnt; + + /** Called to assign an info type to a link. + * Has to allocate enough resources to hold attributes. Can + * use link->l_info to store a pointer. */ + int (*io_alloc)(struct rtnl_link *); + + /** Called to parse the link info attribute. + * Must parse the attribute and assign all values to the link. + */ + int (*io_parse)(struct rtnl_link *, + struct nlattr *, + struct nlattr *); + + /** Called when the link object is dumped. + * Must dump the info type specific attributes. */ + void (*io_dump[NL_DUMP_MAX+1])(struct rtnl_link *, + struct nl_dump_params *); + + /** Called when a link object is cloned. + * Must clone all info type specific attributes. */ + int (*io_clone)(struct rtnl_link *, struct rtnl_link *); + + /** Called when construction a link netlink message. + * Must append all info type specific attributes to the message. */ + int (*io_put_attrs)(struct nl_msg *, struct rtnl_link *); + + /** Called to release all resources previously allocated + * in either io_alloc() or io_parse(). */ + void (*io_free)(struct rtnl_link *); + + /** Called to compare link info parameters between two links. */ + int (*io_compare)(struct rtnl_link *, struct rtnl_link *, + int flags); + + struct nl_list_head io_list; +}; + +extern struct rtnl_link_info_ops *rtnl_link_info_ops_lookup(const char *); +extern void rtnl_link_info_ops_put(struct rtnl_link_info_ops *); +extern int rtnl_link_register_info(struct rtnl_link_info_ops *); +extern int rtnl_link_unregister_info(struct rtnl_link_info_ops *); + + +/** + * @ingroup link_api + * + * Available operations to modules implementing a link address family. + */ +struct rtnl_link_af_ops +{ + /** The address family this operations set implements */ + const unsigned int ao_family; + + /** Number of users of this operations, DO NOT MODIFY. */ + int ao_refcnt; + + /** Validation policy for IFLA_PROTINFO attribute. This pointer + * can be set to a nla_policy structure describing the minimal + * requirements the attribute must meet. Failure of meeting these + * requirements will result in a parsing error. */ + const struct nla_policy *ao_protinfo_policy; + + /** Called after address family has been assigned to link. Must + * allocate data buffer to hold address family specific data and + * store it in link->l_af_data. */ + void * (*ao_alloc)(struct rtnl_link *); + + /** Called when the link is cloned, must allocate a clone of the + * address family specific buffer and return it. */ + void * (*ao_clone)(struct rtnl_link *, void *); + + /** Called when the link gets freed. Must free all allocated data */ + void (*ao_free)(struct rtnl_link *, void *); + + /** Called if a IFLA_PROTINFO attribute needs to be parsed. Typically + * stores the parsed data in the address family specific buffer. */ + int (*ao_parse_protinfo)(struct rtnl_link *, + struct nlattr *, void *); + + /** Called if a IFLA_AF_SPEC attribute needs to be parsed. Typically + * stores the parsed data in the address family specific buffer. */ + int (*ao_parse_af)(struct rtnl_link *, + struct nlattr *, void *); + + /** Called if a link message is sent to the kernel. Must append the + * link address family specific attributes to the message. */ + int (*ao_fill_af)(struct rtnl_link *, + struct nl_msg *msg, void *); + + /** Called if the full IFLA_AF_SPEC data needs to be parsed. Typically + * stores the parsed data in the address family specific buffer. */ + int (*ao_parse_af_full)(struct rtnl_link *, + struct nlattr *, void *); + + /** Called for GETLINK message to the kernel. Used to append + * link address family specific attributes to the request message. */ + int (*ao_get_af)(struct nl_msg *msg, + uint32_t *ext_filter_mask); + + /** Dump address family specific link attributes */ + void (*ao_dump[NL_DUMP_MAX+1])(struct rtnl_link *, + struct nl_dump_params *, + void *); + + /** Comparison function + * + * Will be called when two links are compared for their af data. It + * takes two link objects in question, an object specific bitmask + * defining which attributes should be compared and flags to control + * the behaviour + * + * The function must return a bitmask with the relevant bit set for + * each attribute that mismatches + */ + int (*ao_compare)(struct rtnl_link *, + struct rtnl_link *, int, uint32_t, int); + + /* RTM_NEWLINK override + * + * Called if a change link request is set to the kernel. If this returns + * anything other than zero, RTM_NEWLINK will be overriden with + * RTM_SETLINK when rtnl_link_build_change_request() is called. + */ + int (*ao_override_rtm)(struct rtnl_link *); + + /** Called if a link message is sent to the kernel. Must append the + * link protocol specific attributes to the message. (IFLA_PROTINFO) */ + int (*ao_fill_pi)(struct rtnl_link *, + struct nl_msg *msg, void *); + + /** PROTINFO type + * + * Called if a link message is sent to the kernel. If this is set, + * the default IFLA_PROTINFO is bitmasked with what is specified + * here. (eg. NLA_F_NESTED) + */ + const int ao_fill_pi_flags; + + /** IFLA_AF_SPEC nesting override + * + * Called if a link message is sent to the kernel. If this is set, + * the AF specific nest is not created. Instead, AF specific attributes + * are nested directly in the IFLA_AF_SPEC attribute. + */ + const int ao_fill_af_no_nest; +}; + +extern struct rtnl_link_af_ops *rtnl_link_af_ops_lookup(unsigned int); +extern void rtnl_link_af_ops_put(struct rtnl_link_af_ops *); +extern void * rtnl_link_af_alloc(struct rtnl_link *, + const struct rtnl_link_af_ops *); +extern void * rtnl_link_af_data(const struct rtnl_link *, + const struct rtnl_link_af_ops *); +extern int rtnl_link_af_register(struct rtnl_link_af_ops *); +extern int rtnl_link_af_unregister(struct rtnl_link_af_ops *); +extern int rtnl_link_af_data_compare(struct rtnl_link *a, + struct rtnl_link *b, + int family); +extern int rtnl_link_info_data_compare(struct rtnl_link *a, + struct rtnl_link *b, + int flags); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink-private/route/link/sriov.h b/libnl/include/netlink-private/route/link/sriov.h new file mode 100644 index 0000000..f7c027a --- /dev/null +++ b/libnl/include/netlink-private/route/link/sriov.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2016 Intel Corp. All rights reserved. + * Copyright (c) 2016 Jef Oliver + */ + +#ifndef NETLINK_PRIV_LINK_SRIOV_H_ +#define NETLINK_PRIV_LINK_SRIOV_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int rtnl_link_sriov_clone(struct rtnl_link *, struct rtnl_link *); +extern void rtnl_link_sriov_dump_details(struct rtnl_link *, struct nl_dump_params *); +extern void rtnl_link_sriov_dump_stats(struct rtnl_link *, struct nl_dump_params *); +extern int rtnl_link_sriov_fill_vflist(struct nl_msg *, struct rtnl_link *); +extern void rtnl_link_sriov_free_data(struct rtnl_link *); +extern int rtnl_link_sriov_parse_vflist(struct rtnl_link *, struct nlattr **); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink-private/route/mpls.h b/libnl/include/netlink-private/route/mpls.h new file mode 100644 index 0000000..502fd34 --- /dev/null +++ b/libnl/include/netlink-private/route/mpls.h @@ -0,0 +1,15 @@ +#ifndef MPLS_H_ +#define MPLS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +extern const char *mpls_ntop(int af, const void *addr, char *buf, size_t buflen); +extern int mpls_pton(int af, const char *src, void *addr, size_t alen); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink-private/route/nexthop-encap.h b/libnl/include/netlink-private/route/nexthop-encap.h new file mode 100644 index 0000000..dde1bfb --- /dev/null +++ b/libnl/include/netlink-private/route/nexthop-encap.h @@ -0,0 +1,35 @@ +#ifndef NETLINK_NEXTHOP_ENCAP_H_ +#define NETLINK_NEXTHOP_ENCAP_H_ + +struct nh_encap_ops { + uint16_t encap_type; + + int (*build_msg)(struct nl_msg *msg, void *priv); + int (*parse_msg)(struct nlattr *nla, struct rtnl_nexthop *rtnh); + + int (*compare)(void *a, void *b); + + void (*dump)(void *priv, struct nl_dump_params *dp); + void (*destructor)(void *priv); +}; + +struct rtnl_nh_encap; + +/* + * generic nexthop encap + */ +void nh_set_encap(struct rtnl_nexthop *nh, struct rtnl_nh_encap *rtnh_encap); + +int nh_encap_parse_msg(struct nlattr *encap, struct nlattr *encap_type, + struct rtnl_nexthop *rtnh); +int nh_encap_build_msg(struct nl_msg *msg, struct rtnl_nh_encap *rtnh_encap); + +void nh_encap_dump(struct rtnl_nh_encap *rtnh_encap, struct nl_dump_params *dp); + +int nh_encap_compare(struct rtnl_nh_encap *a, struct rtnl_nh_encap *b); + +/* + * MPLS encap + */ +extern struct nh_encap_ops mpls_encap_ops; +#endif diff --git a/libnl/include/netlink-private/route/tc-api.h b/libnl/include/netlink-private/route/tc-api.h new file mode 100644 index 0000000..1eb27dc --- /dev/null +++ b/libnl/include/netlink-private/route/tc-api.h @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2011-2013 Thomas Graf + */ + +#ifndef NETLINK_TC_API_H_ +#define NETLINK_TC_API_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Traffic control object operations + * @ingroup tc + * + * This structure holds function pointers and settings implementing + * the features of each traffic control object implementation. + */ +struct rtnl_tc_ops +{ + /** + * Name of traffic control module + */ + char *to_kind; + + /** + * Type of traffic control object + */ + enum rtnl_tc_type to_type; + + + /** + * Size of private data + */ + size_t to_size; + + /** + * Dump callbacks + */ + void (*to_dump[NL_DUMP_MAX+1])(struct rtnl_tc *, void *, + struct nl_dump_params *); + /** + * Used to fill the contents of TCA_OPTIONS + */ + int (*to_msg_fill)(struct rtnl_tc *, void *, struct nl_msg *); + + /** + * Uesd to to fill tc related messages, unlike with to_msg_fill, + * the contents is not encapsulated with a TCA_OPTIONS nested + * attribute. + */ + int (*to_msg_fill_raw)(struct rtnl_tc *, void *, struct nl_msg *); + + /** + * TCA_OPTIONS message parser + */ + int (*to_msg_parser)(struct rtnl_tc *, void *); + + /** + * Called before a tc object is destroyed + */ + void (*to_free_data)(struct rtnl_tc *, void *); + + /** + * Called whenever a classifier object needs to be cloned + */ + int (*to_clone)(void *, void *); + + /** + * Internal, don't touch + */ + struct nl_list_head to_list; +}; + +struct rtnl_tc_type_ops +{ + enum rtnl_tc_type tt_type; + + char *tt_dump_prefix; + + /** + * Dump callbacks + */ + void (*tt_dump[NL_DUMP_MAX+1])(struct rtnl_tc *, + struct nl_dump_params *); +}; + +extern int rtnl_tc_msg_parse(struct nlmsghdr *, + struct rtnl_tc *); +extern int rtnl_tc_msg_build(struct rtnl_tc *, int, + int, struct nl_msg **); + +extern void rtnl_tc_free_data(struct nl_object *); +extern int rtnl_tc_clone(struct nl_object *, + struct nl_object *); +extern void rtnl_tc_dump_line(struct nl_object *, + struct nl_dump_params *); +extern void rtnl_tc_dump_details(struct nl_object *, + struct nl_dump_params *); +extern void rtnl_tc_dump_stats(struct nl_object *, + struct nl_dump_params *); +extern uint64_t rtnl_tc_compare(struct nl_object *, + struct nl_object *, + uint64_t, int); + +void * rtnl_tc_data_peek(struct rtnl_tc *tc); +extern void * rtnl_tc_data(struct rtnl_tc *); +extern void * rtnl_tc_data_check(struct rtnl_tc *, + struct rtnl_tc_ops *, int *); + +extern struct rtnl_tc_ops * rtnl_tc_lookup_ops(enum rtnl_tc_type, + const char *); +extern struct rtnl_tc_ops * rtnl_tc_get_ops(struct rtnl_tc *); +extern int rtnl_tc_register(struct rtnl_tc_ops *); +extern void rtnl_tc_unregister(struct rtnl_tc_ops *); + +extern void rtnl_tc_type_register(struct rtnl_tc_type_ops *); +extern void rtnl_tc_type_unregister(struct rtnl_tc_type_ops *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink-private/route/utils.h b/libnl/include/netlink-private/route/utils.h new file mode 100644 index 0000000..65ff531 --- /dev/null +++ b/libnl/include/netlink-private/route/utils.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#ifndef NETLINK_ROUTE_UTILS_PRIV_H_ +#define NETLINK_ROUTE_UTILS_PRIV_H_ + +extern const uint8_t *const _nltst_map_stat_id_from_IPSTATS_MIB_v2; + +#endif diff --git a/libnl/include/netlink-private/socket.h b/libnl/include/netlink-private/socket.h new file mode 100644 index 0000000..5fe77fa --- /dev/null +++ b/libnl/include/netlink-private/socket.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2014 Thomas Graf + */ + +#ifndef NETLINK_SOCKET_PRIV_H_ +#define NETLINK_SOCKET_PRIV_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int _nl_socket_is_local_port_unspecified (struct nl_sock *sk); +uint32_t _nl_socket_set_local_port_no_release(struct nl_sock *sk, int generate_other); + +void _nl_socket_used_ports_release_all(const uint32_t *used_ports); +void _nl_socket_used_ports_set(uint32_t *used_ports, uint32_t port); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink-private/tc.h b/libnl/include/netlink-private/tc.h new file mode 100644 index 0000000..5f5d1ff --- /dev/null +++ b/libnl/include/netlink-private/tc.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + */ + +#ifndef NETLINK_TC_PRIV_H_ +#define NETLINK_TC_PRIV_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define TCA_ATTR_HANDLE 0x0001 +#define TCA_ATTR_PARENT 0x0002 +#define TCA_ATTR_IFINDEX 0x0004 +#define TCA_ATTR_KIND 0x0008 +#define TCA_ATTR_FAMILY 0x0010 +#define TCA_ATTR_INFO 0x0020 +#define TCA_ATTR_OPTS 0x0040 +#define TCA_ATTR_STATS 0x0080 +#define TCA_ATTR_XSTATS 0x0100 +#define TCA_ATTR_LINK 0x0200 +#define TCA_ATTR_MTU 0x0400 +#define TCA_ATTR_MPU 0x0800 +#define TCA_ATTR_OVERHEAD 0x1000 +#define TCA_ATTR_LINKTYPE 0x2000 +#define TCA_ATTR_CHAIN 0x4000 +#define TCA_ATTR_MAX TCA_ATTR_CHAIN + +extern int tca_parse(struct nlattr **, int, struct rtnl_tc *, + const struct nla_policy *); + +#define RTNL_TC_RTABLE_SIZE 256 + +extern int rtnl_tc_build_rate_table(struct rtnl_tc *tc, struct rtnl_ratespec *, + uint32_t *); + + +static inline void *tca_xstats(struct rtnl_tc *tca) +{ + return tca->tc_xstats->d_data; +} + +extern struct nl_af_group tc_groups[]; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink-private/types.h b/libnl/include/netlink-private/types.h new file mode 100644 index 0000000..92facac --- /dev/null +++ b/libnl/include/netlink-private/types.h @@ -0,0 +1,1335 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + * Copyright (c) 2013 Sassano Systems LLC + */ + +#ifndef NETLINK_LOCAL_TYPES_H_ +#define NETLINK_LOCAL_TYPES_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NL_SOCK_PASSCRED (1<<1) +#define NL_OWN_PORT (1<<2) +#define NL_MSG_PEEK (1<<3) +#define NL_MSG_PEEK_EXPLICIT (1<<4) +#define NL_NO_AUTO_ACK (1<<5) + +#define NL_MSG_CRED_PRESENT 1 + +struct nl_cache_ops; +struct nl_sock; +struct nl_object; +struct nl_hash_table; +struct nl_vf_vlans; + +struct nl_cb +{ + nl_recvmsg_msg_cb_t cb_set[NL_CB_TYPE_MAX+1]; + void * cb_args[NL_CB_TYPE_MAX+1]; + + nl_recvmsg_err_cb_t cb_err; + void * cb_err_arg; + + /** May be used to replace nl_recvmsgs with your own implementation + * in all internal calls to nl_recvmsgs. */ + int (*cb_recvmsgs_ow)(struct nl_sock *, + struct nl_cb *); + + /** Overwrite internal calls to nl_recv, must return the number of + * octets read and allocate a buffer for the received data. */ + int (*cb_recv_ow)(struct nl_sock *, + struct sockaddr_nl *, + unsigned char **, + struct ucred **); + + /** Overwrites internal calls to nl_send, must send the netlink + * message. */ + int (*cb_send_ow)(struct nl_sock *, + struct nl_msg *); + + int cb_refcnt; + /** indicates the callback that is currently active */ + enum nl_cb_type cb_active; +}; + +struct nl_sock +{ + struct sockaddr_nl s_local; + struct sockaddr_nl s_peer; + int s_fd; + int s_proto; + unsigned int s_seq_next; + unsigned int s_seq_expect; + int s_flags; + struct nl_cb * s_cb; + size_t s_bufsize; +}; + +struct nl_cache +{ + struct nl_list_head c_items; + int c_nitems; + int c_iarg1; + int c_iarg2; + int c_refcnt; + unsigned int c_flags; + struct nl_hash_table * hashtable; + struct nl_cache_ops * c_ops; +}; + +struct nl_cache_assoc +{ + struct nl_cache * ca_cache; + change_func_t ca_change; + change_func_v2_t ca_change_v2; + void * ca_change_data; +}; + +struct nl_cache_mngr +{ + int cm_protocol; + int cm_flags; + int cm_nassocs; + struct nl_sock * cm_sock; + struct nl_sock * cm_sync_sock; + struct nl_cache_assoc * cm_assocs; +}; + +struct nl_parser_param; + +#define LOOSE_COMPARISON 1 +#define ID_COMPARISON 2 + +#define NL_OBJ_MARK 1 + +struct nl_data +{ + size_t d_size; + void * d_data; +}; + +struct nl_addr +{ + int a_family; + unsigned int a_maxsize; + unsigned int a_len; + int a_prefixlen; + int a_refcnt; + char a_addr[0]; +}; + +struct nl_msg +{ + int nm_protocol; + int nm_flags; + struct sockaddr_nl nm_src; + struct sockaddr_nl nm_dst; + struct ucred nm_creds; + struct nlmsghdr * nm_nlh; + size_t nm_size; + int nm_refcnt; +}; + +struct rtnl_link_map +{ + uint64_t lm_mem_start; + uint64_t lm_mem_end; + uint64_t lm_base_addr; + uint16_t lm_irq; + uint8_t lm_dma; + uint8_t lm_port; +}; + +struct rtnl_link_vf +{ + struct nl_list_head vf_list; + int ce_refcnt; + uint32_t ce_mask; + uint32_t vf_index; + uint64_t vf_guid_node; + uint64_t vf_guid_port; + uint32_t vf_linkstate; + struct nl_addr * vf_lladdr; + uint32_t vf_max_tx_rate; + uint32_t vf_min_tx_rate; + uint32_t vf_rate; + uint32_t vf_rss_query_en; + uint32_t vf_spoofchk; + uint64_t vf_stats[RTNL_LINK_VF_STATS_MAX+1]; + uint32_t vf_trust; + struct nl_vf_vlans * vf_vlans; +}; + +#define IFQDISCSIZ 32 + +struct rtnl_link +{ + NLHDR_COMMON + + char l_name[IFNAMSIZ]; + uint32_t l_family; + uint32_t l_arptype; + uint32_t l_index; + uint32_t l_flags; + uint32_t l_change; + uint32_t l_mtu; + uint32_t l_link; + int32_t l_link_netnsid; + uint32_t l_txqlen; + uint32_t l_weight; + uint32_t l_master; + struct nl_addr * l_addr; + struct nl_addr * l_bcast; + char l_qdisc[IFQDISCSIZ]; + struct rtnl_link_map l_map; + uint64_t l_stats[RTNL_LINK_STATS_MAX+1]; + uint32_t l_flag_mask; + uint32_t l_num_vf; + uint8_t l_operstate; + uint8_t l_linkmode; + /* 2 byte hole */ + char * l_info_kind; + char * l_info_slave_kind; + struct rtnl_link_info_ops * l_info_ops; + void * l_af_data[AF_MAX]; + void * l_info; + char * l_ifalias; + uint32_t l_promiscuity; + uint32_t l_num_tx_queues; + uint32_t l_num_rx_queues; + uint32_t l_gso_max_segs; + uint32_t l_gso_max_size; + uint32_t l_group; + uint8_t l_carrier; + /* 3 byte hole */ + uint32_t l_carrier_changes; + struct rtnl_link_af_ops * l_af_ops; + struct nl_data * l_phys_port_id; + char l_phys_port_name[IFNAMSIZ]; + struct nl_data * l_phys_switch_id; + int l_ns_fd; + pid_t l_ns_pid; + struct rtnl_link_vf * l_vf_list; +}; + +struct rtnl_ncacheinfo +{ + uint32_t nci_confirmed; /**< Time since neighbour validty was last confirmed */ + uint32_t nci_used; /**< Time since neighbour entry was last ued */ + uint32_t nci_updated; /**< Time since last update */ + uint32_t nci_refcnt; /**< Reference counter */ +}; + + +struct rtnl_neigh +{ + NLHDR_COMMON + uint32_t n_family; + uint32_t n_ifindex; + uint16_t n_state; + uint8_t n_flags; + uint8_t n_type; + struct nl_addr *n_lladdr; + struct nl_addr *n_dst; + uint32_t n_probes; + struct rtnl_ncacheinfo n_cacheinfo; + uint32_t n_state_mask; + uint32_t n_flag_mask; + uint32_t n_master; + uint16_t n_vlan; +}; + + +struct rtnl_addr_cacheinfo +{ + /* Preferred lifetime in seconds, ticking from when the message gets constructed */ + uint32_t aci_prefered; + + /* Valid lifetime in seconds, ticking from when the message gets constructed */ + uint32_t aci_valid; + + /* Timestamp of creation in 1/100s since boottime, clock_gettime(CLOCK_MONOTONIC) */ + uint32_t aci_cstamp; + + /* Timestamp of last update in 1/100s since boottime, clock_gettime(CLOCK_MONOTONIC) */ + uint32_t aci_tstamp; +}; + +struct rtnl_addr +{ + NLHDR_COMMON + + uint8_t a_family; + uint8_t a_prefixlen; + uint8_t a_scope; + uint32_t a_flags; + uint32_t a_ifindex; + + struct nl_addr *a_peer; + struct nl_addr *a_local; + struct nl_addr *a_bcast; + struct nl_addr *a_anycast; + struct nl_addr *a_multicast; + + struct rtnl_addr_cacheinfo a_cacheinfo; + + char a_label[IFNAMSIZ]; + uint32_t a_flag_mask; + struct rtnl_link *a_link; +}; + +struct rtnl_nh_encap +{ + struct nh_encap_ops *ops; + void *priv; /* private data for encap type */ +}; + +struct rtnl_nexthop +{ + uint8_t rtnh_flags; + uint8_t rtnh_flag_mask; + uint8_t rtnh_weight; + /* 1 byte spare */ + uint32_t rtnh_ifindex; + struct nl_addr * rtnh_gateway; + uint32_t ce_mask; /* HACK to support attr macros */ + struct nl_list_head rtnh_list; + uint32_t rtnh_realms; + struct nl_addr * rtnh_newdst; + struct nl_addr * rtnh_via; + struct rtnl_nh_encap * rtnh_encap; +}; + +struct rtnl_route +{ + NLHDR_COMMON + + uint8_t rt_family; + uint8_t rt_dst_len; + uint8_t rt_src_len; + uint8_t rt_tos; + uint8_t rt_protocol; + uint8_t rt_scope; + uint8_t rt_type; + uint8_t rt_nmetrics; + uint8_t rt_ttl_propagate; + uint32_t rt_flags; + struct nl_addr * rt_dst; + struct nl_addr * rt_src; + uint32_t rt_table; + uint32_t rt_iif; + uint32_t rt_prio; + uint32_t rt_metrics[RTAX_MAX]; + uint32_t rt_metrics_mask; + uint32_t rt_nr_nh; + struct nl_addr * rt_pref_src; + struct nl_list_head rt_nexthops; + struct rtnl_rtcacheinfo rt_cacheinfo; + uint32_t rt_flag_mask; +}; + +struct rtnl_rule +{ + NLHDR_COMMON + uint8_t r_family; + uint8_t r_action; + uint8_t r_dsfield; /* ipv4 only */ + uint8_t r_l3mdev; + uint8_t r_protocol; /* protocol that installed rule */ + uint8_t r_ip_proto; /* IP/IPv6 protocol */ + uint32_t r_table; + uint32_t r_flags; + uint32_t r_prio; + uint32_t r_mark; + uint32_t r_mask; + uint32_t r_goto; + uint32_t r_flow; /* ipv4 only */ + struct nl_addr *r_src; + struct nl_addr *r_dst; + char r_iifname[IFNAMSIZ]; + char r_oifname[IFNAMSIZ]; + + struct fib_rule_port_range r_sport; + struct fib_rule_port_range r_dport; +}; + +struct rtnl_neightbl_parms +{ + /** + * Interface index of the device this parameter set is assigned + * to or 0 for the default set. + */ + uint32_t ntp_ifindex; + + /** + * Number of references to this parameter set. + */ + uint32_t ntp_refcnt; + + /** + * Queue length for pending arp requests, i.e. the number of + * packets which are accepted from other layers while the + * neighbour address is still being resolved + */ + uint32_t ntp_queue_len; + + /** + * Number of requests to send to the user level ARP daemon. + * Specify 0 to disable. + */ + uint32_t ntp_app_probes; + + /** + * Maximum number of retries for unicast solicitation. + */ + uint32_t ntp_ucast_probes; + + /** + * Maximum number of retries for multicast solicitation. + */ + uint32_t ntp_mcast_probes; + + /** + * Base value in milliseconds to ompute reachable time, see RFC2461. + */ + uint64_t ntp_base_reachable_time; + + /** + * Actual reachable time (read-only) + */ + uint64_t ntp_reachable_time; /* secs */ + + /** + * The time in milliseconds between retransmitted Neighbor + * Solicitation messages. + */ + uint64_t ntp_retrans_time; + + /** + * Interval in milliseconds to check for stale neighbour + * entries. + */ + uint64_t ntp_gc_stale_time; /* secs */ + + /** + * Delay in milliseconds for the first time probe if + * the neighbour is reachable. + */ + uint64_t ntp_probe_delay; /* secs */ + + /** + * Maximum delay in milliseconds of an answer to a neighbour + * solicitation message. + */ + uint64_t ntp_anycast_delay; + + /** + * Minimum age in milliseconds before a neighbour entry + * may be replaced. + */ + uint64_t ntp_locktime; + + /** + * Delay in milliseconds before answering to an ARP request + * for which a proxy ARP entry exists. + */ + uint64_t ntp_proxy_delay; + + /** + * Queue length for the delayed proxy arp requests. + */ + uint32_t ntp_proxy_qlen; + + /** + * Mask of available parameter attributes + */ + uint32_t ntp_mask; +}; + +#define NTBLNAMSIZ 32 + +/** + * Neighbour table + * @ingroup neightbl + */ +struct rtnl_neightbl +{ + NLHDR_COMMON + + char nt_name[NTBLNAMSIZ]; + uint32_t nt_family; + uint32_t nt_gc_thresh1; + uint32_t nt_gc_thresh2; + uint32_t nt_gc_thresh3; + uint64_t nt_gc_interval; + struct ndt_config nt_config; + struct rtnl_neightbl_parms nt_parms; + struct ndt_stats nt_stats; +}; + +struct rtnl_ratespec +{ + uint64_t rs_rate64; + uint16_t rs_overhead; + int16_t rs_cell_align; + uint16_t rs_mpu; + uint8_t rs_cell_log; +}; + +struct rtnl_tstats +{ + struct { + uint64_t bytes; + uint64_t packets; + } tcs_basic; + + struct { + uint32_t bps; + uint32_t pps; + } tcs_rate_est; + + struct { + uint32_t qlen; + uint32_t backlog; + uint32_t drops; + uint32_t requeues; + uint32_t overlimits; + } tcs_queue; +}; + +#define TCKINDSIZ 32 + +#define NL_TC_GENERIC(pre) \ + NLHDR_COMMON \ + uint32_t pre ##_family; \ + uint32_t pre ##_ifindex; \ + uint32_t pre ##_handle; \ + uint32_t pre ##_parent; \ + uint32_t pre ##_info; \ + uint32_t pre ##_mtu; \ + uint32_t pre ##_mpu; \ + uint32_t pre ##_overhead; \ + uint32_t pre ##_linktype; \ + char pre ##_kind[TCKINDSIZ]; \ + struct nl_data * pre ##_opts; \ + uint64_t pre ##_stats[RTNL_TC_STATS_MAX+1]; \ + struct nl_data * pre ##_xstats; \ + struct nl_data * pre ##_subdata; \ + struct rtnl_link * pre ##_link; \ + struct rtnl_tc_ops * pre ##_ops; \ + enum rtnl_tc_type pre ##_type; \ + uint32_t pre ##_chain + +struct rtnl_tc +{ + NL_TC_GENERIC(tc); +}; + +struct rtnl_qdisc +{ + NL_TC_GENERIC(q); +}; + +struct rtnl_class +{ + NL_TC_GENERIC(c); +}; + +struct rtnl_cls +{ + NL_TC_GENERIC(c); + uint16_t c_prio; + uint16_t c_protocol; +}; + +struct rtnl_act +{ + NL_TC_GENERIC(c); + struct rtnl_act * a_next; +}; + +struct rtnl_mirred +{ + struct tc_mirred m_parm; +}; + +struct rtnl_skbedit +{ + struct tc_skbedit s_parm; + uint32_t s_flags; + uint32_t s_mark; + uint32_t s_prio; + uint16_t s_queue_mapping; +}; + +struct rtnl_gact +{ + struct tc_gact g_parm; +}; + +struct rtnl_u32 +{ + uint32_t cu_divisor; + uint32_t cu_hash; + uint32_t cu_classid; + uint32_t cu_link; + struct nl_data * cu_pcnt; + struct nl_data * cu_selector; + struct nl_data * cu_mark; + struct rtnl_act* cu_act; + struct nl_data * cu_police; + char cu_indev[IFNAMSIZ]; + int cu_mask; +}; + +struct rtnl_mall +{ + uint32_t m_classid; + uint32_t m_flags; + struct rtnl_act *m_act; + int m_mask; +}; + +struct rtnl_cgroup +{ + struct rtnl_ematch_tree *cg_ematch; + int cg_mask; +}; + +struct rtnl_fw +{ + uint32_t cf_classid; + struct nl_data * cf_act; + struct nl_data * cf_police; + char cf_indev[IFNAMSIZ]; + uint32_t cf_fwmask; + int cf_mask; +}; + +struct rtnl_ematch +{ + uint16_t e_id; + uint16_t e_kind; + uint16_t e_flags; + uint16_t e_index; + size_t e_datalen; + + struct nl_list_head e_childs; + struct nl_list_head e_list; + struct rtnl_ematch_ops *e_ops; + + void * e_data; +}; + +struct rtnl_ematch_tree +{ + uint16_t et_progid; + struct nl_list_head et_list; + +}; + +struct rtnl_dsmark_qdisc +{ + uint16_t qdm_indices; + uint16_t qdm_default_index; + uint32_t qdm_set_tc_index; + uint32_t qdm_mask; +}; + +struct rtnl_dsmark_class +{ + uint8_t cdm_bmask; + uint8_t cdm_value; + uint32_t cdm_mask; +}; + +struct rtnl_fifo +{ + uint32_t qf_limit; + uint32_t qf_mask; +}; + +struct rtnl_prio +{ + uint32_t qp_bands; + uint8_t qp_priomap[TC_PRIO_MAX+1]; + uint32_t qp_mask; +}; + +struct rtnl_mqprio +{ + uint8_t qm_num_tc; + uint8_t qm_prio_map[TC_QOPT_BITMASK + 1]; + uint8_t qm_hw; + uint16_t qm_count[TC_QOPT_MAX_QUEUE]; + uint16_t qm_offset[TC_QOPT_MAX_QUEUE]; + uint16_t qm_mode; + uint16_t qm_shaper; + uint64_t qm_min_rate[TC_QOPT_MAX_QUEUE]; + uint64_t qm_max_rate[TC_QOPT_MAX_QUEUE]; + uint32_t qm_mask; +}; + +struct rtnl_tbf +{ + uint32_t qt_limit; + struct rtnl_ratespec qt_rate; + uint32_t qt_rate_bucket; + uint32_t qt_rate_txtime; + struct rtnl_ratespec qt_peakrate; + uint32_t qt_peakrate_bucket; + uint32_t qt_peakrate_txtime; + uint32_t qt_mask; +}; + +struct rtnl_sfq +{ + uint32_t qs_quantum; + uint32_t qs_perturb; + uint32_t qs_limit; + uint32_t qs_divisor; + uint32_t qs_flows; + uint32_t qs_mask; +}; + +struct rtnl_netem_corr +{ + uint32_t nmc_delay; + uint32_t nmc_loss; + uint32_t nmc_duplicate; +}; + +struct rtnl_netem_reo +{ + uint32_t nmro_probability; + uint32_t nmro_correlation; +}; + +struct rtnl_netem_crpt +{ + uint32_t nmcr_probability; + uint32_t nmcr_correlation; +}; + +struct rtnl_netem_dist +{ + int16_t * dist_data; + size_t dist_size; +}; + +struct rtnl_netem +{ + uint32_t qnm_latency; + uint32_t qnm_limit; + uint32_t qnm_loss; + uint32_t qnm_gap; + uint32_t qnm_duplicate; + uint32_t qnm_jitter; + uint32_t qnm_mask; + struct rtnl_netem_corr qnm_corr; + struct rtnl_netem_reo qnm_ro; + struct rtnl_netem_crpt qnm_crpt; + struct rtnl_netem_dist qnm_dist; +}; + +struct rtnl_htb_qdisc +{ + uint32_t qh_rate2quantum; + uint32_t qh_defcls; + uint32_t qh_mask; + uint32_t qh_direct_pkts; +}; + +struct rtnl_htb_class +{ + uint32_t ch_prio; + struct rtnl_ratespec ch_rate; + struct rtnl_ratespec ch_ceil; + uint32_t ch_rbuffer; + uint32_t ch_cbuffer; + uint32_t ch_quantum; + uint32_t ch_mask; + uint32_t ch_level; +}; + +struct rtnl_cbq +{ + struct tc_cbq_lssopt cbq_lss; + struct tc_ratespec cbq_rate; + struct tc_cbq_wrropt cbq_wrr; + struct tc_cbq_ovl cbq_ovl; + struct tc_cbq_fopt cbq_fopt; + struct tc_cbq_police cbq_police; +}; + +struct rtnl_red +{ + uint32_t qr_limit; + uint32_t qr_qth_min; + uint32_t qr_qth_max; + uint8_t qr_flags; + uint8_t qr_wlog; + uint8_t qr_plog; + uint8_t qr_scell_log; + uint32_t qr_mask; +}; + +struct rtnl_plug +{ + int action; + uint32_t limit; +}; + +struct rtnl_fq_codel +{ + int fq_limit; + uint32_t fq_target; + uint32_t fq_interval; + int fq_flows; + uint32_t fq_quantum; + int fq_ecn; + uint32_t fq_mask; +}; + +struct rtnl_hfsc_qdisc +{ + uint32_t qh_defcls; + uint32_t qh_mask; +}; + +struct rtnl_hfsc_class +{ + struct tc_service_curve ch_rsc; + struct tc_service_curve ch_fsc; + struct tc_service_curve ch_usc; + uint32_t ch_mask; +}; + +struct flnl_request +{ + NLHDR_COMMON + + struct nl_addr * lr_addr; + uint32_t lr_fwmark; + uint8_t lr_tos; + uint8_t lr_scope; + uint8_t lr_table; +}; + + +struct flnl_result +{ + NLHDR_COMMON + + struct flnl_request * fr_req; + uint8_t fr_table_id; + uint8_t fr_prefixlen; + uint8_t fr_nh_sel; + uint8_t fr_type; + uint8_t fr_scope; + uint32_t fr_error; +}; + +#define GENL_OP_HAS_POLICY 1 +#define GENL_OP_HAS_DOIT 2 +#define GENL_OP_HAS_DUMPIT 4 + +struct genl_family_op +{ + uint32_t o_id; + uint32_t o_flags; + + struct nl_list_head o_list; +}; + +struct genl_family_grp { + struct genl_family *family; /* private */ + struct nl_list_head list; /* private */ + char name[GENL_NAMSIZ]; + u_int32_t id; +}; + +struct genl_family +{ + NLHDR_COMMON + + uint16_t gf_id; + char gf_name[GENL_NAMSIZ]; + uint32_t gf_version; + uint32_t gf_hdrsize; + uint32_t gf_maxattr; + + struct nl_list_head gf_ops; + struct nl_list_head gf_mc_grps; +}; + +union nfnl_ct_proto +{ + struct { + uint16_t src; + uint16_t dst; + } port; + struct { + uint16_t id; + uint8_t type; + uint8_t code; + } icmp; +}; + +struct nfnl_ct_dir { + struct nl_addr * src; + struct nl_addr * dst; + union nfnl_ct_proto proto; + uint64_t packets; + uint64_t bytes; +}; + +union nfnl_ct_protoinfo { + struct { + uint8_t state; + } tcp; +}; + +struct nfnl_ct { + NLHDR_COMMON + + uint8_t ct_family; + uint8_t ct_proto; + union nfnl_ct_protoinfo ct_protoinfo; + + uint32_t ct_status; + uint32_t ct_status_mask; + uint32_t ct_timeout; + uint32_t ct_mark; + uint32_t ct_use; + uint32_t ct_id; + uint16_t ct_zone; + + struct nfnl_ct_dir ct_orig; + struct nfnl_ct_dir ct_repl; + + struct nfnl_ct_timestamp ct_tstamp; +}; + +union nfnl_exp_protodata { + struct { + uint16_t src; + uint16_t dst; + } port; + struct { + uint16_t id; + uint8_t type; + uint8_t code; + } icmp; +}; + +// Allow for different master/expect l4 protocols +struct nfnl_exp_proto +{ + uint8_t l4protonum; + union nfnl_exp_protodata l4protodata; +}; + +struct nfnl_exp_dir { + struct nl_addr * src; + struct nl_addr * dst; + struct nfnl_exp_proto proto; +}; + +struct nfnl_exp { + NLHDR_COMMON + + uint8_t exp_family; + uint32_t exp_timeout; + uint32_t exp_id; + uint16_t exp_zone; + uint32_t exp_class; + uint32_t exp_flags; + char * exp_helper_name; + char * exp_fn; + uint8_t exp_nat_dir; + + struct nfnl_exp_dir exp_expect; + struct nfnl_exp_dir exp_master; + struct nfnl_exp_dir exp_mask; + struct nfnl_exp_dir exp_nat; +}; + +struct nfnl_log { + NLHDR_COMMON + + uint16_t log_group; + uint8_t log_copy_mode; + uint32_t log_copy_range; + uint32_t log_flush_timeout; + uint32_t log_alloc_size; + uint32_t log_queue_threshold; + uint32_t log_flags; + uint32_t log_flag_mask; +}; + +struct nfnl_log_msg { + NLHDR_COMMON + + uint8_t log_msg_family; + uint8_t log_msg_hook; + uint16_t log_msg_hwproto; + uint32_t log_msg_mark; + struct timeval log_msg_timestamp; + uint32_t log_msg_indev; + uint32_t log_msg_outdev; + uint32_t log_msg_physindev; + uint32_t log_msg_physoutdev; + uint8_t log_msg_hwaddr[8]; + int log_msg_hwaddr_len; + void * log_msg_payload; + int log_msg_payload_len; + char * log_msg_prefix; + uint32_t log_msg_uid; + uint32_t log_msg_gid; + uint32_t log_msg_seq; + uint32_t log_msg_seq_global; +}; + +struct nfnl_queue { + NLHDR_COMMON + + uint16_t queue_group; + uint32_t queue_maxlen; + uint32_t queue_copy_range; + uint8_t queue_copy_mode; +}; + +struct nfnl_queue_msg { + NLHDR_COMMON + + uint16_t queue_msg_group; + uint8_t queue_msg_family; + uint8_t queue_msg_hook; + uint16_t queue_msg_hwproto; + uint32_t queue_msg_packetid; + uint32_t queue_msg_mark; + struct timeval queue_msg_timestamp; + uint32_t queue_msg_indev; + uint32_t queue_msg_outdev; + uint32_t queue_msg_physindev; + uint32_t queue_msg_physoutdev; + uint8_t queue_msg_hwaddr[8]; + int queue_msg_hwaddr_len; + void * queue_msg_payload; + int queue_msg_payload_len; + uint32_t queue_msg_verdict; +}; + +struct ematch_quoted { + char * data; + size_t len; + int index; +}; + +struct idiagnl_meminfo { + NLHDR_COMMON + + uint32_t idiag_rmem; + uint32_t idiag_wmem; + uint32_t idiag_fmem; + uint32_t idiag_tmem; +}; + +struct idiagnl_vegasinfo { + NLHDR_COMMON + + uint32_t tcpv_enabled; + uint32_t tcpv_rttcnt; + uint32_t tcpv_rtt; + uint32_t tcpv_minrtt; +}; + +struct idiagnl_msg { + NLHDR_COMMON + + uint8_t idiag_family; + uint8_t idiag_state; + uint8_t idiag_timer; + uint8_t idiag_retrans; + uint16_t idiag_sport; + uint16_t idiag_dport; + struct nl_addr * idiag_src; + struct nl_addr * idiag_dst; + uint32_t idiag_ifindex; + uint32_t idiag_expires; + uint32_t idiag_rqueue; + uint32_t idiag_wqueue; + uint32_t idiag_uid; + uint32_t idiag_inode; + + uint8_t idiag_tos; + uint8_t idiag_tclass; + uint8_t idiag_shutdown; + char * idiag_cong; + struct idiagnl_meminfo * idiag_meminfo; + struct idiagnl_vegasinfo * idiag_vegasinfo; + struct tcp_info idiag_tcpinfo; + uint32_t idiag_skmeminfo[SK_MEMINFO_VARS]; +}; + +struct idiagnl_req { + NLHDR_COMMON + + uint8_t idiag_family; + uint8_t idiag_ext; + struct nl_addr * idiag_src; + struct nl_addr * idiag_dst; + uint32_t idiag_ifindex; + uint32_t idiag_states; + uint32_t idiag_dbs; +}; + +// XFRM related definitions + +/* Selector, used as selector both on policy rules (SPD) and SAs. */ +struct xfrmnl_sel { + uint32_t refcnt; + struct nl_addr* daddr; + struct nl_addr* saddr; + uint16_t dport; + uint16_t dport_mask; + uint16_t sport; + uint16_t sport_mask; + uint16_t family; + uint8_t prefixlen_d; + uint8_t prefixlen_s; + uint8_t proto; + int32_t ifindex; + uint32_t user; +}; + +/* Lifetime configuration, used for both policy rules (SPD) and SAs. */ +struct xfrmnl_ltime_cfg { + uint32_t refcnt; + uint64_t soft_byte_limit; + uint64_t hard_byte_limit; + uint64_t soft_packet_limit; + uint64_t hard_packet_limit; + uint64_t soft_add_expires_seconds; + uint64_t hard_add_expires_seconds; + uint64_t soft_use_expires_seconds; + uint64_t hard_use_expires_seconds; +}; + +/* Current lifetime, used for both policy rules (SPD) and SAs. */ +struct xfrmnl_lifetime_cur { + uint64_t bytes; + uint64_t packets; + uint64_t add_time; + uint64_t use_time; +}; + +struct xfrmnl_replay_state { + uint32_t oseq; + uint32_t seq; + uint32_t bitmap; +}; + +struct xfrmnl_replay_state_esn { + uint32_t bmp_len; + uint32_t oseq; + uint32_t seq; + uint32_t oseq_hi; + uint32_t seq_hi; + uint32_t replay_window; + uint32_t bmp[0]; +}; + +struct xfrmnl_mark { + uint32_t v; /* value */ + uint32_t m; /* mask */ +}; + +/* XFRM AE related definitions */ + +struct xfrmnl_sa_id { + struct nl_addr* daddr; + uint32_t spi; + uint16_t family; + uint8_t proto; +}; + +struct xfrmnl_ae { + NLHDR_COMMON + + struct xfrmnl_sa_id sa_id; + struct nl_addr* saddr; + uint32_t flags; + uint32_t reqid; + struct xfrmnl_mark mark; + struct xfrmnl_lifetime_cur lifetime_cur; + uint32_t replay_maxage; + uint32_t replay_maxdiff; + struct xfrmnl_replay_state replay_state; + struct xfrmnl_replay_state_esn* replay_state_esn; +}; + +/* XFRM SA related definitions */ + +struct xfrmnl_id { + struct nl_addr* daddr; + uint32_t spi; + uint8_t proto; +}; + +struct xfrmnl_stats { + uint32_t replay_window; + uint32_t replay; + uint32_t integrity_failed; +}; + +struct xfrmnl_algo_aead { + char alg_name[64]; + uint32_t alg_key_len; /* in bits */ + uint32_t alg_icv_len; /* in bits */ + char alg_key[0]; +}; + +struct xfrmnl_algo_auth { + char alg_name[64]; + uint32_t alg_key_len; /* in bits */ + uint32_t alg_trunc_len; /* in bits */ + char alg_key[0]; +}; + +struct xfrmnl_algo { + char alg_name[64]; + uint32_t alg_key_len; /* in bits */ + char alg_key[0]; +}; + +struct xfrmnl_encap_tmpl { + uint16_t encap_type; + uint16_t encap_sport; + uint16_t encap_dport; + struct nl_addr* encap_oa; +}; + +struct xfrmnl_sa { + NLHDR_COMMON + + struct xfrmnl_sel* sel; + struct xfrmnl_id id; + struct nl_addr* saddr; + struct xfrmnl_ltime_cfg* lft; + struct xfrmnl_lifetime_cur curlft; + struct xfrmnl_stats stats; + uint32_t seq; + uint32_t reqid; + uint16_t family; + uint8_t mode; /* XFRM_MODE_xxx */ + uint8_t replay_window; + uint8_t flags; + struct xfrmnl_algo_aead* aead; + struct xfrmnl_algo_auth* auth; + struct xfrmnl_algo* crypt; + struct xfrmnl_algo* comp; + struct xfrmnl_encap_tmpl* encap; + uint32_t tfcpad; + struct nl_addr* coaddr; + struct xfrmnl_mark mark; + struct xfrmnl_user_sec_ctx* sec_ctx; + uint32_t replay_maxage; + uint32_t replay_maxdiff; + struct xfrmnl_replay_state replay_state; + struct xfrmnl_replay_state_esn* replay_state_esn; + uint8_t hard; +}; + +struct xfrmnl_usersa_flush { + uint8_t proto; +}; + + +/* XFRM SP related definitions */ + +struct xfrmnl_userpolicy_id { + struct xfrmnl_sel sel; + uint32_t index; + uint8_t dir; +}; + +struct xfrmnl_user_sec_ctx { + uint16_t len; + uint16_t exttype; + uint8_t ctx_alg; + uint8_t ctx_doi; + uint16_t ctx_len; + char ctx[0]; +}; + +struct xfrmnl_userpolicy_type { + uint8_t type; + uint16_t reserved1; + uint16_t reserved2; +}; + +struct xfrmnl_user_tmpl { + struct xfrmnl_id id; + uint16_t family; + struct nl_addr* saddr; + uint32_t reqid; + uint8_t mode; + uint8_t share; + uint8_t optional; + uint32_t aalgos; + uint32_t ealgos; + uint32_t calgos; + struct nl_list_head utmpl_list; +}; + +struct xfrmnl_sp { + NLHDR_COMMON + + struct xfrmnl_sel* sel; + struct xfrmnl_ltime_cfg* lft; + struct xfrmnl_lifetime_cur curlft; + uint32_t priority; + uint32_t index; + uint8_t dir; + uint8_t action; + uint8_t flags; + uint8_t share; + struct xfrmnl_user_sec_ctx* sec_ctx; + struct xfrmnl_userpolicy_type uptype; + uint32_t nr_user_tmpl; + struct nl_list_head usertmpl_list; + struct xfrmnl_mark mark; +}; + +struct rtnl_vlan +{ + struct tc_vlan v_parm; + uint16_t v_vid; + uint16_t v_proto; + uint8_t v_prio; + uint32_t v_flags; +}; + +#endif diff --git a/libnl/include/netlink-private/utils.h b/libnl/include/netlink-private/utils.h new file mode 100644 index 0000000..afb6f16 --- /dev/null +++ b/libnl/include/netlink-private/utils.h @@ -0,0 +1,255 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +#ifndef NETLINK_UTILS_PRIV_H_ +#define NETLINK_UTILS_PRIV_H_ + +#include +#include + +#if __BYTE_ORDER == __BIG_ENDIAN +#define ntohll(x) (x) +#elif __BYTE_ORDER == __LITTLE_ENDIAN +#define ntohll(x) bswap_64((x)) +#endif +#define htonll(x) ntohll(x) + +/*****************************************************************************/ + +#define _NL_STRINGIFY_ARG(contents) #contents +#define _NL_STRINGIFY(macro_or_string) _NL_STRINGIFY_ARG (macro_or_string) + +/*****************************************************************************/ + +#if defined (__GNUC__) +#define _NL_PRAGMA_WARNING_DO(warning) _NL_STRINGIFY(GCC diagnostic ignored warning) +#elif defined (__clang__) +#define _NL_PRAGMA_WARNING_DO(warning) _NL_STRINGIFY(clang diagnostic ignored warning) +#endif + +/* you can only suppress a specific warning that the compiler + * understands. Otherwise you will get another compiler warning + * about invalid pragma option. + * It's not that bad however, because gcc and clang often have the + * same name for the same warning. */ + +#if defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) +#define _NL_PRAGMA_WARNING_DISABLE(warning) \ + _Pragma("GCC diagnostic push") \ + _Pragma(_NL_PRAGMA_WARNING_DO("-Wpragmas")) \ + _Pragma(_NL_PRAGMA_WARNING_DO(warning)) +#elif defined (__clang__) +#define _NL_PRAGMA_WARNING_DISABLE(warning) \ + _Pragma("clang diagnostic push") \ + _Pragma(_NL_PRAGMA_WARNING_DO("-Wunknown-warning-option")) \ + _Pragma(_NL_PRAGMA_WARNING_DO(warning)) +#else +#define _NL_PRAGMA_WARNING_DISABLE(warning) +#endif + +#if defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) +#define _NL_PRAGMA_WARNING_REENABLE \ + _Pragma("GCC diagnostic pop") +#elif defined (__clang__) +#define _NL_PRAGMA_WARNING_REENABLE \ + _Pragma("clang diagnostic pop") +#else +#define _NL_PRAGMA_WARNING_REENABLE +#endif + +/*****************************************************************************/ + +#define _nl_unused __attribute__ ((__unused__)) +#define _nl_auto(fcn) __attribute__ ((__cleanup__(fcn))) + +/*****************************************************************************/ + +#define _NL_STATIC_ASSERT(cond) ((void) sizeof (char[(cond) ? 1 : -1])) + +/*****************************************************************************/ + +#if defined(NL_MORE_ASSERTS) && NL_MORE_ASSERTS > 0 +#define _nl_assert(cond) assert(cond) +#else +#define _nl_assert(cond) do { if (0) { assert(cond); } } while (0) +#endif + +#define _nl_assert_not_reached() assert(0) + +/*****************************************************************************/ + +extern const char *nl_strerror_l(int err); + +/*****************************************************************************/ + +/* internal macro to calculate the size of a struct @type up to (and including) @field. + * this will be used for .minlen policy fields, so that we require only a field of up + * to the given size. */ +#define _nl_offsetofend(type, field) (offsetof (type, field) + sizeof (((type *) NULL)->field)) + +/*****************************************************************************/ + +#define _nl_clear_pointer(pp, destroy) \ + ({ \ + __typeof__ (*(pp)) *_pp = (pp); \ + __typeof__ (*_pp) _p; \ + int _changed = 0; \ + \ + if ( _pp \ + && (_p = *_pp)) { \ + _nl_unused const void *const _p_check_is_pointer = _p; \ + \ + *_pp = NULL; \ + \ + (destroy) (_p); \ + \ + _changed = 1; \ + } \ + _changed; \ + }) + +#define _nl_clear_free(pp) _nl_clear_pointer (pp, free) + +#define _nl_steal_pointer(pp) \ + ({ \ + __typeof__ (*(pp)) *const _pp = (pp); \ + __typeof__ (*_pp) _p = NULL; \ + \ + if ( _pp \ + && (_p = *_pp)) { \ + *_pp = NULL; \ + } \ + \ + _p; \ + }) + +/*****************************************************************************/ + +#define _nl_malloc_maybe_a(alloca_maxlen, bytes, to_free) \ + ({ \ + const size_t _bytes = (bytes); \ + __typeof__ (to_free) _to_free = (to_free); \ + __typeof__ (*_to_free) _ptr; \ + \ + _NL_STATIC_ASSERT ((alloca_maxlen) <= 500); \ + _nl_assert (_to_free && !*_to_free); \ + \ + if (_bytes <= (alloca_maxlen)) { \ + _ptr = alloca (_bytes); \ + } else { \ + _ptr = malloc (_bytes); \ + *_to_free = _ptr; \ + }; \ + \ + _ptr; \ + }) + +/*****************************************************************************/ + +static inline char * +_nl_strncpy_trunc(char *dst, const char *src, size_t len) +{ + /* we don't use/reimplement strlcpy(), because we want the fill-all-with-NUL + * behavior of strncpy(). This is just strncpy() with gracefully handling truncation + * (and disabling the "-Wstringop-truncation" warning). + * + * Note that truncation is silently accepted. + */ + + _NL_PRAGMA_WARNING_DISABLE ("-Wstringop-truncation"); + _NL_PRAGMA_WARNING_DISABLE ("-Wstringop-overflow"); + + if (len > 0) { + _nl_assert(dst); + _nl_assert(src); + + strncpy(dst, src, len); + + dst[len - 1] = '\0'; + } + + _NL_PRAGMA_WARNING_REENABLE; + _NL_PRAGMA_WARNING_REENABLE; + + return dst; +} + +static inline char * +_nl_strncpy_assert(char *dst, const char *src, size_t len) +{ + /* we don't use/reimplement strlcpy(), because we want the fill-all-with-NUL + * behavior of strncpy(). This is just strncpy() with assertion against truncation + * (and disabling the "-Wstringop-truncation" warning). + * + * Note that truncation is still a bug and there is an _nl_assert() + * against that. + */ + + _NL_PRAGMA_WARNING_DISABLE ("-Wstringop-truncation"); + _NL_PRAGMA_WARNING_DISABLE ("-Wstringop-overflow"); + + if (len > 0) { + _nl_assert(dst); + _nl_assert(src); + + strncpy(dst, src, len); + + _nl_assert (dst[len - 1] == '\0'); + + dst[len - 1] = '\0'; + } + + _NL_PRAGMA_WARNING_REENABLE; + _NL_PRAGMA_WARNING_REENABLE; + + return dst; +} + +#include "nl-auto.h" + +#define _NL_RETURN_ON_ERR(cmd) \ + do { \ + int _err; \ + \ + _err = (cmd); \ + if (_err < 0) \ + return _err; \ + } while (0) + +#define _NL_RETURN_E_ON_ERR(e, cmd) \ + do { \ + int _err; \ + \ + _err = (cmd); \ + if (_err < 0) { \ + _NL_STATIC_ASSERT((e) > 0); \ + return -(e); \ + } \ + } while (0) + +/* _NL_RETURN_ON_PUT_ERR() shall only be used with a put command (nla_put or nlmsg_append). + * These commands can either fail with a regular error code (which gets propagated) + * or with -NLE_NOMEM. However, they don't really try to allocate memory, so we don't + * want to propagate -NLE_NOMEM. Instead, we coerce such failure to -NLE_MSGSIZE. */ +#define _NL_RETURN_ON_PUT_ERR(put_cmd) \ + do { \ + int _err; \ + \ + _err = (put_cmd); \ + if (_err < 0) { \ + if (_err == -NLE_NOMEM) { \ + /* nla_put() returns -NLE_NOMEM in case of out of buffer size. We don't + * want to propagate that error and map it to -NLE_MSGSIZE. */ \ + return -NLE_MSGSIZE; \ + } \ + /* any other error can only be due to invalid parameters. Propagate the + * error, however also assert that it cannot be reached. */ \ + _nl_assert_not_reached (); \ + return _err; \ + } else \ + _nl_assert (_err == 0); \ + } while (0) + +#endif diff --git a/libnl/include/netlink/addr.h b/libnl/include/netlink/addr.h new file mode 100644 index 0000000..851e940 --- /dev/null +++ b/libnl/include/netlink/addr.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + */ + +#ifndef NETLINK_ADDR_H_ +#define NETLINK_ADDR_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nlattr; + +struct nl_addr; + +/* Creation */ +extern struct nl_addr * nl_addr_alloc(size_t); +extern struct nl_addr * nl_addr_alloc_attr(const struct nlattr *, int); +extern struct nl_addr * nl_addr_build(int, const void *, size_t); +extern int nl_addr_parse(const char *, int, struct nl_addr **); +extern struct nl_addr * nl_addr_clone(const struct nl_addr *); + +/* Usage Management */ +extern struct nl_addr * nl_addr_get(struct nl_addr *); +extern void nl_addr_put(struct nl_addr *); +extern int nl_addr_shared(const struct nl_addr *); + +extern int nl_addr_cmp(const struct nl_addr *, + const struct nl_addr *); +extern int nl_addr_cmp_prefix(const struct nl_addr *, + const struct nl_addr *); +extern int nl_addr_iszero(const struct nl_addr *); +extern int nl_addr_valid(const char *, int); +extern int nl_addr_guess_family(const struct nl_addr *); +extern int nl_addr_fill_sockaddr(const struct nl_addr *, + struct sockaddr *, socklen_t *); +extern int nl_addr_info(const struct nl_addr *, + struct addrinfo **); +extern int nl_addr_resolve(const struct nl_addr *, char *, size_t); + +/* Access Functions */ +extern void nl_addr_set_family(struct nl_addr *, int); +extern int nl_addr_get_family(const struct nl_addr *); +extern int nl_addr_set_binary_addr(struct nl_addr *, const void *, + size_t); +extern void * nl_addr_get_binary_addr(const struct nl_addr *); +extern unsigned int nl_addr_get_len(const struct nl_addr *); +extern void nl_addr_set_prefixlen(struct nl_addr *, int); +extern unsigned int nl_addr_get_prefixlen(const struct nl_addr *); + +/* Address Family Translations */ +extern char * nl_af2str(int, char *, size_t); +extern int nl_str2af(const char *); + +/* Translations to Strings */ +extern char * nl_addr2str(const struct nl_addr *, char *, size_t); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/attr.h b/libnl/include/netlink/attr.h new file mode 100644 index 0000000..2cd32ee --- /dev/null +++ b/libnl/include/netlink/attr.h @@ -0,0 +1,335 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + */ + +#ifndef NETLINK_ATTR_H_ +#define NETLINK_ATTR_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nlattr; + +struct nl_msg; + +/** + * @name Basic Attribute Data Types + * @{ + */ + +/** + * @ingroup attr + * Basic attribute data types + * + * See section @core_doc{core_attr_parse,Attribute Parsing} for more details. + */ +enum { + NLA_UNSPEC, /**< Unspecified type, binary data chunk */ + NLA_U8, /**< 8 bit integer */ + NLA_U16, /**< 16 bit integer */ + NLA_U32, /**< 32 bit integer */ + NLA_U64, /**< 64 bit integer */ + NLA_STRING, /**< NUL terminated character string */ + NLA_FLAG, /**< Flag */ + NLA_MSECS, /**< Micro seconds (64bit) */ + NLA_NESTED, /**< Nested attributes */ + NLA_NESTED_COMPAT, + NLA_NUL_STRING, + NLA_BINARY, + NLA_S8, + NLA_S16, + NLA_S32, + NLA_S64, + __NLA_TYPE_MAX, +}; + +#define NLA_TYPE_MAX (__NLA_TYPE_MAX - 1) + +/** @} */ + +/** + * @ingroup attr + * Attribute validation policy. + * + * See section @core_doc{core_attr_parse,Attribute Parsing} for more details. + */ +struct nla_policy { + /** Type of attribute or NLA_UNSPEC */ + uint16_t type; + + /** Minimal length of payload required */ + uint16_t minlen; + + /** Maximal length of payload allowed */ + uint16_t maxlen; +}; + +/* Size calculations */ +extern int nla_attr_size(int payload); +extern int nla_total_size(int payload); +extern int nla_padlen(int payload); + +/* Attribute parsing */ +extern int nla_type(const struct nlattr *); +extern void * nla_data(const struct nlattr *); +extern int nla_len(const struct nlattr *); +extern int nla_ok(const struct nlattr *, int); +extern struct nlattr * nla_next(const struct nlattr *, int *); +extern int nla_parse(struct nlattr **, int, struct nlattr *, + int, const struct nla_policy *); +extern int nla_validate(const struct nlattr *, int, int, + const struct nla_policy *); +extern struct nlattr * nla_find(const struct nlattr *, int, int); + +/* Helper Functions */ +extern int nla_memcpy(void *, const struct nlattr *, int); +extern size_t nla_strlcpy(char *, const struct nlattr *, size_t); +extern int nla_memcmp(const struct nlattr *, const void *, size_t); +extern int nla_strcmp(const struct nlattr *, const char *); + +/* Unspecific attribute */ +extern struct nlattr * nla_reserve(struct nl_msg *, int, int); +extern int nla_put(struct nl_msg *, int, int, const void *); +extern int nla_put_data(struct nl_msg *, int, + const struct nl_data *); +extern int nla_put_addr(struct nl_msg *, int, struct nl_addr *); + +/* Integer attribute */ +extern int8_t nla_get_s8(const struct nlattr *); +extern int nla_put_s8(struct nl_msg *, int, int8_t); +extern uint8_t nla_get_u8(const struct nlattr *); +extern int nla_put_u8(struct nl_msg *, int, uint8_t); +extern int16_t nla_get_s16(const struct nlattr *); +extern int nla_put_s16(struct nl_msg *, int, int16_t); +extern uint16_t nla_get_u16(const struct nlattr *); +extern int nla_put_u16(struct nl_msg *, int, uint16_t); +extern int32_t nla_get_s32(const struct nlattr *); +extern int nla_put_s32(struct nl_msg *, int, int32_t); +extern uint32_t nla_get_u32(const struct nlattr *); +extern int nla_put_u32(struct nl_msg *, int, uint32_t); +extern int64_t nla_get_s64(const struct nlattr *); +extern int nla_put_s64(struct nl_msg *, int, int64_t); +extern uint64_t nla_get_u64(const struct nlattr *); +extern int nla_put_u64(struct nl_msg *, int, uint64_t); + +/* String attribute */ +extern char * nla_get_string(const struct nlattr *); +extern char * nla_strdup(const struct nlattr *); +extern int nla_put_string(struct nl_msg *, int, const char *); + +/* Flag attribute */ +extern int nla_get_flag(const struct nlattr *); +extern int nla_put_flag(struct nl_msg *, int); + +/* Msec attribute */ +extern unsigned long nla_get_msecs(const struct nlattr *); +extern int nla_put_msecs(struct nl_msg *, int, unsigned long); + +/* Attribute nesting */ +extern int nla_put_nested(struct nl_msg *, int, + const struct nl_msg *); +extern struct nlattr * nla_nest_start(struct nl_msg *, int); +extern int nla_nest_end(struct nl_msg *, struct nlattr *); +extern int nla_nest_end_keep_empty(struct nl_msg *, struct nlattr *); +extern void nla_nest_cancel(struct nl_msg *, const struct nlattr *); +extern int nla_parse_nested(struct nlattr **, int, struct nlattr *, + const struct nla_policy *); +extern int nla_is_nested(const struct nlattr *); + +/** + * @name Attribute Construction (Exception Based) + * @{ + */ + +/** + * @ingroup attr + * Add unspecific attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg attrlen Length of attribute payload. + * @arg data Head of attribute payload. + */ +#define NLA_PUT(msg, attrtype, attrlen, data) \ + do { \ + if (nla_put(msg, attrtype, attrlen, data) < 0) \ + goto nla_put_failure; \ + } while(0) + +/** + * @ingroup attr + * Add atomic type attribute to netlink message. + * @arg msg Netlink message. + * @arg type Atomic type. + * @arg attrtype Attribute type. + * @arg value Head of attribute payload. + */ +#define NLA_PUT_TYPE(msg, type, attrtype, value) \ + do { \ + type __tmp = value; \ + NLA_PUT(msg, attrtype, sizeof(type), &__tmp); \ + } while(0) + +/** + * Add 8 bit signed integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value. + */ +#define NLA_PUT_S8(msg, attrtype, value) \ + NLA_PUT_TYPE(msg, int8_t, attrtype, value) + +/** + * Add 8 bit integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value. + */ +#define NLA_PUT_U8(msg, attrtype, value) \ + NLA_PUT_TYPE(msg, uint8_t, attrtype, value) + +/** + * Add 16 bit signed integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value. + */ +#define NLA_PUT_S16(msg, attrtype, value) \ + NLA_PUT_TYPE(msg, int16_t, attrtype, value) + +/** + * Add 16 bit integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value. + */ +#define NLA_PUT_U16(msg, attrtype, value) \ + NLA_PUT_TYPE(msg, uint16_t, attrtype, value) + +/** + * Add 32 bit signed integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value. + */ +#define NLA_PUT_S32(msg, attrtype, value) \ + NLA_PUT_TYPE(msg, int32_t, attrtype, value) + +/** + * Add 32 bit integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value. + */ +#define NLA_PUT_U32(msg, attrtype, value) \ + NLA_PUT_TYPE(msg, uint32_t, attrtype, value) + +/** + * Add 64 bit signed integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value. + */ +#define NLA_PUT_S64(msg, attrtype, value) \ + NLA_PUT_TYPE(msg, int64_t, attrtype, value) + +/** + * Add 64 bit integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value. + */ +#define NLA_PUT_U64(msg, attrtype, value) \ + NLA_PUT_TYPE(msg, uint64_t, attrtype, value) + +/** + * Add string attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value NUL terminated character string. + */ +#define NLA_PUT_STRING(msg, attrtype, value) \ + NLA_PUT(msg, attrtype, (int) strlen(value) + 1, value) + +/** + * Add flag attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + */ +#define NLA_PUT_FLAG(msg, attrtype) \ + NLA_PUT(msg, attrtype, 0, NULL) + +/** + * Add msecs attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg msecs Numeric value in micro seconds. + */ +#define NLA_PUT_MSECS(msg, attrtype, msecs) \ + NLA_PUT_U64(msg, attrtype, msecs) + +/** + * Add address attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg addr Abstract address object. + */ +#define NLA_PUT_ADDR(msg, attrtype, addr) \ + NLA_PUT(msg, attrtype, nl_addr_get_len(addr), \ + nl_addr_get_binary_addr(addr)) + +/** + * Add abstract data attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg data Abstract data object. + */ +#define NLA_PUT_DATA(msg, attrtype, data) \ + NLA_PUT(msg, attrtype, nl_data_get_size(data), \ + nl_data_get(data)) + +/** @} */ + +/** + * @name Iterators + * @{ + */ + +/** + * @ingroup attr + * Iterate over a stream of attributes + * @arg pos loop counter, set to current attribute + * @arg head head of attribute stream + * @arg len length of attribute stream + * @arg rem initialized to len, holds bytes currently remaining in stream + */ +#define nla_for_each_attr(pos, head, len, rem) \ + for (pos = head, rem = len; \ + nla_ok(pos, rem); \ + pos = nla_next(pos, &(rem))) + +/** + * @ingroup attr + * Iterate over a stream of nested attributes + * @arg pos loop counter, set to current attribute + * @arg nla attribute containing the nested attributes + * @arg rem initialized to len, holds bytes currently remaining in stream + */ +#define nla_for_each_nested(pos, nla, rem) \ + for (pos = (struct nlattr *) nla_data(nla), rem = nla_len(nla); \ + nla_ok(pos, rem); \ + pos = nla_next(pos, &(rem))) + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/cache-api.h b/libnl/include/netlink/cache-api.h new file mode 100644 index 0000000..851eca0 --- /dev/null +++ b/libnl/include/netlink/cache-api.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Thomas Graf + */ + +#ifndef NETLINK_DUMMY_CACHE_API_H_ +#define NETLINK_DUMMY_CACHE_API_H_ + +#include +#include + +#warning "You are including a deprecated header file, include ." + +#endif diff --git a/libnl/include/netlink/cache.h b/libnl/include/netlink/cache.h new file mode 100644 index 0000000..abeeccb --- /dev/null +++ b/libnl/include/netlink/cache.h @@ -0,0 +1,177 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +#ifndef NETLINK_CACHE_H_ +#define NETLINK_CACHE_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + NL_ACT_UNSPEC, + NL_ACT_NEW, + NL_ACT_DEL, + NL_ACT_GET, + NL_ACT_SET, + NL_ACT_CHANGE, + __NL_ACT_MAX, +}; + +#define NL_ACT_MAX (__NL_ACT_MAX - 1) + +struct nl_cache; +typedef void (*change_func_t)(struct nl_cache *, struct nl_object *, int, void *); +typedef void (*change_func_v2_t)(struct nl_cache *, struct nl_object *old_obj, + struct nl_object *new_obj, uint64_t, int, void *); + +/** + * @ingroup cache + * Explicitely iterate over all address families when updating the cache + */ +#define NL_CACHE_AF_ITER 0x0001 + +/* Access Functions */ +extern int nl_cache_nitems(struct nl_cache *); +extern int nl_cache_nitems_filter(struct nl_cache *, + struct nl_object *); +extern struct nl_cache_ops * nl_cache_get_ops(struct nl_cache *); +extern struct nl_object * nl_cache_get_first(struct nl_cache *); +extern struct nl_object * nl_cache_get_last(struct nl_cache *); +extern struct nl_object * nl_cache_get_next(struct nl_object *); +extern struct nl_object * nl_cache_get_prev(struct nl_object *); + +extern struct nl_cache * nl_cache_alloc(struct nl_cache_ops *); +extern int nl_cache_alloc_and_fill(struct nl_cache_ops *, + struct nl_sock *, + struct nl_cache **); +extern int nl_cache_alloc_name(const char *, + struct nl_cache **); +extern struct nl_cache * nl_cache_subset(struct nl_cache *, + struct nl_object *); +extern struct nl_cache * nl_cache_clone(struct nl_cache *); +extern void nl_cache_clear(struct nl_cache *); +extern void nl_cache_get(struct nl_cache *); +extern void nl_cache_free(struct nl_cache *); +extern void nl_cache_put(struct nl_cache *cache); + +/* Cache modification */ +extern int nl_cache_add(struct nl_cache *, + struct nl_object *); +extern int nl_cache_parse_and_add(struct nl_cache *, + struct nl_msg *); +extern int nl_cache_move(struct nl_cache *, + struct nl_object *); +extern void nl_cache_remove(struct nl_object *); +extern int nl_cache_refill(struct nl_sock *, + struct nl_cache *); +extern int nl_cache_pickup(struct nl_sock *, + struct nl_cache *); +extern int nl_cache_pickup_checkdup(struct nl_sock *, + struct nl_cache *); +extern int nl_cache_resync(struct nl_sock *, + struct nl_cache *, + change_func_t, + void *); +extern int nl_cache_include(struct nl_cache *, + struct nl_object *, + change_func_t, + void *); +extern int nl_cache_include_v2(struct nl_cache *, + struct nl_object *, + change_func_v2_t, + void *); +extern void nl_cache_set_arg1(struct nl_cache *, int); +extern void nl_cache_set_arg2(struct nl_cache *, int); +extern void nl_cache_set_flags(struct nl_cache *, unsigned int); + +/* General */ +extern int nl_cache_is_empty(struct nl_cache *); +extern struct nl_object * nl_cache_search(struct nl_cache *, + struct nl_object *); +extern struct nl_object *nl_cache_find(struct nl_cache *, + struct nl_object *); +extern void nl_cache_mark_all(struct nl_cache *); + +/* Dumping */ +extern void nl_cache_dump(struct nl_cache *, + struct nl_dump_params *); +extern void nl_cache_dump_filter(struct nl_cache *, + struct nl_dump_params *, + struct nl_object *); + +/* Iterators */ +extern void nl_cache_foreach(struct nl_cache *, + void (*cb)(struct nl_object *, + void *), + void *arg); +extern void nl_cache_foreach_filter(struct nl_cache *, + struct nl_object *, + void (*cb)(struct + nl_object *, + void *), + void *arg); + +/* --- cache management --- */ + +/* Cache type management */ +extern struct nl_cache_ops * nl_cache_ops_lookup(const char *); +extern struct nl_cache_ops * nl_cache_ops_lookup_safe(const char *); +extern struct nl_cache_ops * nl_cache_ops_associate(int, int); +extern struct nl_cache_ops * nl_cache_ops_associate_safe(int, int); +extern struct nl_msgtype * nl_msgtype_lookup(struct nl_cache_ops *, int); +extern void nl_cache_ops_foreach(void (*cb)(struct nl_cache_ops *, void *), void *); +extern int nl_cache_mngt_register(struct nl_cache_ops *); +extern int nl_cache_mngt_unregister(struct nl_cache_ops *); + +/* Global cache provisioning/requiring */ +extern void nl_cache_mngt_provide(struct nl_cache *); +extern void nl_cache_mngt_unprovide(struct nl_cache *); +extern struct nl_cache * nl_cache_mngt_require(const char *); +extern struct nl_cache * nl_cache_mngt_require_safe(const char *); +extern struct nl_cache * __nl_cache_mngt_require(const char *); + +struct nl_cache_mngr; + +#define NL_AUTO_PROVIDE 1 +#define NL_ALLOCATED_SOCK 2 /* For internal use only, do not use */ + +extern int nl_cache_mngr_alloc(struct nl_sock *, + int, int, + struct nl_cache_mngr **); +extern int nl_cache_mngr_add(struct nl_cache_mngr *, + const char *, + change_func_t, + void *, + struct nl_cache **); +extern int nl_cache_mngr_add_cache(struct nl_cache_mngr *mngr, + struct nl_cache *cache, + change_func_t cb, void *data); +extern int nl_cache_mngr_add_cache_v2(struct nl_cache_mngr *mngr, + struct nl_cache *cache, + change_func_v2_t cb, void *data); +extern int nl_cache_mngr_get_fd(struct nl_cache_mngr *); +extern int nl_cache_mngr_poll(struct nl_cache_mngr *, + int); +extern int nl_cache_mngr_data_ready(struct nl_cache_mngr *); +extern void nl_cache_mngr_info(struct nl_cache_mngr *, + struct nl_dump_params *); +extern void nl_cache_mngr_free(struct nl_cache_mngr *); + +extern void nl_cache_ops_get(struct nl_cache_ops *); +extern void nl_cache_ops_put(struct nl_cache_ops *); +extern void nl_cache_ops_set_flags(struct nl_cache_ops *, + unsigned int); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/data.h b/libnl/include/netlink/data.h new file mode 100644 index 0000000..c9c76a1 --- /dev/null +++ b/libnl/include/netlink/data.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + */ + +#ifndef NETLINK_DATA_H_ +#define NETLINK_DATA_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nlattr; + +struct nl_data; + +/* General */ +extern struct nl_data * nl_data_alloc(const void *, size_t); +extern struct nl_data * nl_data_alloc_attr(const struct nlattr *); +extern struct nl_data * nl_data_clone(const struct nl_data *); +extern int nl_data_append(struct nl_data *, const void *, size_t); +extern void nl_data_free(struct nl_data *); + +/* Access Functions */ +extern void * nl_data_get(const struct nl_data *); +extern size_t nl_data_get_size(const struct nl_data *); + +/* Misc */ +extern int nl_data_cmp(const struct nl_data *, + const struct nl_data *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/errno.h b/libnl/include/netlink/errno.h new file mode 100644 index 0000000..6a5b4de --- /dev/null +++ b/libnl/include/netlink/errno.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008 Thomas Graf + */ + +#ifndef NETLINK_ERRNO_H_ +#define NETLINK_ERRNO_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define NLE_SUCCESS 0 +#define NLE_FAILURE 1 +#define NLE_INTR 2 +#define NLE_BAD_SOCK 3 +#define NLE_AGAIN 4 +#define NLE_NOMEM 5 +#define NLE_EXIST 6 +#define NLE_INVAL 7 +#define NLE_RANGE 8 +#define NLE_MSGSIZE 9 +#define NLE_OPNOTSUPP 10 +#define NLE_AF_NOSUPPORT 11 +#define NLE_OBJ_NOTFOUND 12 +#define NLE_NOATTR 13 +#define NLE_MISSING_ATTR 14 +#define NLE_AF_MISMATCH 15 +#define NLE_SEQ_MISMATCH 16 +#define NLE_MSG_OVERFLOW 17 +#define NLE_MSG_TRUNC 18 +#define NLE_NOADDR 19 +#define NLE_SRCRT_NOSUPPORT 20 +#define NLE_MSG_TOOSHORT 21 +#define NLE_MSGTYPE_NOSUPPORT 22 +#define NLE_OBJ_MISMATCH 23 +#define NLE_NOCACHE 24 +#define NLE_BUSY 25 +#define NLE_PROTO_MISMATCH 26 +#define NLE_NOACCESS 27 +#define NLE_PERM 28 +#define NLE_PKTLOC_FILE 29 +#define NLE_PARSE_ERR 30 +#define NLE_NODEV 31 +#define NLE_IMMUTABLE 32 +#define NLE_DUMP_INTR 33 +#define NLE_ATTRSIZE 34 + +#define NLE_MAX NLE_ATTRSIZE + +extern const char * nl_geterror(int); +extern void nl_perror(int, const char *); +extern int nl_syserr2nlerr(int); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/genl/ctrl.h b/libnl/include/netlink/genl/ctrl.h new file mode 100644 index 0000000..92d60b3 --- /dev/null +++ b/libnl/include/netlink/genl/ctrl.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +#ifndef NETLINK_GENL_CTRL_H_ +#define NETLINK_GENL_CTRL_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct genl_family; + +extern int genl_ctrl_alloc_cache(struct nl_sock *, + struct nl_cache **); +extern struct genl_family * genl_ctrl_search(struct nl_cache *, int); +extern struct genl_family * genl_ctrl_search_by_name(struct nl_cache *, + const char *); +extern int genl_ctrl_resolve(struct nl_sock *, + const char *); +extern int genl_ctrl_resolve_grp(struct nl_sock *sk, + const char *family, + const char *grp); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/genl/family.h b/libnl/include/netlink/genl/family.h new file mode 100644 index 0000000..2e9f9fd --- /dev/null +++ b/libnl/include/netlink/genl/family.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +#ifndef NETLINK_GENL_FAMILY_H_ +#define NETLINK_GENL_FAMILY_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct genl_family; + +extern struct genl_family * genl_family_alloc(void); +extern void genl_family_put(struct genl_family *); + +extern unsigned int genl_family_get_id(struct genl_family *); +extern void genl_family_set_id(struct genl_family *, unsigned int); +extern char * genl_family_get_name(struct genl_family *); +extern void genl_family_set_name(struct genl_family *, const char *); +extern uint8_t genl_family_get_version(struct genl_family *); +extern void genl_family_set_version(struct genl_family *, uint8_t); +extern uint32_t genl_family_get_hdrsize(struct genl_family *); +extern void genl_family_set_hdrsize(struct genl_family *, uint32_t); +extern uint32_t genl_family_get_maxattr(struct genl_family *); +extern void genl_family_set_maxattr(struct genl_family *, uint32_t); + +extern int genl_family_add_op(struct genl_family *, int, int); +extern int genl_family_add_grp(struct genl_family *, uint32_t , + const char *); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/genl/genl.h b/libnl/include/netlink/genl/genl.h new file mode 100644 index 0000000..f1e3a65 --- /dev/null +++ b/libnl/include/netlink/genl/genl.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +#ifndef NETLINK_GENL_H_ +#define NETLINK_GENL_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int genl_connect(struct nl_sock *); +extern int genl_send_simple(struct nl_sock *, int, int, + int, int); + +extern void * genlmsg_put(struct nl_msg *, uint32_t, uint32_t, + int, int, int, uint8_t, uint8_t); + +extern int genlmsg_valid_hdr(struct nlmsghdr *, int); +extern int genlmsg_validate(struct nlmsghdr *, int, int, + const struct nla_policy *); +extern int genlmsg_parse(struct nlmsghdr *, int, struct nlattr **, + int, const struct nla_policy *); +extern struct genlmsghdr * + genlmsg_hdr(struct nlmsghdr *); +extern void * genlmsg_data(const struct genlmsghdr *); +extern void * genlmsg_user_hdr(const struct genlmsghdr *); +extern void * genlmsg_user_data(const struct genlmsghdr *, const int); +extern int genlmsg_user_datalen(const struct genlmsghdr *, + const int); +extern int genlmsg_len(const struct genlmsghdr *); +extern struct nlattr * genlmsg_attrdata(const struct genlmsghdr *, int); +extern int genlmsg_attrlen(const struct genlmsghdr *, int); + +extern char * genl_op2name(int, int, char *, size_t); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/genl/mngt.h b/libnl/include/netlink/genl/mngt.h new file mode 100644 index 0000000..af9edb3 --- /dev/null +++ b/libnl/include/netlink/genl/mngt.h @@ -0,0 +1,170 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +#ifndef NETLINK_GENL_MNGT_H_ +#define NETLINK_GENL_MNGT_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nl_cache_ops; + +/** + * @ingroup genl_mngt + * @struct genl_info netlink/genl/mngt.h + * + * Informative structure passed on to message parser callbacks + * + * This structure is passed on to all message parser callbacks and contains + * information about the sender of the message as well as pointers to all + * relevant sections of the parsed message. + * + * @see genl_cmd::c_msg_parser + */ +struct genl_info +{ + /** Socket address of sender */ + struct sockaddr_nl * who; + + /** Pointer to Netlink message header */ + struct nlmsghdr * nlh; + + /** Pointer to Generic Netlink message header */ + struct genlmsghdr * genlhdr; + + /** Pointer to user header */ + void * userhdr; + + /** Pointer to array of parsed attributes */ + struct nlattr ** attrs; +}; + +/** + * @ingroup genl_mngt + * @struct genl_cmd netlink/genl/mngt.h + * + * Definition of a Generic Netlink command. + * + * This structure is used to define the list of available commands on the + * receiving side. + * + * @par Example: + * @code + * static struct genl_cmd foo_cmds[] = { + * { + * .c_id = FOO_CMD_NEW, + * .c_name = "NEWFOO" , + * .c_maxattr = FOO_ATTR_MAX, + * .c_attr_policy = foo_policy, + * .c_msg_parser = foo_msg_parser, + * }, + * { + * .c_id = FOO_CMD_DEL, + * .c_name = "DELFOO" , + * }, + * }; + * + * static struct genl_ops my_genl_ops = { + * [...] + * .o_cmds = foo_cmds, + * .o_ncmds = ARRAY_SIZE(foo_cmds), + * }; + * @endcode + */ +struct genl_cmd +{ + /** Numeric command identifier (required) */ + int c_id; + + /** Human readable name (required) */ + char * c_name; + + /** Maximum attribute identifier that the command is prepared to handle. */ + int c_maxattr; + + /** Called whenever a message for this command is received */ + int (*c_msg_parser)(struct nl_cache_ops *, + struct genl_cmd *, + struct genl_info *, void *); + + /** Attribute validation policy, enforced before the callback is called */ + struct nla_policy * c_attr_policy; +}; + +/** + * @ingroup genl_mngt + * @struct genl_ops netlink/genl/mngt.h + * + * Definition of a Generic Netlink family + * + * @par Example: + * @code + * static struct genl_cmd foo_cmds[] = { + * [...] + * }; + * + * static struct genl_ops my_genl_ops = { + * .o_name = "foo", + * .o_hdrsize = sizeof(struct my_hdr), + * .o_cmds = foo_cmds, + * .o_ncmds = ARRAY_SIZE(foo_cmds), + * }; + * + * if ((err = genl_register_family(&my_genl_ops)) < 0) + * // ERROR + * @endcode + * + * @see genl_cmd + */ +struct genl_ops +{ + /** Length of user header */ + unsigned int o_hdrsize; + + /** Numeric identifier, automatically filled in by genl_ops_resolve() */ + int o_id; + + /** Human readable name, used by genl_ops_resolve() to resolve numeric id */ + char * o_name; + + /** + * If registered via genl_register(), will point to the related + * cache operations. + */ + struct nl_cache_ops * o_cache_ops; + + /** Optional array defining the available Generic Netlink commands */ + struct genl_cmd * o_cmds; + + /** Number of elements in \c o_cmds array */ + int o_ncmds; + + /** + * @private + * Used internally to link together all registered operations. + */ + struct nl_list_head o_list; +}; + +extern int genl_register_family(struct genl_ops *); +extern int genl_unregister_family(struct genl_ops *); +extern int genl_handle_msg(struct nl_msg *, void *); + +extern int genl_register(struct nl_cache_ops *); +extern void genl_unregister(struct nl_cache_ops *); + +extern int genl_ops_resolve(struct nl_sock *, struct genl_ops *); +extern int genl_mngt_resolve(struct nl_sock *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/handlers.h b/libnl/include/netlink/handlers.h new file mode 100644 index 0000000..2043844 --- /dev/null +++ b/libnl/include/netlink/handlers.h @@ -0,0 +1,145 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_HANDLERS_H_ +#define NETLINK_HANDLERS_H_ + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nlmsgerr; +struct sockaddr_nl; +struct ucred; + +struct nl_cb; +struct nl_sock; +struct nl_msg; + +/** + * @name Callback Typedefs + * @{ + */ + +/** + * nl_recvmsgs() callback for message processing customization + * @ingroup cb + * @arg msg netlink message being processed + * @arg arg argument passed on through caller + */ +typedef int (*nl_recvmsg_msg_cb_t)(struct nl_msg *msg, void *arg); + +/** + * nl_recvmsgs() callback for error message processing customization + * @ingroup cb + * @arg nla netlink address of the peer + * @arg nlerr netlink error message being processed + * @arg arg argument passed on through caller + */ +typedef int (*nl_recvmsg_err_cb_t)(struct sockaddr_nl *nla, + struct nlmsgerr *nlerr, void *arg); + +/** @} */ + +/** + * Callback actions + * @ingroup cb + */ +enum nl_cb_action { + /** Proceed with whatever would come next */ + NL_OK, + /** Skip this message */ + NL_SKIP, + /** Stop parsing altogether and discard remaining messages */ + NL_STOP, +}; + +/** + * Callback kinds + * @ingroup cb + */ +enum nl_cb_kind { + /** Default handlers (quiet) */ + NL_CB_DEFAULT, + /** Verbose default handlers (error messages printed) */ + NL_CB_VERBOSE, + /** Debug handlers for debugging */ + NL_CB_DEBUG, + /** Customized handler specified by the user */ + NL_CB_CUSTOM, + __NL_CB_KIND_MAX, +}; + +#define NL_CB_KIND_MAX (__NL_CB_KIND_MAX - 1) + +/** + * Callback types + * @ingroup cb + */ +enum nl_cb_type { + /** Message is valid */ + NL_CB_VALID, + /** Last message in a series of multi part messages received */ + NL_CB_FINISH, + /** Report received that data was lost */ + NL_CB_OVERRUN, + /** Message wants to be skipped */ + NL_CB_SKIPPED, + /** Message is an acknowledgement */ + NL_CB_ACK, + /** Called for every message received */ + NL_CB_MSG_IN, + /** Called for every message sent out except for nl_sendto() */ + NL_CB_MSG_OUT, + /** Message is malformed and invalid */ + NL_CB_INVALID, + /** Called instead of internal sequence number checking */ + NL_CB_SEQ_CHECK, + /** Sending of an acknowledge message has been requested */ + NL_CB_SEND_ACK, + /** Flag NLM_F_DUMP_INTR is set in message */ + NL_CB_DUMP_INTR, + __NL_CB_TYPE_MAX, +}; + +#define NL_CB_TYPE_MAX (__NL_CB_TYPE_MAX - 1) + +extern struct nl_cb * nl_cb_alloc(enum nl_cb_kind); +extern struct nl_cb * nl_cb_clone(struct nl_cb *); +extern struct nl_cb * nl_cb_get(struct nl_cb *); +extern void nl_cb_put(struct nl_cb *); + +extern int nl_cb_set(struct nl_cb *, enum nl_cb_type, enum nl_cb_kind, + nl_recvmsg_msg_cb_t, void *); +extern int nl_cb_set_all(struct nl_cb *, enum nl_cb_kind, + nl_recvmsg_msg_cb_t, void *); +extern int nl_cb_err(struct nl_cb *, enum nl_cb_kind, nl_recvmsg_err_cb_t, + void *); + +extern void nl_cb_overwrite_recvmsgs(struct nl_cb *, + int (*func)(struct nl_sock *, + struct nl_cb *)); +extern void nl_cb_overwrite_recv(struct nl_cb *, + int (*func)(struct nl_sock *, + struct sockaddr_nl *, + unsigned char **, + struct ucred **)); +extern void nl_cb_overwrite_send(struct nl_cb *, + int (*func)(struct nl_sock *, + struct nl_msg *)); + +extern enum nl_cb_type nl_cb_active_type(struct nl_cb *cb); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/hash.h b/libnl/include/netlink/hash.h new file mode 100644 index 0000000..0bda74e --- /dev/null +++ b/libnl/include/netlink/hash.h @@ -0,0 +1,69 @@ +/* + * This file was taken from http://ccodearchive.net/info/hash.html + * Changes to the original file include cleanups and removal of unwanted code + * and also code that depended on build_asert + */ +#ifndef CCAN_HASH_H +#define CCAN_HASH_H +#include +#include +#include + +/* Stolen mostly from: lookup3.c, by Bob Jenkins, May 2006, Public Domain. + * + * http://burtleburtle.net/bob/c/lookup3.c + */ + +#ifdef __LITTLE_ENDIAN +# define HAVE_LITTLE_ENDIAN 1 +#elif __BIG_ENDIAN +# define HAVE_BIG_ENDIAN 1 +#else +#error Unknown endianness. Failure in endian.h +#endif + +/** + * hash - fast hash of an array for internal use + * @p: the array or pointer to first element + * @num: the number of elements to hash + * @base: the base number to roll into the hash (usually 0) + * + * The memory region pointed to by p is combined with the base to form + * a 32-bit hash. + * + * This hash will have different results on different machines, so is + * only useful for internal hashes (ie. not hashes sent across the + * network or saved to disk). + * + * It may also change with future versions: it could even detect at runtime + * what the fastest hash to use is. + * + * See also: hash64, hash_stable. + * + * Example: + * #include + * #include + * #include + * #include + * + * // Simple demonstration: idential strings will have the same hash, but + * // two different strings will probably not. + * int main(int argc, char *argv[]) + * { + * uint32_t hash1, hash2; + * + * if (argc != 3) + * err(1, "Usage: %s ", argv[0]); + * + * hash1 = __nl_hash(argv[1], strlen(argv[1]), 0); + * hash2 = __nl_hash(argv[2], strlen(argv[2]), 0); + * printf("Hash is %s\n", hash1 == hash2 ? "same" : "different"); + * return 0; + * } + */ +#define __nl_hash(p, num, base) nl_hash_any((p), (num)*sizeof(*(p)), (base)) + +/* Our underlying operations. */ +uint32_t nl_hash_any(const void *key, size_t length, uint32_t base); + +#endif /* HASH_H */ diff --git a/libnl/include/netlink/hashtable.h b/libnl/include/netlink/hashtable.h new file mode 100644 index 0000000..32578dc --- /dev/null +++ b/libnl/include/netlink/hashtable.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2012 Cumulus Networks, Inc + */ + +#ifndef NETLINK_HASHTABLE_H_ +#define NETLINK_HASHTABLE_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct nl_hash_node { + uint32_t key; + uint32_t key_size; + struct nl_object * obj; + struct nl_hash_node * next; +} nl_hash_node_t; + +typedef struct nl_hash_table { + int size; + nl_hash_node_t ** nodes; +} nl_hash_table_t; + +/* Default hash table size */ +#define NL_MAX_HASH_ENTRIES 1024 + +/* Access Functions */ +extern nl_hash_table_t * nl_hash_table_alloc(int size); +extern void nl_hash_table_free(nl_hash_table_t *ht); + +extern int nl_hash_table_add(nl_hash_table_t *ht, + struct nl_object *obj); +extern int nl_hash_table_del(nl_hash_table_t *ht, + struct nl_object *obj); + +extern struct nl_object * nl_hash_table_lookup(nl_hash_table_t *ht, + struct nl_object *obj); +extern uint32_t nl_hash(void *k, size_t length, + uint32_t initval); + +#ifdef __cplusplus +} +#endif + +#endif /* NETLINK_HASHTABLE_H_ */ diff --git a/libnl/include/netlink/idiag/idiagnl.h b/libnl/include/netlink/idiag/idiagnl.h new file mode 100644 index 0000000..7385d5f --- /dev/null +++ b/libnl/include/netlink/idiag/idiagnl.h @@ -0,0 +1,145 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Sassano Systems LLC + */ + +#ifndef NETLINK_IDIAGNL_H_ +#define NETLINK_IDIAGNL_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/************************************************************* + * The following part contains DEPRECATED names and defines. + * Don't use them. + *************************************************************/ + +/** + * Inet Diag message types + * + * deprecated: use TCPDIAG_GETSOCK, DCCPDIAG_GETSOCK and + * INET_DIAG_GETSOCK_MAX from linux/inet_diag.h + */ +#define IDIAG_TCPDIAG_GETSOCK 18 +#define IDIAG_DCCPDIAG_GETSOCK 19 +#define IDIAG_GETSOCK_MAX 24 + +/** + * Socket state identifiers + * @ingroup idiag + * @deprecated: use instead the TCP_* defines from netinet/tcp.h. + */ +enum { + IDIAG_SS_UNKNOWN = 0, + + IDIAG_SS_ESTABLISHED = 1, /* TCP_ESTABLISHED */ + IDIAG_SS_SYN_SENT = 2, /* TCP_SYN_SENT */ + IDIAG_SS_SYN_RECV = 3, /* TCP_SYN_RECV */ + IDIAG_SS_FIN_WAIT1 = 4, /* TCP_FIN_WAIT1 */ + IDIAG_SS_FIN_WAIT2 = 5, /* TCP_FIN_WAIT2 */ + IDIAG_SS_TIME_WAIT = 6, /* TCP_TIME_WAIT */ + IDIAG_SS_CLOSE = 7, /* TCP_CLOSE */ + IDIAG_SS_CLOSE_WAIT = 8, /* TCP_CLOSE_WAIT */ + IDIAG_SS_LAST_ACK = 9, /* TCP_LAST_ACK */ + IDIAG_SS_LISTEN = 10, /* TCP_LISTEN */ + IDIAG_SS_CLOSING = 11, /* TCP_CLOSING */ + + IDIAG_SS_MAX = 12, +}; + +/** + * Macro to represent all socket states. + * @ingroup idiag + * @deprecated + */ +#define IDIAG_SS_ALL IDIAGNL_SS_ALL + + +/** + * Inet Diag extended attributes + * @ingroup idiag + * @deprecated These attributes should not be used. They mirror the + * INET_DIAG_* extension flags from kernel headers. Use those instead. */ +enum { + IDIAG_ATTR_NONE = 0, /* INET_DIAG_NONE */ + IDIAG_ATTR_MEMINFO = 1, /* INET_DIAG_MEMINFO */ + IDIAG_ATTR_INFO = 2, /* INET_DIAG_INFO */ + IDIAG_ATTR_VEGASINFO = 3, /* INET_DIAG_VEGASINFO */ + IDIAG_ATTR_CONG = 4, /* INET_DIAG_CONG */ + IDIAG_ATTR_TOS = 5, /* INET_DIAG_TOS */ + IDIAG_ATTR_TCLASS = 6, /* INET_DIAG_TCLASS */ + IDIAG_ATTR_SKMEMINFO = 7, /* INET_DIAG_SKMEMINFO */ + IDIAG_ATTR_SHUTDOWN = 8, /* INET_DIAG_SHUTDOWN */ + + /* IDIAG_ATTR_MAX was wrong, because it did not correspond to + * INET_DIAG_MAX. Anyway, freeze it to the previous value. */ + IDIAG_ATTR_MAX = 9, + + IDIAG_ATTR_ALL = (1< + */ + +#ifndef NETLINK_IDIAGNL_MEMINFO_H_ +#define NETLINK_IDIAGNL_MEMINFO_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +extern struct nl_object_ops idiagnl_meminfo_obj_ops; + +extern struct idiagnl_meminfo *idiagnl_meminfo_alloc(void); +extern void idiagnl_meminfo_get(struct idiagnl_meminfo *); +extern void idiagnl_meminfo_put(struct idiagnl_meminfo *); + +extern uint32_t idiagnl_meminfo_get_rmem(const struct idiagnl_meminfo *); +extern uint32_t idiagnl_meminfo_get_wmem(const struct idiagnl_meminfo *); +extern uint32_t idiagnl_meminfo_get_fmem(const struct idiagnl_meminfo *); +extern uint32_t idiagnl_meminfo_get_tmem(const struct idiagnl_meminfo *); + +extern void idiagnl_meminfo_set_rmem(struct idiagnl_meminfo *, uint32_t); +extern void idiagnl_meminfo_set_wmem(struct idiagnl_meminfo *, uint32_t); +extern void idiagnl_meminfo_set_fmem(struct idiagnl_meminfo *, uint32_t); +extern void idiagnl_meminfo_set_tmem(struct idiagnl_meminfo *, uint32_t); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* NETLINK_IDIAGNL_MEMINFO_H_ */ diff --git a/libnl/include/netlink/idiag/msg.h b/libnl/include/netlink/idiag/msg.h new file mode 100644 index 0000000..f2a79ce --- /dev/null +++ b/libnl/include/netlink/idiag/msg.h @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Sassano Systems LLC + */ + +#ifndef NETLINK_IDIAGNL_MSG_H_ +#define NETLINK_IDIAGNL_MSG_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +struct idiagnl_msg; + +/* @deprecated: DO NOT USE this variable. */ +extern struct nl_object_ops idiagnl_msg_obj_ops; + +extern struct idiagnl_msg * idiagnl_msg_alloc(void); +extern int idiagnl_msg_alloc_cache(struct nl_sock *, int, int, + struct nl_cache**); +extern void idiagnl_msg_get(struct idiagnl_msg *); +extern void idiagnl_msg_put(struct idiagnl_msg *); +extern uint8_t idiagnl_msg_get_family(const struct idiagnl_msg *); +extern void idiagnl_msg_set_family(struct idiagnl_msg *, uint8_t); +extern uint8_t idiagnl_msg_get_state(const struct idiagnl_msg *); +extern void idiagnl_msg_set_state(struct idiagnl_msg *, uint8_t); +extern uint8_t idiagnl_msg_get_timer(const struct idiagnl_msg *); +extern void idiagnl_msg_set_timer(struct idiagnl_msg *, uint8_t); +extern uint8_t idiagnl_msg_get_retrans(const struct idiagnl_msg *); +extern void idiagnl_msg_set_retrans(struct idiagnl_msg *, uint8_t); +extern uint16_t idiagnl_msg_get_sport(struct idiagnl_msg *); +extern void idiagnl_msg_set_sport(struct idiagnl_msg *, uint16_t); +extern uint16_t idiagnl_msg_get_dport(struct idiagnl_msg *); +extern void idiagnl_msg_set_dport(struct idiagnl_msg *, uint16_t); +extern struct nl_addr * idiagnl_msg_get_src(const struct idiagnl_msg *); +extern int idiagnl_msg_set_src(struct idiagnl_msg *, + struct nl_addr *); +extern struct nl_addr * idiagnl_msg_get_dst(const struct idiagnl_msg *); +extern int idiagnl_msg_set_dst(struct idiagnl_msg *, + struct nl_addr *); +extern uint32_t idiagnl_msg_get_ifindex(const struct idiagnl_msg *); +extern void idiagnl_msg_set_ifindex(struct idiagnl_msg *, uint32_t); +extern uint32_t idiagnl_msg_get_expires(const struct idiagnl_msg *); +extern void idiagnl_msg_set_expires(struct idiagnl_msg *, uint32_t); +extern uint32_t idiagnl_msg_get_rqueue(const struct idiagnl_msg *); +extern void idiagnl_msg_set_rqueue(struct idiagnl_msg *, uint32_t); +extern uint32_t idiagnl_msg_get_wqueue(const struct idiagnl_msg *); +extern void idiagnl_msg_set_wqueue(struct idiagnl_msg *, uint32_t); +extern uint32_t idiagnl_msg_get_uid(const struct idiagnl_msg *); +extern void idiagnl_msg_set_uid(struct idiagnl_msg *, uint32_t); +extern uint32_t idiagnl_msg_get_inode(const struct idiagnl_msg *); +extern void idiagnl_msg_set_inode(struct idiagnl_msg *, uint32_t); +extern uint8_t idiagnl_msg_get_tos(const struct idiagnl_msg *); +extern void idiagnl_msg_set_tos(struct idiagnl_msg *, uint8_t); +extern uint8_t idiagnl_msg_get_tclass(const struct idiagnl_msg *); +extern void idiagnl_msg_set_tclass(struct idiagnl_msg *, uint8_t); +extern uint8_t idiagnl_msg_get_shutdown(const struct idiagnl_msg *); +extern void idiagnl_msg_set_shutdown(struct idiagnl_msg *, uint8_t); +extern char * idiagnl_msg_get_cong(const struct idiagnl_msg *); +extern void idiagnl_msg_set_cong(struct idiagnl_msg *, char *); +extern struct idiagnl_meminfo *idiagnl_msg_get_meminfo(const struct idiagnl_msg *); +extern void idiagnl_msg_set_meminfo(struct idiagnl_msg *, + struct idiagnl_meminfo *); +extern struct idiagnl_vegasinfo *idiagnl_msg_get_vegasinfo(const struct idiagnl_msg *); +extern void idiagnl_msg_set_vegasinfo(struct idiagnl_msg *, + struct idiagnl_vegasinfo *); +extern struct tcp_info idiagnl_msg_get_tcpinfo(const struct idiagnl_msg *); +extern void idiagnl_msg_set_tcpinfo(struct idiagnl_msg *, + struct tcp_info *); + +extern int idiagnl_msg_parse(struct nlmsghdr *, + struct idiagnl_msg **); +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* NETLINK_IDIAGNL_MSG_H_ */ diff --git a/libnl/include/netlink/idiag/req.h b/libnl/include/netlink/idiag/req.h new file mode 100644 index 0000000..fb75b96 --- /dev/null +++ b/libnl/include/netlink/idiag/req.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Sassano Systems LLC + */ + +#ifndef NETLINK_IDIAGNL_REQ_H_ +#define NETLINK_IDIAGNL_REQ_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +struct idiagnl_req; +extern struct nl_object_ops idiagnl_req_obj_ops; + +extern struct idiagnl_req * idiagnl_req_alloc(void); +extern void idiagnl_req_get(struct idiagnl_req *); +extern void idiagnl_req_put(struct idiagnl_req *); +extern uint8_t idiagnl_req_get_family(const struct idiagnl_req *); +extern void idiagnl_req_set_family(struct idiagnl_req *, + uint8_t); +extern uint8_t idiagnl_req_get_ext(const struct idiagnl_req *); +extern void idiagnl_req_set_ext(struct idiagnl_req *, uint8_t); +extern uint32_t idiagnl_req_get_ifindex(const struct idiagnl_req *); +extern void idiagnl_req_set_ifindex(struct idiagnl_req *, + uint32_t); +extern uint32_t idiagnl_req_get_states(const struct idiagnl_req *); +extern void idiagnl_req_set_states(struct idiagnl_req *, + uint32_t); +extern uint32_t idiagnl_req_get_dbs(const struct idiagnl_req *); +extern void idiagnl_req_set_dbs(struct idiagnl_req *, uint32_t); +extern struct nl_addr * idiagnl_req_get_src(const struct idiagnl_req *); +extern int idiagnl_req_set_src(struct idiagnl_req *, + struct nl_addr *); +extern struct nl_addr * idiagnl_req_get_dst(const struct idiagnl_req *); +extern int idiagnl_req_set_dst(struct idiagnl_req *, + struct nl_addr *); + +extern int idiagnl_req_parse(struct nlmsghdr *nlh, + struct idiagnl_req **result); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* NETLINK_IDIAGNL_REQ_H_ */ diff --git a/libnl/include/netlink/idiag/vegasinfo.h b/libnl/include/netlink/idiag/vegasinfo.h new file mode 100644 index 0000000..890c175 --- /dev/null +++ b/libnl/include/netlink/idiag/vegasinfo.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Sassano Systems LLC + */ + +#ifndef NETLINK_IDIAGNL_VEGASINFO_H_ +#define NETLINK_IDIAGNL_VEGASINFO_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +extern struct nl_object_ops idiagnl_vegasinfo_obj_ops; +extern struct idiagnl_vegasinfo * idiagnl_vegasinfo_alloc(void); +extern void idiagnl_vegasinfo_get(struct idiagnl_vegasinfo *); +extern void idiagnl_vegasinfo_put(struct idiagnl_vegasinfo *); + +extern uint32_t idiagnl_vegasinfo_get_enabled(const struct idiagnl_vegasinfo *); +extern uint32_t idiagnl_vegasinfo_get_rttcnt(const struct idiagnl_vegasinfo *); +extern uint32_t idiagnl_vegasinfo_get_rtt(const struct idiagnl_vegasinfo *); +extern uint32_t idiagnl_vegasinfo_get_minrtt(const struct idiagnl_vegasinfo *); + +extern void idiagnl_vegasinfo_set_enabled(struct idiagnl_vegasinfo *, + uint32_t); +extern void idiagnl_vegasinfo_set_rttcnt(struct idiagnl_vegasinfo *, + uint32_t); +extern void idiagnl_vegasinfo_set_rtt(struct idiagnl_vegasinfo *, uint32_t); +extern void idiagnl_vegasinfo_set_minrtt(struct idiagnl_vegasinfo *, + uint32_t); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* NETLINK_IDIAGNL_VEGASINFO_H_ */ diff --git a/libnl/include/netlink/list.h b/libnl/include/netlink/list.h new file mode 100644 index 0000000..7ab1438 --- /dev/null +++ b/libnl/include/netlink/list.h @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_LIST_H_ +#define NETLINK_LIST_H_ + +#include + +struct nl_list_head +{ + struct nl_list_head * next; + struct nl_list_head * prev; +}; + +static inline void NL_INIT_LIST_HEAD(struct nl_list_head *list) +{ + list->next = list; + list->prev = list; +} + +static inline void __nl_list_add(struct nl_list_head *obj, + struct nl_list_head *prev, + struct nl_list_head *next) +{ + prev->next = obj; + obj->prev = prev; + next->prev = obj; + obj->next = next; +} + +static inline void nl_list_add_tail(struct nl_list_head *obj, + struct nl_list_head *head) +{ + __nl_list_add(obj, head->prev, head); +} + +static inline void nl_list_add_head(struct nl_list_head *obj, + struct nl_list_head *head) +{ + __nl_list_add(obj, head, head->next); +} + +static inline void nl_list_del(struct nl_list_head *obj) +{ + obj->next->prev = obj->prev; + obj->prev->next = obj->next; +} + +static inline int nl_list_empty(struct nl_list_head *head) +{ + return head->next == head; +} + +#define nl_container_of(ptr, type, member) ({ \ + const __typeof__( ((type *)0)->member ) *__mptr = (ptr);\ + (type *)( (char *)__mptr - (offsetof(type, member)));}) + +#define nl_list_entry(ptr, type, member) \ + nl_container_of(ptr, type, member) + +#define nl_list_at_tail(pos, head, member) \ + ((pos)->member.next == (head)) + +#define nl_list_at_head(pos, head, member) \ + ((pos)->member.prev == (head)) + +#define NL_LIST_HEAD(name) \ + struct nl_list_head name = { &(name), &(name) } + +#define nl_list_first_entry(head, type, member) \ + nl_list_entry((head)->next, type, member) + +#define nl_list_for_each_entry(pos, head, member) \ + for (pos = nl_list_entry((head)->next, __typeof__(*pos), member); \ + &(pos)->member != (head); \ + (pos) = nl_list_entry((pos)->member.next, __typeof__(*(pos)), member)) + +#define nl_list_for_each_entry_safe(pos, n, head, member) \ + for (pos = nl_list_entry((head)->next, __typeof__(*pos), member), \ + n = nl_list_entry(pos->member.next, __typeof__(*pos), member); \ + &(pos)->member != (head); \ + pos = n, n = nl_list_entry(n->member.next, __typeof__(*n), member)) + +#define nl_init_list_head(head) \ + do { (head)->next = (head); (head)->prev = (head); } while (0) + +#endif diff --git a/libnl/include/netlink/msg.h b/libnl/include/netlink/msg.h new file mode 100644 index 0000000..84aaa60 --- /dev/null +++ b/libnl/include/netlink/msg.h @@ -0,0 +1,143 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_MSG_H_ +#define NETLINK_MSG_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nlmsghdr; + +#define NL_DONTPAD 0 + +/** + * @ingroup msg + * @brief + * Will cause the netlink port to be set to the port assigned to + * the netlink icoket ust before sending the message off. + * + * @note Requires the use of nl_send_auto()! + */ +#define NL_AUTO_PORT 0 +#define NL_AUTO_PID NL_AUTO_PORT + +/** + * @ingroup msg + * @brief + * May be used to refer to a sequence number which should be + * automatically set just before sending the message off. + * + * @note Requires the use of nl_send_auto()! + */ +#define NL_AUTO_SEQ 0 + +struct nl_msg; +struct nl_tree; +struct ucred; + +extern int nlmsg_size(int); +extern int nlmsg_total_size(int); +extern int nlmsg_padlen(int); + +extern void * nlmsg_data(const struct nlmsghdr *); +extern int nlmsg_datalen(const struct nlmsghdr *); +extern void * nlmsg_tail(const struct nlmsghdr *); + +/* attribute access */ +extern struct nlattr * nlmsg_attrdata(const struct nlmsghdr *, int); +extern int nlmsg_attrlen(const struct nlmsghdr *, int); + +/* message parsing */ +extern int nlmsg_valid_hdr(const struct nlmsghdr *, int); +extern int nlmsg_ok(const struct nlmsghdr *, int); +extern struct nlmsghdr * nlmsg_next(struct nlmsghdr *, int *); +extern int nlmsg_parse(struct nlmsghdr *, int, struct nlattr **, + int, const struct nla_policy *); +extern struct nlattr * nlmsg_find_attr(struct nlmsghdr *, int, int); +extern int nlmsg_validate(struct nlmsghdr *, int, int, + const struct nla_policy *); + +extern struct nl_msg * nlmsg_alloc(void); +extern struct nl_msg * nlmsg_alloc_size(size_t); +extern struct nl_msg * nlmsg_alloc_simple(int, int); +extern void nlmsg_set_default_size(size_t); +extern struct nl_msg * nlmsg_inherit(struct nlmsghdr *); +extern struct nl_msg * nlmsg_convert(struct nlmsghdr *); +extern void * nlmsg_reserve(struct nl_msg *, size_t, int); +extern int nlmsg_append(struct nl_msg *, void *, size_t, int); +extern int nlmsg_expand(struct nl_msg *, size_t); + +extern struct nlmsghdr * nlmsg_put(struct nl_msg *, uint32_t, uint32_t, + int, int, int); +extern struct nlmsghdr * nlmsg_hdr(struct nl_msg *); +extern void nlmsg_get(struct nl_msg *); +extern void nlmsg_free(struct nl_msg *); + +/* attribute modification */ +extern void nlmsg_set_proto(struct nl_msg *, int); +extern int nlmsg_get_proto(struct nl_msg *); +extern size_t nlmsg_get_max_size(struct nl_msg *); +extern void nlmsg_set_src(struct nl_msg *, struct sockaddr_nl *); +extern struct sockaddr_nl *nlmsg_get_src(struct nl_msg *); +extern void nlmsg_set_dst(struct nl_msg *, struct sockaddr_nl *); +extern struct sockaddr_nl *nlmsg_get_dst(struct nl_msg *); +extern void nlmsg_set_creds(struct nl_msg *, struct ucred *); +extern struct ucred * nlmsg_get_creds(struct nl_msg *); + +extern char * nl_nlmsgtype2str(int, char *, size_t); +extern int nl_str2nlmsgtype(const char *); + +extern char * nl_nlmsg_flags2str(int, char *, size_t); + +extern int nl_msg_parse(struct nl_msg *, + void (*cb)(struct nl_object *, void *), + void *); + +extern void nl_msg_dump(struct nl_msg *, FILE *); + +/** + * @name Iterators + * @{ + */ + +/** + * @ingroup msg + * Iterate over a stream of attributes in a message + * @arg pos loop counter, set to current attribute + * @arg nlh netlink message header + * @arg hdrlen length of family header + * @arg rem initialized to len, holds bytes currently remaining in stream + */ +#define nlmsg_for_each_attr(pos, nlh, hdrlen, rem) \ + nla_for_each_attr(pos, nlmsg_attrdata(nlh, hdrlen), \ + nlmsg_attrlen(nlh, hdrlen), rem) + +/** + * Iterate over a stream of messages + * @arg pos loop counter, set to current message + * @arg head head of message stream + * @arg len length of message stream + */ +#define nlmsg_for_each(pos, head, len) \ + for (int rem = len, pos = head; \ + nlmsg_ok(pos, rem); \ + pos = nlmsg_next(pos, &rem)) + +#define nlmsg_for_each_msg(pos, head, len, rem) \ + nlmsg_for_each(pos, head, len) + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/netfilter/ct.h b/libnl/include/netlink/netfilter/ct.h new file mode 100644 index 0000000..b172ba6 --- /dev/null +++ b/libnl/include/netlink/netfilter/ct.h @@ -0,0 +1,134 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + */ + +#ifndef NETLINK_CT_H_ +#define NETLINK_CT_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nfnl_ct; + +struct nfnl_ct_timestamp { + uint64_t start; + uint64_t stop; +}; + +extern struct nl_object_ops ct_obj_ops; + +extern struct nfnl_ct * nfnl_ct_alloc(void); +extern int nfnl_ct_alloc_cache(struct nl_sock *, struct nl_cache **); + +extern int nfnlmsg_ct_group(struct nlmsghdr *); +extern int nfnlmsg_ct_parse(struct nlmsghdr *, struct nfnl_ct **); + +extern void nfnl_ct_get(struct nfnl_ct *); +extern void nfnl_ct_put(struct nfnl_ct *); + +extern int nfnl_ct_dump_request(struct nl_sock *); + +extern int nfnl_ct_build_add_request(const struct nfnl_ct *, int, + struct nl_msg **); +extern int nfnl_ct_add(struct nl_sock *, const struct nfnl_ct *, int); + +extern int nfnl_ct_build_delete_request(const struct nfnl_ct *, int, + struct nl_msg **); +extern int nfnl_ct_del(struct nl_sock *, const struct nfnl_ct *, int); + +extern int nfnl_ct_build_query_request(const struct nfnl_ct *, int, + struct nl_msg **); +extern int nfnl_ct_query(struct nl_sock *, const struct nfnl_ct *, int); + +extern void nfnl_ct_set_family(struct nfnl_ct *, uint8_t); +extern uint8_t nfnl_ct_get_family(const struct nfnl_ct *); + +extern void nfnl_ct_set_proto(struct nfnl_ct *, uint8_t); +extern int nfnl_ct_test_proto(const struct nfnl_ct *); +extern uint8_t nfnl_ct_get_proto(const struct nfnl_ct *); + +extern void nfnl_ct_set_tcp_state(struct nfnl_ct *, uint8_t); +extern int nfnl_ct_test_tcp_state(const struct nfnl_ct *); +extern uint8_t nfnl_ct_get_tcp_state(const struct nfnl_ct *); +extern char * nfnl_ct_tcp_state2str(uint8_t, char *, size_t); +extern int nfnl_ct_str2tcp_state(const char *name); + +extern void nfnl_ct_set_status(struct nfnl_ct *, uint32_t); +extern void nfnl_ct_unset_status(struct nfnl_ct *, uint32_t); +extern int nfnl_ct_test_status(const struct nfnl_ct *ct); +extern uint32_t nfnl_ct_get_status(const struct nfnl_ct *); +extern char * nfnl_ct_status2str(int, char *, size_t); +extern int nfnl_ct_str2status(const char *); + +extern void nfnl_ct_set_timeout(struct nfnl_ct *, uint32_t); +extern int nfnl_ct_test_timeout(const struct nfnl_ct *); +extern uint32_t nfnl_ct_get_timeout(const struct nfnl_ct *); + +extern void nfnl_ct_set_mark(struct nfnl_ct *, uint32_t); +extern int nfnl_ct_test_mark(const struct nfnl_ct *); +extern uint32_t nfnl_ct_get_mark(const struct nfnl_ct *); + +extern void nfnl_ct_set_use(struct nfnl_ct *, uint32_t); +extern int nfnl_ct_test_use(const struct nfnl_ct *); +extern uint32_t nfnl_ct_get_use(const struct nfnl_ct *); + +extern void nfnl_ct_set_id(struct nfnl_ct *, uint32_t); +extern int nfnl_ct_test_id(const struct nfnl_ct *); +extern uint32_t nfnl_ct_get_id(const struct nfnl_ct *); + +extern void nfnl_ct_set_zone(struct nfnl_ct *, uint16_t); +extern int nfnl_ct_test_zone(const struct nfnl_ct *); +extern uint16_t nfnl_ct_get_zone(const struct nfnl_ct *); + +extern int nfnl_ct_set_src(struct nfnl_ct *, int, struct nl_addr *); +extern struct nl_addr * nfnl_ct_get_src(const struct nfnl_ct *, int); + +extern int nfnl_ct_set_dst(struct nfnl_ct *, int, struct nl_addr *); +extern struct nl_addr * nfnl_ct_get_dst(const struct nfnl_ct *, int); + +extern void nfnl_ct_set_src_port(struct nfnl_ct *, int, uint16_t); +extern int nfnl_ct_test_src_port(const struct nfnl_ct *, int); +extern uint16_t nfnl_ct_get_src_port(const struct nfnl_ct *, int); + +extern void nfnl_ct_set_dst_port(struct nfnl_ct *, int, uint16_t); +extern int nfnl_ct_test_dst_port(const struct nfnl_ct *, int); +extern uint16_t nfnl_ct_get_dst_port(const struct nfnl_ct *, int); + +extern void nfnl_ct_set_icmp_id(struct nfnl_ct *, int, uint16_t); +extern int nfnl_ct_test_icmp_id(const struct nfnl_ct *, int); +extern uint16_t nfnl_ct_get_icmp_id(const struct nfnl_ct *, int); + +extern void nfnl_ct_set_icmp_type(struct nfnl_ct *, int, uint8_t); +extern int nfnl_ct_test_icmp_type(const struct nfnl_ct *, int); +extern uint8_t nfnl_ct_get_icmp_type(const struct nfnl_ct *, int); + +extern void nfnl_ct_set_icmp_code(struct nfnl_ct *, int, uint8_t); +extern int nfnl_ct_test_icmp_code(const struct nfnl_ct *, int); +extern uint8_t nfnl_ct_get_icmp_code(const struct nfnl_ct *, int); + +extern void nfnl_ct_set_packets(struct nfnl_ct *, int, uint64_t); +extern int nfnl_ct_test_packets(const struct nfnl_ct *, int); +extern uint64_t nfnl_ct_get_packets(const struct nfnl_ct *,int); + +extern void nfnl_ct_set_bytes(struct nfnl_ct *, int, uint64_t); +extern int nfnl_ct_test_bytes(const struct nfnl_ct *, int); +extern uint64_t nfnl_ct_get_bytes(const struct nfnl_ct *, int); + +extern void nfnl_ct_set_timestamp(struct nfnl_ct *, uint64_t, uint64_t); +extern int nfnl_ct_test_timestamp(const struct nfnl_ct *); +extern const struct nfnl_ct_timestamp *nfnl_ct_get_timestamp(const struct nfnl_ct *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/netfilter/exp.h b/libnl/include/netlink/netfilter/exp.h new file mode 100644 index 0000000..bd80514 --- /dev/null +++ b/libnl/include/netlink/netfilter/exp.h @@ -0,0 +1,126 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + * Copyright (c) 2012 Rich Fought + */ + +#ifndef NETLINK_EXP_H_ +#define NETLINK_EXP_H_ + +#include +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nfnl_exp; + +enum nfnl_exp_tuples { + NFNL_EXP_TUPLE_EXPECT, + NFNL_EXP_TUPLE_MASTER, + NFNL_EXP_TUPLE_MASK, + NFNL_EXP_TUPLE_NAT, + NFNL_EXP_TUPLE_MAX +}; + +extern struct nl_object_ops exp_obj_ops; + +extern struct nfnl_exp * nfnl_exp_alloc(void); +extern int nfnl_exp_alloc_cache(struct nl_sock *, struct nl_cache **); + +extern int nfnlmsg_exp_group(struct nlmsghdr *); +extern int nfnlmsg_exp_parse(struct nlmsghdr *, struct nfnl_exp **); + +extern void nfnl_exp_get(struct nfnl_exp *); +extern void nfnl_exp_put(struct nfnl_exp *); + +extern int nfnl_exp_dump_request(struct nl_sock *); + +extern int nfnl_exp_build_add_request(const struct nfnl_exp *, int, + struct nl_msg **); +extern int nfnl_exp_add(struct nl_sock *, const struct nfnl_exp *, int); + +extern int nfnl_exp_build_delete_request(const struct nfnl_exp *, int, + struct nl_msg **); +extern int nfnl_exp_del(struct nl_sock *, const struct nfnl_exp *, int); + +extern int nfnl_exp_build_query_request(const struct nfnl_exp *, int, + struct nl_msg **); +extern int nfnl_exp_query(struct nl_sock *, const struct nfnl_exp *, int); + +extern void nfnl_exp_set_family(struct nfnl_exp *, uint8_t); +extern uint8_t nfnl_exp_get_family(const struct nfnl_exp *); + +extern void nfnl_exp_set_timeout(struct nfnl_exp *, uint32_t); +extern int nfnl_exp_test_timeout(const struct nfnl_exp *); +extern uint32_t nfnl_exp_get_timeout(const struct nfnl_exp *); + +extern void nfnl_exp_set_id(struct nfnl_exp *, uint32_t); +extern int nfnl_exp_test_id(const struct nfnl_exp *); +extern uint32_t nfnl_exp_get_id(const struct nfnl_exp *); + +extern int nfnl_exp_set_helper_name(struct nfnl_exp *, void *); +extern int nfnl_exp_test_helper_name(const struct nfnl_exp *); +extern const char * nfnl_exp_get_helper_name(const struct nfnl_exp *); + +extern void nfnl_exp_set_zone(struct nfnl_exp *, uint16_t); +extern int nfnl_exp_test_zone(const struct nfnl_exp *); +extern uint16_t nfnl_exp_get_zone(const struct nfnl_exp *); + +extern void nfnl_exp_set_flags(struct nfnl_exp *, uint32_t); +extern int nfnl_exp_test_flags(const struct nfnl_exp *); +extern void nfnl_exp_unset_flags(struct nfnl_exp *exp, uint32_t flags); +extern uint32_t nfnl_exp_get_flags(const struct nfnl_exp *); +extern char * nfnl_exp_flags2str(int flags, char *buf, size_t len); +int nfnl_exp_str2flags(const char *name); + +extern void nfnl_exp_set_class(struct nfnl_exp *, uint32_t); +extern int nfnl_exp_test_class(const struct nfnl_exp *); +extern uint32_t nfnl_exp_get_class(const struct nfnl_exp *); + +extern int nfnl_exp_set_fn(struct nfnl_exp *, void *); +extern int nfnl_exp_test_fn(const struct nfnl_exp *); +extern const char * nfnl_exp_get_fn(const struct nfnl_exp *); + +extern void nfnl_exp_set_nat_dir(struct nfnl_exp *, uint8_t); +extern int nfnl_exp_test_nat_dir(const struct nfnl_exp *); +extern uint8_t nfnl_exp_get_nat_dir(const struct nfnl_exp *); + +// The int argument specifies which nfnl_exp_dir (expect, master, mask or nat) +// Expectation objects only use orig, not reply + +extern int nfnl_exp_set_src(struct nfnl_exp *, int, struct nl_addr *); +extern int nfnl_exp_test_src(const struct nfnl_exp *, int); +extern struct nl_addr * nfnl_exp_get_src(const struct nfnl_exp *, int); + +extern int nfnl_exp_set_dst(struct nfnl_exp *, int, struct nl_addr *); +extern int nfnl_exp_test_dst(const struct nfnl_exp *, int); +extern struct nl_addr * nfnl_exp_get_dst(const struct nfnl_exp *, int); + +extern void nfnl_exp_set_l4protonum(struct nfnl_exp *, int, uint8_t); +extern int nfnl_exp_test_l4protonum(const struct nfnl_exp *, int); +extern uint8_t nfnl_exp_get_l4protonum(const struct nfnl_exp *, int); + +extern void nfnl_exp_set_ports(struct nfnl_exp *, int, uint16_t, uint16_t); +extern int nfnl_exp_test_ports(const struct nfnl_exp *, int); +extern uint16_t nfnl_exp_get_src_port(const struct nfnl_exp *, int); +extern uint16_t nfnl_exp_get_dst_port(const struct nfnl_exp *, int); + +extern void nfnl_exp_set_icmp(struct nfnl_exp *, int, uint16_t, uint8_t, uint8_t); +extern int nfnl_exp_test_icmp(const struct nfnl_exp *, int); +extern uint16_t nfnl_exp_get_icmp_id(const struct nfnl_exp *, int); +extern uint8_t nfnl_exp_get_icmp_type(const struct nfnl_exp *, int); +extern uint8_t nfnl_exp_get_icmp_code(const struct nfnl_exp *, int); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/netfilter/log.h b/libnl/include/netlink/netfilter/log.h new file mode 100644 index 0000000..c167fd1 --- /dev/null +++ b/libnl/include/netlink/netfilter/log.h @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2006 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + * Copyright (c) 2008 Patrick McHardy + */ + +#ifndef NETLINK_LOG_H_ +#define NETLINK_LOG_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nl_sock; +struct nlmsghdr; +struct nfnl_log; + +extern struct nl_object_ops log_obj_ops; + +enum nfnl_log_copy_mode { + NFNL_LOG_COPY_NONE, + NFNL_LOG_COPY_META, + NFNL_LOG_COPY_PACKET, +}; + +enum nfnl_log_flags { + NFNL_LOG_FLAG_SEQ = 0x1, + NFNL_LOG_FLAG_SEQ_GLOBAL = 0x2, +}; + +/* General */ +extern struct nfnl_log * nfnl_log_alloc(void); +extern int nfnlmsg_log_parse(struct nlmsghdr *, + struct nfnl_log **); + +extern void nfnl_log_get(struct nfnl_log *); +extern void nfnl_log_put(struct nfnl_log *); + +/* Attributes */ +extern void nfnl_log_set_group(struct nfnl_log *, uint16_t); +extern int nfnl_log_test_group(const struct nfnl_log *); +extern uint16_t nfnl_log_get_group(const struct nfnl_log *); + +extern void nfnl_log_set_copy_mode(struct nfnl_log *, + enum nfnl_log_copy_mode); +extern int nfnl_log_test_copy_mode(const struct nfnl_log *); +extern enum nfnl_log_copy_mode nfnl_log_get_copy_mode(const struct nfnl_log *); + +extern char * nfnl_log_copy_mode2str(enum nfnl_log_copy_mode, + char *, size_t); +extern int nfnl_log_str2copy_mode(const char *); + +extern void nfnl_log_set_copy_range(struct nfnl_log *, uint32_t); +extern int nfnl_log_test_copy_range(const struct nfnl_log *); +extern uint32_t nfnl_log_get_copy_range(const struct nfnl_log *); + +extern void nfnl_log_set_flush_timeout(struct nfnl_log *, uint32_t); +extern int nfnl_log_test_flush_timeout(const struct nfnl_log *); +extern uint32_t nfnl_log_get_flush_timeout(const struct nfnl_log *); + +extern void nfnl_log_set_alloc_size(struct nfnl_log *, uint32_t); +extern int nfnl_log_test_alloc_size(const struct nfnl_log *); +extern uint32_t nfnl_log_get_alloc_size(const struct nfnl_log *); + +extern void nfnl_log_set_queue_threshold(struct nfnl_log *, uint32_t); +extern int nfnl_log_test_queue_threshold(const struct nfnl_log *); +extern uint32_t nfnl_log_get_queue_threshold(const struct nfnl_log *); + +extern void nfnl_log_set_flags(struct nfnl_log *, unsigned int); +extern void nfnl_log_unset_flags(struct nfnl_log *, unsigned int); +extern unsigned int nfnl_log_get_flags(const struct nfnl_log *); + +extern char * nfnl_log_flags2str(unsigned int, char *, size_t); +extern unsigned int nfnl_log_str2flags(const char *); + +extern int nfnl_log_build_pf_bind(uint8_t, struct nl_msg **); +extern int nfnl_log_pf_bind(struct nl_sock *, uint8_t); + +extern int nfnl_log_build_pf_unbind(uint8_t, struct nl_msg **); +extern int nfnl_log_pf_unbind(struct nl_sock *, uint8_t); + +extern int nfnl_log_build_create_request(const struct nfnl_log *, + struct nl_msg **); +extern int nfnl_log_create(struct nl_sock *, const struct nfnl_log *); + +extern int nfnl_log_build_change_request(const struct nfnl_log *, + struct nl_msg **); +extern int nfnl_log_change(struct nl_sock *, const struct nfnl_log *); + +extern int nfnl_log_build_delete_request(const struct nfnl_log *, + struct nl_msg **); +extern int nfnl_log_delete(struct nl_sock *, const struct nfnl_log *); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/libnl/include/netlink/netfilter/log_msg.h b/libnl/include/netlink/netfilter/log_msg.h new file mode 100644 index 0000000..8778d56 --- /dev/null +++ b/libnl/include/netlink/netfilter/log_msg.h @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + * Copyright (c) 2008 Patrick McHardy + */ + +#ifndef NETLINK_LOG_MSG_H_ +#define NETLINK_LOG_MSG_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nlmsghdr; +struct nfnl_log_msg; + +extern struct nl_object_ops log_msg_obj_ops; + +/* General */ +extern struct nfnl_log_msg *nfnl_log_msg_alloc(void); +extern int nfnlmsg_log_msg_parse(struct nlmsghdr *, + struct nfnl_log_msg **); + +extern void nfnl_log_msg_get(struct nfnl_log_msg *); +extern void nfnl_log_msg_put(struct nfnl_log_msg *); + +extern void nfnl_log_msg_set_family(struct nfnl_log_msg *, uint8_t); +extern uint8_t nfnl_log_msg_get_family(const struct nfnl_log_msg *); + +extern void nfnl_log_msg_set_hwproto(struct nfnl_log_msg *, uint16_t); +extern int nfnl_log_msg_test_hwproto(const struct nfnl_log_msg *); +extern uint16_t nfnl_log_msg_get_hwproto(const struct nfnl_log_msg *); + +extern void nfnl_log_msg_set_hook(struct nfnl_log_msg *, uint8_t); +extern int nfnl_log_msg_test_hook(const struct nfnl_log_msg *); +extern uint8_t nfnl_log_msg_get_hook(const struct nfnl_log_msg *); + +extern void nfnl_log_msg_set_mark(struct nfnl_log_msg *, uint32_t); +extern int nfnl_log_msg_test_mark(const struct nfnl_log_msg *); +extern uint32_t nfnl_log_msg_get_mark(const struct nfnl_log_msg *); + +extern void nfnl_log_msg_set_timestamp(struct nfnl_log_msg *, + struct timeval *); +extern const struct timeval *nfnl_log_msg_get_timestamp(const struct nfnl_log_msg *); + +extern void nfnl_log_msg_set_indev(struct nfnl_log_msg *, uint32_t); +extern uint32_t nfnl_log_msg_get_indev(const struct nfnl_log_msg *); + +extern void nfnl_log_msg_set_outdev(struct nfnl_log_msg *, uint32_t); +extern uint32_t nfnl_log_msg_get_outdev(const struct nfnl_log_msg *); + +extern void nfnl_log_msg_set_physindev(struct nfnl_log_msg *, uint32_t); +extern uint32_t nfnl_log_msg_get_physindev(const struct nfnl_log_msg *); + +extern void nfnl_log_msg_set_physoutdev(struct nfnl_log_msg *, uint32_t); +extern uint32_t nfnl_log_msg_get_physoutdev(const struct nfnl_log_msg *); + +extern void nfnl_log_msg_set_hwaddr(struct nfnl_log_msg *, uint8_t *, int); +extern const uint8_t * nfnl_log_msg_get_hwaddr(const struct nfnl_log_msg *, int *); + +extern int nfnl_log_msg_set_payload(struct nfnl_log_msg *, uint8_t *, int); +extern const void * nfnl_log_msg_get_payload(const struct nfnl_log_msg *, int *); + +extern int nfnl_log_msg_set_prefix(struct nfnl_log_msg *, void *); +extern const char * nfnl_log_msg_get_prefix(const struct nfnl_log_msg *); + +extern void nfnl_log_msg_set_uid(struct nfnl_log_msg *, uint32_t); +extern int nfnl_log_msg_test_uid(const struct nfnl_log_msg *); +extern uint32_t nfnl_log_msg_get_uid(const struct nfnl_log_msg *); + +extern void nfnl_log_msg_set_gid(struct nfnl_log_msg *, uint32_t); +extern int nfnl_log_msg_test_gid(const struct nfnl_log_msg *); +extern uint32_t nfnl_log_msg_get_gid(const struct nfnl_log_msg *); + +extern void nfnl_log_msg_set_seq(struct nfnl_log_msg *, uint32_t); +extern int nfnl_log_msg_test_seq(const struct nfnl_log_msg *); +extern uint32_t nfnl_log_msg_get_seq(const struct nfnl_log_msg *); + +extern void nfnl_log_msg_set_seq_global(struct nfnl_log_msg *, uint32_t); +extern int nfnl_log_msg_test_seq_global(const struct nfnl_log_msg *); +extern uint32_t nfnl_log_msg_get_seq_global(const struct nfnl_log_msg *); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/libnl/include/netlink/netfilter/netfilter.h b/libnl/include/netlink/netfilter/netfilter.h new file mode 100644 index 0000000..f365a21 --- /dev/null +++ b/libnl/include/netlink/netfilter/netfilter.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008 Patrick McHardy + */ + +#ifndef NETLINK_NETFILTER_H_ +#define NETLINK_NETFILTER_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern char * nfnl_verdict2str(unsigned int, char *, size_t); +extern unsigned int nfnl_str2verdict(const char *); + +extern char * nfnl_inet_hook2str(unsigned int, char *, size_t); +extern unsigned int nfnl_str2inet_hook(const char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/netfilter/nfnl.h b/libnl/include/netlink/netfilter/nfnl.h new file mode 100644 index 0000000..6e349ad --- /dev/null +++ b/libnl/include/netlink/netfilter/nfnl.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + */ + +#ifndef NETLINK_NFNL_H_ +#define NETLINK_NFNL_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define NFNL_HDRLEN NLMSG_ALIGN(sizeof(struct nfgenmsg)) +#define NFNLMSG_TYPE(subsys, subtype) (((subsys) << 8) | (subtype)) + +extern int nfnl_connect(struct nl_sock *); + +extern uint8_t nfnlmsg_subsys(struct nlmsghdr *); +extern uint8_t nfnlmsg_subtype(struct nlmsghdr *); +extern uint8_t nfnlmsg_family(struct nlmsghdr *); +extern uint16_t nfnlmsg_res_id(struct nlmsghdr *); + +extern int nfnl_send_simple(struct nl_sock *, uint8_t, uint8_t, + int, uint8_t, uint16_t); +extern struct nl_msg * nfnlmsg_alloc_simple(uint8_t, uint8_t, int, + uint8_t, uint16_t); +extern int nfnlmsg_put(struct nl_msg *, uint32_t, uint32_t, + uint8_t, uint8_t, int, uint8_t, uint16_t); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/netfilter/queue.h b/libnl/include/netlink/netfilter/queue.h new file mode 100644 index 0000000..f393f4e --- /dev/null +++ b/libnl/include/netlink/netfilter/queue.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2007, 2008 Patrick McHardy + */ + +#ifndef NETLINK_QUEUE_H_ +#define NETLINK_QUEUE_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nl_sock; +struct nlmsghdr; +struct nfnl_queue; + +extern struct nl_object_ops queue_obj_ops; + +enum nfnl_queue_copy_mode { + NFNL_QUEUE_COPY_NONE, + NFNL_QUEUE_COPY_META, + NFNL_QUEUE_COPY_PACKET, +}; + +/* General */ +extern struct nl_sock * nfnl_queue_socket_alloc(void); + +extern struct nfnl_queue * nfnl_queue_alloc(void); + +extern void nfnl_queue_get(struct nfnl_queue *); +extern void nfnl_queue_put(struct nfnl_queue *); + +/* Attributes */ +extern void nfnl_queue_set_group(struct nfnl_queue *, uint16_t); +extern int nfnl_queue_test_group(const struct nfnl_queue *); +extern uint16_t nfnl_queue_get_group(const struct nfnl_queue *); + +extern void nfnl_queue_set_maxlen(struct nfnl_queue *, uint32_t); +extern int nfnl_queue_test_maxlen(const struct nfnl_queue *); +extern uint32_t nfnl_queue_get_maxlen(const struct nfnl_queue *); + +extern void nfnl_queue_set_copy_mode(struct nfnl_queue *, + enum nfnl_queue_copy_mode); +extern int nfnl_queue_test_copy_mode(const struct nfnl_queue *); +extern enum nfnl_queue_copy_mode nfnl_queue_get_copy_mode(const struct nfnl_queue *); + +extern char * nfnl_queue_copy_mode2str(enum nfnl_queue_copy_mode, + char *, size_t); +extern int nfnl_queue_str2copy_mode(const char *); + +extern void nfnl_queue_set_copy_range(struct nfnl_queue *, + uint32_t); +extern int nfnl_queue_test_copy_range(const struct nfnl_queue *); +extern uint32_t nfnl_queue_get_copy_range(const struct nfnl_queue *); + +extern int nfnl_queue_build_pf_bind(uint8_t, struct nl_msg **); +extern int nfnl_queue_pf_bind(struct nl_sock *, uint8_t); + +extern int nfnl_queue_build_pf_unbind(uint8_t, struct nl_msg **); +extern int nfnl_queue_pf_unbind(struct nl_sock *, uint8_t); + +extern int nfnl_queue_build_create_request(const struct nfnl_queue *, + struct nl_msg **); +extern int nfnl_queue_create(struct nl_sock *, + const struct nfnl_queue *); + +extern int nfnl_queue_build_change_request(const struct nfnl_queue *, + struct nl_msg **); +extern int nfnl_queue_change(struct nl_sock *, + const struct nfnl_queue *); + +extern int nfnl_queue_build_delete_request(const struct nfnl_queue *, + struct nl_msg **); +extern int nfnl_queue_delete(struct nl_sock *, + const struct nfnl_queue *); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/libnl/include/netlink/netfilter/queue_msg.h b/libnl/include/netlink/netfilter/queue_msg.h new file mode 100644 index 0000000..dc333e0 --- /dev/null +++ b/libnl/include/netlink/netfilter/queue_msg.h @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2007, 2008 Patrick McHardy + */ + +#ifndef NETLINK_QUEUE_MSG_H_ +#define NETLINK_QUEUE_MSG_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nl_sock; +struct nlmsghdr; +struct nfnl_queue_msg; + +extern struct nl_object_ops queue_msg_obj_ops; + +/* General */ +extern struct nfnl_queue_msg * nfnl_queue_msg_alloc(void); +extern int nfnlmsg_queue_msg_parse(struct nlmsghdr *, + struct nfnl_queue_msg **); + +extern void nfnl_queue_msg_get(struct nfnl_queue_msg *); +extern void nfnl_queue_msg_put(struct nfnl_queue_msg *); + +extern void nfnl_queue_msg_set_group(struct nfnl_queue_msg *, uint16_t); +extern int nfnl_queue_msg_test_group(const struct nfnl_queue_msg *); +extern uint16_t nfnl_queue_msg_get_group(const struct nfnl_queue_msg *); + +extern void nfnl_queue_msg_set_family(struct nfnl_queue_msg *, uint8_t); +extern int nfnl_queue_msg_test_family(const struct nfnl_queue_msg *); +extern uint8_t nfnl_queue_msg_get_family(const struct nfnl_queue_msg *); + +extern void nfnl_queue_msg_set_packetid(struct nfnl_queue_msg *, uint32_t); +extern int nfnl_queue_msg_test_packetid(const struct nfnl_queue_msg *); +extern uint32_t nfnl_queue_msg_get_packetid(const struct nfnl_queue_msg *); + +extern void nfnl_queue_msg_set_hwproto(struct nfnl_queue_msg *, uint16_t); +extern int nfnl_queue_msg_test_hwproto(const struct nfnl_queue_msg *); +extern uint16_t nfnl_queue_msg_get_hwproto(const struct nfnl_queue_msg *); + +extern void nfnl_queue_msg_set_hook(struct nfnl_queue_msg *, uint8_t); +extern int nfnl_queue_msg_test_hook(const struct nfnl_queue_msg *); +extern uint8_t nfnl_queue_msg_get_hook(const struct nfnl_queue_msg *); + +extern void nfnl_queue_msg_set_mark(struct nfnl_queue_msg *, uint32_t); +extern int nfnl_queue_msg_test_mark(const struct nfnl_queue_msg *); +extern uint32_t nfnl_queue_msg_get_mark(const struct nfnl_queue_msg *); + +extern void nfnl_queue_msg_set_timestamp(struct nfnl_queue_msg *, + struct timeval *); +extern int nfnl_queue_msg_test_timestamp(const struct nfnl_queue_msg *); +extern const struct timeval * nfnl_queue_msg_get_timestamp(const struct nfnl_queue_msg *); + +extern void nfnl_queue_msg_set_indev(struct nfnl_queue_msg *, uint32_t); +extern int nfnl_queue_msg_test_indev(const struct nfnl_queue_msg *); +extern uint32_t nfnl_queue_msg_get_indev(const struct nfnl_queue_msg *); + +extern void nfnl_queue_msg_set_outdev(struct nfnl_queue_msg *, uint32_t); +extern int nfnl_queue_msg_test_outdev(const struct nfnl_queue_msg *); +extern uint32_t nfnl_queue_msg_get_outdev(const struct nfnl_queue_msg *); + +extern void nfnl_queue_msg_set_physindev(struct nfnl_queue_msg *, uint32_t); +extern int nfnl_queue_msg_test_physindev(const struct nfnl_queue_msg *); +extern uint32_t nfnl_queue_msg_get_physindev(const struct nfnl_queue_msg *); + +extern void nfnl_queue_msg_set_physoutdev(struct nfnl_queue_msg *, uint32_t); +extern int nfnl_queue_msg_test_physoutdev(const struct nfnl_queue_msg *); +extern uint32_t nfnl_queue_msg_get_physoutdev(const struct nfnl_queue_msg *); + +extern void nfnl_queue_msg_set_hwaddr(struct nfnl_queue_msg *, uint8_t *, int); +extern int nfnl_queue_msg_test_hwaddr(const struct nfnl_queue_msg *); +extern const uint8_t * nfnl_queue_msg_get_hwaddr(const struct nfnl_queue_msg *, int *); + +extern int nfnl_queue_msg_set_payload(struct nfnl_queue_msg *, uint8_t *, int); +extern int nfnl_queue_msg_test_payload(const struct nfnl_queue_msg *); +extern const void * nfnl_queue_msg_get_payload(const struct nfnl_queue_msg *, int *); + +extern void nfnl_queue_msg_set_verdict(struct nfnl_queue_msg *, + unsigned int); +extern int nfnl_queue_msg_test_verdict(const struct nfnl_queue_msg *); +extern unsigned int nfnl_queue_msg_get_verdict(const struct nfnl_queue_msg *); + +extern struct nl_msg * nfnl_queue_msg_build_verdict(const struct nfnl_queue_msg *); +extern int nfnl_queue_msg_send_verdict(struct nl_sock *, + const struct nfnl_queue_msg *); + +extern struct nl_msg * nfnl_queue_msg_build_verdict_batch(const struct nfnl_queue_msg *msg); +extern int nfnl_queue_msg_send_verdict_batch(struct nl_sock *, + const struct nfnl_queue_msg *); +extern int nfnl_queue_msg_send_verdict_payload(struct nl_sock *, + const struct nfnl_queue_msg *, + const void *, unsigned ); +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/libnl/include/netlink/netlink-compat.h b/libnl/include/netlink/netlink-compat.h new file mode 100644 index 0000000..ed71ed4 --- /dev/null +++ b/libnl/include/netlink/netlink-compat.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_COMPAT_H_ +#define NETLINK_COMPAT_H_ + +#if !defined _LINUX_SOCKET_H && !defined _BITS_SOCKADDR_H +typedef unsigned short sa_family_t; +#endif + +#ifndef IFNAMSIZ +/** Maximum length of a interface name */ +#define IFNAMSIZ 16 +#endif + +/* patch 2.4.x if_arp */ +#ifndef ARPHRD_INFINIBAND +#define ARPHRD_INFINIBAND 32 +#endif + +/* patch 2.4.x eth header file */ +#ifndef ETH_P_MPLS_UC +#define ETH_P_MPLS_UC 0x8847 +#endif + +#ifndef ETH_P_MPLS_MC +#define ETH_P_MPLS_MC 0x8848 +#endif + +#ifndef ETH_P_EDP2 +#define ETH_P_EDP2 0x88A2 +#endif + +#ifndef ETH_P_HDLC +#define ETH_P_HDLC 0x0019 +#endif + +#ifndef AF_LLC +#define AF_LLC 26 +#endif + +#ifndef AF_MPLS +#define AF_MPLS 28 +#endif + +#endif diff --git a/libnl/include/netlink/netlink-kernel.h b/libnl/include/netlink/netlink-kernel.h new file mode 100644 index 0000000..f09051d --- /dev/null +++ b/libnl/include/netlink/netlink-kernel.h @@ -0,0 +1,293 @@ +#ifndef __NETLINK_KERNEL_H_ +#define __NETLINK_KERNEL_H_ + +#if 0 + +/* + * FIXME: Goal is to preseve the documentation but make it simple + * to keep linux/netlink.h in sync. Maybe use named documentation + * sections. + */ + +/** + * Netlink socket address + * @ingroup nl + */ +struct sockaddr_nl +{ + /** socket family (AF_NETLINK) */ + sa_family_t nl_family; + + /** Padding (unused) */ + unsigned short nl_pad; + + /** Unique process ID */ + uint32_t nl_pid; + + /** Multicast group subscriptions */ + uint32_t nl_groups; +}; + +/** + * @addtogroup msg + * @{ + */ + + +/** + * Netlink message header + */ +struct nlmsghdr +{ + /** Length of message including header and padding. */ + uint32_t nlmsg_len; + + /** Message type (content type) */ + uint16_t nlmsg_type; + + /** Message flags */ + uint16_t nlmsg_flags; + + /** Sequence number of message \see core_sk_seq_num. */ + uint32_t nlmsg_seq; + + /** Netlink port */ + uint32_t nlmsg_pid; +}; + +/** + * @name Standard message flags + * @{ + */ + +/** + * Must be set on all request messages (typically from user space to + * kernel space). + */ +#define NLM_F_REQUEST 1 + +/** + * Indicates the message is part of a multipart message terminated + * by NLMSG_DONE. + */ +#define NLM_F_MULTI 2 + +/** + * Request for an acknowledgment on success. + */ +#define NLM_F_ACK 4 + +/** + * Echo this request + */ +#define NLM_F_ECHO 8 + +/** @} */ + +/** + * @name Additional message flags for GET requests + * @{ + */ + +/** + * Return the complete table instead of a single entry. + */ +#define NLM_F_ROOT 0x100 + +/** + * Return all entries matching criteria passed in message content. + */ +#define NLM_F_MATCH 0x200 + +/** + * Return an atomic snapshot of the table being referenced. This + * may require special privileges because it has the potential to + * interrupt service in the FE for a longer time. + */ +#define NLM_F_ATOMIC 0x400 + +/** + * Dump all entries + */ +#define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH) + +/** @} */ + +/** + * @name Additional messsage flags for NEW requests + * @{ + */ + +/** + * Replace existing matching config object with this request. + */ +#define NLM_F_REPLACE 0x100 + +/** + * Don't replace the config object if it already exists. + */ +#define NLM_F_EXCL 0x200 + +/** + * Create config object if it doesn't already exist. + */ +#define NLM_F_CREATE 0x400 + +/** + * Add to the end of the object list. + */ +#define NLM_F_APPEND 0x800 + +/** @} */ + +/** + * @name Standard Message types + * @{ + */ + +/** + * No operation, message must be ignored + */ +#define NLMSG_NOOP 0x1 + +/** + * The message signals an error and the payload contains a nlmsgerr + * structure. This can be looked at as a NACK and typically it is + * from FEC to CPC. + */ +#define NLMSG_ERROR 0x2 + +/** + * Message terminates a multipart message. + */ +#define NLMSG_DONE 0x3 + +/** + * The message signals that data got lost + */ +#define NLMSG_OVERRUN 0x4 + +/** + * Lower limit of reserved message types + */ +#define NLMSG_MIN_TYPE 0x10 + +/** @} */ + +/** + * Netlink error message header + */ +struct nlmsgerr +{ + /** Error code (errno number) */ + int error; + + /** Original netlink message causing the error */ + struct nlmsghdr msg; +}; + +struct nl_pktinfo +{ + __u32 group; +}; + +/** + * Netlink alignment constant, all boundries within messages must be align to this. + * + * See \ref core_msg_fmt_align for more information on message alignment. + */ +#define NLMSG_ALIGNTO 4 + +/** + * Returns \p len properly aligned to NLMSG_ALIGNTO. + * + * See \ref core_msg_fmt_align for more information on message alignment. + */ +#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) ) + +/** + * Length of a netlink message header including padding. + * + * See \ref core_msg_fmt_align for more information on message alignment. + */ +#define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr))) + +/** @} */ + +/** + * @addtogroup attr + * @{ + */ + +/* + */ + +/** + * Netlink attribute structure + * + * @code + * <------- NLA_HDRLEN ------> <-- NLA_ALIGN(payload)--> + * +---------------------+- - -+- - - - - - - - - -+- - -+ + * | Header | Pad | Payload | Pad | + * | (struct nlattr) | ing | | ing | + * +---------------------+- - -+- - - - - - - - - -+- - -+ + * <-------------- nlattr->nla_len --------------> + * @endcode + */ +struct nlattr { + /** + * Attribute length in bytes including header + */ + __u16 nla_len; + + /** + * Netlink attribute type + */ + __u16 nla_type; +}; + +/** + * @name Attribute Type Flags + * + * @code + * nla_type (16 bits) + * +---+---+-------------------------------+ + * | N | O | Attribute Type | + * +---+---+-------------------------------+ + * N := Carries nested attributes + * O := Payload stored in network byte order + * @endcode + * + * @note The N and O flag are mutually exclusive. + * + * @{ + */ + +/* + */ +#define NLA_F_NESTED (1 << 15) +#define NLA_F_NET_BYTEORDER (1 << 14) +#define NLA_TYPE_MASK ~(NLA_F_NESTED | NLA_F_NET_BYTEORDER) + +/** @} */ + +#define NLA_ALIGNTO 4 + +/** + * Returns \p len properly aligned to NLA_ALIGNTO. + * + * See \ref core_msg_fmt_align for more information on message alignment. + */ +#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1)) + +/** + * Length of a netlink attribute header including padding. + * + * See \ref core_msg_fmt_align for more information on message alignment. + */ +#define NLA_HDRLEN ((int) NLA_ALIGN(sizeof(struct nlattr))) + +/** @} */ + +#endif +#endif /* __LINUX_NETLINK_H */ diff --git a/libnl/include/netlink/netlink.h b/libnl/include/netlink/netlink.h new file mode 100644 index 0000000..c5ec350 --- /dev/null +++ b/libnl/include/netlink/netlink.h @@ -0,0 +1,98 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + */ + +#ifndef NETLINK_NETLINK_H_ +#define NETLINK_NETLINK_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nlmsghdr; +struct ucred; +struct nl_cache_ops; +struct nl_parser_param; +struct nl_object; +struct nl_sock; + +extern int nl_debug; +extern struct nl_dump_params nl_debug_dp; + +/* Connection Management */ +extern int nl_connect(struct nl_sock *, int); +extern void nl_close(struct nl_sock *); + +/* Send */ +extern int nl_sendto(struct nl_sock *, void *, size_t); +extern int nl_sendmsg(struct nl_sock *, struct nl_msg *, + struct msghdr *); +extern int nl_send(struct nl_sock *, struct nl_msg *); +extern int nl_send_iovec(struct nl_sock *, struct nl_msg *, + struct iovec *, unsigned); +extern void nl_complete_msg(struct nl_sock *, + struct nl_msg *); +extern void nl_auto_complete(struct nl_sock *, + struct nl_msg *); +extern int nl_send_auto(struct nl_sock *, struct nl_msg *); +extern int nl_send_auto_complete(struct nl_sock *, + struct nl_msg *); +extern int nl_send_sync(struct nl_sock *, struct nl_msg *); +extern int nl_send_simple(struct nl_sock *, int, int, + void *, size_t); + +/* Receive */ +extern int nl_recv(struct nl_sock *, + struct sockaddr_nl *, unsigned char **, + struct ucred **); + +extern int nl_recvmsgs(struct nl_sock *, struct nl_cb *); +extern int nl_recvmsgs_report(struct nl_sock *, struct nl_cb *); + +extern int nl_recvmsgs_default(struct nl_sock *); + +extern int nl_wait_for_ack(struct nl_sock *); + +extern int nl_pickup(struct nl_sock *, + int (*parser)(struct nl_cache_ops *, + struct sockaddr_nl *, + struct nlmsghdr *, + struct nl_parser_param *), + struct nl_object **); +extern int nl_pickup_keep_syserr(struct nl_sock *sk, + int (*parser)(struct nl_cache_ops *, struct sockaddr_nl *, + struct nlmsghdr *, struct nl_parser_param *), + struct nl_object **result, + int *syserror); +/* Netlink Family Translations */ +extern char * nl_nlfamily2str(int, char *, size_t); +extern int nl_str2nlfamily(const char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/object-api.h b/libnl/include/netlink/object-api.h new file mode 100644 index 0000000..d08bafa --- /dev/null +++ b/libnl/include/netlink/object-api.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Thomas Graf + */ + +#ifndef NETLINK_DUMMY_OBJECT_API_H_ +#define NETLINK_DUMMY_OBJECT_API_H_ + +#include +#include +#include + +#endif diff --git a/libnl/include/netlink/object.h b/libnl/include/netlink/object.h new file mode 100644 index 0000000..7448df6 --- /dev/null +++ b/libnl/include/netlink/object.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +#ifndef NETLINK_OBJECT_H_ +#define NETLINK_OBJECT_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nl_cache; +struct nl_object; +struct nl_object_ops; + +#define OBJ_CAST(ptr) ((struct nl_object *) (ptr)) + +/* General */ +extern struct nl_object * nl_object_alloc(struct nl_object_ops *); +extern int nl_object_alloc_name(const char *, + struct nl_object **); +extern void nl_object_free(struct nl_object *); +extern struct nl_object * nl_object_clone(struct nl_object *obj); +extern int nl_object_update(struct nl_object *dst, + struct nl_object *src); +extern void nl_object_get(struct nl_object *); +extern void nl_object_put(struct nl_object *); +extern int nl_object_shared(struct nl_object *); +extern void nl_object_dump(struct nl_object *, + struct nl_dump_params *); +extern void nl_object_dump_buf(struct nl_object *, char *, size_t); +extern int nl_object_identical(struct nl_object *, + struct nl_object *); +extern uint32_t nl_object_diff(struct nl_object *, + struct nl_object *); +extern uint64_t nl_object_diff64(struct nl_object *, + struct nl_object *); +extern int nl_object_match_filter(struct nl_object *, + struct nl_object *); +extern char * nl_object_attrs2str(struct nl_object *, + uint32_t attrs, char *buf, + size_t); +extern char * nl_object_attr_list(struct nl_object *, + char *, size_t); +extern void nl_object_keygen(struct nl_object *, + uint32_t *, uint32_t); + +/* Marks */ +extern void nl_object_mark(struct nl_object *); +extern void nl_object_unmark(struct nl_object *); +extern int nl_object_is_marked(struct nl_object *); + +/* Access Functions */ +extern int nl_object_get_refcnt(struct nl_object *); +extern struct nl_cache * nl_object_get_cache(struct nl_object *); +extern const char * nl_object_get_type(const struct nl_object *); +extern int nl_object_get_msgtype(const struct nl_object *); +struct nl_object_ops * nl_object_get_ops(const struct nl_object *); +uint32_t nl_object_get_id_attrs(struct nl_object *obj); + + +static inline void * nl_object_priv(struct nl_object *obj) +{ + return obj; +} + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/act/gact.h b/libnl/include/netlink/route/act/gact.h new file mode 100644 index 0000000..b3a57f7 --- /dev/null +++ b/libnl/include/netlink/route/act/gact.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2016 Sushma Sitaram + */ + +#ifndef NETLINK_GACT_H_ +#define NETLINK_GACT_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int rtnl_gact_set_action(struct rtnl_act *act, int action); +extern int rtnl_gact_get_action(struct rtnl_act *act); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/act/mirred.h b/libnl/include/netlink/route/act/mirred.h new file mode 100644 index 0000000..14fe988 --- /dev/null +++ b/libnl/include/netlink/route/act/mirred.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Cong Wang + */ + +#ifndef NETLINK_MIRRED_H_ +#define NETLINK_MIRRED_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int rtnl_mirred_set_action(struct rtnl_act *, int); +extern int rtnl_mirred_get_action(struct rtnl_act *); +extern int rtnl_mirred_set_ifindex(struct rtnl_act *, uint32_t); +extern uint32_t rtnl_mirred_get_ifindex(struct rtnl_act *); +extern int rtnl_mirred_set_policy(struct rtnl_act *, int); +extern int rtnl_mirred_get_policy(struct rtnl_act *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/act/skbedit.h b/libnl/include/netlink/route/act/skbedit.h new file mode 100644 index 0000000..3e662ad --- /dev/null +++ b/libnl/include/netlink/route/act/skbedit.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2015 Cong Wang + */ + +#ifndef NETLINK_SKBEDIT_H_ +#define NETLINK_SKBEDIT_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int rtnl_skbedit_set_action(struct rtnl_act *act, int action); +extern int rtnl_skbedit_get_action(struct rtnl_act *act); +extern int rtnl_skbedit_set_queue_mapping(struct rtnl_act *act, uint16_t index); +extern int rtnl_skbedit_get_queue_mapping(struct rtnl_act *act, uint16_t *index); +extern int rtnl_skbedit_set_mark(struct rtnl_act *act, uint32_t mark); +extern int rtnl_skbedit_get_mark(struct rtnl_act *act, uint32_t *mark); +extern int rtnl_skbedit_set_priority(struct rtnl_act *act, uint32_t prio); +extern int rtnl_skbedit_get_priority(struct rtnl_act *act, uint32_t *prio); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/act/vlan.h b/libnl/include/netlink/route/act/vlan.h new file mode 100644 index 0000000..f5001ba --- /dev/null +++ b/libnl/include/netlink/route/act/vlan.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2018 Volodymyr Bendiuga + */ + +#ifndef NETLINK_VLAN_H_ +#define NETLINK_VLAN_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int rtnl_vlan_set_mode(struct rtnl_act *act, int mode); +extern int rtnl_vlan_get_mode(struct rtnl_act *act, int *out_mode); +extern int rtnl_vlan_set_action(struct rtnl_act *act, int action); +extern int rtnl_vlan_get_action(struct rtnl_act *act, int *out_action); +extern int rtnl_vlan_set_protocol(struct rtnl_act *act, uint16_t protocol); +extern int rtnl_vlan_get_protocol(struct rtnl_act *act, uint16_t *out_protocol); +extern int rtnl_vlan_set_vlan_id(struct rtnl_act *act, uint16_t vid); +extern int rtnl_vlan_get_vlan_id(struct rtnl_act *act, uint16_t *out_vid); +extern int rtnl_vlan_set_vlan_prio(struct rtnl_act *act, uint8_t prio); +extern int rtnl_vlan_get_vlan_prio(struct rtnl_act *act, uint8_t *out_prio); + +#ifdef __cplusplus +} +#endif + +#endif /* NETLINK_VLAN_H_ */ diff --git a/libnl/include/netlink/route/action.h b/libnl/include/netlink/route/action.h new file mode 100644 index 0000000..fed491f --- /dev/null +++ b/libnl/include/netlink/route/action.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Cong Wang + */ + +#ifndef NETLINK_ACTION_H_ +#define NETLINK_ACTION_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct rtnl_act *rtnl_act_alloc(void); +extern struct rtnl_act *rtnl_act_next(struct rtnl_act *); +extern void rtnl_act_get(struct rtnl_act *); +extern void rtnl_act_put(struct rtnl_act *); +extern int rtnl_act_build_add_request(struct rtnl_act *, int, + struct nl_msg **); +extern int rtnl_act_add(struct nl_sock *, struct rtnl_act *, int); +extern int rtnl_act_change(struct nl_sock *, struct rtnl_act *, int); + +extern int rtnl_act_build_change_request(struct rtnl_act *, int, + struct nl_msg **); +extern int rtnl_act_build_delete_request(struct rtnl_act *, int, + struct nl_msg **); +extern int rtnl_act_delete(struct nl_sock *, struct rtnl_act *, + int); +extern int rtnl_act_append(struct rtnl_act **, struct rtnl_act *); +extern int rtnl_act_remove(struct rtnl_act **, struct rtnl_act *); +extern int rtnl_act_fill(struct nl_msg *, int, struct rtnl_act *); +extern void rtnl_act_put_all(struct rtnl_act **); +extern int rtnl_act_parse(struct rtnl_act **, struct nlattr *); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/addr.h b/libnl/include/netlink/route/addr.h new file mode 100644 index 0000000..240ae48 --- /dev/null +++ b/libnl/include/netlink/route/addr.h @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + * Copyright (c) 2003-2006 Baruch Even + * Copyright (c) 2003-2006 Mediatrix Telecom, inc. + */ + +#ifndef NETADDR_ADDR_H_ +#define NETADDR_ADDR_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtnl_addr; + +/* General */ +extern struct rtnl_addr *rtnl_addr_alloc(void); +extern void rtnl_addr_put(struct rtnl_addr *); + +extern int rtnl_addr_alloc_cache(struct nl_sock *, struct nl_cache **); +extern struct rtnl_addr * + rtnl_addr_get(struct nl_cache *, int, struct nl_addr *); + +extern int rtnl_addr_build_add_request(struct rtnl_addr *, int, + struct nl_msg **); +extern int rtnl_addr_add(struct nl_sock *, struct rtnl_addr *, int); + +extern int rtnl_addr_build_delete_request(struct rtnl_addr *, int, + struct nl_msg **); +extern int rtnl_addr_delete(struct nl_sock *, + struct rtnl_addr *, int); + +extern char * rtnl_addr_flags2str(int, char *, size_t); +extern int rtnl_addr_str2flags(const char *); + +extern int rtnl_addr_set_label(struct rtnl_addr *, const char *); +extern char * rtnl_addr_get_label(struct rtnl_addr *); + +extern void rtnl_addr_set_ifindex(struct rtnl_addr *, int); +extern int rtnl_addr_get_ifindex(struct rtnl_addr *); + +extern void rtnl_addr_set_link(struct rtnl_addr *, struct rtnl_link *); +extern struct rtnl_link * + rtnl_addr_get_link(struct rtnl_addr *); + +extern void rtnl_addr_set_family(struct rtnl_addr *, int); +extern int rtnl_addr_get_family(struct rtnl_addr *); + +extern void rtnl_addr_set_prefixlen(struct rtnl_addr *, int); +extern int rtnl_addr_get_prefixlen(struct rtnl_addr *); + +extern void rtnl_addr_set_scope(struct rtnl_addr *, int); +extern int rtnl_addr_get_scope(struct rtnl_addr *); + +extern void rtnl_addr_set_flags(struct rtnl_addr *, unsigned int); +extern void rtnl_addr_unset_flags(struct rtnl_addr *, unsigned int); +extern unsigned int rtnl_addr_get_flags(struct rtnl_addr *); + +extern int rtnl_addr_set_local(struct rtnl_addr *, + struct nl_addr *); +extern struct nl_addr *rtnl_addr_get_local(struct rtnl_addr *); + +extern int rtnl_addr_set_peer(struct rtnl_addr *, struct nl_addr *); +extern struct nl_addr *rtnl_addr_get_peer(struct rtnl_addr *); + +extern int rtnl_addr_set_broadcast(struct rtnl_addr *, struct nl_addr *); +extern struct nl_addr *rtnl_addr_get_broadcast(struct rtnl_addr *); + +extern int rtnl_addr_set_multicast(struct rtnl_addr *, struct nl_addr *); +extern struct nl_addr *rtnl_addr_get_multicast(struct rtnl_addr *); + +extern int rtnl_addr_set_anycast(struct rtnl_addr *, struct nl_addr *); +extern struct nl_addr *rtnl_addr_get_anycast(struct rtnl_addr *); + +extern uint32_t rtnl_addr_get_valid_lifetime(struct rtnl_addr *); +extern void rtnl_addr_set_valid_lifetime(struct rtnl_addr *, uint32_t); +extern uint32_t rtnl_addr_get_preferred_lifetime(struct rtnl_addr *); +extern void rtnl_addr_set_preferred_lifetime(struct rtnl_addr *, uint32_t); +extern uint32_t rtnl_addr_get_create_time(struct rtnl_addr *); +extern uint32_t rtnl_addr_get_last_update_time(struct rtnl_addr *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/class.h b/libnl/include/netlink/route/class.h new file mode 100644 index 0000000..91252f1 --- /dev/null +++ b/libnl/include/netlink/route/class.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +#ifndef NETLINK_CLASS_H_ +#define NETLINK_CLASS_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtnl_class; + +extern struct rtnl_class * + rtnl_class_alloc(void); +extern void rtnl_class_put(struct rtnl_class *); + +extern int rtnl_class_alloc_cache(struct nl_sock *, int, + struct nl_cache **); +extern struct rtnl_class * + rtnl_class_get(struct nl_cache *, int, uint32_t); + +extern struct rtnl_class * + rtnl_class_get_by_parent(struct nl_cache *, int, uint32_t); + +extern struct rtnl_qdisc * + rtnl_class_leaf_qdisc(struct rtnl_class *, + struct nl_cache *); + +extern int rtnl_class_build_add_request(struct rtnl_class *, int, + struct nl_msg **); +extern int rtnl_class_add(struct nl_sock *, struct rtnl_class *, + int); + +extern int rtnl_class_build_delete_request(struct rtnl_class *, + struct nl_msg **); +extern int rtnl_class_delete(struct nl_sock *, + struct rtnl_class *); + +/* deprecated functions */ +extern void rtnl_class_foreach_child(struct rtnl_class *, + struct nl_cache *, + void (*cb)(struct nl_object *, + void *), + void *) + __attribute__((deprecated)); +extern void rtnl_class_foreach_cls(struct rtnl_class *, + struct nl_cache *, + void (*cb)(struct nl_object *, + void *), + void *) + __attribute__((deprecated)); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/classifier.h b/libnl/include/netlink/route/classifier.h new file mode 100644 index 0000000..48f7e6d --- /dev/null +++ b/libnl/include/netlink/route/classifier.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +#ifndef NETLINK_CLASSIFIER_H_ +#define NETLINK_CLASSIFIER_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct rtnl_cls *rtnl_cls_alloc(void); +extern void rtnl_cls_put(struct rtnl_cls *); + +extern int rtnl_cls_alloc_cache(struct nl_sock *, int, uint32_t, + struct nl_cache **); + +extern void rtnl_cls_cache_set_tc_params(struct nl_cache *, int, uint32_t); + +extern int rtnl_cls_build_add_request(struct rtnl_cls *, int, + struct nl_msg **); +extern int rtnl_cls_add(struct nl_sock *, struct rtnl_cls *, int); +extern int rtnl_cls_change(struct nl_sock *, struct rtnl_cls *, int); + +extern int rtnl_cls_build_change_request(struct rtnl_cls *, int, + struct nl_msg **); +extern int rtnl_cls_build_delete_request(struct rtnl_cls *, int, + struct nl_msg **); +extern int rtnl_cls_delete(struct nl_sock *, struct rtnl_cls *, + int); + +extern void rtnl_cls_set_prio(struct rtnl_cls *, uint16_t); +extern uint16_t rtnl_cls_get_prio(struct rtnl_cls *); + +extern void rtnl_cls_set_protocol(struct rtnl_cls *, uint16_t); +extern uint16_t rtnl_cls_get_protocol(struct rtnl_cls *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/cls/basic.h b/libnl/include/netlink/route/cls/basic.h new file mode 100644 index 0000000..98bbe67 --- /dev/null +++ b/libnl/include/netlink/route/cls/basic.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008-2010 Thomas Graf + */ + +#ifndef NETLINK_BASIC_H_ +#define NETLINK_BASIC_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void rtnl_basic_set_target(struct rtnl_cls *, uint32_t); +extern uint32_t rtnl_basic_get_target(struct rtnl_cls *); +extern void rtnl_basic_set_ematch(struct rtnl_cls *, + struct rtnl_ematch_tree *); +extern struct rtnl_ematch_tree *rtnl_basic_get_ematch(struct rtnl_cls *); +extern int rtnl_basic_add_action(struct rtnl_cls *, struct rtnl_act *); +extern int rtnl_basic_del_action(struct rtnl_cls *, struct rtnl_act *); +extern struct rtnl_act* rtnl_basic_get_action(struct rtnl_cls *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/cls/cgroup.h b/libnl/include/netlink/route/cls/cgroup.h new file mode 100644 index 0000000..8452f6c --- /dev/null +++ b/libnl/include/netlink/route/cls/cgroup.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2009-2010 Thomas Graf + */ + +#ifndef NETLINK_CLS_CGROUP_H_ +#define NETLINK_CLS_CGROUP_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void rtnl_cgroup_set_ematch(struct rtnl_cls *, + struct rtnl_ematch_tree *); +struct rtnl_ematch_tree * rtnl_cgroup_get_ematch(struct rtnl_cls *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/cls/ematch.h b/libnl/include/netlink/route/cls/ematch.h new file mode 100644 index 0000000..4362423 --- /dev/null +++ b/libnl/include/netlink/route/cls/ematch.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008-2010 Thomas Graf + */ + +#ifndef NETLINK_CLS_EMATCH_H_ +#define NETLINK_CLS_EMATCH_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* FIXME: Should be moved to the kernel header at some point */ +#define RTNL_EMATCH_PROGID 2 + +struct rtnl_ematch; +struct rtnl_ematch_tree; + +/** + * Extended Match Operations + */ +struct rtnl_ematch_ops +{ + int eo_kind; + const char * eo_name; + size_t eo_minlen; + size_t eo_datalen; + + int (*eo_parse)(struct rtnl_ematch *, void *, size_t); + void (*eo_dump)(struct rtnl_ematch *, + struct nl_dump_params *); + int (*eo_fill)(struct rtnl_ematch *, struct nl_msg *); + void (*eo_free)(struct rtnl_ematch *); + struct nl_list_head eo_list; +}; + +extern int rtnl_ematch_register(struct rtnl_ematch_ops *); +extern struct rtnl_ematch_ops * rtnl_ematch_lookup_ops(int); +extern struct rtnl_ematch_ops * rtnl_ematch_lookup_ops_by_name(const char *); + +extern struct rtnl_ematch * rtnl_ematch_alloc(void); +extern int rtnl_ematch_add_child(struct rtnl_ematch *, + struct rtnl_ematch *); +extern void rtnl_ematch_unlink(struct rtnl_ematch *); +extern void rtnl_ematch_free(struct rtnl_ematch *); + +extern void * rtnl_ematch_data(struct rtnl_ematch *); +extern void rtnl_ematch_set_flags(struct rtnl_ematch *, + uint16_t); +extern void rtnl_ematch_unset_flags(struct rtnl_ematch *, + uint16_t); +extern uint16_t rtnl_ematch_get_flags(struct rtnl_ematch *); +extern int rtnl_ematch_set_ops(struct rtnl_ematch *, + struct rtnl_ematch_ops *); +extern int rtnl_ematch_set_kind(struct rtnl_ematch *, + uint16_t); +extern int rtnl_ematch_set_name(struct rtnl_ematch *, + const char *); + +extern struct rtnl_ematch_tree *rtnl_ematch_tree_alloc(uint16_t); +extern void rtnl_ematch_tree_free(struct rtnl_ematch_tree *); +extern void rtnl_ematch_tree_add(struct rtnl_ematch_tree *, + struct rtnl_ematch *); + +extern struct rtnl_ematch_tree *rtnl_ematch_tree_clone(struct rtnl_ematch_tree *); + +extern int rtnl_ematch_parse_attr(struct nlattr *, + struct rtnl_ematch_tree **); +extern int rtnl_ematch_fill_attr(struct nl_msg *, int, + struct rtnl_ematch_tree *); +extern void rtnl_ematch_tree_dump(struct rtnl_ematch_tree *, + struct nl_dump_params *); + + +extern int rtnl_ematch_parse_expr(const char *, char **, + struct rtnl_ematch_tree **); + +extern char * rtnl_ematch_offset2txt(uint8_t, uint16_t, + char *, size_t); +extern char * rtnl_ematch_opnd2txt(uint8_t, char *, size_t); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/cls/ematch/cmp.h b/libnl/include/netlink/route/cls/ematch/cmp.h new file mode 100644 index 0000000..8aacb51 --- /dev/null +++ b/libnl/include/netlink/route/cls/ematch/cmp.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008-2010 Thomas Graf + */ + +#ifndef NETLINK_CLS_EMATCH_CMP_H_ +#define NETLINK_CLS_EMATCH_CMP_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct tcf_em_cmp; + +extern void rtnl_ematch_cmp_set(struct rtnl_ematch *, + struct tcf_em_cmp *); +extern struct tcf_em_cmp * + rtnl_ematch_cmp_get(struct rtnl_ematch *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/cls/ematch/meta.h b/libnl/include/netlink/route/cls/ematch/meta.h new file mode 100644 index 0000000..b197246 --- /dev/null +++ b/libnl/include/netlink/route/cls/ematch/meta.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010 Thomas Graf + */ + +#ifndef NETLINK_CLS_EMATCH_META_H_ +#define NETLINK_CLS_EMATCH_META_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtnl_meta_value; + +extern struct rtnl_meta_value * rtnl_meta_value_alloc_int(uint64_t); +extern struct rtnl_meta_value * rtnl_meta_value_alloc_var(void *, size_t); +extern struct rtnl_meta_value * rtnl_meta_value_alloc_id(uint8_t, uint16_t, + uint8_t, uint64_t); +extern void rtnl_meta_value_put(struct rtnl_meta_value *); + +extern void rtnl_ematch_meta_set_lvalue(struct rtnl_ematch *, + struct rtnl_meta_value *); +void rtnl_ematch_meta_set_rvalue(struct rtnl_ematch *, + struct rtnl_meta_value *); +extern void rtnl_ematch_meta_set_operand(struct rtnl_ematch *, uint8_t); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/cls/ematch/nbyte.h b/libnl/include/netlink/route/cls/ematch/nbyte.h new file mode 100644 index 0000000..6bcf7f2 --- /dev/null +++ b/libnl/include/netlink/route/cls/ematch/nbyte.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010 Thomas Graf + */ + +#ifndef NETLINK_CLS_EMATCH_NBYTE_H_ +#define NETLINK_CLS_EMATCH_NBYTE_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void rtnl_ematch_nbyte_set_offset(struct rtnl_ematch *, + uint8_t, uint16_t); +extern uint16_t rtnl_ematch_nbyte_get_offset(struct rtnl_ematch *); +extern uint8_t rtnl_ematch_nbyte_get_layer(struct rtnl_ematch *); +extern void rtnl_ematch_nbyte_set_pattern(struct rtnl_ematch *, + uint8_t *, size_t); +extern uint8_t * rtnl_ematch_nbyte_get_pattern(struct rtnl_ematch *); +extern size_t rtnl_ematch_nbyte_get_len(struct rtnl_ematch *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/cls/ematch/text.h b/libnl/include/netlink/route/cls/ematch/text.h new file mode 100644 index 0000000..1493b09 --- /dev/null +++ b/libnl/include/netlink/route/cls/ematch/text.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010 Thomas Graf + */ + +#ifndef NETLINK_CLS_EMATCH_TEXT_H_ +#define NETLINK_CLS_EMATCH_TEXT_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void rtnl_ematch_text_set_from(struct rtnl_ematch *, + uint8_t, uint16_t); +extern uint16_t rtnl_ematch_text_get_from_offset(struct rtnl_ematch *); +extern uint8_t rtnl_ematch_text_get_from_layer(struct rtnl_ematch *); +extern void rtnl_ematch_text_set_to(struct rtnl_ematch *, + uint8_t, uint16_t); +extern uint16_t rtnl_ematch_text_get_to_offset(struct rtnl_ematch *); +extern uint8_t rtnl_ematch_text_get_to_layer(struct rtnl_ematch *); +extern void rtnl_ematch_text_set_pattern(struct rtnl_ematch *, + char *, size_t); +extern char * rtnl_ematch_text_get_pattern(struct rtnl_ematch *); +extern size_t rtnl_ematch_text_get_len(struct rtnl_ematch *); +extern void rtnl_ematch_text_set_algo(struct rtnl_ematch *, const char *); +extern char * rtnl_ematch_text_get_algo(struct rtnl_ematch *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/cls/fw.h b/libnl/include/netlink/route/cls/fw.h new file mode 100644 index 0000000..57d7c3f --- /dev/null +++ b/libnl/include/netlink/route/cls/fw.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2006 Thomas Graf + * Copyright (c) 2006 Petr Gotthard + * Copyright (c) 2006 Siemens AG Oesterreich + */ + +#ifndef NETLINK_FW_H_ +#define NETLINK_FW_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int rtnl_fw_set_classid(struct rtnl_cls *, uint32_t); +extern int rtnl_fw_set_mask(struct rtnl_cls *, uint32_t); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/cls/matchall.h b/libnl/include/netlink/route/cls/matchall.h new file mode 100644 index 0000000..bfe1e7c --- /dev/null +++ b/libnl/include/netlink/route/cls/matchall.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2017 Volodymyr Bendiuga + */ + +#ifndef NETLINK_MATCHALL_H_ +#define NETLINK_MATCHALL_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int rtnl_mall_set_classid(struct rtnl_cls *, uint32_t); +extern int rtnl_mall_get_classid(struct rtnl_cls *, uint32_t *); +extern int rtnl_mall_set_flags(struct rtnl_cls *, uint32_t); +extern int rtnl_mall_get_flags(struct rtnl_cls *, uint32_t *); +extern int rtnl_mall_append_action(struct rtnl_cls *, struct rtnl_act *); +extern struct rtnl_act *rtnl_mall_get_first_action(struct rtnl_cls *); +extern int rtnl_mall_del_action(struct rtnl_cls *, struct rtnl_act *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/cls/police.h b/libnl/include/netlink/route/cls/police.h new file mode 100644 index 0000000..9c18b87 --- /dev/null +++ b/libnl/include/netlink/route/cls/police.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_CLS_POLICE_H_ +#define NETLINK_CLS_POLICE_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern char * nl_police2str(int, char *, size_t); +extern int nl_str2police(const char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/cls/u32.h b/libnl/include/netlink/route/cls/u32.h new file mode 100644 index 0000000..b8f0223 --- /dev/null +++ b/libnl/include/netlink/route/cls/u32.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_U32_H_ +#define NETLINK_U32_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void rtnl_u32_set_handle(struct rtnl_cls *, int, int, int); +extern int rtnl_u32_set_classid(struct rtnl_cls *, uint32_t); +extern int rtnl_u32_get_classid(struct rtnl_cls *, uint32_t *); +extern int rtnl_u32_set_divisor(struct rtnl_cls *, uint32_t); +extern int rtnl_u32_set_link(struct rtnl_cls *, uint32_t); +extern int rtnl_u32_set_hashtable(struct rtnl_cls *, uint32_t); +extern int rtnl_u32_set_hashmask(struct rtnl_cls *, uint32_t, uint32_t); +extern int rtnl_u32_set_selector(struct rtnl_cls *, int, uint32_t, char, uint16_t, char); +extern int rtnl_u32_set_cls_terminal(struct rtnl_cls *); + +extern int rtnl_u32_set_flags(struct rtnl_cls *, int); +extern int rtnl_u32_add_mark(struct rtnl_cls *, uint32_t, uint32_t); +extern int rtnl_u32_del_mark(struct rtnl_cls *); +extern int rtnl_u32_add_key(struct rtnl_cls *, uint32_t, uint32_t, + int, int); +extern int rtnl_u32_get_key(struct rtnl_cls *, uint8_t, uint32_t *, uint32_t *, + int *, int *); +extern int rtnl_u32_add_key_uint8(struct rtnl_cls *, uint8_t, uint8_t, + int, int); +extern int rtnl_u32_add_key_uint16(struct rtnl_cls *, uint16_t, uint16_t, + int, int); +extern int rtnl_u32_add_key_uint32(struct rtnl_cls *, uint32_t, uint32_t, + int, int); +extern int rtnl_u32_add_key_in_addr(struct rtnl_cls *, const struct in_addr *, + uint8_t, int, int); +extern int rtnl_u32_add_key_in6_addr(struct rtnl_cls *, const struct in6_addr *, + uint8_t, int, int); +extern int rtnl_u32_add_action(struct rtnl_cls *, struct rtnl_act *); +extern int rtnl_u32_del_action(struct rtnl_cls *, struct rtnl_act *); +extern struct rtnl_act* rtnl_u32_get_action(struct rtnl_cls *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link.h b/libnl/include/netlink/route/link.h new file mode 100644 index 0000000..add464b --- /dev/null +++ b/libnl/include/netlink/route/link.h @@ -0,0 +1,272 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +#ifndef NETLINK_LINK_H_ +#define NETLINK_LINK_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @struct rtnl_link link.h "netlink/route/link.h" + * @brief Link object + * @implements nl_object + * @ingroup link + * + * @copydoc private_struct + */ +struct rtnl_link; + +/** + * @ingroup link + */ +typedef enum { + RTNL_LINK_RX_PACKETS, /*!< Packets received */ + RTNL_LINK_TX_PACKETS, /*!< Packets sent */ + RTNL_LINK_RX_BYTES, /*!< Bytes received */ + RTNL_LINK_TX_BYTES, /*!< Bytes sent */ + RTNL_LINK_RX_ERRORS, /*!< Receive errors */ + RTNL_LINK_TX_ERRORS, /*!< Send errors */ + RTNL_LINK_RX_DROPPED, /*!< Received packets dropped */ + RTNL_LINK_TX_DROPPED, /*!< Packets dropped during transmit */ + RTNL_LINK_RX_COMPRESSED, /*!< Compressed packets received */ + RTNL_LINK_TX_COMPRESSED, /*!< Compressed packets sent */ + RTNL_LINK_RX_FIFO_ERR, /*!< Receive FIFO errors */ + RTNL_LINK_TX_FIFO_ERR, /*!< Send FIFO errors */ + RTNL_LINK_RX_LEN_ERR, /*!< Length errors */ + RTNL_LINK_RX_OVER_ERR, /*!< Over errors */ + RTNL_LINK_RX_CRC_ERR, /*!< CRC errors */ + RTNL_LINK_RX_FRAME_ERR, /*!< Frame errors */ + RTNL_LINK_RX_MISSED_ERR, /*!< Missed errors */ + RTNL_LINK_TX_ABORT_ERR, /*!< Aborted errors */ + RTNL_LINK_TX_CARRIER_ERR, /*!< Carrier errors */ + RTNL_LINK_TX_HBEAT_ERR, /*!< Heartbeat errors */ + RTNL_LINK_TX_WIN_ERR, /*!< Window errors */ + RTNL_LINK_COLLISIONS, /*!< Send collisions */ + RTNL_LINK_MULTICAST, /*!< Multicast */ + RTNL_LINK_IP6_INPKTS, /*!< IPv6 SNMP InReceives */ + RTNL_LINK_IP6_INHDRERRORS, /*!< IPv6 SNMP InHdrErrors */ + RTNL_LINK_IP6_INTOOBIGERRORS, /*!< IPv6 SNMP InTooBigErrors */ + RTNL_LINK_IP6_INNOROUTES, /*!< IPv6 SNMP InNoRoutes */ + RTNL_LINK_IP6_INADDRERRORS, /*!< IPv6 SNMP InAddrErrors */ + RTNL_LINK_IP6_INUNKNOWNPROTOS, /*!< IPv6 SNMP InUnknownProtos */ + RTNL_LINK_IP6_INTRUNCATEDPKTS, /*!< IPv6 SNMP InTruncatedPkts */ + RTNL_LINK_IP6_INDISCARDS, /*!< IPv6 SNMP InDiscards */ + RTNL_LINK_IP6_INDELIVERS, /*!< IPv6 SNMP InDelivers */ + RTNL_LINK_IP6_OUTFORWDATAGRAMS, /*!< IPv6 SNMP OutForwDatagrams */ + RTNL_LINK_IP6_OUTPKTS, /*!< IPv6 SNMP OutRequests */ + RTNL_LINK_IP6_OUTDISCARDS, /*!< IPv6 SNMP OutDiscards */ + RTNL_LINK_IP6_OUTNOROUTES, /*!< IPv6 SNMP OutNoRoutes */ + RTNL_LINK_IP6_REASMTIMEOUT, /*!< IPv6 SNMP ReasmTimeout */ + RTNL_LINK_IP6_REASMREQDS, /*!< IPv6 SNMP ReasmReqds */ + RTNL_LINK_IP6_REASMOKS, /*!< IPv6 SNMP ReasmOKs */ + RTNL_LINK_IP6_REASMFAILS, /*!< IPv6 SNMP ReasmFails */ + RTNL_LINK_IP6_FRAGOKS, /*!< IPv6 SNMP FragOKs */ + RTNL_LINK_IP6_FRAGFAILS, /*!< IPv6 SNMP FragFails */ + RTNL_LINK_IP6_FRAGCREATES, /*!< IPv6 SNMP FragCreates */ + RTNL_LINK_IP6_INMCASTPKTS, /*!< IPv6 SNMP InMcastPkts */ + RTNL_LINK_IP6_OUTMCASTPKTS, /*!< IPv6 SNMP OutMcastPkts */ + RTNL_LINK_IP6_INBCASTPKTS, /*!< IPv6 SNMP InBcastPkts */ + RTNL_LINK_IP6_OUTBCASTPKTS, /*!< IPv6 SNMP OutBcastPkts */ + RTNL_LINK_IP6_INOCTETS, /*!< IPv6 SNMP InOctets */ + RTNL_LINK_IP6_OUTOCTETS, /*!< IPv6 SNMP OutOctets */ + RTNL_LINK_IP6_INMCASTOCTETS, /*!< IPv6 SNMP InMcastOctets */ + RTNL_LINK_IP6_OUTMCASTOCTETS, /*!< IPv6 SNMP OutMcastOctets */ + RTNL_LINK_IP6_INBCASTOCTETS, /*!< IPv6 SNMP InBcastOctets */ + RTNL_LINK_IP6_OUTBCASTOCTETS, /*!< IPv6 SNMP OutBcastOctets */ + RTNL_LINK_ICMP6_INMSGS, /*!< ICMPv6 SNMP InMsgs */ + RTNL_LINK_ICMP6_INERRORS, /*!< ICMPv6 SNMP InErrors */ + RTNL_LINK_ICMP6_OUTMSGS, /*!< ICMPv6 SNMP OutMsgs */ + RTNL_LINK_ICMP6_OUTERRORS, /*!< ICMPv6 SNMP OutErrors */ + RTNL_LINK_ICMP6_CSUMERRORS, /*!< ICMPv6 SNMP InCsumErrors */ + RTNL_LINK_IP6_CSUMERRORS, /*!< IPv6 SNMP InCsumErrors */ + RTNL_LINK_IP6_NOECTPKTS, /*!< IPv6 SNMP InNoECTPkts */ + RTNL_LINK_IP6_ECT1PKTS, /*!< IPv6 SNMP InECT1Pkts */ + RTNL_LINK_IP6_ECT0PKTS, /*!< IPv6 SNMP InECT0Pkts */ + RTNL_LINK_IP6_CEPKTS, /*!< IPv6 SNMP InCEPkts */ + RTNL_LINK_RX_NOHANDLER, /*!< Received packets dropped on inactive device */ + RTNL_LINK_REASM_OVERLAPS, /*!< SNMP ReasmOverlaps */ + __RTNL_LINK_STATS_MAX, +} rtnl_link_stat_id_t; + +#define RTNL_LINK_STATS_MAX (__RTNL_LINK_STATS_MAX - 1) + +extern struct nla_policy rtln_link_policy[]; + +extern struct rtnl_link *rtnl_link_alloc(void); +extern void rtnl_link_put(struct rtnl_link *); + +extern int rtnl_link_alloc_cache(struct nl_sock *, int, struct nl_cache **); +extern int rtnl_link_alloc_cache_flags(struct nl_sock *, int, + struct nl_cache **, + unsigned int flags); +extern struct rtnl_link *rtnl_link_get(struct nl_cache *, int); +extern struct rtnl_link *rtnl_link_get_by_name(struct nl_cache *, const char *); + + +extern int rtnl_link_build_add_request(struct rtnl_link *, int, + struct nl_msg **); +extern int rtnl_link_add(struct nl_sock *, struct rtnl_link *, int); +extern int rtnl_link_build_change_request(struct rtnl_link *, + struct rtnl_link *, int, + struct nl_msg **); +extern int rtnl_link_change(struct nl_sock *, struct rtnl_link *, + struct rtnl_link *, int); + +extern int rtnl_link_build_delete_request(const struct rtnl_link *, + struct nl_msg **); +extern int rtnl_link_delete(struct nl_sock *, const struct rtnl_link *); +extern int rtnl_link_build_get_request(int, const char *, + struct nl_msg **); +extern int rtnl_link_get_kernel(struct nl_sock *, int, const char *, + struct rtnl_link **); + +/* Name <-> Index Translations */ +extern char * rtnl_link_i2name(struct nl_cache *, int, char *, size_t); +extern int rtnl_link_name2i(struct nl_cache *, const char *); + +/* Name <-> Statistic Translations */ +extern char * rtnl_link_stat2str(int, char *, size_t); +extern int rtnl_link_str2stat(const char *); + +/* Link Flags Translations */ +extern char * rtnl_link_flags2str(int, char *, size_t); +extern int rtnl_link_str2flags(const char *); + +extern char * rtnl_link_operstate2str(uint8_t, char *, size_t); +extern int rtnl_link_str2operstate(const char *); + +extern char * rtnl_link_mode2str(uint8_t, char *, size_t); +extern int rtnl_link_str2mode(const char *); + +/* Carrier State Translations */ +extern char * rtnl_link_carrier2str(uint8_t, char *, size_t); +extern int rtnl_link_str2carrier(const char *); + +/* Access Functions */ +extern void rtnl_link_set_qdisc(struct rtnl_link *, const char *); +extern char * rtnl_link_get_qdisc(struct rtnl_link *); + +extern void rtnl_link_set_name(struct rtnl_link *, const char *); +extern char * rtnl_link_get_name(struct rtnl_link *); + +extern void rtnl_link_set_group(struct rtnl_link *, uint32_t); +extern uint32_t rtnl_link_get_group(struct rtnl_link *); + +extern void rtnl_link_set_flags(struct rtnl_link *, unsigned int); +extern void rtnl_link_unset_flags(struct rtnl_link *, unsigned int); +extern unsigned int rtnl_link_get_flags(struct rtnl_link *); + +extern void rtnl_link_set_mtu(struct rtnl_link *, unsigned int); +extern unsigned int rtnl_link_get_mtu(struct rtnl_link *); + +extern void rtnl_link_set_txqlen(struct rtnl_link *, unsigned int); +extern unsigned int rtnl_link_get_txqlen(struct rtnl_link *); + +extern void rtnl_link_set_ifindex(struct rtnl_link *, int); +extern int rtnl_link_get_ifindex(struct rtnl_link *); + +extern void rtnl_link_set_family(struct rtnl_link *, int); +extern int rtnl_link_get_family(struct rtnl_link *); + +extern void rtnl_link_set_arptype(struct rtnl_link *, unsigned int); +extern unsigned int rtnl_link_get_arptype(struct rtnl_link *); + +extern void rtnl_link_set_addr(struct rtnl_link *, struct nl_addr *); +extern struct nl_addr *rtnl_link_get_addr(struct rtnl_link *); + +extern void rtnl_link_set_broadcast(struct rtnl_link *, struct nl_addr *); +extern struct nl_addr *rtnl_link_get_broadcast(struct rtnl_link *); + +extern void rtnl_link_set_link(struct rtnl_link *, int); +extern int rtnl_link_get_link(struct rtnl_link *); + +extern void rtnl_link_set_master(struct rtnl_link *, int); +extern int rtnl_link_get_master(struct rtnl_link *); + +extern void rtnl_link_set_carrier(struct rtnl_link *, uint8_t); +extern uint8_t rtnl_link_get_carrier(struct rtnl_link *); + +extern int rtnl_link_get_carrier_changes(struct rtnl_link *, uint32_t *); + +extern void rtnl_link_set_operstate(struct rtnl_link *, uint8_t); +extern uint8_t rtnl_link_get_operstate(struct rtnl_link *); + +extern void rtnl_link_set_linkmode(struct rtnl_link *, uint8_t); +extern uint8_t rtnl_link_get_linkmode(struct rtnl_link *); + +int rtnl_link_set_link_netnsid(struct rtnl_link *link, int32_t link_netnsid); +int rtnl_link_get_link_netnsid(const struct rtnl_link *link, int32_t *out_link_netnsid); + +extern const char * rtnl_link_get_ifalias(struct rtnl_link *); +extern void rtnl_link_set_ifalias(struct rtnl_link *, const char *); + +extern int rtnl_link_get_num_vf(struct rtnl_link *, uint32_t *); + +extern uint64_t rtnl_link_get_stat(struct rtnl_link *, rtnl_link_stat_id_t); +extern int rtnl_link_set_stat(struct rtnl_link *, rtnl_link_stat_id_t, + const uint64_t); + +extern int rtnl_link_set_type(struct rtnl_link *, const char *); +extern char * rtnl_link_get_type(struct rtnl_link *); + +extern int rtnl_link_set_slave_type(struct rtnl_link *, const char *); +extern const char * rtnl_link_get_slave_type(const struct rtnl_link *); + +extern void rtnl_link_set_promiscuity(struct rtnl_link *, uint32_t); +extern uint32_t rtnl_link_get_promiscuity(struct rtnl_link *); + +extern void rtnl_link_set_num_tx_queues(struct rtnl_link *, uint32_t); +extern uint32_t rtnl_link_get_num_tx_queues(struct rtnl_link *); + +extern void rtnl_link_set_num_rx_queues(struct rtnl_link *, uint32_t); +extern uint32_t rtnl_link_get_num_rx_queues(struct rtnl_link *); + +extern int rtnl_link_get_gso_max_segs(struct rtnl_link *, uint32_t *); + +extern int rtnl_link_get_gso_max_size(struct rtnl_link *, uint32_t *); + +extern struct nl_data * rtnl_link_get_phys_port_id(struct rtnl_link *); + +extern char* rtnl_link_get_phys_port_name(struct rtnl_link *); + +extern struct nl_data * rtnl_link_get_phys_switch_id(struct rtnl_link *); + +extern void rtnl_link_set_ns_fd(struct rtnl_link *, int); +extern int rtnl_link_get_ns_fd(struct rtnl_link *); +extern void rtnl_link_set_ns_pid(struct rtnl_link *, pid_t); +extern pid_t rtnl_link_get_ns_pid(struct rtnl_link *); + +extern int rtnl_link_enslave_ifindex(struct nl_sock *, int, int); +extern int rtnl_link_enslave(struct nl_sock *, struct rtnl_link *, + struct rtnl_link *); +extern int rtnl_link_release_ifindex(struct nl_sock *, int); +extern int rtnl_link_release(struct nl_sock *, struct rtnl_link *); +extern int rtnl_link_fill_info(struct nl_msg *, struct rtnl_link *); +extern int rtnl_link_info_parse(struct rtnl_link *, struct nlattr **); + +extern int rtnl_link_has_vf_list(struct rtnl_link *); +extern void rtnl_link_set_vf_list(struct rtnl_link *); +extern void rtnl_link_unset_vf_list(struct rtnl_link *); + + +/* deprecated */ +extern int rtnl_link_set_info_type(struct rtnl_link *, const char *) __attribute__((deprecated)); +extern char * rtnl_link_get_info_type(struct rtnl_link *) __attribute__((deprecated)); +extern void rtnl_link_set_weight(struct rtnl_link *, unsigned int) __attribute__((deprecated)); +extern unsigned int rtnl_link_get_weight(struct rtnl_link *) __attribute__((deprecated)); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/api.h b/libnl/include/netlink/route/link/api.h new file mode 100644 index 0000000..abdd8b2 --- /dev/null +++ b/libnl/include/netlink/route/link/api.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Thomas Graf + */ + +#ifndef NETLINK_DUMMY_LINK_API_H_ +#define NETLINK_DUMMY_LINK_API_H_ + +#include +#include + +#warning "You are including a deprecated header file, include ." + +#endif diff --git a/libnl/include/netlink/route/link/bonding.h b/libnl/include/netlink/route/link/bonding.h new file mode 100644 index 0000000..09d495e --- /dev/null +++ b/libnl/include/netlink/route/link/bonding.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2011-2013 Thomas Graf + */ + +#ifndef NETLINK_LINK_BONDING_H_ +#define NETLINK_LINK_BONDING_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct rtnl_link *rtnl_link_bond_alloc(void); + +extern int rtnl_link_bond_add(struct nl_sock *, const char *, + struct rtnl_link *); + +extern int rtnl_link_bond_enslave_ifindex(struct nl_sock *, int, int); +extern int rtnl_link_bond_enslave(struct nl_sock *, struct rtnl_link *, + struct rtnl_link *); + +extern int rtnl_link_bond_release_ifindex(struct nl_sock *, int); +extern int rtnl_link_bond_release(struct nl_sock *, struct rtnl_link *); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/libnl/include/netlink/route/link/bridge.h b/libnl/include/netlink/route/link/bridge.h new file mode 100644 index 0000000..e606bd4 --- /dev/null +++ b/libnl/include/netlink/route/link/bridge.h @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Thomas Graf + */ + +#ifndef NETLINK_LINK_BRIDGE_H_ +#define NETLINK_LINK_BRIDGE_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define RTNL_LINK_BRIDGE_VLAN_BITMAP_MAX 4096 +#define RTNL_LINK_BRIDGE_VLAN_BITMAP_LEN (RTNL_LINK_BRIDGE_VLAN_BITMAP_MAX / 32) + +struct rtnl_link_bridge_vlan +{ + uint16_t pvid; + uint32_t vlan_bitmap[RTNL_LINK_BRIDGE_VLAN_BITMAP_LEN]; + uint32_t untagged_bitmap[RTNL_LINK_BRIDGE_VLAN_BITMAP_LEN]; +}; + +/** + * Bridge flags + * @ingroup bridge + */ +enum rtnl_link_bridge_flags { + RTNL_BRIDGE_HAIRPIN_MODE = 0x0001, + RTNL_BRIDGE_BPDU_GUARD = 0x0002, + RTNL_BRIDGE_ROOT_BLOCK = 0x0004, + RTNL_BRIDGE_FAST_LEAVE = 0x0008, + RTNL_BRIDGE_UNICAST_FLOOD = 0x0010, + RTNL_BRIDGE_LEARNING = 0x0020, + RTNL_BRIDGE_LEARNING_SYNC = 0x0040, +}; + +#define RTNL_BRIDGE_HWMODE_VEB BRIDGE_MODE_VEB +#define RTNL_BRIDGE_HWMODE_VEPA BRIDGE_MODE_VEPA +#define RTNL_BRIDGE_HWMODE_MAX BRIDGE_MODE_VEPA +#define RTNL_BRIDGE_HWMODE_UNDEF BRIDGE_MODE_UNDEF + +extern struct rtnl_link *rtnl_link_bridge_alloc(void); + +extern int rtnl_link_is_bridge(struct rtnl_link *); +extern int rtnl_link_bridge_has_ext_info(struct rtnl_link *); + +extern int rtnl_link_bridge_set_port_state(struct rtnl_link *, uint8_t ); +extern int rtnl_link_bridge_get_port_state(struct rtnl_link *); + +extern int rtnl_link_bridge_set_priority(struct rtnl_link *, uint16_t); +extern int rtnl_link_bridge_get_priority(struct rtnl_link *); + +extern int rtnl_link_bridge_set_cost(struct rtnl_link *, uint32_t); +extern int rtnl_link_bridge_get_cost(struct rtnl_link *, uint32_t *); + +extern int rtnl_link_bridge_unset_flags(struct rtnl_link *, unsigned int); +extern int rtnl_link_bridge_set_flags(struct rtnl_link *, unsigned int); +extern int rtnl_link_bridge_get_flags(struct rtnl_link *); + +extern int rtnl_link_bridge_set_self(struct rtnl_link *); + +extern int rtnl_link_bridge_get_hwmode(struct rtnl_link *, uint16_t *); +extern int rtnl_link_bridge_set_hwmode(struct rtnl_link *, uint16_t); + +extern char * rtnl_link_bridge_flags2str(int, char *, size_t); +extern int rtnl_link_bridge_str2flags(const char *); + +extern char * rtnl_link_bridge_portstate2str(int, char *, size_t); +extern int rtnl_link_bridge_str2portstate(const char *); + +extern char * rtnl_link_bridge_hwmode2str(uint16_t, char *, size_t); +extern uint16_t rtnl_link_bridge_str2hwmode(const char *); + +extern int rtnl_link_bridge_add(struct nl_sock *sk, const char *name); + +extern int rtnl_link_bridge_pvid(struct rtnl_link *link); +extern int rtnl_link_bridge_has_vlan(struct rtnl_link *link); + +extern struct rtnl_link_bridge_vlan *rtnl_link_bridge_get_port_vlan(struct rtnl_link *link); +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/libnl/include/netlink/route/link/can.h b/libnl/include/netlink/route/link/can.h new file mode 100644 index 0000000..8555506 --- /dev/null +++ b/libnl/include/netlink/route/link/can.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2012 Benedikt Spranger + */ + +#ifndef NETLINK_LINK_CAN_H_ +#define NETLINK_LINK_CAN_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct can_bittiming_const; +struct can_bittiming; +struct can_berr_counter; + +extern int rtnl_link_is_can(struct rtnl_link *link); + +extern char *rtnl_link_can_ctrlmode2str(int, char *, size_t); +extern int rtnl_link_can_str2ctrlmode(const char *); + +extern int rtnl_link_can_restart(struct rtnl_link *); +extern int rtnl_link_can_freq(struct rtnl_link *, uint32_t *); +extern int rtnl_link_can_state(struct rtnl_link *, uint32_t *); + +extern int rtnl_link_can_berr_rx(struct rtnl_link *); +extern int rtnl_link_can_berr_tx(struct rtnl_link *); +extern int rtnl_link_can_berr(struct rtnl_link *, struct can_berr_counter *); + +extern int rtnl_link_can_get_bt_const(struct rtnl_link *, + struct can_bittiming_const *); +extern int rtnl_link_can_get_bittiming(struct rtnl_link *, + struct can_bittiming *); +extern int rtnl_link_can_set_bittiming(struct rtnl_link *, + struct can_bittiming *); + +extern int rtnl_link_can_get_bitrate(struct rtnl_link *, uint32_t *); +extern int rtnl_link_can_set_bitrate(struct rtnl_link *, uint32_t); + +extern int rtnl_link_can_get_sample_point(struct rtnl_link *, uint32_t *); +extern int rtnl_link_can_set_sample_point(struct rtnl_link *, uint32_t); + +extern int rtnl_link_can_get_restart_ms(struct rtnl_link *, uint32_t *); +extern int rtnl_link_can_set_restart_ms(struct rtnl_link *, uint32_t); + +extern int rtnl_link_can_get_ctrlmode(struct rtnl_link *, uint32_t *); +extern int rtnl_link_can_set_ctrlmode(struct rtnl_link *, uint32_t); +extern int rtnl_link_can_unset_ctrlmode(struct rtnl_link *, uint32_t); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/geneve.h b/libnl/include/netlink/route/link/geneve.h new file mode 100644 index 0000000..cf37c4e --- /dev/null +++ b/libnl/include/netlink/route/link/geneve.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#ifndef NETLINK_LINK_GENEVE_H_ +#define NETLINK_LINK_GENEVE_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define RTNL_GENEVE_ID_MAX 16777215 + +#define RTNL_LINK_GENEVE_F_COLLECT_METADATA (1<<0) + +extern struct rtnl_link *rtnl_link_geneve_alloc(void); +extern int rtnl_link_is_geneve(struct rtnl_link *); + +extern int rtnl_link_geneve_set_id(struct rtnl_link *, uint32_t); +extern int rtnl_link_geneve_get_id(struct rtnl_link *, uint32_t *); + +extern int rtnl_link_geneve_set_remote(struct rtnl_link *, struct nl_addr *); +extern int rtnl_link_geneve_get_remote(struct rtnl_link *, struct nl_addr **); + +extern int rtnl_link_geneve_set_ttl(struct rtnl_link *, uint8_t); +extern int rtnl_link_geneve_get_ttl(struct rtnl_link *); + +extern int rtnl_link_geneve_set_tos(struct rtnl_link *, uint8_t); +extern int rtnl_link_geneve_get_tos(struct rtnl_link *); + +extern int rtnl_link_geneve_set_port(struct rtnl_link *, uint32_t); +extern int rtnl_link_geneve_get_port(struct rtnl_link *, uint32_t *); + +extern int rtnl_link_geneve_set_label(struct rtnl_link *, uint32_t); +extern int rtnl_link_geneve_get_label(struct rtnl_link *, uint32_t *); + +extern int rtnl_link_geneve_set_udp_csum(struct rtnl_link *, uint8_t); +extern int rtnl_link_geneve_get_udp_csum(struct rtnl_link *); + +extern int rtnl_link_geneve_set_udp_zero_csum6_tx(struct rtnl_link *, uint8_t); +extern int rtnl_link_geneve_get_udp_zero_csum6_tx(struct rtnl_link *); + +extern int rtnl_link_geneve_set_udp_zero_csum6_rx(struct rtnl_link *, uint8_t); +extern int rtnl_link_geneve_get_udp_zero_csum6_rx(struct rtnl_link *); + +extern int rtnl_link_geneve_set_flags(struct rtnl_link *, uint8_t flags, int enable); +extern int rtnl_link_geneve_get_flags(struct rtnl_link *, uint8_t *flags); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/inet.h b/libnl/include/netlink/route/link/inet.h new file mode 100644 index 0000000..2e93cc5 --- /dev/null +++ b/libnl/include/netlink/route/link/inet.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010 Thomas Graf + */ + +#ifndef NETLINK_LINK_INET_H_ +#define NETLINK_LINK_INET_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern const char * rtnl_link_inet_devconf2str(int, char *, size_t); +extern int rtnl_link_inet_str2devconf(const char *); + +extern int rtnl_link_inet_get_conf(struct rtnl_link *, + const unsigned int, uint32_t *); +extern int rtnl_link_inet_set_conf(struct rtnl_link *, + const unsigned int, uint32_t); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/inet6.h b/libnl/include/netlink/route/link/inet6.h new file mode 100644 index 0000000..cf257ca --- /dev/null +++ b/libnl/include/netlink/route/link/inet6.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2014 Dan Williams + */ + +#ifndef NETLINK_LINK_INET6_H_ +#define NETLINK_LINK_INET6_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +const char * rtnl_link_inet6_addrgenmode2str (uint8_t mode, + char *buf, + size_t len); + +uint8_t rtnl_link_inet6_str2addrgenmode (const char *mode); + +extern int rtnl_link_inet6_get_token(struct rtnl_link *, + struct nl_addr **); + +extern int rtnl_link_inet6_set_token(struct rtnl_link *, + struct nl_addr *); + +extern int rtnl_link_inet6_get_addr_gen_mode(struct rtnl_link *, + uint8_t *); + +extern int rtnl_link_inet6_set_addr_gen_mode(struct rtnl_link *, + uint8_t); + +extern int rtnl_link_inet6_get_flags(struct rtnl_link *, + uint32_t *); + +extern int rtnl_link_inet6_set_flags(struct rtnl_link *, + uint32_t); + +/* Link Flags Translations */ +extern char * rtnl_link_inet6_flags2str(int, char *, size_t); +extern int rtnl_link_inet6_str2flags(const char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/info-api.h b/libnl/include/netlink/route/link/info-api.h new file mode 100644 index 0000000..11cffcf --- /dev/null +++ b/libnl/include/netlink/route/link/info-api.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Thomas Graf + */ + +#ifndef NETLINK_DUMMY_LINK_INFO_API_H_ +#define NETLINK_DUMMY_LINK_INFO_API_H_ + +#include +#include + +#warning "You are including a deprecated header file, include ." + +#endif diff --git a/libnl/include/netlink/route/link/ip6tnl.h b/libnl/include/netlink/route/link/ip6tnl.h new file mode 100644 index 0000000..88866e6 --- /dev/null +++ b/libnl/include/netlink/route/link/ip6tnl.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2014 Susant Sahani + */ + +#ifndef NETLINK_LINK_IP6TNL_H_ +#define NETLINK_LINK_IP6TNL_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + extern struct rtnl_link *rtnl_link_ip6_tnl_alloc(void); + extern int rtnl_link_ip6_tnl_add(struct nl_sock *sk, const char *name); + + extern int rtnl_link_is_ip6_tnl(struct rtnl_link *link); + + extern int rtnl_link_ip6_tnl_set_link(struct rtnl_link *link, uint32_t index); + extern uint32_t rtnl_link_ip6_tnl_get_link(struct rtnl_link *link); + + extern int rtnl_link_ip6_tnl_set_local(struct rtnl_link *link, struct in6_addr *); + extern int rtnl_link_ip6_tnl_get_local(struct rtnl_link *link, struct in6_addr *addr); + + extern int rtnl_link_ip6_tnl_set_remote(struct rtnl_link *link, struct in6_addr *); + extern int rtnl_link_ip6_tnl_get_remote(struct rtnl_link *link, struct in6_addr *); + + extern int rtnl_link_ip6_tnl_set_ttl(struct rtnl_link *link, uint8_t ttl); + extern uint8_t rtnl_link_ip6_tnl_get_ttl(struct rtnl_link *link); + + extern int rtnl_link_ip6_tnl_set_tos(struct rtnl_link *link, uint8_t tos); + extern uint8_t rtnl_link_ip6_tnl_get_tos(struct rtnl_link *link); + + extern int rtnl_link_ip6_tnl_set_encaplimit(struct rtnl_link *link, uint8_t encap_limit); + extern uint8_t rtnl_link_ip6_tnl_get_encaplimit(struct rtnl_link *link); + + extern int rtnl_link_ip6_tnl_set_flags(struct rtnl_link *link, uint32_t flags); + extern uint32_t rtnl_link_ip6_tnl_get_flags(struct rtnl_link *link); + + extern uint32_t rtnl_link_ip6_tnl_get_flowinfo(struct rtnl_link *link); + extern int rtnl_link_ip6_tnl_set_flowinfo(struct rtnl_link *link, uint32_t flowinfo); + + extern int rtnl_link_ip6_tnl_set_proto(struct rtnl_link *link, uint8_t proto); + extern uint8_t rtnl_link_ip6_tnl_get_proto(struct rtnl_link *link); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/ipgre.h b/libnl/include/netlink/route/link/ipgre.h new file mode 100644 index 0000000..9e757af --- /dev/null +++ b/libnl/include/netlink/route/link/ipgre.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2014 Susant Sahani + */ + +#ifndef NETLINK_LINK_IPGRE_H_ +#define NETLINK_LINK_IPGRE_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + extern int rtnl_link_is_ipgre(struct rtnl_link *link); + extern int rtnl_link_is_ipgretap(struct rtnl_link *link); + + extern struct rtnl_link *rtnl_link_ipgre_alloc(void); + extern struct rtnl_link *rtnl_link_ipgretap_alloc(void); + extern int rtnl_link_ipgre_add(struct nl_sock *sk, const char *name); + extern int rtnl_link_ipgretap_add(struct nl_sock *sk, const char *name); + + extern int rtnl_link_ipgre_set_link(struct rtnl_link *link, uint32_t index); + extern uint32_t rtnl_link_ipgre_get_link(struct rtnl_link *link); + + extern int rtnl_link_ipgre_set_iflags(struct rtnl_link *link, uint16_t iflags); + extern uint16_t rtnl_link_ipgre_get_iflags(struct rtnl_link *link); + + extern int rtnl_link_ipgre_set_oflags(struct rtnl_link *link, uint16_t oflags); + extern uint16_t rtnl_link_ipgre_get_oflags(struct rtnl_link *link); + + extern int rtnl_link_ipgre_set_ikey(struct rtnl_link *link, uint32_t ikey); + extern uint32_t rtnl_link_ipgre_get_ikey(struct rtnl_link *link); + + extern int rtnl_link_ipgre_set_okey(struct rtnl_link *link, uint32_t okey); + extern uint32_t rtnl_link_ipgre_get_okey(struct rtnl_link *link); + + extern int rtnl_link_ipgre_set_local(struct rtnl_link *link, uint32_t addr); + extern uint32_t rtnl_link_ipgre_get_local(struct rtnl_link *link); + + extern int rtnl_link_ipgre_set_remote(struct rtnl_link *link, uint32_t addr); + extern uint32_t rtnl_link_ipgre_get_remote(struct rtnl_link *link); + + extern int rtnl_link_ipgre_set_ttl(struct rtnl_link *link, uint8_t ttl); + extern uint8_t rtnl_link_ipgre_get_ttl(struct rtnl_link *link); + + extern int rtnl_link_ipgre_set_tos(struct rtnl_link *link, uint8_t tos); + extern uint8_t rtnl_link_ipgre_get_tos(struct rtnl_link *link); + + extern int rtnl_link_ipgre_set_pmtudisc(struct rtnl_link *link, uint8_t pmtudisc); + extern uint8_t rtnl_link_ipgre_get_pmtudisc(struct rtnl_link *link); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/ipip.h b/libnl/include/netlink/route/link/ipip.h new file mode 100644 index 0000000..38db21a --- /dev/null +++ b/libnl/include/netlink/route/link/ipip.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2014 Susant Sahani + */ + +#ifndef NETLINK_LINK_IPIP_H_ +#define NETLINK_LINK_IPIP_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + extern struct rtnl_link *rtnl_link_ipip_alloc(void); + extern int rtnl_link_ipip_add(struct nl_sock *sk, const char *name); + + extern int rtnl_link_is_ipip(struct rtnl_link *link); + + extern uint32_t rtnl_link_ipip_get_link(struct rtnl_link *link); + extern int rtnl_link_ipip_set_link(struct rtnl_link *link, uint32_t index); + + extern int rtnl_link_ipip_set_local(struct rtnl_link *link, uint32_t addr); + extern uint32_t rtnl_link_ipip_get_local(struct rtnl_link *link); + + extern int rtnl_link_ipip_set_remote(struct rtnl_link *link, uint32_t addr); + extern uint32_t rtnl_link_ipip_get_remote(struct rtnl_link *link); + + extern int rtnl_link_ipip_set_ttl(struct rtnl_link *link, uint8_t ttl); + extern uint8_t rtnl_link_ipip_get_ttl(struct rtnl_link *link); + + extern int rtnl_link_ipip_set_tos(struct rtnl_link *link, uint8_t tos); + extern uint8_t rtnl_link_ipip_get_tos(struct rtnl_link *link); + + extern int rtnl_link_ipip_set_pmtudisc(struct rtnl_link *link, uint8_t pmtudisc); + extern uint8_t rtnl_link_ipip_get_pmtudisc(struct rtnl_link *link); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/ipvlan.h b/libnl/include/netlink/route/link/ipvlan.h new file mode 100644 index 0000000..09b1d2d --- /dev/null +++ b/libnl/include/netlink/route/link/ipvlan.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2015 Cong Wang + */ + +#ifndef NETLINK_LINK_IPVLAN_H_ +#define NETLINK_LINK_IPVLAN_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct rtnl_link *rtnl_link_ipvlan_alloc(void); + +extern int rtnl_link_is_ipvlan(struct rtnl_link *); + +extern char * rtnl_link_ipvlan_mode2str(int, char *, size_t); +extern int rtnl_link_ipvlan_str2mode(const char *); + +extern int rtnl_link_ipvlan_set_mode(struct rtnl_link *, + uint16_t); +extern int rtnl_link_ipvlan_get_mode(struct rtnl_link *, uint16_t *out_mode); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/ipvti.h b/libnl/include/netlink/route/link/ipvti.h new file mode 100644 index 0000000..cb8f2d9 --- /dev/null +++ b/libnl/include/netlink/route/link/ipvti.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2014 Susant Sahani + */ + +#ifndef NETLINK_LINK_IPVTI_H_ +#define NETLINK_LINK_IPVTI_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + extern struct rtnl_link *rtnl_link_ipvti_alloc(void); + extern int rtnl_link_ipvti_add(struct nl_sock *sk, const char *name); + + extern int rtnl_link_is_ipvti(struct rtnl_link *link); + + extern int rtnl_link_ipvti_set_link(struct rtnl_link *link, uint32_t index); + extern uint32_t rtnl_link_ipvti_get_link(struct rtnl_link *link); + + extern int rtnl_link_ipvti_set_ikey(struct rtnl_link *link, uint32_t ikey); + extern uint32_t rtnl_link_ipvti_get_ikey(struct rtnl_link *link); + + extern int rtnl_link_ipvti_set_okey(struct rtnl_link *link, uint32_t okey); + extern uint32_t rtnl_link_ipvti_get_okey(struct rtnl_link *link); + + extern int rtnl_link_ipvti_set_local(struct rtnl_link *link, uint32_t addr); + extern uint32_t rtnl_link_ipvti_get_local(struct rtnl_link *link); + + extern int rtnl_link_ipvti_set_remote(struct rtnl_link *link, uint32_t addr); + extern uint32_t rtnl_link_ipvti_get_remote(struct rtnl_link *link); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/macsec.h b/libnl/include/netlink/route/link/macsec.h new file mode 100644 index 0000000..69344fb --- /dev/null +++ b/libnl/include/netlink/route/link/macsec.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2016 Sabrina Dubroca + */ + +#ifndef NETLINK_LINK_MACSEC_H_ +#define NETLINK_LINK_MACSEC_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum macsec_validation_type; + +struct rtnl_link *rtnl_link_macsec_alloc(void); + +int rtnl_link_macsec_set_sci(struct rtnl_link *, uint64_t); +int rtnl_link_macsec_get_sci(struct rtnl_link *, uint64_t *); + +int rtnl_link_macsec_set_port(struct rtnl_link *, uint16_t); +int rtnl_link_macsec_get_port(struct rtnl_link *, uint16_t *); + +int rtnl_link_macsec_set_cipher_suite(struct rtnl_link *, uint64_t); +int rtnl_link_macsec_get_cipher_suite(struct rtnl_link *, uint64_t *); + +int rtnl_link_macsec_set_icv_len(struct rtnl_link *, uint16_t); +int rtnl_link_macsec_get_icv_len(struct rtnl_link *, uint16_t *); + +int rtnl_link_macsec_set_protect(struct rtnl_link *, uint8_t); +int rtnl_link_macsec_get_protect(struct rtnl_link *, uint8_t *); + +int rtnl_link_macsec_set_encrypt(struct rtnl_link *, uint8_t); +int rtnl_link_macsec_get_encrypt(struct rtnl_link *, uint8_t *); + +int rtnl_link_macsec_set_encoding_sa(struct rtnl_link *, uint8_t); +int rtnl_link_macsec_get_encoding_sa(struct rtnl_link *, uint8_t *); + +int rtnl_link_macsec_set_validation_type(struct rtnl_link *, + enum macsec_validation_type); +int rtnl_link_macsec_get_validation_type(struct rtnl_link *, + enum macsec_validation_type *); + +int rtnl_link_macsec_set_replay_protect(struct rtnl_link *, uint8_t); +int rtnl_link_macsec_get_replay_protect(struct rtnl_link *, uint8_t *); + +int rtnl_link_macsec_set_window(struct rtnl_link *, uint32_t); +int rtnl_link_macsec_get_window(struct rtnl_link *, uint32_t *); + +int rtnl_link_macsec_set_send_sci(struct rtnl_link *, uint8_t); +int rtnl_link_macsec_get_send_sci(struct rtnl_link *, uint8_t *); + +int rtnl_link_macsec_set_end_station(struct rtnl_link *, uint8_t); +int rtnl_link_macsec_get_end_station(struct rtnl_link *, uint8_t *); + +int rtnl_link_macsec_set_scb(struct rtnl_link *, uint8_t); +int rtnl_link_macsec_get_scb(struct rtnl_link *, uint8_t *); + + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/macvlan.h b/libnl/include/netlink/route/link/macvlan.h new file mode 100644 index 0000000..7c133cb --- /dev/null +++ b/libnl/include/netlink/route/link/macvlan.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Michael Braun + */ + +#ifndef NETLINK_LINK_MACVLAN_H_ +#define NETLINK_LINK_MACVLAN_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct rtnl_link *rtnl_link_macvlan_alloc(void); + +extern int rtnl_link_is_macvlan(struct rtnl_link *); + +extern char * rtnl_link_macvlan_mode2str(int, char *, size_t); +extern int rtnl_link_macvlan_str2mode(const char *); + +extern char * rtnl_link_macvlan_flags2str(int, char *, size_t); +extern int rtnl_link_macvlan_str2flags(const char *); + +extern char * rtnl_link_macvlan_macmode2str(int, char *, size_t); +extern int rtnl_link_macvlan_str2macmode(const char *); + +extern int rtnl_link_macvlan_set_mode(struct rtnl_link *, + uint32_t); +extern uint32_t rtnl_link_macvlan_get_mode(struct rtnl_link *); + +extern int rtnl_link_macvlan_set_flags(struct rtnl_link *, + uint16_t); +extern int rtnl_link_macvlan_unset_flags(struct rtnl_link *, + uint16_t); +extern uint16_t rtnl_link_macvlan_get_flags(struct rtnl_link *); + +extern int rtnl_link_macvlan_set_macmode(struct rtnl_link *, + uint32_t); +extern int rtnl_link_macvlan_get_macmode(struct rtnl_link *link, + uint32_t *out_macmode); + +extern int rtnl_link_macvlan_count_macaddr(struct rtnl_link *link, + uint32_t *out_count); +extern int rtnl_link_macvlan_get_macaddr(struct rtnl_link *link, + uint32_t idx, + const struct nl_addr **addr); +extern int rtnl_link_macvlan_add_macaddr(struct rtnl_link *link, + struct nl_addr *addr); +extern int rtnl_link_macvlan_del_macaddr(struct rtnl_link *link, + struct nl_addr *addr); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/macvtap.h b/libnl/include/netlink/route/link/macvtap.h new file mode 100644 index 0000000..6fff30d --- /dev/null +++ b/libnl/include/netlink/route/link/macvtap.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2015 Beniamino Galvani + */ + +#ifndef NETLINK_LINK_MACVTAP_H_ +#define NETLINK_LINK_MACVTAP_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct rtnl_link *rtnl_link_macvtap_alloc(void); + +extern int rtnl_link_is_macvtap(struct rtnl_link *); + +extern char * rtnl_link_macvtap_mode2str(int, char *, size_t); +extern int rtnl_link_macvtap_str2mode(const char *); + +extern char * rtnl_link_macvtap_flags2str(int, char *, size_t); +extern int rtnl_link_macvtap_str2flags(const char *); + +extern int rtnl_link_macvtap_set_mode(struct rtnl_link *, + uint32_t); +extern uint32_t rtnl_link_macvtap_get_mode(struct rtnl_link *); + +extern int rtnl_link_macvtap_set_flags(struct rtnl_link *, + uint16_t); +extern int rtnl_link_macvtap_unset_flags(struct rtnl_link *, + uint16_t); +extern uint16_t rtnl_link_macvtap_get_flags(struct rtnl_link *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/ppp.h b/libnl/include/netlink/route/link/ppp.h new file mode 100644 index 0000000..d65582a --- /dev/null +++ b/libnl/include/netlink/route/link/ppp.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2016 Jonas Johansson + */ + +#ifndef NETLINK_LINK_PPP_H_ +#define NETLINK_LINK_PPP_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct rtnl_link *rtnl_link_ppp_alloc(void); +extern int rtnl_link_ppp_set_fd(struct rtnl_link *, int32_t); +extern int rtnl_link_ppp_get_fd(struct rtnl_link *, int32_t *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/sit.h b/libnl/include/netlink/route/link/sit.h new file mode 100644 index 0000000..07bb3f1 --- /dev/null +++ b/libnl/include/netlink/route/link/sit.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2014 Susant Sahani + */ + +#ifndef NETLINK_LINK_SIT_H_ +#define NETLINK_LINK_SIT_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + extern struct rtnl_link *rtnl_link_sit_alloc(void); + extern int rtnl_link_sit_add(struct nl_sock *sk, const char *name); + + extern int rtnl_link_is_sit(struct rtnl_link *link); + + extern int rtnl_link_sit_set_link(struct rtnl_link *link, uint32_t index); + extern uint32_t rtnl_link_sit_get_link(struct rtnl_link *link); + + extern int rtnl_link_sit_set_local(struct rtnl_link *link, uint32_t addr); + extern uint32_t rtnl_link_sit_get_local(struct rtnl_link *link); + + extern int rtnl_link_sit_set_remote(struct rtnl_link *link, uint32_t addr); + extern uint32_t rtnl_link_sit_get_remote(struct rtnl_link *link); + + extern int rtnl_link_sit_set_ttl(struct rtnl_link *link, uint8_t ttl); + extern uint8_t rtnl_link_sit_get_ttl(struct rtnl_link *link); + + extern int rtnl_link_sit_set_tos(struct rtnl_link *link, uint8_t tos); + extern uint8_t rtnl_link_sit_get_tos(struct rtnl_link *link); + + extern int rtnl_link_sit_set_pmtudisc(struct rtnl_link *link, uint8_t pmtudisc); + extern uint8_t rtnl_link_sit_get_pmtudisc(struct rtnl_link *link); + + extern int rtnl_link_sit_set_flags(struct rtnl_link *link, uint16_t flags); + extern uint16_t rtnl_link_sit_get_flags(struct rtnl_link *link); + + int rtnl_link_sit_set_proto(struct rtnl_link *link, uint8_t proto); + uint8_t rtnl_link_sit_get_proto(struct rtnl_link *link); + + int rtnl_link_sit_set_ip6rd_prefix(struct rtnl_link *link, const struct in6_addr *prefix); + int rtnl_link_sit_get_ip6rd_prefix(const struct rtnl_link *link, struct in6_addr *prefix); + + int rtnl_link_sit_set_ip6rd_prefixlen(struct rtnl_link *link, uint16_t prefixlen); + int rtnl_link_sit_get_ip6rd_prefixlen(struct rtnl_link *link, uint16_t *prefixlen); + + int rtnl_link_sit_set_ip6rd_relay_prefix(struct rtnl_link *link, uint32_t prefix); + int rtnl_link_sit_get_ip6rd_relay_prefix(const struct rtnl_link *link, uint32_t *prefix); + + int rtnl_link_sit_set_ip6rd_relay_prefixlen(struct rtnl_link *link, uint16_t prefix); + int rtnl_link_sit_get_ip6rd_relay_prefixlen(struct rtnl_link *link, uint16_t *prefix); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/sriov.h b/libnl/include/netlink/route/link/sriov.h new file mode 100644 index 0000000..36d66bf --- /dev/null +++ b/libnl/include/netlink/route/link/sriov.h @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2016 Intel Corp. All rights reserved. + * Copyright (c) 2016 Jef Oliver + */ + +#ifndef NETLINK_LINK_SRIOV_H_ +#define NETLINK_LINK_SRIOV_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define RTNL_VF_GUID_STR_LEN 23 + +/** + * @ingroup sriov + */ +typedef enum { + RTNL_LINK_VF_RATE_API_UNSPEC, /*!< Unspecified API type */ + RTNL_LINK_VF_RATE_API_OLD, /*!< Old Rate setting API */ + RTNL_LINK_VF_RATE_API_NEW, /*!< New Rate setting API */ + __RTNL_LINK_VF_RATE_API_MAX, +} rtnl_link_rate_api_t; + +#define RTNL_LINK_VF_RATE_API_MAX (__RTNL_LINK_VF_RATE_API_MAX - 1) + +/** + * @ingroup sriov + */ +typedef enum { + RTNL_LINK_VF_STATS_RX_PACKETS, /*!< Packets Received */ + RTNL_LINK_VF_STATS_TX_PACKETS, /*!< Packets Sent */ + RTNL_LINK_VF_STATS_RX_BYTES, /*!< Bytes Recieved */ + RTNL_LINK_VF_STATS_TX_BYTES, /*!< Bytes Sent */ + RTNL_LINK_VF_STATS_BROADCAST, /*!< Broadcast packets received */ + RTNL_LINK_VF_STATS_MULTICAST, /*!< Multicast packets received */ + __RTNL_LINK_VF_STATS_MAX, +} rtnl_link_vf_stats_t; + +#define RTNL_LINK_VF_STATS_MAX (__RTNL_LINK_VF_STATS_MAX - 1) + +/** + * @struct rtnl_link_vf sriov.h "netlink/route/link/sriov.h" + * @brief SRIOV VF object + * @ingroup sriov + * + * @copydoc private_struct + */ +struct rtnl_link_vf; + +/** + * @brief SRIOV VF VFLAN settings + * @ingroup sriov + */ +typedef struct nl_vf_vlan_info { + uint32_t vf_vlan; /*!< VLAN number */ + uint32_t vf_vlan_qos; /*!< VLAN QOS value */ + uint16_t vf_vlan_proto; /*!< VLAN protocol */ +} nl_vf_vlan_info_t; + +/** + * @brief SRIOV VF VLANs information + * @ingroup sriov + */ +typedef struct nl_vf_vlans { + int ce_refcnt; /*!< Reference counter. Don't change this value */ + int size; /*!< Number of VLANs on the SRIOV VF */ + nl_vf_vlan_info_t * vlans; /*!< nl_vf_vlan_info_t array of SRIOV VF VLANs */ +} nl_vf_vlans_t; + +/** + * @brief VF Rate information structure + * @ingroup sriov + */ +struct nl_vf_rate { + int api; /*!< rtnl_link_rate_api_t API Version to use */ + uint32_t rate; /*!< Old API Max Rate in Mbps */ + uint32_t max_tx_rate; /*!< New API Max Rate in Mbps */ + uint32_t min_tx_rate; /*!< New API Mix Rate in Mbps */ +}; + +extern int rtnl_link_vf_add(struct rtnl_link *, struct rtnl_link_vf *); +extern struct rtnl_link_vf *rtnl_link_vf_alloc(void); +extern void rtnl_link_vf_free(struct rtnl_link_vf *); +extern struct rtnl_link_vf *rtnl_link_vf_get(struct rtnl_link *, uint32_t); +extern void rtnl_link_vf_put(struct rtnl_link_vf *); + +extern int rtnl_link_vf_get_addr(struct rtnl_link_vf *, struct nl_addr **); +extern void rtnl_link_vf_set_addr(struct rtnl_link_vf *, struct nl_addr *); + +extern void rtnl_link_vf_set_ib_node_guid(struct rtnl_link_vf *, uint64_t); +extern void rtnl_link_vf_set_ib_port_guid(struct rtnl_link_vf *, uint64_t); + +extern int rtnl_link_vf_get_index(struct rtnl_link_vf *, uint32_t *); +extern void rtnl_link_vf_set_index(struct rtnl_link_vf *, uint32_t); + +extern int rtnl_link_vf_get_linkstate(struct rtnl_link_vf *, uint32_t *); +extern void rtnl_link_vf_set_linkstate(struct rtnl_link_vf *, uint32_t); + +extern int rtnl_link_vf_get_rate(struct rtnl_link_vf *, struct nl_vf_rate *); +extern void rtnl_link_vf_set_rate(struct rtnl_link_vf *, struct nl_vf_rate *); + +extern int rtnl_link_vf_get_rss_query_en(struct rtnl_link_vf *, uint32_t *); +extern void rtnl_link_vf_set_rss_query_en(struct rtnl_link_vf *, uint32_t); + +extern int rtnl_link_vf_get_spoofchk(struct rtnl_link_vf *, uint32_t *); +extern void rtnl_link_vf_set_spoofchk(struct rtnl_link_vf *, uint32_t); + +extern int rtnl_link_vf_get_stat(struct rtnl_link_vf *, rtnl_link_vf_stats_t, + uint64_t *); + +extern int rtnl_link_vf_get_trust(struct rtnl_link_vf *, uint32_t *); +extern void rtnl_link_vf_set_trust(struct rtnl_link_vf *, uint32_t); + +extern int rtnl_link_vf_get_vlans(struct rtnl_link_vf *, nl_vf_vlans_t **); +extern void rtnl_link_vf_set_vlans(struct rtnl_link_vf *, nl_vf_vlans_t *); + +extern int rtnl_link_vf_vlan_alloc(nl_vf_vlans_t **, int); +extern void rtnl_link_vf_vlan_free(nl_vf_vlans_t *vf_vlans); +extern void rtnl_link_vf_vlan_put(nl_vf_vlans_t *); + +/* Utility functions */ +extern char *rtnl_link_vf_linkstate2str(uint32_t, char *, size_t); +extern int rtnl_link_vf_str2linkstate(const char *); + +extern char *rtnl_link_vf_vlanproto2str(uint16_t, char *, size_t); +extern int rtnl_link_vf_str2vlanproto(const char *); + +extern int rtnl_link_vf_str2guid(uint64_t *, const char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/veth.h b/libnl/include/netlink/route/link/veth.h new file mode 100644 index 0000000..66a0d93 --- /dev/null +++ b/libnl/include/netlink/route/link/veth.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Cong Wang + */ + +#ifndef NETLINK_LINK_VETH_H_ +#define NETLINK_LINK_VETH_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct rtnl_link *rtnl_link_veth_alloc(void); +extern void rtnl_link_veth_release(struct rtnl_link *); + +extern int rtnl_link_is_veth(struct rtnl_link *); + +extern struct rtnl_link *rtnl_link_veth_get_peer(struct rtnl_link *); +extern int rtnl_link_veth_add(struct nl_sock *sock, const char *name, + const char *peer, pid_t pid); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/vlan.h b/libnl/include/netlink/route/link/vlan.h new file mode 100644 index 0000000..2729d6e --- /dev/null +++ b/libnl/include/netlink/route/link/vlan.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + */ + +#ifndef NETLINK_LINK_VLAN_H_ +#define NETLINK_LINK_VLAN_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct vlan_map +{ + uint32_t vm_from; + uint32_t vm_to; +}; + +#define VLAN_PRIO_MAX 7 + +extern struct rtnl_link *rtnl_link_vlan_alloc(void); + +extern int rtnl_link_is_vlan(struct rtnl_link *); + +extern char * rtnl_link_vlan_flags2str(int, char *, size_t); +extern int rtnl_link_vlan_str2flags(const char *); + +extern int rtnl_link_vlan_set_protocol(struct rtnl_link *link, uint16_t); +extern int rtnl_link_vlan_get_protocol(struct rtnl_link *link); + +extern int rtnl_link_vlan_set_id(struct rtnl_link *, uint16_t); +extern int rtnl_link_vlan_get_id(struct rtnl_link *); + +extern int rtnl_link_vlan_set_flags(struct rtnl_link *, + unsigned int); +extern int rtnl_link_vlan_unset_flags(struct rtnl_link *, + unsigned int); +extern int rtnl_link_vlan_get_flags(struct rtnl_link *); + +extern int rtnl_link_vlan_set_ingress_map(struct rtnl_link *, + int, uint32_t); +extern uint32_t * rtnl_link_vlan_get_ingress_map(struct rtnl_link *); + +extern int rtnl_link_vlan_set_egress_map(struct rtnl_link *, + uint32_t, int); +extern struct vlan_map *rtnl_link_vlan_get_egress_map(struct rtnl_link *, + int *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/vrf.h b/libnl/include/netlink/route/link/vrf.h new file mode 100644 index 0000000..5d49437 --- /dev/null +++ b/libnl/include/netlink/route/link/vrf.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2015 Cumulus Networks. All rights reserved. + * Copyright (c) 2015 David Ahern + */ + +#ifndef NETLINK_LINK_VRF_H_ +#define NETLINK_LINK_VRF_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct rtnl_link *rtnl_link_vrf_alloc(void); +extern int rtnl_link_is_vrf(struct rtnl_link *link); +extern int rtnl_link_vrf_get_tableid(struct rtnl_link *link, uint32_t *id); +extern int rtnl_link_vrf_set_tableid(struct rtnl_link *link, uint32_t id); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/vxlan.h b/libnl/include/netlink/route/link/vxlan.h new file mode 100644 index 0000000..6321b09 --- /dev/null +++ b/libnl/include/netlink/route/link/vxlan.h @@ -0,0 +1,118 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Yasunobu Chiba + */ + +#ifndef NETLINK_LINK_VXLAN_H_ +#define NETLINK_LINK_VXLAN_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct ifla_vxlan_port_range; + +#define VXLAN_ID_MAX 16777215 + +enum { + RTNL_LINK_VXLAN_F_GBP = 1 << 0, +#define RTNL_LINK_VXLAN_F_GBP RTNL_LINK_VXLAN_F_GBP + RTNL_LINK_VXLAN_F_GPE = 1 << 1, +#define RTNL_LINK_VXLAN_F_GPE RTNL_LINK_VXLAN_F_GPE + RTNL_LINK_VXLAN_F_REMCSUM_NOPARTIAL = 1 << 2, +#define RTNL_LINK_VXLAN_F_REMCSUM_NOPARTIAL RTNL_LINK_VXLAN_F_REMCSUM_NOPARTIAL +}; + +extern struct rtnl_link *rtnl_link_vxlan_alloc(void); + +extern int rtnl_link_is_vxlan(struct rtnl_link *); + +extern int rtnl_link_vxlan_set_id(struct rtnl_link *, uint32_t); +extern int rtnl_link_vxlan_get_id(struct rtnl_link *, uint32_t *); + +extern int rtnl_link_vxlan_set_group(struct rtnl_link *, struct nl_addr *); +extern int rtnl_link_vxlan_get_group(struct rtnl_link *, struct nl_addr **); + +extern int rtnl_link_vxlan_set_link(struct rtnl_link *, uint32_t); +extern int rtnl_link_vxlan_get_link(struct rtnl_link *, uint32_t *); + +extern int rtnl_link_vxlan_set_local(struct rtnl_link *, struct nl_addr *); +extern int rtnl_link_vxlan_get_local(struct rtnl_link *, struct nl_addr **); + +extern int rtnl_link_vxlan_set_ttl(struct rtnl_link *, uint8_t); +extern int rtnl_link_vxlan_get_ttl(struct rtnl_link *); + +extern int rtnl_link_vxlan_set_tos(struct rtnl_link *, uint8_t); +extern int rtnl_link_vxlan_get_tos(struct rtnl_link *); + +extern int rtnl_link_vxlan_set_learning(struct rtnl_link *, uint8_t); +extern int rtnl_link_vxlan_get_learning(struct rtnl_link *); +extern int rtnl_link_vxlan_enable_learning(struct rtnl_link *); +extern int rtnl_link_vxlan_disable_learning(struct rtnl_link *); + +extern int rtnl_link_vxlan_set_ageing(struct rtnl_link *, uint32_t); +extern int rtnl_link_vxlan_get_ageing(struct rtnl_link *, uint32_t *); + +extern int rtnl_link_vxlan_set_limit(struct rtnl_link *, uint32_t); +extern int rtnl_link_vxlan_get_limit(struct rtnl_link *, uint32_t *); + +extern int rtnl_link_vxlan_set_port_range(struct rtnl_link *, + struct ifla_vxlan_port_range *); +extern int rtnl_link_vxlan_get_port_range(struct rtnl_link *, + struct ifla_vxlan_port_range *); + +extern int rtnl_link_vxlan_set_proxy(struct rtnl_link *, uint8_t); +extern int rtnl_link_vxlan_get_proxy(struct rtnl_link *); +extern int rtnl_link_vxlan_enable_proxy(struct rtnl_link *); +extern int rtnl_link_vxlan_disable_proxy(struct rtnl_link *); + +extern int rtnl_link_vxlan_set_rsc(struct rtnl_link *, uint8_t); +extern int rtnl_link_vxlan_get_rsc(struct rtnl_link *); +extern int rtnl_link_vxlan_enable_rsc(struct rtnl_link *); +extern int rtnl_link_vxlan_disable_rsc(struct rtnl_link *); + +extern int rtnl_link_vxlan_set_l2miss(struct rtnl_link *, uint8_t); +extern int rtnl_link_vxlan_get_l2miss(struct rtnl_link *); +extern int rtnl_link_vxlan_enable_l2miss(struct rtnl_link *); +extern int rtnl_link_vxlan_disable_l2miss(struct rtnl_link *); + +extern int rtnl_link_vxlan_set_l3miss(struct rtnl_link *, uint8_t); +extern int rtnl_link_vxlan_get_l3miss(struct rtnl_link *); +extern int rtnl_link_vxlan_enable_l3miss(struct rtnl_link *); +extern int rtnl_link_vxlan_disable_l3miss(struct rtnl_link *); + +extern int rtnl_link_vxlan_set_port(struct rtnl_link *, uint32_t); +extern int rtnl_link_vxlan_get_port(struct rtnl_link *, uint32_t *); + +extern int rtnl_link_vxlan_set_udp_csum(struct rtnl_link *, uint8_t); +extern int rtnl_link_vxlan_get_udp_csum(struct rtnl_link *); + +extern int rtnl_link_vxlan_set_udp_zero_csum6_tx(struct rtnl_link *, uint8_t); +extern int rtnl_link_vxlan_get_udp_zero_csum6_tx(struct rtnl_link *); + +extern int rtnl_link_vxlan_set_udp_zero_csum6_rx(struct rtnl_link *, uint8_t); +extern int rtnl_link_vxlan_get_udp_zero_csum6_rx(struct rtnl_link *); + +extern int rtnl_link_vxlan_set_remcsum_tx(struct rtnl_link *, uint8_t); +extern int rtnl_link_vxlan_get_remcsum_tx(struct rtnl_link *); + +extern int rtnl_link_vxlan_set_remcsum_rx(struct rtnl_link *, uint8_t); +extern int rtnl_link_vxlan_get_remcsum_rx(struct rtnl_link *); + +extern int rtnl_link_vxlan_set_flags(struct rtnl_link *, uint32_t flags, int enable); +extern int rtnl_link_vxlan_get_flags(struct rtnl_link *, uint32_t *out_flags); + +extern int rtnl_link_vxlan_set_collect_metadata(struct rtnl_link *, uint8_t); +extern int rtnl_link_vxlan_get_collect_metadata(struct rtnl_link *); + +extern int rtnl_link_vxlan_set_label(struct rtnl_link *, uint32_t); +extern int rtnl_link_vxlan_get_label(struct rtnl_link *, uint32_t *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/link/xfrmi.h b/libnl/include/netlink/route/link/xfrmi.h new file mode 100644 index 0000000..094ea11 --- /dev/null +++ b/libnl/include/netlink/route/link/xfrmi.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2019 Eyal Birger + * + * Based on netlink/route/link/ipvti.h + */ + +#ifndef NETLINK_LINK_XFRMI_H_ +#define NETLINK_LINK_XFRMI_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + extern struct rtnl_link *rtnl_link_xfrmi_alloc(void); + + extern int rtnl_link_is_xfrmi(struct rtnl_link *link); + + extern int rtnl_link_xfrmi_set_link(struct rtnl_link *link, uint32_t index); + extern int rtnl_link_xfrmi_get_link(struct rtnl_link *link, uint32_t *out_link); + + extern int rtnl_link_xfrmi_set_if_id(struct rtnl_link *link, uint32_t if_id); + extern int rtnl_link_xfrmi_get_if_id(struct rtnl_link *link, uint32_t *out_if_id); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/neighbour.h b/libnl/include/netlink/route/neighbour.h new file mode 100644 index 0000000..3760414 --- /dev/null +++ b/libnl/include/netlink/route/neighbour.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + */ + +#ifndef NETLINK_NEIGHBOUR_H_ +#define NETLINK_NEIGHBOUR_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtnl_neigh; + +extern struct rtnl_neigh *rtnl_neigh_alloc(void); +extern void rtnl_neigh_put(struct rtnl_neigh *); + +extern int rtnl_neigh_alloc_cache(struct nl_sock *, struct nl_cache **); +extern int rtnl_neigh_alloc_cache_flags(struct nl_sock *, + struct nl_cache **, + unsigned int); +extern struct rtnl_neigh *rtnl_neigh_get(struct nl_cache *, int, + struct nl_addr *); +extern struct rtnl_neigh *rtnl_neigh_get_by_vlan(struct nl_cache *, int, + struct nl_addr *, int); + +extern int rtnl_neigh_parse(struct nlmsghdr *, struct rtnl_neigh **); + +extern char * rtnl_neigh_state2str(int, char *, size_t); +extern int rtnl_neigh_str2state(const char *); + +extern char * rtnl_neigh_flags2str(int, char *, size_t); +extern int rtnl_neigh_str2flag(const char *); + +extern int rtnl_neigh_add(struct nl_sock *, struct rtnl_neigh *, int); +extern int rtnl_neigh_build_add_request(struct rtnl_neigh *, int, + struct nl_msg **); + +extern int rtnl_neigh_delete(struct nl_sock *, struct rtnl_neigh *, int); +extern int rtnl_neigh_build_delete_request(struct rtnl_neigh *, int, + struct nl_msg **); + +extern void rtnl_neigh_set_state(struct rtnl_neigh *, int); +extern int rtnl_neigh_get_state(struct rtnl_neigh *); +extern void rtnl_neigh_unset_state(struct rtnl_neigh *, + int); + +extern void rtnl_neigh_set_flags(struct rtnl_neigh *, + unsigned int); +extern void rtnl_neigh_unset_flags(struct rtnl_neigh *, + unsigned int); +extern unsigned int rtnl_neigh_get_flags(struct rtnl_neigh *); + +extern void rtnl_neigh_set_ifindex(struct rtnl_neigh *, + int); +extern int rtnl_neigh_get_ifindex(struct rtnl_neigh *); + +extern void rtnl_neigh_set_lladdr(struct rtnl_neigh *, + struct nl_addr *); +extern struct nl_addr * rtnl_neigh_get_lladdr(struct rtnl_neigh *); + +extern int rtnl_neigh_set_dst(struct rtnl_neigh *, + struct nl_addr *); +extern struct nl_addr * rtnl_neigh_get_dst(struct rtnl_neigh *); + +extern void rtnl_neigh_set_type(struct rtnl_neigh *, int); +extern int rtnl_neigh_get_type(struct rtnl_neigh *); + +extern void rtnl_neigh_set_family(struct rtnl_neigh *, int); +extern int rtnl_neigh_get_family(struct rtnl_neigh *); + +extern void rtnl_neigh_set_vlan(struct rtnl_neigh *, int); +extern int rtnl_neigh_get_vlan(struct rtnl_neigh *); + +extern void rtnl_neigh_set_master(struct rtnl_neigh *, int); +extern int rtnl_neigh_get_master(struct rtnl_neigh *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/neightbl.h b/libnl/include/netlink/route/neightbl.h new file mode 100644 index 0000000..8bcdab2 --- /dev/null +++ b/libnl/include/netlink/route/neightbl.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + */ + +#ifndef NETLINK_NEIGHTBL_H_ +#define NETLINK_NEIGHTBL_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtnl_neightbl; + +extern struct rtnl_neightbl *rtnl_neightbl_alloc(void); +extern void rtnl_neightbl_put(struct rtnl_neightbl *); +extern void rtnl_neightbl_free(struct rtnl_neightbl *); +extern int rtnl_neightbl_alloc_cache(struct nl_sock *, struct nl_cache **); +extern struct rtnl_neightbl *rtnl_neightbl_get(struct nl_cache *, + const char *, int); +extern void rtnl_neightbl_dump(struct rtnl_neightbl *, FILE *, + struct nl_dump_params *); + +extern int rtnl_neightbl_build_change_request(struct rtnl_neightbl *, + struct rtnl_neightbl *, + struct nl_msg **); +extern int rtnl_neightbl_change(struct nl_sock *, struct rtnl_neightbl *, + struct rtnl_neightbl *); + +extern void rtnl_neightbl_set_family(struct rtnl_neightbl *, int); +extern void rtnl_neightbl_set_gc_tresh1(struct rtnl_neightbl *, int); +extern void rtnl_neightbl_set_gc_tresh2(struct rtnl_neightbl *, int); +extern void rtnl_neightbl_set_gc_tresh3(struct rtnl_neightbl *, int); +extern void rtnl_neightbl_set_gc_interval(struct rtnl_neightbl *, uint64_t); +extern void rtnl_neightbl_set_name(struct rtnl_neightbl *, const char *); +extern void rtnl_neightbl_set_dev(struct rtnl_neightbl *, int); +extern void rtnl_neightbl_set_queue_len(struct rtnl_neightbl *, int); +extern void rtnl_neightbl_set_proxy_queue_len(struct rtnl_neightbl *, int); +extern void rtnl_neightbl_set_app_probes(struct rtnl_neightbl *, int); +extern void rtnl_neightbl_set_ucast_probes(struct rtnl_neightbl *, int); +extern void rtnl_neightbl_set_mcast_probes(struct rtnl_neightbl *, int); +extern void rtnl_neightbl_set_base_reachable_time(struct rtnl_neightbl *, + uint64_t); +extern void rtnl_neightbl_set_retrans_time(struct rtnl_neightbl *, uint64_t); +extern void rtnl_neightbl_set_gc_stale_time(struct rtnl_neightbl *, uint64_t); +extern void rtnl_neightbl_set_delay_probe_time(struct rtnl_neightbl *, + uint64_t); +extern void rtnl_neightbl_set_anycast_delay(struct rtnl_neightbl *, uint64_t); +extern void rtnl_neightbl_set_proxy_delay(struct rtnl_neightbl *, uint64_t); +extern void rtnl_neightbl_set_locktime(struct rtnl_neightbl *, uint64_t); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/netconf.h b/libnl/include/netlink/route/netconf.h new file mode 100644 index 0000000..7006c6e --- /dev/null +++ b/libnl/include/netlink/route/netconf.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2017 David Ahern + */ + +#ifndef NETCONF_H_ +#define NETCONF_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtnl_netconf; + +int rtnl_netconf_alloc_cache(struct nl_sock *sk, struct nl_cache **result); + +struct rtnl_netconf *rtnl_netconf_get_by_idx(struct nl_cache *cache, int family, + int ifindex); +struct rtnl_netconf *rtnl_netconf_get_all(struct nl_cache *cache, + int family); +struct rtnl_netconf *rtnl_netconf_get_default(struct nl_cache *cache, + int family); +void rtnl_netconf_put(struct rtnl_netconf *nc); + +int rtnl_netconf_get_family(struct rtnl_netconf *nc, int *val); +int rtnl_netconf_get_ifindex(struct rtnl_netconf *nc, int *val); +int rtnl_netconf_get_forwarding(struct rtnl_netconf *nc, int *val); +int rtnl_netconf_get_mc_forwarding(struct rtnl_netconf *nc, int *val); +int rtnl_netconf_get_rp_filter(struct rtnl_netconf *nc, int *val); +int rtnl_netconf_get_proxy_neigh(struct rtnl_netconf *nc, int *val); +int rtnl_netconf_get_ignore_routes_linkdown(struct rtnl_netconf *nc, int *val); +int rtnl_netconf_get_input(struct rtnl_netconf *nc, int *val); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/nexthop.h b/libnl/include/netlink/route/nexthop.h new file mode 100644 index 0000000..d43ebf4 --- /dev/null +++ b/libnl/include/netlink/route/nexthop.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + */ + +#ifndef NETLINK_ROUTE_NEXTHOP_H_ +#define NETLINK_ROUTE_NEXTHOP_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtnl_nexthop; + +enum { + NH_DUMP_FROM_ONELINE = -2, + NH_DUMP_FROM_DETAILS = -1, + NH_DUMP_FROM_ENV = 0, + /* > 0 reserved for nexthop index */ +}; + +extern struct rtnl_nexthop * rtnl_route_nh_alloc(void); +extern struct rtnl_nexthop * rtnl_route_nh_clone(struct rtnl_nexthop *); +extern void rtnl_route_nh_free(struct rtnl_nexthop *); + +extern int rtnl_route_nh_compare(struct rtnl_nexthop *, + struct rtnl_nexthop *, + uint32_t, int); + +extern void rtnl_route_nh_dump(struct rtnl_nexthop *, + struct nl_dump_params *); + +extern void rtnl_route_nh_set_weight(struct rtnl_nexthop *, uint8_t); +extern uint8_t rtnl_route_nh_get_weight(struct rtnl_nexthop *); +extern void rtnl_route_nh_set_ifindex(struct rtnl_nexthop *, int); +extern int rtnl_route_nh_get_ifindex(struct rtnl_nexthop *); +extern void rtnl_route_nh_set_gateway(struct rtnl_nexthop *, + struct nl_addr *); +extern struct nl_addr * rtnl_route_nh_get_gateway(struct rtnl_nexthop *); +extern void rtnl_route_nh_set_flags(struct rtnl_nexthop *, + unsigned int); +extern void rtnl_route_nh_unset_flags(struct rtnl_nexthop *, + unsigned int); +extern unsigned int rtnl_route_nh_get_flags(struct rtnl_nexthop *); +extern void rtnl_route_nh_set_realms(struct rtnl_nexthop *, + uint32_t); +extern uint32_t rtnl_route_nh_get_realms(struct rtnl_nexthop *); + +extern int rtnl_route_nh_set_newdst(struct rtnl_nexthop *, + struct nl_addr *); +extern struct nl_addr * rtnl_route_nh_get_newdst(struct rtnl_nexthop *); +extern int rtnl_route_nh_set_via(struct rtnl_nexthop *, + struct nl_addr *); +extern struct nl_addr * rtnl_route_nh_get_via(struct rtnl_nexthop *); +extern char * rtnl_route_nh_flags2str(int, char *, size_t); +extern int rtnl_route_nh_str2flags(const char *); + +/* + * nexthop encapsulations + */ +extern int rtnl_route_nh_encap_mpls(struct rtnl_nexthop *nh, + struct nl_addr *addr, + uint8_t ttl); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/pktloc.h b/libnl/include/netlink/route/pktloc.h new file mode 100644 index 0000000..ab83821 --- /dev/null +++ b/libnl/include/netlink/route/pktloc.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010 Thomas Graf + */ + +#ifndef NETLINK_PKTLOC_H_ +#define NETLINK_PKTLOC_H_ + +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtnl_pktloc +{ + char * name; + uint8_t layer; + uint8_t shift; + uint16_t offset; + uint16_t align; + uint32_t mask; + uint32_t refcnt; + + struct nl_list_head list; +}; + +extern int rtnl_pktloc_lookup(const char *, struct rtnl_pktloc **); +extern struct rtnl_pktloc *rtnl_pktloc_alloc(void); +extern void rtnl_pktloc_put(struct rtnl_pktloc *); +extern int rtnl_pktloc_add(struct rtnl_pktloc *); +extern void rtnl_pktloc_foreach(void (*cb)(struct rtnl_pktloc *, void *), + void *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/qdisc.h b/libnl/include/netlink/route/qdisc.h new file mode 100644 index 0000000..02000fa --- /dev/null +++ b/libnl/include/netlink/route/qdisc.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +#ifndef NETLINK_QDISC_H_ +#define NETLINK_QDISC_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtnl_qdisc; + +extern struct rtnl_qdisc * + rtnl_qdisc_alloc(void); +extern void rtnl_qdisc_put(struct rtnl_qdisc *); + +extern int rtnl_qdisc_alloc_cache(struct nl_sock *, struct nl_cache **); + +extern struct rtnl_qdisc * + rtnl_qdisc_get(struct nl_cache *, int, uint32_t); + +extern struct rtnl_qdisc * + rtnl_qdisc_get_by_parent(struct nl_cache *, int, uint32_t); + +extern int rtnl_qdisc_build_add_request(struct rtnl_qdisc *, int, + struct nl_msg **); +extern int rtnl_qdisc_add(struct nl_sock *, struct rtnl_qdisc *, int); + +extern int rtnl_qdisc_build_update_request(struct rtnl_qdisc *, + struct rtnl_qdisc *, + int, struct nl_msg **); + +extern int rtnl_qdisc_update(struct nl_sock *, struct rtnl_qdisc *, + struct rtnl_qdisc *, int); + +extern int rtnl_qdisc_build_delete_request(struct rtnl_qdisc *, + struct nl_msg **); +extern int rtnl_qdisc_delete(struct nl_sock *, struct rtnl_qdisc *); + +/* Deprecated functions */ +extern void rtnl_qdisc_foreach_child(struct rtnl_qdisc *, struct nl_cache *, + void (*cb)(struct nl_object *, void *), + void *) __attribute__ ((deprecated)); + +extern void rtnl_qdisc_foreach_cls(struct rtnl_qdisc *, struct nl_cache *, + void (*cb)(struct nl_object *, void *), + void *) __attribute__ ((deprecated)); + +extern int rtnl_qdisc_build_change_request(struct rtnl_qdisc *, + struct rtnl_qdisc *, + struct nl_msg **) + __attribute__ ((deprecated)); + +extern int rtnl_qdisc_change(struct nl_sock *, struct rtnl_qdisc *, + struct rtnl_qdisc *) __attribute__ ((deprecated)); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/qdisc/cbq.h b/libnl/include/netlink/route/qdisc/cbq.h new file mode 100644 index 0000000..3af6d88 --- /dev/null +++ b/libnl/include/netlink/route/qdisc/cbq.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_CBQ_H_ +#define NETLINK_CBQ_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern char * nl_ovl_strategy2str(int, char *, size_t); +extern int nl_str2ovl_strategy(const char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/qdisc/dsmark.h b/libnl/include/netlink/route/qdisc/dsmark.h new file mode 100644 index 0000000..b8c358f --- /dev/null +++ b/libnl/include/netlink/route/qdisc/dsmark.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +#ifndef NETLINK_DSMARK_H_ +#define NETLINK_DSMARK_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int rtnl_class_dsmark_set_bitmask(struct rtnl_class *, uint8_t); +extern int rtnl_class_dsmark_get_bitmask(struct rtnl_class *); + +extern int rtnl_class_dsmark_set_value(struct rtnl_class *, uint8_t); +extern int rtnl_class_dsmark_get_value(struct rtnl_class *); + +extern int rtnl_qdisc_dsmark_set_indices(struct rtnl_qdisc *, uint16_t); +extern int rtnl_qdisc_dsmark_get_indices(struct rtnl_qdisc *); + +extern int rtnl_qdisc_dsmark_set_default_index(struct rtnl_qdisc *, + uint16_t); +extern int rtnl_qdisc_dsmark_get_default_index(struct rtnl_qdisc *); + +extern int rtnl_qdisc_dsmark_set_set_tc_index(struct rtnl_qdisc *, int); +extern int rtnl_qdisc_dsmark_get_set_tc_index(struct rtnl_qdisc *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/qdisc/fifo.h b/libnl/include/netlink/route/qdisc/fifo.h new file mode 100644 index 0000000..291aac5 --- /dev/null +++ b/libnl/include/netlink/route/qdisc/fifo.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_FIFO_H_ +#define NETLINK_FIFO_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int rtnl_qdisc_fifo_set_limit(struct rtnl_qdisc *, int); +extern int rtnl_qdisc_fifo_get_limit(struct rtnl_qdisc *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/qdisc/fq_codel.h b/libnl/include/netlink/route/qdisc/fq_codel.h new file mode 100644 index 0000000..1a69cd8 --- /dev/null +++ b/libnl/include/netlink/route/qdisc/fq_codel.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Cong Wang + */ + +#ifndef NETLINK_FQ_CODEL_H_ +#define NETLINK_FQ_CODEL_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int rtnl_qdisc_fq_codel_set_limit(struct rtnl_qdisc *, int); +extern int rtnl_qdisc_fq_codel_get_limit(struct rtnl_qdisc *); + +extern int rtnl_qdisc_fq_codel_set_target(struct rtnl_qdisc *, uint32_t); +extern uint32_t rtnl_qdisc_fq_codel_get_target(struct rtnl_qdisc *); + +extern int rtnl_qdisc_fq_codel_set_interval(struct rtnl_qdisc *, uint32_t); +extern uint32_t rtnl_qdisc_fq_codel_get_interval(struct rtnl_qdisc *); + +extern int rtnl_qdisc_fq_codel_set_quantum(struct rtnl_qdisc *, uint32_t); +extern uint32_t rtnl_qdisc_fq_codel_get_quantum(struct rtnl_qdisc *); + +extern int rtnl_qdisc_fq_codel_set_flows(struct rtnl_qdisc *, int); +extern int rtnl_qdisc_fq_codel_get_flows(struct rtnl_qdisc *); + +extern int rtnl_qdisc_fq_codel_set_ecn(struct rtnl_qdisc *, int); +extern int rtnl_qdisc_fq_codel_get_ecn(struct rtnl_qdisc *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/qdisc/hfsc.h b/libnl/include/netlink/route/qdisc/hfsc.h new file mode 100644 index 0000000..d6a6417 --- /dev/null +++ b/libnl/include/netlink/route/qdisc/hfsc.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2014 Cong Wang + */ + +#ifndef NETLINK_HFSC_H_ +#define NETLINK_HFSC_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct tc_service_curve; + +extern uint32_t rtnl_qdisc_hfsc_get_defcls(const struct rtnl_qdisc *); +extern int rtnl_qdisc_hfsc_set_defcls(struct rtnl_qdisc *, uint32_t); + +extern int rtnl_class_hfsc_get_rsc(const struct rtnl_class *cls, struct tc_service_curve *tsc); +extern int rtnl_class_hfsc_set_rsc(struct rtnl_class *cls, const struct tc_service_curve *tsc); +extern int rtnl_class_hfsc_get_fsc(const struct rtnl_class *cls, struct tc_service_curve *tsc); +extern int rtnl_class_hfsc_set_fsc(struct rtnl_class *cls, const struct tc_service_curve *tsc); +extern int rtnl_class_hfsc_get_usc(const struct rtnl_class *cls, struct tc_service_curve *tsc); +extern int rtnl_class_hfsc_set_usc(struct rtnl_class *cls, const struct tc_service_curve *tsc); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/qdisc/htb.h b/libnl/include/netlink/route/qdisc/htb.h new file mode 100644 index 0000000..b751855 --- /dev/null +++ b/libnl/include/netlink/route/qdisc/htb.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + * Copyright (c) 2005 Petr Gotthard + * Copyright (c) 2005 Siemens AG Oesterreich + */ + +#ifndef NETLINK_HTB_H_ +#define NETLINK_HTB_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern uint32_t rtnl_htb_get_rate2quantum(struct rtnl_qdisc *); +extern int rtnl_htb_set_rate2quantum(struct rtnl_qdisc *, uint32_t); +extern uint32_t rtnl_htb_get_defcls(struct rtnl_qdisc *); +extern int rtnl_htb_set_defcls(struct rtnl_qdisc *, uint32_t); + +extern uint32_t rtnl_htb_get_prio(struct rtnl_class *); +extern int rtnl_htb_set_prio(struct rtnl_class *, uint32_t); + +extern uint32_t rtnl_htb_get_rate(struct rtnl_class *); +extern int rtnl_htb_set_rate(struct rtnl_class *, uint32_t); +extern uint32_t rtnl_htb_get_ceil(struct rtnl_class *); +extern int rtnl_htb_set_ceil(struct rtnl_class *, uint32_t); + +extern int rtnl_htb_get_rate64(struct rtnl_class *, uint64_t *); +extern int rtnl_htb_set_rate64(struct rtnl_class *, uint64_t); +extern int rtnl_htb_get_ceil64(struct rtnl_class *, uint64_t *); +extern int rtnl_htb_set_ceil64(struct rtnl_class *, uint64_t); + +extern uint32_t rtnl_htb_get_rbuffer(struct rtnl_class *); +extern int rtnl_htb_set_rbuffer(struct rtnl_class *, uint32_t); +extern uint32_t rtnl_htb_get_cbuffer(struct rtnl_class *); +extern int rtnl_htb_set_cbuffer(struct rtnl_class *, uint32_t); +extern uint32_t rtnl_htb_get_quantum(struct rtnl_class *); +extern int rtnl_htb_set_quantum(struct rtnl_class *, uint32_t); +extern int rtnl_htb_set_level(struct rtnl_class *, int); +extern int rtnl_htb_get_level(struct rtnl_class *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/qdisc/mqprio.h b/libnl/include/netlink/route/qdisc/mqprio.h new file mode 100644 index 0000000..0b26fdb --- /dev/null +++ b/libnl/include/netlink/route/qdisc/mqprio.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2018 Volodymyr Bendiuga + */ + +#ifndef NETLINK_MQPRIO_H_ +#define NETLINK_MQPRIO_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int rtnl_qdisc_mqprio_set_num_tc(struct rtnl_qdisc *qdisc, int num_tc); +extern int rtnl_qdisc_mqprio_get_num_tc(struct rtnl_qdisc *qdisc); +extern int rtnl_qdisc_mqprio_set_priomap(struct rtnl_qdisc *qdisc, uint8_t priomap[], + int len); +extern uint8_t *rtnl_qdisc_mqprio_get_priomap(struct rtnl_qdisc *qdisc); +extern int rtnl_qdisc_mqprio_hw_offload(struct rtnl_qdisc *qdisc, int offload); +extern int rtnl_qdisc_mqprio_get_hw_offload(struct rtnl_qdisc *qdisc); +extern int rtnl_qdisc_mqprio_set_queue(struct rtnl_qdisc *qdisc, uint16_t count[], + uint16_t offset[], int len); +extern int rtnl_qdisc_mqprio_get_queue(struct rtnl_qdisc *qdisc, uint16_t *count, + uint16_t *offset); +extern int rtnl_qdisc_mqprio_set_mode(struct rtnl_qdisc *qdisc, uint16_t mode); +extern int rtnl_qdisc_mqprio_get_mode(struct rtnl_qdisc *qdisc); +extern int rtnl_qdisc_mqprio_set_shaper(struct rtnl_qdisc *qdisc, uint16_t shaper); +extern int rtnl_qdisc_mqprio_get_shaper(struct rtnl_qdisc *qdisc); +extern int rtnl_qdisc_mqprio_set_min_rate(struct rtnl_qdisc *qdisc, uint64_t min[], + int len); +extern int rtnl_qdisc_mqprio_get_min_rate(struct rtnl_qdisc *qdisc, uint64_t *min); +extern int rtnl_qdisc_mqprio_set_max_rate(struct rtnl_qdisc *qdisc, uint64_t max[], + int len); +extern int rtnl_qdisc_mqprio_get_max_rate(struct rtnl_qdisc *qdisc, uint64_t *max); + +#ifdef __cplusplus +} +#endif + +#endif /* NETLINK_MQPRIO_H_ */ diff --git a/libnl/include/netlink/route/qdisc/netem.h b/libnl/include/netlink/route/qdisc/netem.h new file mode 100644 index 0000000..5012ef5 --- /dev/null +++ b/libnl/include/netlink/route/qdisc/netem.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_NETEM_H_ +#define NETLINK_NETEM_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void rtnl_netem_set_limit(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_limit(struct rtnl_qdisc *); + +/* Packet Re-ordering */ +extern void rtnl_netem_set_gap(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_gap(struct rtnl_qdisc *); + +extern void rtnl_netem_set_reorder_probability(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_reorder_probability(struct rtnl_qdisc *); + +extern void rtnl_netem_set_reorder_correlation(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_reorder_correlation(struct rtnl_qdisc *); + +/* Corruption */ +extern void rtnl_netem_set_corruption_probability(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_corruption_probability(struct rtnl_qdisc *); + +extern void rtnl_netem_set_corruption_correlation(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_corruption_correlation(struct rtnl_qdisc *); + +/* Packet Loss */ +extern void rtnl_netem_set_loss(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_loss(struct rtnl_qdisc *); + +extern void rtnl_netem_set_loss_correlation(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_loss_correlation(struct rtnl_qdisc *); + +/* Packet Duplication */ +extern void rtnl_netem_set_duplicate(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_duplicate(struct rtnl_qdisc *); + +extern void rtnl_netem_set_duplicate_correlation(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_duplicate_correlation(struct rtnl_qdisc *); + +/* Packet Delay */ +extern void rtnl_netem_set_delay(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_delay(struct rtnl_qdisc *); + +extern void rtnl_netem_set_jitter(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_jitter(struct rtnl_qdisc *); + +extern void rtnl_netem_set_delay_correlation(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_delay_correlation(struct rtnl_qdisc *); + +/* Delay Distribution */ +#define MAXDIST 65536 +extern int rtnl_netem_set_delay_distribution(struct rtnl_qdisc *, const char *); +extern int rtnl_netem_set_delay_distribution_data(struct rtnl_qdisc *, const int16_t *, size_t len); +extern int rtnl_netem_get_delay_distribution_size(struct rtnl_qdisc *); +extern int rtnl_netem_get_delay_distribution(struct rtnl_qdisc *, int16_t **); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/qdisc/plug.h b/libnl/include/netlink/route/qdisc/plug.h new file mode 100644 index 0000000..f14c043 --- /dev/null +++ b/libnl/include/netlink/route/qdisc/plug.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2012 Shriram Rajagopalan + */ + +#ifndef NETLINK_PLUG_H_ +#define NETLINK_PLUG_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int rtnl_qdisc_plug_set_limit(struct rtnl_qdisc *, int); +extern int rtnl_qdisc_plug_buffer(struct rtnl_qdisc *); +extern int rtnl_qdisc_plug_release_one(struct rtnl_qdisc *); +extern int rtnl_qdisc_plug_release_indefinite(struct rtnl_qdisc *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/qdisc/prio.h b/libnl/include/netlink/route/qdisc/prio.h new file mode 100644 index 0000000..28bcc1b --- /dev/null +++ b/libnl/include/netlink/route/qdisc/prio.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +#ifndef NETLINK_PRIO_H_ +#define NETLINK_PRIO_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name Default Values + * @{ + */ + +/** + * Default number of bands. + * @ingroup prio + */ +#define QDISC_PRIO_DEFAULT_BANDS 3 + +/** + * Default priority mapping. + * @ingroup prio + */ +#define QDISC_PRIO_DEFAULT_PRIOMAP \ + { 1, 2, 2, 2, 1, 2, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 } + +/** @} */ + +extern void rtnl_qdisc_prio_set_bands(struct rtnl_qdisc *, int); +extern int rtnl_qdisc_prio_get_bands(struct rtnl_qdisc *); +extern int rtnl_qdisc_prio_set_priomap(struct rtnl_qdisc *, uint8_t[], int); +extern uint8_t *rtnl_qdisc_prio_get_priomap(struct rtnl_qdisc *); + +extern char * rtnl_prio2str(int, char *, size_t); +extern int rtnl_str2prio(const char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/qdisc/red.h b/libnl/include/netlink/route/qdisc/red.h new file mode 100644 index 0000000..51d38ed --- /dev/null +++ b/libnl/include/netlink/route/qdisc/red.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_RED_H_ +#define NETLINK_RED_H_ + +#include + +extern void rtnl_red_set_limit(struct rtnl_qdisc *qdisc, int limit); +extern int rtnl_red_get_limit(struct rtnl_qdisc *qdisc); + +#endif diff --git a/libnl/include/netlink/route/qdisc/sfq.h b/libnl/include/netlink/route/qdisc/sfq.h new file mode 100644 index 0000000..38ee0ce --- /dev/null +++ b/libnl/include/netlink/route/qdisc/sfq.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +#ifndef NETLINK_SFQ_H_ +#define NETLINK_SFQ_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void rtnl_sfq_set_quantum(struct rtnl_qdisc *, int); +extern int rtnl_sfq_get_quantum(struct rtnl_qdisc *); + +extern void rtnl_sfq_set_limit(struct rtnl_qdisc *, int); +extern int rtnl_sfq_get_limit(struct rtnl_qdisc *); + +extern void rtnl_sfq_set_perturb(struct rtnl_qdisc *, int); +extern int rtnl_sfq_get_perturb(struct rtnl_qdisc *); + +extern int rtnl_sfq_get_divisor(struct rtnl_qdisc *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/qdisc/tbf.h b/libnl/include/netlink/route/qdisc/tbf.h new file mode 100644 index 0000000..b6c4f3d --- /dev/null +++ b/libnl/include/netlink/route/qdisc/tbf.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +#ifndef NETLINK_TBF_H_ +#define NETLINK_TBF_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void rtnl_qdisc_tbf_set_limit(struct rtnl_qdisc *, int); +extern int rtnl_qdisc_tbf_set_limit_by_latency(struct rtnl_qdisc *, int); +extern int rtnl_qdisc_tbf_get_limit(struct rtnl_qdisc *); + +extern void rtnl_qdisc_tbf_set_rate(struct rtnl_qdisc *, int, int, int); +extern int rtnl_qdisc_tbf_get_rate(struct rtnl_qdisc *); +extern int rtnl_qdisc_tbf_get_rate_bucket(struct rtnl_qdisc *); +extern int rtnl_qdisc_tbf_get_rate_cell(struct rtnl_qdisc *); + +extern int rtnl_qdisc_tbf_set_peakrate(struct rtnl_qdisc *, int, int, int); +extern int rtnl_qdisc_tbf_get_peakrate(struct rtnl_qdisc *); +extern int rtnl_qdisc_tbf_get_peakrate_bucket(struct rtnl_qdisc *); +extern int rtnl_qdisc_tbf_get_peakrate_cell(struct rtnl_qdisc *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/route.h b/libnl/include/netlink/route/route.h new file mode 100644 index 0000000..c7c0bf5 --- /dev/null +++ b/libnl/include/netlink/route/route.h @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +#ifndef NETLINK_ROUTE_H_ +#define NETLINK_ROUTE_H_ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @ingroup route + * When passed to rtnl_route_alloc_cache() the cache will + * correspond to the contents of the routing cache instead + * of the actual routes. + */ +#define ROUTE_CACHE_CONTENT 1 + +struct rtnl_route; + +struct rtnl_rtcacheinfo +{ + uint32_t rtci_clntref; + uint32_t rtci_last_use; + uint32_t rtci_expires; + int32_t rtci_error; + uint32_t rtci_used; + uint32_t rtci_id; + uint32_t rtci_ts; + uint32_t rtci_tsage; +}; + +extern struct nl_object_ops route_obj_ops; + +extern struct rtnl_route * rtnl_route_alloc(void); +extern void rtnl_route_put(struct rtnl_route *); +extern int rtnl_route_alloc_cache(struct nl_sock *, int, int, + struct nl_cache **); + +extern void rtnl_route_get(struct rtnl_route *); + +extern int rtnl_route_parse(struct nlmsghdr *, struct rtnl_route **); +extern int rtnl_route_build_msg(struct nl_msg *, struct rtnl_route *); + +extern int rtnl_route_build_add_request(struct rtnl_route *, int, + struct nl_msg **); +extern int rtnl_route_add(struct nl_sock *, struct rtnl_route *, int); +extern int rtnl_route_build_del_request(struct rtnl_route *, int, + struct nl_msg **); +extern int rtnl_route_delete(struct nl_sock *, struct rtnl_route *, int); + +extern void rtnl_route_set_table(struct rtnl_route *, uint32_t); +extern uint32_t rtnl_route_get_table(struct rtnl_route *); +extern void rtnl_route_set_scope(struct rtnl_route *, uint8_t); +extern uint8_t rtnl_route_get_scope(struct rtnl_route *); +extern void rtnl_route_set_tos(struct rtnl_route *, uint8_t); +extern uint8_t rtnl_route_get_tos(struct rtnl_route *); +extern void rtnl_route_set_protocol(struct rtnl_route *, uint8_t); +extern uint8_t rtnl_route_get_protocol(struct rtnl_route *); +extern void rtnl_route_set_priority(struct rtnl_route *, uint32_t); +extern uint32_t rtnl_route_get_priority(struct rtnl_route *); +extern int rtnl_route_set_family(struct rtnl_route *, uint8_t); +extern uint8_t rtnl_route_get_family(struct rtnl_route *); +extern int rtnl_route_set_type(struct rtnl_route *, uint8_t); +extern uint8_t rtnl_route_get_type(struct rtnl_route *); +extern void rtnl_route_set_flags(struct rtnl_route *, uint32_t); +extern void rtnl_route_unset_flags(struct rtnl_route *, uint32_t); +extern uint32_t rtnl_route_get_flags(struct rtnl_route *); +extern int rtnl_route_set_metric(struct rtnl_route *, int, unsigned int); +extern int rtnl_route_unset_metric(struct rtnl_route *, int); +extern int rtnl_route_get_metric(struct rtnl_route *, int, uint32_t *); +extern int rtnl_route_set_dst(struct rtnl_route *, struct nl_addr *); +extern struct nl_addr *rtnl_route_get_dst(struct rtnl_route *); +extern int rtnl_route_set_src(struct rtnl_route *, struct nl_addr *); +extern struct nl_addr *rtnl_route_get_src(struct rtnl_route *); +extern int rtnl_route_set_pref_src(struct rtnl_route *, struct nl_addr *); +extern struct nl_addr *rtnl_route_get_pref_src(struct rtnl_route *); +extern void rtnl_route_set_iif(struct rtnl_route *, int); +extern int rtnl_route_get_iif(struct rtnl_route *); +extern int rtnl_route_get_src_len(struct rtnl_route *); +extern void rtnl_route_set_ttl_propagate(struct rtnl_route *route, + uint8_t ttl_prop); +extern int rtnl_route_get_ttl_propagate(struct rtnl_route *route); + +extern void rtnl_route_add_nexthop(struct rtnl_route *, + struct rtnl_nexthop *); +extern void rtnl_route_remove_nexthop(struct rtnl_route *, + struct rtnl_nexthop *); +extern struct nl_list_head *rtnl_route_get_nexthops(struct rtnl_route *); +extern int rtnl_route_get_nnexthops(struct rtnl_route *); + +extern void rtnl_route_foreach_nexthop(struct rtnl_route *r, + void (*cb)(struct rtnl_nexthop *, void *), + void *arg); + +extern struct rtnl_nexthop * rtnl_route_nexthop_n(struct rtnl_route *r, int n); + +extern int rtnl_route_guess_scope(struct rtnl_route *); + +extern char * rtnl_route_table2str(int, char *, size_t); +extern int rtnl_route_str2table(const char *); +extern int rtnl_route_read_table_names(const char *); + +extern char * rtnl_route_proto2str(int, char *, size_t); +extern int rtnl_route_str2proto(const char *); +extern int rtnl_route_read_protocol_names(const char *); + +extern char * rtnl_route_metric2str(int, char *, size_t); +extern int rtnl_route_str2metric(const char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/rtnl.h b/libnl/include/netlink/route/rtnl.h new file mode 100644 index 0000000..6cae88f --- /dev/null +++ b/libnl/include/netlink/route/rtnl.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + */ + +#ifndef NETLINK_RTNL_H_ +#define NETLINK_RTNL_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name Realms + * @{ + */ + +/** + * Mask specying the size of each realm part + * @ingroup rtnl + */ +#define RTNL_REALM_MASK (0xFFFF) + +/** + * Extract FROM realm from a realms field + */ +#define RTNL_REALM_FROM(realm) ((realm) >> 16) + +/** + * Extract TO realm from a realms field + */ +#define RTNL_REALM_TO(realm) ((realm) & RTNL_REALM_MASK) + +/** + * Build a realms field + */ +#define RTNL_MAKE_REALM(from, to) \ + ((RTNL_REALM_TO(from) << 16) & RTNL_REALM_TO(to)) + +/** @} */ + + +/* General */ +extern int nl_rtgen_request(struct nl_sock *, int, int, int); + +/* Routing Type Translations */ +extern char * nl_rtntype2str(int, char *, size_t); +extern int nl_str2rtntype(const char *); + +/* Scope Translations */ +extern char * rtnl_scope2str(int, char *, size_t); +extern int rtnl_str2scope(const char *); + +/* Realms Translations */ +extern char * rtnl_realms2str(uint32_t, char *, size_t); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/rule.h b/libnl/include/netlink/route/rule.h new file mode 100644 index 0000000..26f5b52 --- /dev/null +++ b/libnl/include/netlink/route/rule.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2010 Thomas Graf + */ + +#ifndef NETLINK_RULE_H_ +#define NETLINK_RULE_H_ + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtnl_rule; + +/* General */ +extern struct rtnl_rule * rtnl_rule_alloc(void); +extern void rtnl_rule_put(struct rtnl_rule *); + +extern int rtnl_rule_alloc_cache(struct nl_sock *, int, + struct nl_cache **); +extern void rtnl_rule_dump(struct rtnl_rule *, FILE *, struct nl_dump_params *); + +extern int rtnl_rule_build_add_request(struct rtnl_rule *, int, + struct nl_msg **); +extern int rtnl_rule_add(struct nl_sock *, struct rtnl_rule *, int); +extern int rtnl_rule_build_delete_request(struct rtnl_rule *, int, + struct nl_msg **); +extern int rtnl_rule_delete(struct nl_sock *, struct rtnl_rule *, int); + + +/* attribute modification */ +extern void rtnl_rule_set_family(struct rtnl_rule *, int); +extern int rtnl_rule_get_family(struct rtnl_rule *); +extern void rtnl_rule_set_prio(struct rtnl_rule *, uint32_t); +extern uint32_t rtnl_rule_get_prio(struct rtnl_rule *); +extern void rtnl_rule_set_mark(struct rtnl_rule *, uint32_t); +extern uint32_t rtnl_rule_get_mark(struct rtnl_rule *); +extern void rtnl_rule_set_mask(struct rtnl_rule *, uint32_t); +extern uint32_t rtnl_rule_get_mask(struct rtnl_rule *); +extern void rtnl_rule_set_table(struct rtnl_rule *, uint32_t); +extern uint32_t rtnl_rule_get_table(struct rtnl_rule *); +extern void rtnl_rule_set_dsfield(struct rtnl_rule *, uint8_t); +extern uint8_t rtnl_rule_get_dsfield(struct rtnl_rule *); +extern int rtnl_rule_set_src(struct rtnl_rule *, struct nl_addr *); +extern struct nl_addr * rtnl_rule_get_src(struct rtnl_rule *); +extern int rtnl_rule_set_dst(struct rtnl_rule *, struct nl_addr *); +extern struct nl_addr * rtnl_rule_get_dst(struct rtnl_rule *); +extern void rtnl_rule_set_action(struct rtnl_rule *, uint8_t); +extern uint8_t rtnl_rule_get_action(struct rtnl_rule *); +extern int rtnl_rule_set_iif(struct rtnl_rule *, const char *); +extern char * rtnl_rule_get_iif(struct rtnl_rule *); +extern int rtnl_rule_set_oif(struct rtnl_rule *, const char *); +extern char * rtnl_rule_get_oif(struct rtnl_rule *); +extern void rtnl_rule_set_realms(struct rtnl_rule *, uint32_t); +extern uint32_t rtnl_rule_get_realms(struct rtnl_rule *); +extern void rtnl_rule_set_goto(struct rtnl_rule *, uint32_t); +extern uint32_t rtnl_rule_get_goto(struct rtnl_rule *); +extern void rtnl_rule_set_l3mdev(struct rtnl_rule *, int); +extern int rtnl_rule_get_l3mdev(struct rtnl_rule *); +extern int rtnl_rule_set_protocol(struct rtnl_rule *, uint8_t); +extern int rtnl_rule_get_protocol(struct rtnl_rule *, uint8_t *); +extern int rtnl_rule_set_ipproto(struct rtnl_rule *, uint8_t); +extern int rtnl_rule_get_ipproto(struct rtnl_rule *, uint8_t *); +extern int rtnl_rule_set_sport(struct rtnl_rule *, uint16_t start); +extern int rtnl_rule_set_sport_range(struct rtnl_rule *, + uint16_t start, + uint16_t end); +extern int rtnl_rule_get_sport(struct rtnl_rule *, uint16_t *start, + uint16_t *end); +extern int rtnl_rule_set_dport(struct rtnl_rule *, uint16_t start); +extern int rtnl_rule_set_dport_range(struct rtnl_rule *, + uint16_t start, + uint16_t end); +extern int rtnl_rule_get_dport(struct rtnl_rule *, uint16_t *start, + uint16_t *end); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/route/tc-api.h b/libnl/include/netlink/route/tc-api.h new file mode 100644 index 0000000..3f400ba --- /dev/null +++ b/libnl/include/netlink/route/tc-api.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Thomas Graf + */ + +#ifndef NETLINK_DUMMY_TC_API_H_ +#define NETLINK_DUMMY_TC_API_H_ + +#include +#include +#include + +#warning "You are including a deprecated header file, include ." + +#endif diff --git a/libnl/include/netlink/route/tc.h b/libnl/include/netlink/route/tc.h new file mode 100644 index 0000000..ee55555 --- /dev/null +++ b/libnl/include/netlink/route/tc.h @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +#ifndef NETLINK_TC_H_ +#define NETLINK_TC_H_ + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum rtnl_tc_type { + RTNL_TC_TYPE_QDISC, + RTNL_TC_TYPE_CLASS, + RTNL_TC_TYPE_CLS, + RTNL_TC_TYPE_ACT, + __RTNL_TC_TYPE_MAX, +}; + +#define RTNL_TC_TYPE_MAX (__RTNL_TC_TYPE_MAX - 1) + +/** + * Compute tc handle based on major and minor parts + * @ingroup tc + */ +#define TC_HANDLE(maj, min) (TC_H_MAJ((maj) << 16) | TC_H_MIN(min)) + +/** + * Traffic control object + * @ingroup tc + */ +struct rtnl_tc; + +/** + * Macro to cast qdisc/class/classifier to tc object + * @ingroup tc + * + * @code + * rtnl_tc_set_mpu(TC_CAST(qdisc), 40); + * @endcode + */ +#define TC_CAST(ptr) ((struct rtnl_tc *) (ptr)) + +/** + * Traffic control statistical identifier + * @ingroup tc + * + * @code + * uint64_t n = rtnl_tc_get_stat(TC_CAST(class), RTNL_TC_PACKETS); + * @endcode + */ +enum rtnl_tc_stat { + RTNL_TC_PACKETS, /**< Number of packets seen */ + RTNL_TC_BYTES, /**< Total bytes seen */ + RTNL_TC_RATE_BPS, /**< Current bits/s (rate estimator) */ + RTNL_TC_RATE_PPS, /**< Current packet/s (rate estimator) */ + RTNL_TC_QLEN, /**< Current queue length */ + RTNL_TC_BACKLOG, /**< Current backlog length */ + RTNL_TC_DROPS, /**< Total number of packets dropped */ + RTNL_TC_REQUEUES, /**< Total number of requeues */ + RTNL_TC_OVERLIMITS, /**< Total number of overlimits */ + __RTNL_TC_STATS_MAX, +}; + +#define RTNL_TC_STATS_MAX (__RTNL_TC_STATS_MAX - 1) + +extern void rtnl_tc_set_ifindex(struct rtnl_tc *, int); +extern int rtnl_tc_get_ifindex(struct rtnl_tc *); +extern void rtnl_tc_set_link(struct rtnl_tc *, struct rtnl_link *); +extern struct rtnl_link *rtnl_tc_get_link(struct rtnl_tc *); +extern void rtnl_tc_set_mtu(struct rtnl_tc *, uint32_t); +extern uint32_t rtnl_tc_get_mtu(struct rtnl_tc *); +extern void rtnl_tc_set_mpu(struct rtnl_tc *, uint32_t); +extern uint32_t rtnl_tc_get_mpu(struct rtnl_tc *); +extern void rtnl_tc_set_overhead(struct rtnl_tc *, uint32_t); +extern uint32_t rtnl_tc_get_overhead(struct rtnl_tc *); +extern void rtnl_tc_set_linktype(struct rtnl_tc *, uint32_t); +extern uint32_t rtnl_tc_get_linktype(struct rtnl_tc *); +extern void rtnl_tc_set_handle(struct rtnl_tc *, uint32_t); +extern uint32_t rtnl_tc_get_handle(struct rtnl_tc *); +extern void rtnl_tc_set_parent(struct rtnl_tc *, uint32_t); +extern uint32_t rtnl_tc_get_parent(struct rtnl_tc *); +extern int rtnl_tc_set_kind(struct rtnl_tc *, const char *); +extern char * rtnl_tc_get_kind(struct rtnl_tc *); +extern uint64_t rtnl_tc_get_stat(struct rtnl_tc *, enum rtnl_tc_stat); +extern char * rtnl_tc_stat2str(enum rtnl_tc_stat, char *, size_t); +extern int rtnl_tc_str2stat(const char *); + +extern int rtnl_tc_calc_txtime(int, int); +extern int rtnl_tc_calc_bufsize(int, int); +extern int rtnl_tc_calc_cell_log(int); + +extern int rtnl_tc_read_classid_file(void); +extern char * rtnl_tc_handle2str(uint32_t, char *, size_t); +extern int rtnl_tc_str2handle(const char *, uint32_t *); +extern int rtnl_classid_generate(const char *, uint32_t *, + uint32_t); +extern void rtnl_tc_set_chain(struct rtnl_tc *, uint32_t); +extern int rtnl_tc_get_chain(struct rtnl_tc *, uint32_t *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/socket.h b/libnl/include/netlink/socket.h new file mode 100644 index 0000000..2bd98c3 --- /dev/null +++ b/libnl/include/netlink/socket.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + */ + +#ifndef NETLINK_SOCKET_H_ +#define NETLINK_SOCKET_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct nl_sock * nl_socket_alloc(void); +extern struct nl_sock * nl_socket_alloc_cb(struct nl_cb *); +extern void nl_socket_free(struct nl_sock *); + +extern uint32_t nl_socket_get_local_port(const struct nl_sock *); +extern void nl_socket_set_local_port(struct nl_sock *, uint32_t); + +extern int nl_socket_add_memberships(struct nl_sock *, int, ...); +extern int nl_socket_add_membership(struct nl_sock *, int); +extern int nl_socket_drop_memberships(struct nl_sock *, int, ...); +extern int nl_socket_drop_membership(struct nl_sock *, + int); +extern void nl_join_groups(struct nl_sock *, int); + + +extern uint32_t nl_socket_get_peer_port(const struct nl_sock *); +extern void nl_socket_set_peer_port(struct nl_sock *, + uint32_t); +extern uint32_t nl_socket_get_peer_groups(const struct nl_sock *sk); +extern void nl_socket_set_peer_groups(struct nl_sock *sk, uint32_t groups); +extern struct nl_cb * nl_socket_get_cb(const struct nl_sock *); +extern void nl_socket_set_cb(struct nl_sock *, + struct nl_cb *); +extern int nl_socket_modify_cb(struct nl_sock *, enum nl_cb_type, + enum nl_cb_kind, + nl_recvmsg_msg_cb_t, void *); +extern int nl_socket_modify_err_cb(struct nl_sock *, enum nl_cb_kind, + nl_recvmsg_err_cb_t, void *); + +extern int nl_socket_set_buffer_size(struct nl_sock *, int, int); +extern int nl_socket_set_msg_buf_size(struct nl_sock *, size_t); +extern size_t nl_socket_get_msg_buf_size(struct nl_sock *); +extern int nl_socket_set_passcred(struct nl_sock *, int); +extern int nl_socket_recv_pktinfo(struct nl_sock *, int); + +extern void nl_socket_disable_seq_check(struct nl_sock *); +extern unsigned int nl_socket_use_seq(struct nl_sock *); +extern void nl_socket_disable_auto_ack(struct nl_sock *); +extern void nl_socket_enable_auto_ack(struct nl_sock *); + +extern int nl_socket_get_fd(const struct nl_sock *); +extern int nl_socket_set_fd(struct nl_sock *sk, int protocol, int fd); +extern int nl_socket_set_nonblocking(const struct nl_sock *); +extern void nl_socket_enable_msg_peek(struct nl_sock *); +extern void nl_socket_disable_msg_peek(struct nl_sock *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/types.h b/libnl/include/netlink/types.h new file mode 100644 index 0000000..1e3a9a9 --- /dev/null +++ b/libnl/include/netlink/types.h @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +#ifndef __NETLINK_TYPES_H_ +#define __NETLINK_TYPES_H_ + +#include + +/** + * @ingroup utils + * Enumeration of dumping variations (dp_type) + */ +enum nl_dump_type { + NL_DUMP_LINE, /**< Dump object briefly on one line */ + NL_DUMP_DETAILS, /**< Dump all attributes but no statistics */ + NL_DUMP_STATS, /**< Dump all attributes including statistics */ + __NL_DUMP_MAX, +}; +#define NL_DUMP_MAX (__NL_DUMP_MAX - 1) + +/** + * @ingroup utils + * Dumping parameters + */ +struct nl_dump_params +{ + /** + * Specifies the type of dump that is requested. + */ + enum nl_dump_type dp_type; + + /** + * Specifies the number of whitespaces to be put in front + * of every new line (indentation). + */ + int dp_prefix; + + /** + * Causes the cache index to be printed for each element. + */ + int dp_print_index; + + /** + * Causes each element to be prefixed with the message type. + */ + int dp_dump_msgtype; + + /** + * A callback invoked for output + * + * Passed arguments are: + * - dumping parameters + * - string to append to the output + */ + void (*dp_cb)(struct nl_dump_params *, char *); + + /** + * A callback invoked for every new line, can be used to + * customize the indentation. + * + * Passed arguments are: + * - dumping parameters + * - line number starting from 0 + */ + void (*dp_nl_cb)(struct nl_dump_params *, int); + + /** + * User data pointer, can be used to pass data to callbacks. + */ + void *dp_data; + + /** + * File descriptor the dumping output should go to + */ + FILE * dp_fd; + + /** + * Alternatively the output may be redirected into a buffer + */ + char * dp_buf; + + /** + * Length of the buffer dp_buf + */ + size_t dp_buflen; + + /** + * PRIVATE + * Set if a dump was performed prior to the actual dump handler. + */ + int dp_pre_dump; + + /** + * PRIVATE + * Owned by the current caller + */ + int dp_ivar; + + unsigned int dp_line; +}; + +#endif diff --git a/libnl/include/netlink/utils.h b/libnl/include/netlink/utils.h new file mode 100644 index 0000000..071929e --- /dev/null +++ b/libnl/include/netlink/utils.h @@ -0,0 +1,320 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +#ifndef NETLINK_UTILS_H_ +#define NETLINK_UTILS_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name Probability Constants + * @{ + */ + +/** + * Lower probability limit + * @ingroup utils + */ +#define NL_PROB_MIN 0x0 + +/** + * Upper probability limit nl_dump_type + * @ingroup utils + */ +#define NL_PROB_MAX 0xffffffff + +/** @} */ + +enum { + NL_BYTE_RATE, + NL_BIT_RATE, +}; + +/* unit pretty-printing */ +extern double nl_cancel_down_bytes(unsigned long long, char **); +extern double nl_cancel_down_bits(unsigned long long, char **); +extern int nl_rate2str(unsigned long long, int, char *, size_t); +extern double nl_cancel_down_us(uint32_t, char **); + +/* generic unit translations */ +extern long nl_size2int(const char *); +extern char * nl_size2str(const size_t, char *, const size_t); +extern long nl_prob2int(const char *); + +/* time translations */ +extern int nl_get_user_hz(void); +extern int nl_get_psched_hz(void); +extern uint32_t nl_us2ticks(uint32_t); +extern uint32_t nl_ticks2us(uint32_t); +extern int nl_str2msec(const char *, uint64_t *); +extern char * nl_msec2str(uint64_t, char *, size_t); + +/* link layer protocol translations */ +extern char * nl_llproto2str(int, char *, size_t); +extern int nl_str2llproto(const char *); + +/* ethernet protocol translations */ +extern char * nl_ether_proto2str(int, char *, size_t); +extern int nl_str2ether_proto(const char *); + +/* IP protocol translations */ +extern char * nl_ip_proto2str(int, char *, size_t); +extern int nl_str2ip_proto(const char *); + +/* Dumping helpers */ +extern void nl_new_line(struct nl_dump_params *); +extern void nl_dump(struct nl_dump_params *, const char *, ...); +extern void nl_dump_line(struct nl_dump_params *, const char *, ...); + +enum { + NL_CAPABILITY_NONE, + + /** + * rtnl_route_build_msg() no longer guesses the route scope + * if explicitly set to RT_SCOPE_NOWHERE. + * @ingroup utils + */ + NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE = 1, +#define NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE + + /** + * rtnl_link_veth_get_peer() now returns a reference that is owned by the + * caller and must be released by the caller with rtnl_link_put(). + */ + NL_CAPABILITY_ROUTE_LINK_VETH_GET_PEER_OWN_REFERENCE = 2, +#define NL_CAPABILITY_ROUTE_LINK_VETH_GET_PEER_OWN_REFERENCE NL_CAPABILITY_ROUTE_LINK_VETH_GET_PEER_OWN_REFERENCE + + /** + * rtnl_u32_add_action() and rtnl_basic_add_action() now grab a reference to act + * caller are free to release its own + */ + NL_CAPABILITY_ROUTE_LINK_CLS_ADD_ACT_OWN_REFERENCE = 3, +#define NL_CAPABILITY_ROUTE_LINK_CLS_ADD_ACT_OWN_REFERENCE NL_CAPABILITY_ROUTE_LINK_CLS_ADD_ACT_OWN_REFERENCE + + /** + * Indicate that the local port is unspecified until the user accesses + * it (via nl_socket_get_local_port()) or until nl_connect(). More importantly, + * if the port is left unspecified, nl_connect() will retry generating another + * port when bind() fails with ADDRINUSE. + */ + NL_CAPABILITY_NL_CONNECT_RETRY_GENERATE_PORT_ON_ADDRINUSE = 4, +#define NL_CAPABILITY_NL_CONNECT_RETRY_GENERATE_PORT_ON_ADDRINUSE NL_CAPABILITY_NL_CONNECT_RETRY_GENERATE_PORT_ON_ADDRINUSE + + /** + * Indicate that rtnl_link_get_kernel() fails with -NLE_OPNOTSUPP in case + * of older kernals not supporting lookup by ifname. This changes behavior + * from returning -NLE_INVAL to return -NLE_OPNOTSUPP. + */ + NL_CAPABILITY_ROUTE_LINK_GET_KERNEL_FAIL_OPNOTSUPP = 5, +#define NL_CAPABILITY_ROUTE_LINK_GET_KERNEL_FAIL_OPNOTSUPP NL_CAPABILITY_ROUTE_LINK_GET_KERNEL_FAIL_OPNOTSUPP + + /** + * Also consider the a_cacheinfo field (ADDR_ATTR_CACHEINFO) that contains the + * address timestamps and expiry when comparing struct rtnl_addr objects with + * nl_object_diff(). + */ + NL_CAPABILITY_ROUTE_ADDR_COMPARE_CACHEINFO = 6, +#define NL_CAPABILITY_ROUTE_ADDR_COMPARE_CACHEINFO NL_CAPABILITY_ROUTE_ADDR_COMPARE_CACHEINFO + + /** + * The library version is libnl3 3.2.26 or newer. This capability should never be backported. + */ + NL_CAPABILITY_VERSION_3_2_26 = 7, +#define NL_CAPABILITY_VERSION_3_2_26 NL_CAPABILITY_VERSION_3_2_26 + + /** + * nl_recv() fails with NLE_MSG_TRUNC if a message got truncated + * with NL_MSG_PEEK disabled. Previously, the failed message was wrongly + * discarded and the next message received. + */ + NL_CAPABILITY_NL_RECV_FAIL_TRUNC_NO_PEEK = 8, +#define NL_CAPABILITY_NL_RECV_FAIL_TRUNC_NO_PEEK NL_CAPABILITY_NL_RECV_FAIL_TRUNC_NO_PEEK + + /** + * rtnl_link_build_change_request() and rtnl_link_change() would set ifi.ifi_flags but leave + * ifi.ifi_change at zero. This was later fixed to set ifi.ifi_change to the flags that are actually + * set in changes. + */ + NL_CAPABILITY_LINK_BUILD_CHANGE_REQUEST_SET_CHANGE = 9, +#define NL_CAPABILITY_LINK_BUILD_CHANGE_REQUEST_SET_CHANGE NL_CAPABILITY_LINK_BUILD_CHANGE_REQUEST_SET_CHANGE + + /** + * Between 3.2.14 (64fcb47a36ec12d7e7f00605f6a8952ce985dd08) and 3.2.22 (8571f58f23763d8db7365d02c9b27832ad3d7005), + * rtnl_neigh_get() behaved differently and only returned objects with family AF_UNSPEC. + * This capability indicates, that the function was fixed. The absense of the capability, + * doesn't indicate however which behavior the function will have. So beware. */ + NL_CAPABILITY_RTNL_NEIGH_GET_FILTER_AF_UNSPEC_FIX = 10, +#define NL_CAPABILITY_RTNL_NEIGH_GET_FILTER_AF_UNSPEC_FIX NL_CAPABILITY_RTNL_NEIGH_GET_FILTER_AF_UNSPEC_FIX + + /** + * The library version is libnl3 3.2.27 or newer. This capability should never be backported. + */ + NL_CAPABILITY_VERSION_3_2_27 = 11, +#define NL_CAPABILITY_VERSION_3_2_27 NL_CAPABILITY_VERSION_3_2_27 + + /** + * Properly serialize vlan protocol IFLA_VLAN_PROTOCOL. + */ + NL_CAPABILITY_RTNL_LINK_VLAN_PROTOCOL_SERIALZE = 12, +#define NL_CAPABILITY_RTNL_LINK_VLAN_PROTOCOL_SERIALZE NL_CAPABILITY_RTNL_LINK_VLAN_PROTOCOL_SERIALZE + + /** + * Properly read gre REMOTE port. + */ + NL_CAPABILITY_RTNL_LINK_PARSE_GRE_REMOTE = 13, +#define NL_CAPABILITY_RTNL_LINK_PARSE_GRE_REMOTE NL_CAPABILITY_RTNL_LINK_PARSE_GRE_REMOTE + + /** + * Don't skip over vlan ingress-map entries with "to" field zero when serializing + * a netlink message. Previously such entires would be ignored which inhibits the + * user from clearing ingress map entries. + */ + NL_CAPABILITY_RTNL_LINK_VLAN_INGRESS_MAP_CLEAR = 14, +#define NL_CAPABILITY_RTNL_LINK_VLAN_INGRESS_MAP_CLEAR NL_CAPABILITY_RTNL_LINK_VLAN_INGRESS_MAP_CLEAR + + /** + * Consider vxlan link info for nl_object_diff(). + */ + NL_CAPABILITY_RTNL_LINK_VXLAN_IO_COMPARE = 15, +#define NL_CAPABILITY_RTNL_LINK_VXLAN_IO_COMPARE NL_CAPABILITY_RTNL_LINK_VXLAN_IO_COMPARE + + /** + * Support 64 bit attributes for nl_object_diff(). + */ + NL_CAPABILITY_NL_OBJECT_DIFF64 = 16, +#define NL_CAPABILITY_NL_OBJECT_DIFF64 NL_CAPABILITY_NL_OBJECT_DIFF64 + + /** + * Support omitting @key argument to xfrmnl_sa_get_*_params() to check + * for required buffer size for key. + */ + NL_CAPABILITY_XFRM_SA_KEY_SIZE = 17, +#define NL_CAPABILITY_XFRM_SA_KEY_SIZE NL_CAPABILITY_XFRM_SA_KEY_SIZE + + /** + * Properly handle nl_object_identity() for AF_INET and AF_INET6 addresses + * and properly handle the peer/IFA_ADDRESS for IPv4 addresses. + */ + NL_CAPABILITY_RTNL_ADDR_PEER_FIX = 18, +#define NL_CAPABILITY_RTNL_ADDR_PEER_FIX NL_CAPABILITY_RTNL_ADDR_PEER_FIX + + /** + * The library version is libnl3 3.2.28 or newer. This capability should never be backported. + */ + NL_CAPABILITY_VERSION_3_2_28 = 19, +#define NL_CAPABILITY_VERSION_3_2_28 NL_CAPABILITY_VERSION_3_2_28 + + /** + * After NL_CAPABILITY_RTNL_ADDR_PEER_FIX, a follow up regression to lookup + * IPv4 addresses in the cache was fixed (PR#105). + */ + NL_CAPABILITY_RTNL_ADDR_PEER_ID_FIX = 20, +#define NL_CAPABILITY_RTNL_ADDR_PEER_ID_FIX NL_CAPABILITY_RTNL_ADDR_PEER_ID_FIX + + /** + * nl_addr_fill_sockaddr() properly checks that the provided address to + * avoid read-out-of-bounds for invalid addresses. + */ + NL_CAPABILITY_NL_ADDR_FILL_SOCKADDR = 21, +#define NL_CAPABILITY_NL_ADDR_FILL_SOCKADDR NL_CAPABILITY_NL_ADDR_FILL_SOCKADDR + + /** + * Support omitting @ctx_str argument to xfrmnl_sa_get_sec_ctx() to check + * for required buffer size for context string. + */ + NL_CAPABILITY_XFRM_SEC_CTX_LEN = 22, +#define NL_CAPABILITY_XFRM_SEC_CTX_LEN NL_CAPABILITY_XFRM_SEC_CTX_LEN + + /** + * rtnl_link_build_add_request() would set ifi.ifi_flags but leave ifi.ifi_change at zero. + * This was later fixed to set ifi.ifi_change to the flags that are actually + * set + */ + NL_CAPABILITY_LINK_BUILD_ADD_REQUEST_SET_CHANGE = 23, +#define NL_CAPABILITY_LINK_BUILD_ADD_REQUEST_SET_CHANGE NL_CAPABILITY_LINK_BUILD_ADD_REQUEST_SET_CHANGE + + /* Older versions of libnl3 would not use MSG_PEEK for nl_recvmsgs() unless calling + * nl_socket_enable_msg_peek(). Instead, the user had to specify the buffer size via + * nl_socket_set_msg_buf_size(), which in turn would default to 4*getpagesize(). + * + * The default value might not be large enough, so users who were not aware of the + * problem easily ended up using a too small receive buffer. Usually, one wants to + * avoid MSG_PEEK for recvmsg() because it requires an additional syscall. + * + * Now, as indicated by this capability, nl_recvmsgs() would use MSG_PEEK by default. The + * user still can explicitly disable MSG_PEEK by calling nl_socket_disable_msg_peek() or + * by setting the nl_socket_set_msg_buf_size() to a non-zero value. + */ + NL_CAPABILITY_NL_RECVMSGS_PEEK_BY_DEFAULT = 24, +#define NL_CAPABILITY_NL_RECVMSGS_PEEK_BY_DEFAULT NL_CAPABILITY_NL_RECVMSGS_PEEK_BY_DEFAULT + + /** + * The library version is libnl3 3.2.29 or newer. This capability should never be backported. + */ + NL_CAPABILITY_VERSION_3_2_29 = 25, +#define NL_CAPABILITY_VERSION_3_2_29 NL_CAPABILITY_VERSION_3_2_29 + + /** + * Support omitting @ctx_str argument to xfrmnl_sp_get_sec_ctx() to check + * for required buffer size for context string. + */ + NL_CAPABILITY_XFRM_SP_SEC_CTX_LEN = 26, +#define NL_CAPABILITY_XFRM_SP_SEC_CTX_LEN NL_CAPABILITY_XFRM_SP_SEC_CTX_LEN + + /** + * The library version is libnl3 3.3.0 or newer. This capability should never be backported. + */ + NL_CAPABILITY_VERSION_3_3_0 = 27, +#define NL_CAPABILITY_VERSION_3_3_0 NL_CAPABILITY_VERSION_3_3_0 + + /** + * The library version is libnl3 3.4.0 or newer. This capability should never be backported. + */ + NL_CAPABILITY_VERSION_3_4_0 = 28, +#define NL_CAPABILITY_VERSION_3_4_0 NL_CAPABILITY_VERSION_3_4_0 + + /** + * Fixed memory corruption in rtnl_link_vlan_set_egress_map(). Previously, if you tried + * to add more then 4 mappings, a buffer overflow occured. Also fixed nl_object_clone() + * for VLAN links. + */ + NL_CAPABILITY_ROUTE_FIX_VLAN_SET_EGRESS_MAP = 29, +#define NL_CAPABILITY_ROUTE_FIX_VLAN_SET_EGRESS_MAP NL_CAPABILITY_ROUTE_FIX_VLAN_SET_EGRESS_MAP + + /** + * The library version is libnl3 3.5.0 or newer. This capability should never be backported. + */ + NL_CAPABILITY_VERSION_3_5_0 = 30, +#define NL_CAPABILITY_VERSION_3_5_0 NL_CAPABILITY_VERSION_3_5_0 + + /** + * nl_object_identical() can consider objects identical, if they both lack the same + * set of ID attributes. + */ + NL_CAPABILITY_NL_OBJECT_IDENTICAL_PARTIAL = 31, +#define NL_CAPABILITY_NL_OBJECT_IDENTICAL_PARTIAL NL_CAPABILITY_NL_OBJECT_IDENTICAL_PARTIAL + + __NL_CAPABILITY_MAX, + NL_CAPABILITY_MAX = (__NL_CAPABILITY_MAX - 1), +#define NL_CAPABILITY_MAX NL_CAPABILITY_MAX + + /** + * The range 0x7000 to 0x7FFF is reserved for private capabilities. Upstream libnl3 will + * not register capabilities in this range. However, instead of adding private capabilities, + * better register their number with upstream libnl3. */ +#define NL_CAPABILITY_IS_USER_RESERVED(cap) ( ((cap) & ~0x0FFF) == 0x7000 ) +}; +int nl_has_capability (int capability); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libnl/include/netlink/version.h b/libnl/include/netlink/version.h new file mode 100644 index 0000000..f3dab83 --- /dev/null +++ b/libnl/include/netlink/version.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008-2011 Thomas Graf + */ + +#ifndef NETLINK_VERSION_H_ +#define NETLINK_VERSION_H_ + +/* Compile Time Versioning Information */ + +#define LIBNL_STRING "3.5.0" +#define LIBNL_VERSION "3.5.0" + +#define LIBNL_VER_MAJ 3 +#define LIBNL_VER_MIN 5 +#define LIBNL_VER_MIC 0 +#define LIBNL_VER(maj,min) ((maj) << 8 | (min)) +#define LIBNL_VER_NUM LIBNL_VER(LIBNL_VER_MAJ, LIBNL_VER_MIN) + +#define LIBNL_CURRENT 226 +#define LIBNL_REVISION 0 +#define LIBNL_AGE 26 + +/* Run-time version information */ + +extern const int nl_ver_num; +extern const int nl_ver_maj; +extern const int nl_ver_min; +extern const int nl_ver_mic; + +#endif diff --git a/libnl/lib/addr.c b/libnl/lib/addr.c new file mode 100644 index 0000000..2543e52 --- /dev/null +++ b/libnl/lib/addr.c @@ -0,0 +1,1134 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + */ + +/** + * @ingroup core_types + * @defgroup addr Network Address + * + * Abstract data type representing any kind of network address + * + * Related sections in the development guide: + * - @core_doc{_abstract_address, Network Addresses} + * + * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + +/* All this DECnet stuff is stolen from iproute2, thanks to whoever wrote + * this, probably Alexey. */ +static inline uint16_t dn_ntohs(uint16_t addr) +{ + union { + uint8_t byte[2]; + uint16_t word; + } u = { + .word = addr, + }; + + return ((uint16_t) u.byte[0]) | (((uint16_t) u.byte[1]) << 8); +} + +static inline int do_digit(char *str, uint16_t *addr, uint16_t scale, + size_t *pos, size_t len, int *started) +{ + uint16_t tmp = *addr / scale; + + if (*pos == len) + return 1; + + if (((tmp) > 0) || *started || (scale == 1)) { + *str = tmp + '0'; + *started = 1; + (*pos)++; + *addr -= (tmp * scale); + } + + return 0; +} + +static const char *dnet_ntop(const char *addrbuf, size_t addrlen, char *str, + size_t len) +{ + uint16_t addr = dn_ntohs(*(uint16_t *)addrbuf); + uint16_t area = addr >> 10; + size_t pos = 0; + int started = 0; + + if (addrlen != 2) + return NULL; + + addr &= 0x03ff; + + if (len == 0) + return str; + + if (do_digit(str + pos, &area, 10, &pos, len, &started)) + return str; + + if (do_digit(str + pos, &area, 1, &pos, len, &started)) + return str; + + if (pos == len) + return str; + + *(str + pos) = '.'; + pos++; + started = 0; + + if (do_digit(str + pos, &addr, 1000, &pos, len, &started)) + return str; + + if (do_digit(str + pos, &addr, 100, &pos, len, &started)) + return str; + + if (do_digit(str + pos, &addr, 10, &pos, len, &started)) + return str; + + if (do_digit(str + pos, &addr, 1, &pos, len, &started)) + return str; + + if (pos == len) + return str; + + *(str + pos) = 0; + + return str; +} + +static int dnet_num(const char *src, uint16_t * dst) +{ + int rv = 0; + int tmp; + *dst = 0; + + while ((tmp = *src++) != 0) { + tmp -= '0'; + if ((tmp < 0) || (tmp > 9)) + return rv; + + rv++; + (*dst) *= 10; + (*dst) += tmp; + } + + return rv; +} + +static inline int dnet_pton(const char *src, char *addrbuf) +{ + uint16_t area = 0; + uint16_t node = 0; + int pos; + + pos = dnet_num(src, &area); + if ((pos == 0) || (area > 63) || + ((*(src + pos) != '.') && (*(src + pos) != ','))) + return -NLE_INVAL; + + pos = dnet_num(src + pos + 1, &node); + if ((pos == 0) || (node > 1023)) + return -NLE_INVAL; + + *(uint16_t *)addrbuf = dn_ntohs((area << 10) | node); + + return 1; +} + +static void addr_destroy(struct nl_addr *addr) +{ + if (!addr) + return; + + if (addr->a_refcnt != 1) + BUG(); + + free(addr); +} + +/** + * @name Creating Abstract Network Addresses + * @{ + */ + +/** + * Allocate empty abstract address + * @arg maxsize Upper limit of the binary address to be stored + * + * The new address object will be empty with a prefix length of 0 and will + * be capable of holding binary addresses up to the specified limit. + * + * @see nl_addr_build() + * @see nl_addr_parse() + * @see nl_addr_put() + * + * @return Allocated address object or NULL upon failure. + */ +struct nl_addr *nl_addr_alloc(size_t maxsize) +{ + struct nl_addr *addr; + + addr = calloc(1, sizeof(*addr) + maxsize); + if (!addr) + return NULL; + + addr->a_refcnt = 1; + addr->a_maxsize = maxsize; + + return addr; +} + +/** + * Allocate abstract address based on a binary address. + * @arg family Address family + * @arg buf Binary address + * @arg size Length of binary address + * + * This function will allocate an abstract address capable of holding the + * binary address specified. The prefix length will be set to the full + * length of the binary address provided. + * + * @see nl_addr_alloc() + * @see nl_addr_alloc_attr() + * @see nl_addr_parse() + * @see nl_addr_put() + * + * @return Allocated address object or NULL upon failure. + */ +struct nl_addr *nl_addr_build(int family, const void *buf, size_t size) +{ + struct nl_addr *addr; + + addr = nl_addr_alloc(size); + if (!addr) + return NULL; + + addr->a_family = family; + addr->a_len = size; + switch(family) { + case AF_MPLS: + addr->a_prefixlen = 20; /* MPLS address is a 20-bit label */ + break; + default: + addr->a_prefixlen = size*8; + } + + if (size) + memcpy(addr->a_addr, buf, size); + + return addr; +} + +/** + * Allocate abstract address based on Netlink attribute. + * @arg nla Netlink attribute + * @arg family Address family. + * + * Allocates an abstract address based on the specified Netlink attribute + * by interpreting the payload of the Netlink attribute as the binary + * address. + * + * This function is identical to: + * @code + * nl_addr_build(family, nla_data(nla), nla_len(nla)); + * @endcode + * + * @see nl_addr_alloc() + * @see nl_addr_build() + * @see nl_addr_parse() + * @see nl_addr_put() + * + * @return Allocated address object or NULL upon failure. + */ +struct nl_addr *nl_addr_alloc_attr(const struct nlattr *nla, int family) +{ + return nl_addr_build(family, nla_data(nla), nla_len(nla)); +} + +/** + * Allocate abstract address based on character string + * @arg addrstr Address represented as character string. + * @arg hint Address family hint or AF_UNSPEC. + * @arg result Pointer to store resulting address. + * + * Regognizes the following address formats: + * @code + * Format Len Family + * ---------------------------------------------------------------- + * IPv6 address format 16 AF_INET6 + * ddd.ddd.ddd.ddd 4 AF_INET + * HH:HH:HH:HH:HH:HH 6 AF_LLC + * AA{.|,}NNNN 2 AF_DECnet + * HH:HH:HH:... variable AF_UNSPEC + * @endcode + * + * Special values: + * - none: All bits and length set to 0. + * - {default|all|any}: All bits set to 0, length based on hint or + * AF_INET if no hint is given. + * + * The prefix length may be appened at the end prefixed with a + * slash, e.g. 10.0.0.0/8. + * + * @see nl_addr_alloc() + * @see nl_addr_build() + * @see nl_addr_put() + * + * @return 0 on success or a negative error code. + */ +int nl_addr_parse(const char *addrstr, int hint, struct nl_addr **result) +{ + int err, copy = 0, len = 0, family = AF_UNSPEC, plen = 0; + char *str, *prefix = NULL, buf[256]; + struct nl_addr *addr = NULL; /* gcc ain't that smart */ + + str = strdup(addrstr); + if (!str) { + err = -NLE_NOMEM; + goto errout; + } + + if (hint != AF_MPLS) { + prefix = strchr(str, '/'); + if (prefix) + *prefix = '\0'; + } + + if (!strcasecmp(str, "none")) { + family = hint; + goto prefix; + } + + if (!strcasecmp(str, "default") || + !strcasecmp(str, "all") || + !strcasecmp(str, "any")) { + + len = 0; + + switch (hint) { + case AF_INET: + case AF_UNSPEC: + /* Kind of a hack, we assume that if there is + * no hint given the user wants to have a IPv4 + * address given back. */ + family = AF_INET; + goto prefix; + + case AF_INET6: + family = AF_INET6; + goto prefix; + + case AF_LLC: + family = AF_LLC; + goto prefix; + + default: + err = -NLE_AF_NOSUPPORT; + goto errout; + } + } + + copy = 1; + + if (hint == AF_INET || hint == AF_UNSPEC) { + if (inet_pton(AF_INET, str, buf) > 0) { + family = AF_INET; + len = 4; + goto prefix; + } + if (hint == AF_INET) { + err = -NLE_NOADDR; + goto errout; + } + } + + if (hint == AF_INET6 || hint == AF_UNSPEC) { + if (inet_pton(AF_INET6, str, buf) > 0) { + family = AF_INET6; + len = 16; + goto prefix; + } + if (hint == AF_INET6) { + err = -NLE_NOADDR; + goto errout; + } + } + + if ((hint == AF_LLC || hint == AF_UNSPEC) && strchr(str, ':')) { + unsigned int a, b, c, d, e, f; + + if (sscanf(str, "%02x:%02x:%02x:%02x:%02x:%02x", + &a, &b, &c, &d, &e, &f) == 6) { + family = AF_LLC; + len = 6; + buf[0] = (unsigned char) a; + buf[1] = (unsigned char) b; + buf[2] = (unsigned char) c; + buf[3] = (unsigned char) d; + buf[4] = (unsigned char) e; + buf[5] = (unsigned char) f; + goto prefix; + } + + if (hint == AF_LLC) { + err = -NLE_NOADDR; + goto errout; + } + } + + if ((hint == AF_DECnet || hint == AF_UNSPEC) && + (strchr(str, '.') || strchr(str, ','))) { + if (dnet_pton(str, buf) > 0) { + family = AF_DECnet; + len = 2; + goto prefix; + } + if (hint == AF_DECnet) { + err = -NLE_NOADDR; + goto errout; + } + } + + if (hint == AF_MPLS) { + len = mpls_pton(AF_MPLS, str, buf, sizeof(buf)); + if (len <= 0) { + err = -NLE_INVAL; + goto errout; + } + family = AF_MPLS; + plen = 20; + goto prefix; + } + + if (hint == AF_UNSPEC && strchr(str, ':')) { + size_t i = 0; + char *s = str, *p; + for (;;) { + long l = strtol(s, &p, 16); + + if (s == p || l > 0xff || i >= sizeof(buf)) { + err = -NLE_INVAL; + goto errout; + } + + buf[i++] = (unsigned char) l; + if (*p == '\0') + break; + s = ++p; + } + + len = i; + family = AF_UNSPEC; + goto prefix; + } + + err = -NLE_NOADDR; + goto errout; + +prefix: + addr = nl_addr_alloc(len); + if (!addr) { + err = -NLE_NOMEM; + goto errout; + } + + nl_addr_set_family(addr, family); + + if (copy) + nl_addr_set_binary_addr(addr, buf, len); + + if (prefix) { + char *p; + long pl = strtol(++prefix, &p, 0); + if (p == prefix) { + addr_destroy(addr); + err = -NLE_INVAL; + goto errout; + } + nl_addr_set_prefixlen(addr, pl); + } else { + if (!plen) + plen = len * 8; + nl_addr_set_prefixlen(addr, plen); + } + *result = addr; + err = 0; +errout: + free(str); + + return err; +} + +/** + * Clone existing abstract address object + * @arg addr Abstract address object + * + * Allocates new abstract address representing an identical clone of an + * existing address. + * + * @see nl_addr_alloc() + * @see nl_addr_put() + * + * @return Allocated abstract address or NULL upon failure. + */ +struct nl_addr *nl_addr_clone(const struct nl_addr *addr) +{ + struct nl_addr *new; + + new = nl_addr_build(addr->a_family, addr->a_addr, addr->a_len); + if (new) + new->a_prefixlen = addr->a_prefixlen; + + return new; +} + +/** @} */ + +/** + * @name Managing Usage References + * @{ + */ + +/** + * Increase the reference counter of an abstract address + * @arg addr Abstract address + * + * Increases the reference counter of the address and thus prevents the + * release of the memory resources until the reference is given back + * using the function nl_addr_put(). + * + * @see nl_addr_put() + * + * @return Pointer to the existing abstract address + */ +struct nl_addr *nl_addr_get(struct nl_addr *addr) +{ + addr->a_refcnt++; + + return addr; +} + +/** + * Decrease the reference counter of an abstract address + * @arg addr Abstract addr + * + * @note The resources of the abstract address will be freed after the + * last reference to the address has been returned. + * + * @see nl_addr_get() + */ +void nl_addr_put(struct nl_addr *addr) +{ + if (!addr) + return; + + if (addr->a_refcnt == 1) + addr_destroy(addr); + else + addr->a_refcnt--; +} + +/** + * Check whether an abstract address is shared. + * @arg addr Abstract address object. + * + * @return Non-zero if the abstract address is shared, otherwise 0. + */ +int nl_addr_shared(const struct nl_addr *addr) +{ + return addr->a_refcnt > 1; +} + +/** @} */ + +/** + * @name Miscellaneous + * @{ + */ + +/** + * Compare abstract addresses + * @arg a An abstract address + * @arg b Another abstract address + * + * Verifies whether the address family, address length, prefix length, and + * binary addresses of two abstract addresses matches. + * + * @note This function will *not* respect the prefix length in the sense + * that only the actual prefix will be compared. Please refer to the + * nl_addr_cmp_prefix() function if you require this functionality. + * + * @see nl_addr_cmp_prefix() + * + * @return Integer less than, equal to or greather than zero if the two + * addresses match. + */ +int nl_addr_cmp(const struct nl_addr *a, const struct nl_addr *b) +{ + int d; + + if (a == b) + return 0; + if (!a) + return -1; + if (!b) + return 1; + + d = a->a_family - b->a_family; + if (d == 0) { + d = a->a_len - b->a_len; + + if (a->a_len && d == 0) { + d = memcmp(a->a_addr, b->a_addr, a->a_len); + + if (d == 0) + return (a->a_prefixlen - b->a_prefixlen); + } + } + + return d; +} + +/** + * Compare the prefix of two abstract addresses + * @arg a An abstract address + * @arg b Another abstract address + * + * Verifies whether the address family and the binary address covered by + * the smaller prefix length of the two abstract addresses matches. + * + * @see nl_addr_cmp() + * + * @return Integer less than, equal to or greather than zero if the two + * addresses match. + */ +int nl_addr_cmp_prefix(const struct nl_addr *a, const struct nl_addr *b) +{ + int d = a->a_family - b->a_family; + + if (d == 0) { + int len = min(a->a_prefixlen, b->a_prefixlen); + int bytes = len / 8; + + d = memcmp(a->a_addr, b->a_addr, bytes); + if (d == 0 && (len % 8) != 0) { + int mask = (0xFF00 >> (len % 8)) & 0xFF; + + d = (a->a_addr[bytes] & mask) - + (b->a_addr[bytes] & mask); + } + } + + return d; +} + +/** + * Returns true if the address consists of all zeros + * @arg addr Abstract address + * + * @return 1 if the binary address consists of all zeros, 0 otherwise. + */ +int nl_addr_iszero(const struct nl_addr *addr) +{ + unsigned int i; + + for (i = 0; i < addr->a_len; i++) + if (addr->a_addr[i]) + return 0; + + return 1; +} + +/** + * Check if address string is parseable for a specific address family + * @arg addr Address represented as character string. + * @arg family Desired address family. + * + * @return 1 if the address is parseable assuming the specified address family, + * otherwise 0 is returned. + */ +int nl_addr_valid(const char *addr, int family) +{ + int ret; + char buf[256]; /* MPLS has N-labels at 4-bytes / label */ + + switch (family) { + case AF_INET: + case AF_INET6: + ret = inet_pton(family, addr, buf); + if (ret <= 0) + return 0; + break; + + case AF_MPLS: + ret = mpls_pton(family, addr, buf, sizeof(buf)); + if (ret <= 0) + return 0; + break; + + case AF_DECnet: + ret = dnet_pton(addr, buf); + if (ret <= 0) + return 0; + break; + + case AF_LLC: + if (sscanf(addr, "%*02x:%*02x:%*02x:%*02x:%*02x:%*02x") != 6) + return 0; + break; + } + + return 1; +} + +/** + * Guess address family of abstract address based on address size + * @arg addr Abstract address object. + * + * @return Numeric address family or AF_UNSPEC + */ +int nl_addr_guess_family(const struct nl_addr *addr) +{ + switch (addr->a_len) { + case 4: + return AF_INET; + case 6: + return AF_LLC; + case 16: + return AF_INET6; + default: + return AF_UNSPEC; + } +} + +/** + * Fill out sockaddr structure with values from abstract address object. + * @arg addr Abstract address object. + * @arg sa Destination sockaddr structure buffer. + * @arg salen Length of sockaddr structure buffer. + * + * Fills out the specified sockaddr structure with the data found in the + * specified abstract address. The salen argument needs to be set to the + * size of sa but will be modified to the actual size used during before + * the function exits. + * + * @return 0 on success or a negative error code + */ +int nl_addr_fill_sockaddr(const struct nl_addr *addr, struct sockaddr *sa, + socklen_t *salen) +{ + switch (addr->a_family) { + case AF_INET: { + struct sockaddr_in *sai = (struct sockaddr_in *) sa; + + if (*salen < sizeof(*sai)) + return -NLE_INVAL; + + if (addr->a_len == 4) + memcpy(&sai->sin_addr, addr->a_addr, 4); + else if (addr->a_len != 0) + return -NLE_INVAL; + else + memset(&sai->sin_addr, 0, 4); + + sai->sin_family = addr->a_family; + *salen = sizeof(*sai); + } + break; + + case AF_INET6: { + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) sa; + + if (*salen < sizeof(*sa6)) + return -NLE_INVAL; + + if (addr->a_len == 16) + memcpy(&sa6->sin6_addr, addr->a_addr, 16); + else if (addr->a_len != 0) + return -NLE_INVAL; + else + memset(&sa6->sin6_addr, 0, 16); + + sa6->sin6_family = addr->a_family; + *salen = sizeof(*sa6); + } + break; + + default: + return -NLE_INVAL; + } + + return 0; +} + + +/** @} */ + +/** + * @name Getting Information About Addresses + * @{ + */ + +/** + * Call getaddrinfo() for an abstract address object. + * @arg addr Abstract address object. + * @arg result Pointer to store resulting address list. + * + * Calls getaddrinfo() for the specified abstract address in AI_NUMERICHOST + * mode. + * + * @note The caller is responsible for freeing the linked list using the + * interface provided by getaddrinfo(3). + * + * @return 0 on success or a negative error code. + */ +int nl_addr_info(const struct nl_addr *addr, struct addrinfo **result) +{ + int err; + char buf[INET6_ADDRSTRLEN+5]; + struct addrinfo hint = { + .ai_flags = AI_NUMERICHOST, + .ai_family = addr->a_family, + }; + + nl_addr2str(addr, buf, sizeof(buf)); + + err = getaddrinfo(buf, NULL, &hint, result); + if (err != 0) { + switch (err) { + case EAI_ADDRFAMILY: return -NLE_AF_NOSUPPORT; + case EAI_AGAIN: return -NLE_AGAIN; + case EAI_BADFLAGS: return -NLE_INVAL; + case EAI_FAIL: return -NLE_NOADDR; + case EAI_FAMILY: return -NLE_AF_NOSUPPORT; + case EAI_MEMORY: return -NLE_NOMEM; + case EAI_NODATA: return -NLE_NOADDR; + case EAI_NONAME: return -NLE_OBJ_NOTFOUND; + case EAI_SERVICE: return -NLE_OPNOTSUPP; + case EAI_SOCKTYPE: return -NLE_BAD_SOCK; + default: return -NLE_FAILURE; + } + } + + return 0; +} + +/** + * Resolve abstract address object to a name using getnameinfo(). + * @arg addr Abstract address object. + * @arg host Destination buffer for host name. + * @arg hostlen Length of destination buffer. + * + * Resolves the abstract address to a name and writes the looked up result + * into the host buffer. getnameinfo() is used to perform the lookup and + * is put into NI_NAMEREQD mode so the function will fail if the lookup + * couldn't be performed. + * + * @return 0 on success or a negative error code. + */ +int nl_addr_resolve(const struct nl_addr *addr, char *host, size_t hostlen) +{ + int err; + struct sockaddr_in6 buf; + socklen_t salen = sizeof(buf); + + err = nl_addr_fill_sockaddr(addr, (struct sockaddr *) &buf, &salen); + if (err < 0) + return err; + + err = getnameinfo((struct sockaddr *) &buf, salen, host, hostlen, + NULL, 0, NI_NAMEREQD); + if (err < 0) + return nl_syserr2nlerr(err); + + return 0; +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +/** + * Set address family + * @arg addr Abstract address object + * @arg family Address family + * + * @see nl_addr_get_family() + */ +void nl_addr_set_family(struct nl_addr *addr, int family) +{ + addr->a_family = family; +} + +/** + * Return address family + * @arg addr Abstract address object + * + * @see nl_addr_set_family() + * + * @return The numeric address family or `AF_UNSPEC` + */ +int nl_addr_get_family(const struct nl_addr *addr) +{ + return addr->a_family; +} + +/** + * Set binary address of abstract address object. + * @arg addr Abstract address object. + * @arg buf Buffer containing binary address. + * @arg len Length of buffer containing binary address. + * + * Modifies the binary address portion of the abstract address. The + * abstract address must be capable of holding the required amount + * or this function will fail. + * + * @note This function will *not* modify the prefix length. It is within + * the responsibility of the caller to set the prefix length to the + * desirable length. + * + * @see nl_addr_alloc() + * @see nl_addr_get_binary_addr() + * @see nl_addr_get_len() + * + * @return 0 on success or a negative error code. + */ +int nl_addr_set_binary_addr(struct nl_addr *addr, const void *buf, size_t len) +{ + if (len > addr->a_maxsize) + return -NLE_RANGE; + + addr->a_len = len; + memset(addr->a_addr, 0, addr->a_maxsize); + + if (len) + memcpy(addr->a_addr, buf, len); + + return 0; +} + +/** + * Get binary address of abstract address object. + * @arg addr Abstract address object. + * + * @see nl_addr_set_binary_addr() + * @see nl_addr_get_len() + * + * @return Pointer to binary address of length nl_addr_get_len() + */ +void *nl_addr_get_binary_addr(const struct nl_addr *addr) +{ + return (void*)addr->a_addr; +} + +/** + * Get length of binary address of abstract address object. + * @arg addr Abstract address object. + * + * @see nl_addr_get_binary_addr() + * @see nl_addr_set_binary_addr() + */ +unsigned int nl_addr_get_len(const struct nl_addr *addr) +{ + return addr->a_len; +} + +/** + * Set the prefix length of an abstract address + * @arg addr Abstract address object + * @arg prefixlen New prefix length + * + * @see nl_addr_get_prefixlen() + */ +void nl_addr_set_prefixlen(struct nl_addr *addr, int prefixlen) +{ + addr->a_prefixlen = prefixlen; +} + +/** + * Return prefix length of abstract address object. + * @arg addr Abstract address object + * + * @see nl_addr_set_prefixlen() + */ +unsigned int nl_addr_get_prefixlen(const struct nl_addr *addr) +{ + return addr->a_prefixlen; +} + +/** @} */ + +/** + * @name Translations to Strings + * @{ + */ + +/** + * Convert abstract address object to character string. + * @arg addr Abstract address object. + * @arg buf Destination buffer. + * @arg size Size of destination buffer. + * + * Converts an abstract address to a character string and stores + * the result in the specified destination buffer. + * + * @return Address represented in ASCII stored in destination buffer. + */ +char *nl_addr2str(const struct nl_addr *addr, char *buf, size_t size) +{ + unsigned int i; + char tmp[16]; + + if (!addr || !addr->a_len) { + snprintf(buf, size, "none"); + if (addr) + goto prefix; + else + return buf; + } + + switch (addr->a_family) { + case AF_INET: + inet_ntop(AF_INET, addr->a_addr, buf, size); + break; + + case AF_INET6: + inet_ntop(AF_INET6, addr->a_addr, buf, size); + break; + + case AF_MPLS: + mpls_ntop(AF_MPLS, addr->a_addr, buf, size); + break; + + case AF_DECnet: + dnet_ntop(addr->a_addr, addr->a_len, buf, size); + break; + + case AF_LLC: + default: + snprintf(buf, size, "%02x", + (unsigned char) addr->a_addr[0]); + for (i = 1; i < addr->a_len; i++) { + snprintf(tmp, sizeof(tmp), ":%02x", + (unsigned char) addr->a_addr[i]); + strncat(buf, tmp, size - strlen(buf) - 1); + } + break; + } + +prefix: + if (addr->a_family != AF_MPLS && + addr->a_prefixlen != (8 * addr->a_len)) { + snprintf(tmp, sizeof(tmp), "/%u", addr->a_prefixlen); + strncat(buf, tmp, size - strlen(buf) - 1); + } + + return buf; +} + +/** @} */ + +/** + * @name Address Family Transformations + * @{ + */ + +static const struct trans_tbl afs[] = { + __ADD(AF_UNSPEC,unspec), + __ADD(AF_UNIX,unix), + __ADD(AF_INET,inet), + __ADD(AF_AX25,ax25), + __ADD(AF_IPX,ipx), + __ADD(AF_APPLETALK,appletalk), + __ADD(AF_NETROM,netrom), + __ADD(AF_BRIDGE,bridge), + __ADD(AF_ATMPVC,atmpvc), + __ADD(AF_X25,x25), + __ADD(AF_INET6,inet6), + __ADD(AF_ROSE,rose), + __ADD(AF_DECnet,decnet), + __ADD(AF_NETBEUI,netbeui), + __ADD(AF_SECURITY,security), + __ADD(AF_KEY,key), + __ADD(AF_NETLINK,netlink), + __ADD(AF_PACKET,packet), + __ADD(AF_ASH,ash), + __ADD(AF_ECONET,econet), + __ADD(AF_ATMSVC,atmsvc), +#ifdef AF_RDS + __ADD(AF_RDS,rds), +#endif + __ADD(AF_SNA,sna), + __ADD(AF_IRDA,irda), + __ADD(AF_PPPOX,pppox), + __ADD(AF_WANPIPE,wanpipe), + __ADD(AF_LLC,llc), +#ifdef AF_CAN + __ADD(AF_CAN,can), +#endif +#ifdef AF_TIPC + __ADD(AF_TIPC,tipc), +#endif + __ADD(AF_BLUETOOTH,bluetooth), +#ifdef AF_IUCV + __ADD(AF_IUCV,iucv), +#endif +#ifdef AF_RXRPC + __ADD(AF_RXRPC,rxrpc), +#endif +#ifdef AF_ISDN + __ADD(AF_ISDN,isdn), +#endif +#ifdef AF_PHONET + __ADD(AF_PHONET,phonet), +#endif +#ifdef AF_IEEE802154 + __ADD(AF_IEEE802154,ieee802154), +#endif +#ifdef AF_CAIF + __ADD(AF_CAIF,caif), +#endif +#ifdef AF_ALG + __ADD(AF_ALG,alg), +#endif +#ifdef AF_NFC + __ADD(AF_NFC,nfc), +#endif +#ifdef AF_VSOCK + __ADD(AF_VSOCK,vsock), +#endif + __ADD(AF_MPLS,mpls), +}; + +char *nl_af2str(int family, char *buf, size_t size) +{ + return __type2str(family, buf, size, afs, ARRAY_SIZE(afs)); +} + +int nl_str2af(const char *name) +{ + int fam = __str2type(name, afs, ARRAY_SIZE(afs)); + return fam >= 0 ? fam : -EINVAL; +} + +/** @} */ + +/** @} */ diff --git a/libnl/lib/attr.c b/libnl/lib/attr.c new file mode 100644 index 0000000..6d986af --- /dev/null +++ b/libnl/lib/attr.c @@ -0,0 +1,1034 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +/** + * @ingroup msg + * @defgroup attr Attributes + * Netlink Attributes Construction/Parsing Interface + * + * Related sections in the development guide: + * - @core_doc{core_attr,Netlink Attributes} + * + * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ + */ + +/** + * @name Attribute Size Calculation + * @{ + */ + +/** + * Return size of attribute whithout padding. + * @arg payload Payload length of attribute. + * + * @code + * <-------- nla_attr_size(payload) ---------> + * +------------------+- - -+- - - - - - - - - +- - -+ + * | Attribute Header | Pad | Payload | Pad | + * +------------------+- - -+- - - - - - - - - +- - -+ + * @endcode + * + * @return Size of attribute in bytes without padding. + */ +int nla_attr_size(int payload) +{ + return NLA_HDRLEN + payload; +} + +/** + * Return size of attribute including padding. + * @arg payload Payload length of attribute. + * + * @code + * <----------- nla_total_size(payload) -----------> + * +------------------+- - -+- - - - - - - - - +- - -+ + * | Attribute Header | Pad | Payload | Pad | + * +------------------+- - -+- - - - - - - - - +- - -+ + * @endcode + * + * @return Size of attribute in bytes. + */ +int nla_total_size(int payload) +{ + return NLA_ALIGN(nla_attr_size(payload)); +} + +/** + * Return length of padding at the tail of the attribute. + * @arg payload Payload length of attribute. + * + * @code + * +------------------+- - -+- - - - - - - - - +- - -+ + * | Attribute Header | Pad | Payload | Pad | + * +------------------+- - -+- - - - - - - - - +- - -+ + * <---> + * @endcode + * + * @return Length of padding in bytes. + */ +int nla_padlen(int payload) +{ + return nla_total_size(payload) - nla_attr_size(payload); +} + +/** @} */ + +/** + * @name Parsing Attributes + * @{ + */ + +/** + * Return type of the attribute. + * @arg nla Attribute. + * + * @return Type of attribute. + */ +int nla_type(const struct nlattr *nla) +{ + return nla->nla_type & NLA_TYPE_MASK; +} + +/** + * Return pointer to the payload section. + * @arg nla Attribute. + * + * @return Pointer to start of payload section. + */ +void *nla_data(const struct nlattr *nla) +{ + return (char *) nla + NLA_HDRLEN; +} + +/** + * Return length of the payload . + * @arg nla Attribute + * + * @return Length of payload in bytes. + */ +int nla_len(const struct nlattr *nla) +{ + return nla->nla_len - NLA_HDRLEN; +} + +/** + * Check if the attribute header and payload can be accessed safely. + * @arg nla Attribute of any kind. + * @arg remaining Number of bytes remaining in attribute stream. + * + * Verifies that the header and payload do not exceed the number of + * bytes left in the attribute stream. This function must be called + * before access the attribute header or payload when iterating over + * the attribute stream using nla_next(). + * + * @return True if the attribute can be accessed safely, false otherwise. + */ +int nla_ok(const struct nlattr *nla, int remaining) +{ + return remaining >= (int) sizeof(*nla) && + nla->nla_len >= sizeof(*nla) && + nla->nla_len <= remaining; +} + +/** + * Return next attribute in a stream of attributes. + * @arg nla Attribute of any kind. + * @arg remaining Variable to count remaining bytes in stream. + * + * Calculates the offset to the next attribute based on the attribute + * given. The attribute provided is assumed to be accessible, the + * caller is responsible to use nla_ok() beforehand. The offset (length + * of specified attribute including padding) is then subtracted from + * the remaining bytes variable and a pointer to the next attribute is + * returned. + * + * nla_next() can be called as long as remainig is >0. + * + * @return Pointer to next attribute. + */ +struct nlattr *nla_next(const struct nlattr *nla, int *remaining) +{ + int totlen = NLA_ALIGN(nla->nla_len); + + *remaining -= totlen; + return (struct nlattr *) ((char *) nla + totlen); +} + +static uint16_t nla_attr_minlen[NLA_TYPE_MAX+1] = { + [NLA_U8] = sizeof(uint8_t), + [NLA_U16] = sizeof(uint16_t), + [NLA_U32] = sizeof(uint32_t), + [NLA_U64] = sizeof(uint64_t), + [NLA_STRING] = 1, + [NLA_FLAG] = 0, +}; + +static int validate_nla(const struct nlattr *nla, int maxtype, + const struct nla_policy *policy) +{ + const struct nla_policy *pt; + unsigned int minlen = 0; + int type = nla_type(nla); + + if (type < 0 || type > maxtype) + return 0; + + pt = &policy[type]; + + if (pt->type > NLA_TYPE_MAX) + BUG(); + + if (pt->minlen) + minlen = pt->minlen; + else if (pt->type != NLA_UNSPEC) + minlen = nla_attr_minlen[pt->type]; + + if (nla_len(nla) < minlen) + return -NLE_RANGE; + + if (pt->maxlen && nla_len(nla) > pt->maxlen) + return -NLE_RANGE; + + if (pt->type == NLA_STRING) { + const char *data = nla_data(nla); + if (data[nla_len(nla) - 1] != '\0') + return -NLE_INVAL; + } + + return 0; +} + + +/** + * Create attribute index based on a stream of attributes. + * @arg tb Index array to be filled (maxtype+1 elements). + * @arg maxtype Maximum attribute type expected and accepted. + * @arg head Head of attribute stream. + * @arg len Length of attribute stream. + * @arg policy Attribute validation policy. + * + * Iterates over the stream of attributes and stores a pointer to each + * attribute in the index array using the attribute type as index to + * the array. Attribute with a type greater than the maximum type + * specified will be silently ignored in order to maintain backwards + * compatibility. If \a policy is not NULL, the attribute will be + * validated using the specified policy. + * + * @see nla_validate + * @return 0 on success or a negative error code. + */ +int nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, int len, + const struct nla_policy *policy) +{ + struct nlattr *nla; + int rem, err; + + memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1)); + + nla_for_each_attr(nla, head, len, rem) { + int type = nla_type(nla); + + if (type > maxtype) + continue; + + if (policy) { + err = validate_nla(nla, maxtype, policy); + if (err < 0) + return err; + } + + if (tb[type]) + NL_DBG(1, "Attribute of type %#x found multiple times in message, " + "previous attribute is being ignored.\n", type); + + tb[type] = nla; + } + + if (rem > 0) { + NL_DBG(1, "netlink: %d bytes leftover after parsing " + "attributes.\n", rem); + } + + return 0; +} + +/** + * Validate a stream of attributes. + * @arg head Head of attributes stream. + * @arg len Length of attributes stream. + * @arg maxtype Maximum attribute type expected and accepted. + * @arg policy Validation policy. + * + * Iterates over the stream of attributes and validates each attribute + * one by one using the specified policy. Attributes with a type greater + * than the maximum type specified will be silently ignored in order to + * maintain backwards compatibility. + * + * See section @core_doc{core_attr_parse,Attribute Parsing} for more details. + * + * @return 0 on success or a negative error code. + */ +int nla_validate(const struct nlattr *head, int len, int maxtype, + const struct nla_policy *policy) +{ + const struct nlattr *nla; + int rem, err; + + nla_for_each_attr(nla, head, len, rem) { + err = validate_nla(nla, maxtype, policy); + if (err < 0) + goto errout; + } + + err = 0; +errout: + return err; +} + +/** + * Find a single attribute in a stream of attributes. + * @arg head Head of attributes stream. + * @arg len Length of attributes stream. + * @arg attrtype Attribute type to look for. + * + * Iterates over the stream of attributes and compares each type with + * the type specified. Returns the first attribute which matches the + * type. + * + * @return Pointer to attribute found or NULL. + */ +struct nlattr *nla_find(const struct nlattr *head, int len, int attrtype) +{ + const struct nlattr *nla; + int rem; + + nla_for_each_attr(nla, head, len, rem) + if (nla_type(nla) == attrtype) + return (struct nlattr*)nla; + + return NULL; +} + +/** @} */ + +/** + * @name Helper Functions + * @{ + */ + +/** + * Copy attribute payload to another memory area. + * @arg dest Pointer to destination memory area. + * @arg src Attribute + * @arg count Number of bytes to copy at most. + * + * Note: The number of bytes copied is limited by the length of + * the attribute payload. + * + * @return The number of bytes copied to dest. + */ +int nla_memcpy(void *dest, const struct nlattr *src, int count) +{ + int minlen; + + if (!src) + return 0; + + minlen = min_t(int, count, nla_len(src)); + memcpy(dest, nla_data(src), minlen); + + return minlen; +} + +/** + * Copy string attribute payload to a buffer. + * @arg dst Pointer to destination buffer. + * @arg nla Attribute of type NLA_STRING. + * @arg dstsize Size of destination buffer in bytes. + * + * Copies at most dstsize - 1 bytes to the destination buffer. + * The result is always a valid NUL terminated string. Unlike + * strlcpy the destination buffer is always padded out. + * + * @return The length of string attribute without the terminating NUL. + */ +size_t nla_strlcpy(char *dst, const struct nlattr *nla, size_t dstsize) +{ + size_t srclen = nla_len(nla); + const char *src = nla_data(nla); + + if (srclen > 0 && src[srclen - 1] == '\0') + srclen--; + + if (dstsize > 0) { + size_t len = (srclen >= dstsize) ? dstsize - 1 : srclen; + + memset(dst, 0, dstsize); + memcpy(dst, src, len); + } + + return srclen; +} + +/** + * Compare attribute payload with memory area. + * @arg nla Attribute. + * @arg data Memory area to compare to. + * @arg size Number of bytes to compare. + * + * @see memcmp(3) + * @return An integer less than, equal to, or greater than zero. + */ +int nla_memcmp(const struct nlattr *nla, const void *data, size_t size) +{ + int d = nla_len(nla) - size; + + if (d == 0) + d = memcmp(nla_data(nla), data, size); + + return d; +} + +/** + * Compare string attribute payload with string + * @arg nla Attribute of type NLA_STRING. + * @arg str NUL terminated string. + * + * @see strcmp(3) + * @return An integer less than, equal to, or greater than zero. + */ +int nla_strcmp(const struct nlattr *nla, const char *str) +{ + int len = strlen(str) + 1; + int d = nla_len(nla) - len; + + if (d == 0) + d = memcmp(nla_data(nla), str, len); + + return d; +} + +/** @} */ + +/** + * @name Unspecific Attribute + * @{ + */ + +/** + * Reserve space for a attribute. + * @arg msg Netlink Message. + * @arg attrtype Attribute Type. + * @arg attrlen Length of payload. + * + * Reserves room for a attribute in the specified netlink message and + * fills in the attribute header (type, length). Returns NULL if there + * is unsuficient space for the attribute. + * + * Any padding between payload and the start of the next attribute is + * zeroed out. + * + * @return Pointer to start of attribute or NULL on failure. + */ +struct nlattr *nla_reserve(struct nl_msg *msg, int attrtype, int attrlen) +{ + struct nlattr *nla; + int tlen; + + if (attrlen < 0) + return NULL; + + tlen = NLMSG_ALIGN(msg->nm_nlh->nlmsg_len) + nla_total_size(attrlen); + + if (tlen > msg->nm_size) + return NULL; + + nla = (struct nlattr *) nlmsg_tail(msg->nm_nlh); + nla->nla_type = attrtype; + nla->nla_len = nla_attr_size(attrlen); + + if (attrlen) + memset((unsigned char *) nla + nla->nla_len, 0, nla_padlen(attrlen)); + msg->nm_nlh->nlmsg_len = tlen; + + NL_DBG(2, "msg %p: attr <%p> %d: Reserved %d (%d) bytes at offset +%td " + "nlmsg_len=%d\n", msg, nla, nla->nla_type, + nla_total_size(attrlen), attrlen, + (char *) nla - (char *) nlmsg_data(msg->nm_nlh), + msg->nm_nlh->nlmsg_len); + + return nla; +} + +/** + * Add a unspecific attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg datalen Length of data to be used as payload. + * @arg data Pointer to data to be used as attribute payload. + * + * Reserves room for a unspecific attribute and copies the provided data + * into the message as payload of the attribute. Returns an error if there + * is insufficient space for the attribute. + * + * @see nla_reserve + * @return 0 on success or a negative error code. + */ +int nla_put(struct nl_msg *msg, int attrtype, int datalen, const void *data) +{ + struct nlattr *nla; + + nla = nla_reserve(msg, attrtype, datalen); + if (!nla) { + if (datalen < 0) + return -NLE_INVAL; + + return -NLE_NOMEM; + } + + if (datalen > 0) { + memcpy(nla_data(nla), data, datalen); + NL_DBG(2, "msg %p: attr <%p> %d: Wrote %d bytes at offset +%td\n", + msg, nla, nla->nla_type, datalen, + (char *) nla - (char *) nlmsg_data(msg->nm_nlh)); + } + + return 0; +} + +/** + * Add abstract data as unspecific attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg data Abstract data object. + * + * Equivalent to nla_put() except that the length of the payload is + * derived from the abstract data object. + * + * @see nla_put + * @return 0 on success or a negative error code. + */ +int nla_put_data(struct nl_msg *msg, int attrtype, const struct nl_data *data) +{ + return nla_put(msg, attrtype, nl_data_get_size(data), + nl_data_get(data)); +} + +/** + * Add abstract address as unspecific attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg addr Abstract address object. + * + * @see nla_put + * @return 0 on success or a negative error code. + */ +int nla_put_addr(struct nl_msg *msg, int attrtype, struct nl_addr *addr) +{ + return nla_put(msg, attrtype, nl_addr_get_len(addr), + nl_addr_get_binary_addr(addr)); +} + +/** @} */ + +/** + * @name Integer Attributes + */ + +/** + * Add 8 bit signed integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value to store as payload. + * + * @see nla_put + * @return 0 on success or a negative error code. + */ +int nla_put_s8(struct nl_msg *msg, int attrtype, int8_t value) +{ + return nla_put(msg, attrtype, sizeof(int8_t), &value); +} + +/** + * Return value of 8 bit signed integer attribute. + * @arg nla 8 bit integer attribute + * + * @return Payload as 8 bit integer. + */ +int8_t nla_get_s8(const struct nlattr *nla) +{ + return *(const int8_t *) nla_data(nla); +} + +/** + * Add 8 bit integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value to store as payload. + * + * @see nla_put + * @return 0 on success or a negative error code. + */ +int nla_put_u8(struct nl_msg *msg, int attrtype, uint8_t value) +{ + return nla_put(msg, attrtype, sizeof(uint8_t), &value); +} + +/** + * Return value of 8 bit integer attribute. + * @arg nla 8 bit integer attribute + * + * @return Payload as 8 bit integer. + */ +uint8_t nla_get_u8(const struct nlattr *nla) +{ + return *(const uint8_t *) nla_data(nla); +} + +/** + * Add 16 bit signed integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value to store as payload. + * + * @see nla_put + * @return 0 on success or a negative error code. + */ +int nla_put_s16(struct nl_msg *msg, int attrtype, int16_t value) +{ + return nla_put(msg, attrtype, sizeof(int16_t), &value); +} + +/** + * Return payload of 16 bit signed integer attribute. + * @arg nla 16 bit integer attribute + * + * @return Payload as 16 bit integer. + */ +int16_t nla_get_s16(const struct nlattr *nla) +{ + return *(const int16_t *) nla_data(nla); +} + +/** + * Add 16 bit integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value to store as payload. + * + * @see nla_put + * @return 0 on success or a negative error code. + */ +int nla_put_u16(struct nl_msg *msg, int attrtype, uint16_t value) +{ + return nla_put(msg, attrtype, sizeof(uint16_t), &value); +} + +/** + * Return payload of 16 bit integer attribute. + * @arg nla 16 bit integer attribute + * + * @return Payload as 16 bit integer. + */ +uint16_t nla_get_u16(const struct nlattr *nla) +{ + return *(const uint16_t *) nla_data(nla); +} + +/** + * Add 32 bit signed integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value to store as payload. + * + * @see nla_put + * @return 0 on success or a negative error code. + */ +int nla_put_s32(struct nl_msg *msg, int attrtype, int32_t value) +{ + return nla_put(msg, attrtype, sizeof(int32_t), &value); +} + +/** + * Return payload of 32 bit signed integer attribute. + * @arg nla 32 bit integer attribute. + * + * @return Payload as 32 bit integer. + */ +int32_t nla_get_s32(const struct nlattr *nla) +{ + return *(const int32_t *) nla_data(nla); +} + +/** + * Add 32 bit integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value to store as payload. + * + * @see nla_put + * @return 0 on success or a negative error code. + */ +int nla_put_u32(struct nl_msg *msg, int attrtype, uint32_t value) +{ + return nla_put(msg, attrtype, sizeof(uint32_t), &value); +} + +/** + * Return payload of 32 bit integer attribute. + * @arg nla 32 bit integer attribute. + * + * @return Payload as 32 bit integer. + */ +uint32_t nla_get_u32(const struct nlattr *nla) +{ + return *(const uint32_t *) nla_data(nla); +} + +/** + * Add 64 bit signed integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value to store as payload. + * + * @see nla_put + * @return 0 on success or a negative error code. + */ +int nla_put_s64(struct nl_msg *msg, int attrtype, int64_t value) +{ + return nla_put(msg, attrtype, sizeof(int64_t), &value); +} + +/** + * Return payload of s64 attribute + * @arg nla s64 netlink attribute + * + * @return Payload as 64 bit integer. + */ +int64_t nla_get_s64(const struct nlattr *nla) +{ + int64_t tmp = 0; + + if (nla && nla_len(nla) >= sizeof(tmp)) + memcpy(&tmp, nla_data(nla), sizeof(tmp)); + + return tmp; +} + +/** + * Add 64 bit integer attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg value Numeric value to store as payload. + * + * @see nla_put + * @return 0 on success or a negative error code. + */ +int nla_put_u64(struct nl_msg *msg, int attrtype, uint64_t value) +{ + return nla_put(msg, attrtype, sizeof(uint64_t), &value); +} + +/** + * Return payload of u64 attribute + * @arg nla u64 netlink attribute + * + * @return Payload as 64 bit integer. + */ +uint64_t nla_get_u64(const struct nlattr *nla) +{ + uint64_t tmp = 0; + + if (nla && nla_len(nla) >= sizeof(tmp)) + memcpy(&tmp, nla_data(nla), sizeof(tmp)); + + return tmp; +} + +/** @} */ + +/** + * @name String Attribute + */ + +/** + * Add string attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg str NUL terminated string. + * + * @see nla_put + * @return 0 on success or a negative error code. + */ +int nla_put_string(struct nl_msg *msg, int attrtype, const char *str) +{ + return nla_put(msg, attrtype, strlen(str) + 1, str); +} + +/** + * Return payload of string attribute. + * @arg nla String attribute. + * + * @return Pointer to attribute payload. + */ +char *nla_get_string(const struct nlattr *nla) +{ + return (char *) nla_data(nla); +} + +char *nla_strdup(const struct nlattr *nla) +{ + return strdup(nla_get_string(nla)); +} + +/** @} */ + +/** + * @name Flag Attribute + */ + +/** + * Add flag netlink attribute to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * + * @see nla_put + * @return 0 on success or a negative error code. + */ +int nla_put_flag(struct nl_msg *msg, int attrtype) +{ + return nla_put(msg, attrtype, 0, NULL); +} + +/** + * Return true if flag attribute is set. + * @arg nla Flag netlink attribute. + * + * @return True if flag is set, otherwise false. + */ +int nla_get_flag(const struct nlattr *nla) +{ + return !!nla; +} + +/** @} */ + +/** + * @name Microseconds Attribute + */ + +/** + * Add a msecs netlink attribute to a netlink message + * @arg n netlink message + * @arg attrtype attribute type + * @arg msecs number of msecs + */ +int nla_put_msecs(struct nl_msg *n, int attrtype, unsigned long msecs) +{ + return nla_put_u64(n, attrtype, msecs); +} + +/** + * Return payload of msecs attribute + * @arg nla msecs netlink attribute + * + * @return the number of milliseconds. + */ +unsigned long nla_get_msecs(const struct nlattr *nla) +{ + return nla_get_u64(nla); +} + +/** @} */ + +/** + * @name Nested Attribute + */ + +/** + * Add nested attributes to netlink message. + * @arg msg Netlink message. + * @arg attrtype Attribute type. + * @arg nested Message containing attributes to be nested. + * + * Takes the attributes found in the \a nested message and appends them + * to the message \a msg nested in a container of the type \a attrtype. + * The \a nested message may not have a family specific header. + * + * @see nla_put + * @return 0 on success or a negative error code. + */ +int nla_put_nested(struct nl_msg *msg, int attrtype, + const struct nl_msg *nested) +{ + NL_DBG(2, "msg %p: attr <> %d: adding msg %p as nested attribute\n", + msg, attrtype, nested); + + return nla_put(msg, attrtype, nlmsg_datalen(nested->nm_nlh), + nlmsg_data(nested->nm_nlh)); +} + + +/** + * Start a new level of nested attributes. + * @arg msg Netlink message. + * @arg attrtype Attribute type of container. + * + * @return Pointer to container attribute. + */ +struct nlattr *nla_nest_start(struct nl_msg *msg, int attrtype) +{ + struct nlattr *start = (struct nlattr *) nlmsg_tail(msg->nm_nlh); + + if (nla_put(msg, NLA_F_NESTED | attrtype, 0, NULL) < 0) + return NULL; + + NL_DBG(2, "msg %p: attr <%p> %d: starting nesting\n", + msg, start, start->nla_type); + + return start; +} + +static int _nest_end(struct nl_msg *msg, struct nlattr *start, int keep_empty) +{ + size_t pad, len; + + len = (char *) nlmsg_tail(msg->nm_nlh) - (char *) start; + + if ( len > USHRT_MAX + || (!keep_empty && len == NLA_HDRLEN)) { + /* + * Max nlattr size exceeded or empty nested attribute, trim the + * attribute header again + */ + nla_nest_cancel(msg, start); + + /* Return error only if nlattr size was exceeded */ + return (len == NLA_HDRLEN) ? 0 : -NLE_ATTRSIZE; + } + + start->nla_len = len; + + pad = NLMSG_ALIGN(msg->nm_nlh->nlmsg_len) - msg->nm_nlh->nlmsg_len; + if (pad > 0) { + /* + * Data inside attribute does not end at a alignment boundry. + * Pad accordingly and accoun for the additional space in + * the message. nlmsg_reserve() may never fail in this situation, + * the allocate message buffer must be a multiple of NLMSG_ALIGNTO. + */ + if (!nlmsg_reserve(msg, pad, 0)) + BUG(); + + NL_DBG(2, "msg %p: attr <%p> %d: added %zu bytes of padding\n", + msg, start, start->nla_type, pad); + } + + NL_DBG(2, "msg %p: attr <%p> %d: closing nesting, len=%u\n", + msg, start, start->nla_type, start->nla_len); + + return 0; +} + +/** + * Finalize nesting of attributes. + * @arg msg Netlink message. + * @arg start Container attribute as returned from nla_nest_start(). + * + * Corrects the container attribute header to include the appeneded attributes. + * + * @return 0 on success or a negative error code. + */ +int nla_nest_end(struct nl_msg *msg, struct nlattr *start) +{ + return _nest_end (msg, start, 0); +} + +/** + * Finalize nesting of attributes without stripping off empty attributes. + * @arg msg Netlink message. + * @arg start Container attribute as returned from nla_nest_start(). + * + * Corrects the container attribute header to include the appeneded attributes. + * Keep empty attribute if NO actual attribute payload exists. + * + * @return 0 on success or a negative error code. + */ +int nla_nest_end_keep_empty(struct nl_msg *msg, struct nlattr *start) +{ + return _nest_end (msg, start, 1); +} + +/** + * Cancel the addition of a nested attribute + * @arg msg Netlink message + * @arg attr Nested netlink attribute + * + * Removes any partially added nested Netlink attribute from the message + * by resetting the message to the size before the call to nla_nest_start() + * and by overwriting any potentially touched message segments with 0. + */ +void nla_nest_cancel(struct nl_msg *msg, const struct nlattr *attr) +{ + ssize_t len; + + len = (char *) nlmsg_tail(msg->nm_nlh) - (char *) attr; + if (len < 0) + BUG(); + else if (len > 0) { + msg->nm_nlh->nlmsg_len -= len; + memset(nlmsg_tail(msg->nm_nlh), 0, len); + } +} + +/** + * Create attribute index based on nested attribute + * @arg tb Index array to be filled (maxtype+1 elements). + * @arg maxtype Maximum attribute type expected and accepted. + * @arg nla Nested Attribute. + * @arg policy Attribute validation policy. + * + * Feeds the stream of attributes nested into the specified attribute + * to nla_parse(). + * + * @see nla_parse + * @return 0 on success or a negative error code. + */ +int nla_parse_nested(struct nlattr *tb[], int maxtype, struct nlattr *nla, + const struct nla_policy *policy) +{ + return nla_parse(tb, maxtype, nla_data(nla), nla_len(nla), policy); +} + +/** + * Return true if attribute has NLA_F_NESTED flag set + * @arg attr Netlink attribute + * + * @return True if attribute has NLA_F_NESTED flag set, oterhwise False. + */ +int nla_is_nested(const struct nlattr *attr) +{ + return !!(attr->nla_type & NLA_F_NESTED); +} + +/** @} */ + +/** @} */ diff --git a/libnl/lib/cache.c b/libnl/lib/cache.c new file mode 100644 index 0000000..665c077 --- /dev/null +++ b/libnl/lib/cache.c @@ -0,0 +1,1326 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +/** + * @ingroup cache_mngt + * @defgroup cache Cache + * + * @code + * Cache Management | | Type Specific Cache Operations + * + * | | +----------------+ +------------+ + * | request update | | msg_parser | + * | | +----------------+ +------------+ + * +- - - - -^- - - - - - - -^- -|- - - - + * nl_cache_update: | | | | + * 1) --------- co_request_update ------+ | | + * | | | + * 2) destroy old cache +----------- pp_cb ---------|---+ + * | | | + * 3) ---------- nl_recvmsgs ----------+ +- cb_valid -+ + * +--------------+ | | | | + * | nl_cache_add |<-----+ + - - -v- -|- - - - - - - - - - - + * +--------------+ | | +-------------+ + * | nl_recvmsgs | + * | | +-----|-^-----+ + * +---v-|---+ + * | | | nl_recv | + * +---------+ + * | | Core Netlink + * @endcode + * + * Related sections in the development guide: + * - @core_doc{core_cache, Caching System} + * + * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +/** + * @name Access Functions + * @{ + */ + +/** + * Return the number of items in the cache + * @arg cache cache handle + */ +int nl_cache_nitems(struct nl_cache *cache) +{ + return cache->c_nitems; +} + +/** + * Return the number of items matching a filter in the cache + * @arg cache Cache object. + * @arg filter Filter object. + */ +int nl_cache_nitems_filter(struct nl_cache *cache, struct nl_object *filter) +{ + struct nl_object *obj; + int nitems = 0; + + if (cache->c_ops == NULL) + BUG(); + + nl_list_for_each_entry(obj, &cache->c_items, ce_list) { + if (filter && !nl_object_match_filter(obj, filter)) + continue; + + nitems++; + } + + return nitems; +} + +/** + * Returns \b true if the cache is empty. + * @arg cache Cache to check + * @return \a true if the cache is empty, otherwise \b false is returned. + */ +int nl_cache_is_empty(struct nl_cache *cache) +{ + return nl_list_empty(&cache->c_items); +} + +/** + * Return the operations set of the cache + * @arg cache cache handle + */ +struct nl_cache_ops *nl_cache_get_ops(struct nl_cache *cache) +{ + return cache->c_ops; +} + +/** + * Return the first element in the cache + * @arg cache cache handle + */ +struct nl_object *nl_cache_get_first(struct nl_cache *cache) +{ + if (nl_list_empty(&cache->c_items)) + return NULL; + + return nl_list_entry(cache->c_items.next, + struct nl_object, ce_list); +} + +/** + * Return the last element in the cache + * @arg cache cache handle + */ +struct nl_object *nl_cache_get_last(struct nl_cache *cache) +{ + if (nl_list_empty(&cache->c_items)) + return NULL; + + return nl_list_entry(cache->c_items.prev, + struct nl_object, ce_list); +} + +/** + * Return the next element in the cache + * @arg obj current object + */ +struct nl_object *nl_cache_get_next(struct nl_object *obj) +{ + if (nl_list_at_tail(obj, &obj->ce_cache->c_items, ce_list)) + return NULL; + else + return nl_list_entry(obj->ce_list.next, + struct nl_object, ce_list); +} + +/** + * Return the previous element in the cache + * @arg obj current object + */ +struct nl_object *nl_cache_get_prev(struct nl_object *obj) +{ + if (nl_list_at_head(obj, &obj->ce_cache->c_items, ce_list)) + return NULL; + else + return nl_list_entry(obj->ce_list.prev, + struct nl_object, ce_list); +} + +/** @} */ + +/** + * @name Cache Allocation/Deletion + * @{ + */ + +/** + * Allocate new cache + * @arg ops Cache operations + * + * Allocate and initialize a new cache based on the cache operations + * provided. + * + * @return Allocated cache or NULL if allocation failed. + */ +struct nl_cache *nl_cache_alloc(struct nl_cache_ops *ops) +{ + struct nl_cache *cache; + + cache = calloc(1, sizeof(*cache)); + if (!cache) + return NULL; + + nl_init_list_head(&cache->c_items); + cache->c_ops = ops; + cache->c_flags |= ops->co_flags; + cache->c_refcnt = 1; + + /* + * If object type provides a hash keygen + * functions, allocate a hash table for the + * cache objects for faster lookups + */ + if (ops->co_obj_ops->oo_keygen) { + int hashtable_size; + + if (ops->co_hash_size) + hashtable_size = ops->co_hash_size; + else + hashtable_size = NL_MAX_HASH_ENTRIES; + + cache->hashtable = nl_hash_table_alloc(hashtable_size); + } + + NL_DBG(2, "Allocated cache %p <%s>.\n", cache, nl_cache_name(cache)); + + return cache; +} + +/** + * Allocate new cache and fill it + * @arg ops Cache operations + * @arg sock Netlink socket + * @arg result Result pointer + * + * Allocate new cache and fill it. Equivalent to calling: + * @code + * cache = nl_cache_alloc(ops); + * nl_cache_refill(sock, cache); + * @endcode + * + * @see nl_cache_alloc + * + * @return 0 on success or a negative error code. + */ +int nl_cache_alloc_and_fill(struct nl_cache_ops *ops, struct nl_sock *sock, + struct nl_cache **result) +{ + struct nl_cache *cache; + int err; + + if (!(cache = nl_cache_alloc(ops))) + return -NLE_NOMEM; + + if (sock && (err = nl_cache_refill(sock, cache)) < 0) { + nl_cache_free(cache); + return err; + } + + *result = cache; + return 0; +} + +/** + * Allocate new cache based on type name + * @arg kind Name of cache type + * @arg result Result pointer + * + * Lookup cache ops via nl_cache_ops_lookup() and allocate the cache + * by calling nl_cache_alloc(). Stores the allocated cache in the + * result pointer provided. + * + * @see nl_cache_alloc + * + * @return 0 on success or a negative error code. + */ +int nl_cache_alloc_name(const char *kind, struct nl_cache **result) +{ + struct nl_cache_ops *ops; + struct nl_cache *cache; + + ops = nl_cache_ops_lookup_safe(kind); + if (!ops) + return -NLE_NOCACHE; + + cache = nl_cache_alloc(ops); + nl_cache_ops_put(ops); + if (!cache) + return -NLE_NOMEM; + + *result = cache; + return 0; +} + +/** + * Allocate new cache containing a subset of an existing cache + * @arg orig Original cache to base new cache on + * @arg filter Filter defining the subset to be filled into the new cache + * + * Allocates a new cache matching the type of the cache specified by + * \p orig. Iterates over the \p orig cache applying the specified + * \p filter and copies all objects that match to the new cache. + * + * The copied objects are clones but do not contain a reference to each + * other. Later modifications to objects in the original cache will + * not affect objects in the new cache. + * + * @return A newly allocated cache or NULL. + */ +struct nl_cache *nl_cache_subset(struct nl_cache *orig, + struct nl_object *filter) +{ + struct nl_cache *cache; + struct nl_object *obj; + + if (!filter) + BUG(); + + cache = nl_cache_alloc(orig->c_ops); + if (!cache) + return NULL; + + NL_DBG(2, "Filling subset of cache %p <%s> with filter %p into %p\n", + orig, nl_cache_name(orig), filter, cache); + + nl_list_for_each_entry(obj, &orig->c_items, ce_list) { + if (!nl_object_match_filter(obj, filter)) + continue; + + nl_cache_add(cache, obj); + } + + return cache; +} + +/** + * Allocate new cache and copy the contents of an existing cache + * @arg cache Original cache to base new cache on + * + * Allocates a new cache matching the type of the cache specified by + * \p cache. Iterates over the \p cache cache and copies all objects + * to the new cache. + * + * The copied objects are clones but do not contain a reference to each + * other. Later modifications to objects in the original cache will + * not affect objects in the new cache. + * + * @return A newly allocated cache or NULL. + */ +struct nl_cache *nl_cache_clone(struct nl_cache *cache) +{ + struct nl_cache_ops *ops = nl_cache_get_ops(cache); + struct nl_cache *clone; + struct nl_object *obj; + + clone = nl_cache_alloc(ops); + if (!clone) + return NULL; + + NL_DBG(2, "Cloning %p into %p\n", cache, clone); + + nl_list_for_each_entry(obj, &cache->c_items, ce_list) + nl_cache_add(clone, obj); + + return clone; +} + +/** + * Remove all objects of a cache. + * @arg cache Cache to clear + * + * The objects are unliked/removed from the cache by calling + * nl_cache_remove() on each object in the cache. If any of the objects + * to not contain any further references to them, those objects will + * be freed. + * + * Unlike with nl_cache_free(), the cache is not freed just emptied. + */ +void nl_cache_clear(struct nl_cache *cache) +{ + struct nl_object *obj, *tmp; + + NL_DBG(2, "Clearing cache %p <%s>...\n", cache, nl_cache_name(cache)); + + nl_list_for_each_entry_safe(obj, tmp, &cache->c_items, ce_list) + nl_cache_remove(obj); +} + +static void __nl_cache_free(struct nl_cache *cache) +{ + nl_cache_clear(cache); + + if (cache->hashtable) + nl_hash_table_free(cache->hashtable); + + NL_DBG(2, "Freeing cache %p <%s>...\n", cache, nl_cache_name(cache)); + free(cache); +} + +/** + * Increase reference counter of cache + * @arg cache Cache + */ +void nl_cache_get(struct nl_cache *cache) +{ + cache->c_refcnt++; + + NL_DBG(3, "Incremented cache %p <%s> reference count to %d\n", + cache, nl_cache_name(cache), cache->c_refcnt); +} + +/** + * Free a cache. + * @arg cache Cache to free. + * + * Calls nl_cache_clear() to remove all objects associated with the + * cache and frees the cache afterwards. + * + * @see nl_cache_clear() + */ +void nl_cache_free(struct nl_cache *cache) +{ + if (!cache) + return; + + cache->c_refcnt--; + + NL_DBG(3, "Decremented cache %p <%s> reference count, %d remaining\n", + cache, nl_cache_name(cache), cache->c_refcnt); + + if (cache->c_refcnt <= 0) + __nl_cache_free(cache); +} + +void nl_cache_put(struct nl_cache *cache) +{ + nl_cache_free(cache); +} + +/** @} */ + +/** + * @name Cache Modifications + * @{ + */ + +static int __cache_add(struct nl_cache *cache, struct nl_object *obj) +{ + int ret; + + obj->ce_cache = cache; + + if (cache->hashtable) { + ret = nl_hash_table_add(cache->hashtable, obj); + if (ret < 0) { + obj->ce_cache = NULL; + return ret; + } + } + + nl_list_add_tail(&obj->ce_list, &cache->c_items); + cache->c_nitems++; + + NL_DBG(3, "Added object %p to cache %p <%s>, nitems %d\n", + obj, cache, nl_cache_name(cache), cache->c_nitems); + + return 0; +} + +/** + * Add object to cache. + * @arg cache Cache + * @arg obj Object to be added to the cache + * + * Adds the object \p obj to the specified \p cache. In case the object + * is already associated with another cache, the object is cloned before + * adding it to the cache. In this case, the sole reference to the object + * will be the one of the cache. Therefore clearing/freeing the cache + * will result in the object being freed again. + * + * If the object has not been associated with a cache yet, the reference + * counter of the object is incremented to account for the additional + * reference. + * + * The type of the object and cache must match, otherwise an error is + * returned (-NLE_OBJ_MISMATCH). + * + * @see nl_cache_move() + * + * @return 0 or a negative error code. + */ +int nl_cache_add(struct nl_cache *cache, struct nl_object *obj) +{ + struct nl_object *new; + int ret = 0; + + if (cache->c_ops->co_obj_ops != obj->ce_ops) + return -NLE_OBJ_MISMATCH; + + if (!nl_list_empty(&obj->ce_list)) { + NL_DBG(3, "Object %p already in cache, cloning new object\n", obj); + + new = nl_object_clone(obj); + if (!new) + return -NLE_NOMEM; + } else { + nl_object_get(obj); + new = obj; + } + + ret = __cache_add(cache, new); + if (ret < 0) + nl_object_put(new); + + return ret; +} + +/** + * Move object from one cache to another + * @arg cache Cache to move object to. + * @arg obj Object subject to be moved + * + * Removes the the specified object \p obj from its associated cache + * and moves it to another cache. + * + * If the object is not associated with a cache, the function behaves + * just like nl_cache_add(). + * + * The type of the object and cache must match, otherwise an error is + * returned (-NLE_OBJ_MISMATCH). + * + * @see nl_cache_add() + * + * @return 0 on success or a negative error code. + */ +int nl_cache_move(struct nl_cache *cache, struct nl_object *obj) +{ + if (cache->c_ops->co_obj_ops != obj->ce_ops) + return -NLE_OBJ_MISMATCH; + + NL_DBG(3, "Moving object %p from cache %p to cache %p\n", + obj, obj->ce_cache, cache); + + /* Acquire reference, if already in a cache this will be + * reverted during removal */ + nl_object_get(obj); + + if (!nl_list_empty(&obj->ce_list)) + nl_cache_remove(obj); + + return __cache_add(cache, obj); +} + +/** + * Remove object from cache. + * @arg obj Object to remove from cache + * + * Removes the object \c obj from the cache it is associated with. The + * reference counter of the object will be decremented. If the reference + * to the object was the only one remaining, the object will be freed. + * + * If no cache is associated with the object, this function is a NOP. + */ +void nl_cache_remove(struct nl_object *obj) +{ + int ret; + struct nl_cache *cache = obj->ce_cache; + + if (cache == NULL) + return; + + if (cache->hashtable) { + ret = nl_hash_table_del(cache->hashtable, obj); + if (ret < 0) + NL_DBG(2, "Failed to delete %p from cache %p <%s>.\n", + obj, cache, nl_cache_name(cache)); + } + + nl_list_del(&obj->ce_list); + obj->ce_cache = NULL; + nl_object_put(obj); + cache->c_nitems--; + + NL_DBG(2, "Deleted object %p from cache %p <%s>.\n", + obj, cache, nl_cache_name(cache)); +} + +/** @} */ + +/** + * @name Synchronization + * @{ + */ + +/** + * Set synchronization arg1 of cache + * @arg cache Cache + * @arg arg argument + * + * Synchronization arguments are used to specify filters when + * requesting dumps from the kernel. + */ +void nl_cache_set_arg1(struct nl_cache *cache, int arg) +{ + cache->c_iarg1 = arg; +} + +/** + * Set synchronization arg2 of cache + * @arg cache Cache + * @arg arg argument + * + * Synchronization arguments are used to specify filters when + * requesting dumps from the kernel. + */ +void nl_cache_set_arg2(struct nl_cache *cache, int arg) +{ + cache->c_iarg2 = arg; +} + +/** + * Set cache flags + * @arg cache Cache + * @arg flags Flags + */ +void nl_cache_set_flags(struct nl_cache *cache, unsigned int flags) +{ + cache->c_flags |= flags; +} + +/** + * Invoke the request-update operation + * @arg sk Netlink socket. + * @arg cache Cache + * + * This function causes the \e request-update function of the cache + * operations to be invoked. This usually causes a dump request to + * be sent over the netlink socket which triggers the kernel to dump + * all objects of a specific type to be dumped onto the netlink + * socket for pickup. + * + * The behaviour of this function depends on the implemenation of + * the \e request_update function of each individual type of cache. + * + * This function will not have any effects on the cache (unless the + * request_update implementation of the cache operations does so). + * + * Use nl_cache_pickup() to pick-up (read) the objects from the socket + * and fill them into the cache. + * + * @see nl_cache_pickup(), nl_cache_resync() + * + * @return 0 on success or a negative error code. Some implementations + * of co_request_update() return a positive number on success that is + * the number of bytes sent. Treat any non-negative number as success too. + */ +static int nl_cache_request_full_dump(struct nl_sock *sk, + struct nl_cache *cache) +{ + if (sk->s_proto != cache->c_ops->co_protocol) + return -NLE_PROTO_MISMATCH; + + if (cache->c_ops->co_request_update == NULL) + return -NLE_OPNOTSUPP; + + NL_DBG(2, "Requesting update from kernel for cache %p <%s>\n", + cache, nl_cache_name(cache)); + + return cache->c_ops->co_request_update(cache, sk); +} + +/** @cond SKIP */ +struct update_xdata { + struct nl_cache_ops *ops; + struct nl_parser_param *params; +}; + +static int update_msg_parser(struct nl_msg *msg, void *arg) +{ + struct update_xdata *x = arg; + int ret = 0; + + ret = nl_cache_parse(x->ops, &msg->nm_src, msg->nm_nlh, x->params); + if (ret == -NLE_EXIST) + return NL_SKIP; + else + return ret; +} +/** @endcond */ + +/** + * Pick-up a netlink request-update with your own parser + * @arg sk Netlink socket + * @arg cache Cache + * @arg param Parser parameters + */ +static int __cache_pickup(struct nl_sock *sk, struct nl_cache *cache, + struct nl_parser_param *param) +{ + int err; + struct nl_cb *cb; + struct update_xdata x = { + .ops = cache->c_ops, + .params = param, + }; + + NL_DBG(2, "Picking up answer for cache %p <%s>\n", + cache, nl_cache_name(cache)); + + cb = nl_cb_clone(sk->s_cb); + if (cb == NULL) + return -NLE_NOMEM; + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, update_msg_parser, &x); + + err = nl_recvmsgs(sk, cb); + if (err < 0) + NL_DBG(2, "While picking up for %p <%s>, recvmsgs() returned %d: %s\n", + cache, nl_cache_name(cache), err, nl_geterror(err)); + + nl_cb_put(cb); + + return err; +} + +static int pickup_checkdup_cb(struct nl_object *c, struct nl_parser_param *p) +{ + struct nl_cache *cache = (struct nl_cache *)p->pp_arg; + struct nl_object *old; + + old = nl_cache_search(cache, c); + if (old) { + if (nl_object_update(old, c) == 0) { + nl_object_put(old); + return 0; + } + + nl_cache_remove(old); + nl_object_put(old); + } + + return nl_cache_add(cache, c); +} + +static int pickup_cb(struct nl_object *c, struct nl_parser_param *p) +{ + struct nl_cache *cache = p->pp_arg; + + return nl_cache_add(cache, c); +} + +static int __nl_cache_pickup(struct nl_sock *sk, struct nl_cache *cache, + int checkdup) +{ + struct nl_parser_param p; + + p.pp_cb = checkdup ? pickup_checkdup_cb : pickup_cb; + p.pp_arg = cache; + + if (sk->s_proto != cache->c_ops->co_protocol) + return -NLE_PROTO_MISMATCH; + + return __cache_pickup(sk, cache, &p); +} + +/** + * Pickup a netlink dump response and put it into a cache. + * @arg sk Netlink socket. + * @arg cache Cache to put items into. + * + * Waits for netlink messages to arrive, parses them and puts them into + * the specified cache. + * + * @return 0 on success or a negative error code. + */ +int nl_cache_pickup_checkdup(struct nl_sock *sk, struct nl_cache *cache) +{ + return __nl_cache_pickup(sk, cache, 1); +} + +/** + * Pickup a netlink dump response and put it into a cache. + * @arg sk Netlink socket. + * @arg cache Cache to put items into. + * + * Waits for netlink messages to arrive, parses them and puts them into + * the specified cache. If an old object with same key attributes is + * present in the cache, it is replaced with the new object. + * If the old object type supports an update operation, an update is + * attempted before a replace. + * + * @return 0 on success or a negative error code. + */ +int nl_cache_pickup(struct nl_sock *sk, struct nl_cache *cache) +{ + return __nl_cache_pickup(sk, cache, 0); +} + +static int cache_include(struct nl_cache *cache, struct nl_object *obj, + struct nl_msgtype *type, change_func_t cb, + change_func_v2_t cb_v2, void *data) +{ + struct nl_object *old; + struct nl_object *clone = NULL; + uint64_t diff = 0; + + switch (type->mt_act) { + case NL_ACT_NEW: + case NL_ACT_DEL: + old = nl_cache_search(cache, obj); + if (old) { + if (cb_v2 && old->ce_ops->oo_update) { + clone = nl_object_clone(old); + diff = nl_object_diff64(old, obj); + } + /* + * Some objects types might support merging the new + * object with the old existing cache object. + * Handle them first. + */ + if (nl_object_update(old, obj) == 0) { + if (cb_v2) { + cb_v2(cache, clone, obj, diff, + NL_ACT_CHANGE, data); + nl_object_put(clone); + } else if (cb) + cb(cache, old, NL_ACT_CHANGE, data); + nl_object_put(old); + return 0; + } + nl_object_put(clone); + + nl_cache_remove(old); + if (type->mt_act == NL_ACT_DEL) { + if (cb_v2) + cb_v2(cache, old, NULL, 0, NL_ACT_DEL, + data); + else if (cb) + cb(cache, old, NL_ACT_DEL, data); + nl_object_put(old); + } + } + + if (type->mt_act == NL_ACT_NEW) { + nl_cache_move(cache, obj); + if (old == NULL) { + if (cb_v2) { + cb_v2(cache, NULL, obj, 0, NL_ACT_NEW, + data); + } else if (cb) + cb(cache, obj, NL_ACT_NEW, data); + } else if (old) { + diff = 0; + if (cb || cb_v2) + diff = nl_object_diff64(old, obj); + if (diff && cb_v2) { + cb_v2(cache, old, obj, diff, NL_ACT_CHANGE, + data); + } else if (diff && cb) + cb(cache, obj, NL_ACT_CHANGE, data); + + nl_object_put(old); + } + } + break; + default: + NL_DBG(2, "Unknown action associated to object %p\n", obj); + return 0; + } + + return 0; +} + +int nl_cache_include(struct nl_cache *cache, struct nl_object *obj, + change_func_t change_cb, void *data) +{ + struct nl_cache_ops *ops = cache->c_ops; + int i; + + if (ops->co_obj_ops != obj->ce_ops) + return -NLE_OBJ_MISMATCH; + + for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++) + if (ops->co_msgtypes[i].mt_id == obj->ce_msgtype) + return cache_include(cache, obj, &ops->co_msgtypes[i], + change_cb, NULL, data); + + NL_DBG(3, "Object %p does not seem to belong to cache %p <%s>\n", + obj, cache, nl_cache_name(cache)); + + return -NLE_MSGTYPE_NOSUPPORT; +} + +int nl_cache_include_v2(struct nl_cache *cache, struct nl_object *obj, + change_func_v2_t change_cb, void *data) +{ + struct nl_cache_ops *ops = cache->c_ops; + int i; + + if (ops->co_obj_ops != obj->ce_ops) + return -NLE_OBJ_MISMATCH; + + for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++) + if (ops->co_msgtypes[i].mt_id == obj->ce_msgtype) + return cache_include(cache, obj, &ops->co_msgtypes[i], + NULL, change_cb, data); + + NL_DBG(3, "Object %p does not seem to belong to cache %p <%s>\n", + obj, cache, nl_cache_name(cache)); + + return -NLE_MSGTYPE_NOSUPPORT; +} + +static int resync_cb(struct nl_object *c, struct nl_parser_param *p) +{ + struct nl_cache_assoc *ca = p->pp_arg; + + if (ca->ca_change_v2) + return nl_cache_include_v2(ca->ca_cache, c, ca->ca_change_v2, + ca->ca_change_data); + else + return nl_cache_include(ca->ca_cache, c, ca->ca_change, + ca->ca_change_data); +} + +int nl_cache_resync(struct nl_sock *sk, struct nl_cache *cache, + change_func_t change_cb, void *data) +{ + struct nl_object *obj, *next; + struct nl_af_group *grp; + struct nl_cache_assoc ca = { + .ca_cache = cache, + .ca_change = change_cb, + .ca_change_data = data, + }; + struct nl_parser_param p = { + .pp_cb = resync_cb, + .pp_arg = &ca, + }; + int err; + + if (sk->s_proto != cache->c_ops->co_protocol) + return -NLE_PROTO_MISMATCH; + + NL_DBG(1, "Resyncing cache %p <%s>...\n", cache, nl_cache_name(cache)); + + /* Mark all objects so we can see if some of them are obsolete */ + nl_cache_mark_all(cache); + + grp = cache->c_ops->co_groups; + do { + if (grp && grp->ag_group && + (cache->c_flags & NL_CACHE_AF_ITER)) + nl_cache_set_arg1(cache, grp->ag_family); + +restart: + err = nl_cache_request_full_dump(sk, cache); + if (err < 0) + goto errout; + + err = __cache_pickup(sk, cache, &p); + if (err == -NLE_DUMP_INTR) + goto restart; + else if (err < 0) + goto errout; + + if (grp) + grp++; + } while (grp && grp->ag_group && + (cache->c_flags & NL_CACHE_AF_ITER)); + + nl_list_for_each_entry_safe(obj, next, &cache->c_items, ce_list) { + if (nl_object_is_marked(obj)) { + nl_object_get(obj); + nl_cache_remove(obj); + if (change_cb) + change_cb(cache, obj, NL_ACT_DEL, data); + nl_object_put(obj); + } + } + + NL_DBG(1, "Finished resyncing %p <%s>\n", cache, nl_cache_name(cache)); + + err = 0; +errout: + return err; +} + +/** @} */ + +/** + * @name Parsing + * @{ + */ + +/** @cond SKIP */ +int nl_cache_parse(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, struct nl_parser_param *params) +{ + int i, err; + + if (!nlmsg_valid_hdr(nlh, ops->co_hdrsize)) + return -NLE_MSG_TOOSHORT; + + for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++) { + if (ops->co_msgtypes[i].mt_id == nlh->nlmsg_type) { + err = ops->co_msg_parser(ops, who, nlh, params); + if (err != -NLE_OPNOTSUPP) + goto errout; + } + } + + + err = -NLE_MSGTYPE_NOSUPPORT; +errout: + return err; +} +/** @endcond */ + +/** + * Parse a netlink message and add it to the cache. + * @arg cache cache to add element to + * @arg msg netlink message + * + * Parses a netlink message by calling the cache specific message parser + * and adds the new element to the cache. If an old object with same key + * attributes is present in the cache, it is replaced with the new object. + * If the old object type supports an update operation, an update is + * attempted before a replace. + * + * @return 0 or a negative error code. + */ +int nl_cache_parse_and_add(struct nl_cache *cache, struct nl_msg *msg) +{ + struct nl_parser_param p = { + .pp_cb = pickup_cb, + .pp_arg = cache, + }; + + return nl_cache_parse(cache->c_ops, NULL, nlmsg_hdr(msg), &p); +} + +/** + * (Re)fill a cache with the contents in the kernel. + * @arg sk Netlink socket. + * @arg cache cache to update + * + * Clears the specified cache and fills it with the current state in + * the kernel. + * + * @return 0 or a negative error code. + */ +int nl_cache_refill(struct nl_sock *sk, struct nl_cache *cache) +{ + struct nl_af_group *grp; + int err; + + if (sk->s_proto != cache->c_ops->co_protocol) + return -NLE_PROTO_MISMATCH; + + nl_cache_clear(cache); + grp = cache->c_ops->co_groups; + do { + if (grp && grp->ag_group && + (cache->c_flags & NL_CACHE_AF_ITER)) + nl_cache_set_arg1(cache, grp->ag_family); + +restart: + err = nl_cache_request_full_dump(sk, cache); + if (err < 0) + return err; + + NL_DBG(2, "Updating cache %p <%s> for family %u, request sent, waiting for reply\n", + cache, nl_cache_name(cache), grp ? grp->ag_family : AF_UNSPEC); + + err = nl_cache_pickup(sk, cache); + if (err == -NLE_DUMP_INTR) { + NL_DBG(2, "Dump interrupted, restarting!\n"); + goto restart; + } else if (err < 0) + break; + + if (grp) + grp++; + } while (grp && grp->ag_group && + (cache->c_flags & NL_CACHE_AF_ITER)); + + return err; +} + +/** @} */ + +/** + * @name Utillities + * @{ + */ +static struct nl_object *__cache_fast_lookup(struct nl_cache *cache, + struct nl_object *needle) +{ + struct nl_object *obj; + + obj = nl_hash_table_lookup(cache->hashtable, needle); + if (obj) { + nl_object_get(obj); + return obj; + } + + return NULL; +} + +/** + * Search object in cache + * @arg cache Cache + * @arg needle Object to look for. + * + * Searches the cache for an object which matches the object \p needle. + * The function nl_object_identical() is used to determine if the + * objects match. If a matching object is found, the reference counter + * is incremented and the object is returned. + * + * Therefore, if an object is returned, the reference to the object + * must be returned by calling nl_object_put() after usage. + * + * @return Reference to object or NULL if not found. + */ +struct nl_object *nl_cache_search(struct nl_cache *cache, + struct nl_object *needle) +{ + struct nl_object *obj; + + if (cache->hashtable) + return __cache_fast_lookup(cache, needle); + + nl_list_for_each_entry(obj, &cache->c_items, ce_list) { + if (nl_object_identical(obj, needle)) { + nl_object_get(obj); + return obj; + } + } + + return NULL; +} + +/** + * Find object in cache + * @arg cache Cache + * @arg filter object acting as a filter + * + * Searches the cache for an object which matches the object filter. + * If the filter attributes matches the object type id attributes, + * and the cache supports hash lookups, a faster hashtable lookup + * is used to return the object. Else, function nl_object_match_filter() is + * used to determine if the objects match. If a matching object is + * found, the reference counter is incremented and the object is returned. + * + * Therefore, if an object is returned, the reference to the object + * must be returned by calling nl_object_put() after usage. + * + * @return Reference to object or NULL if not found. + */ +struct nl_object *nl_cache_find(struct nl_cache *cache, + struct nl_object *filter) +{ + struct nl_object *obj; + + if (cache->c_ops == NULL) + BUG(); + + if ((nl_object_get_id_attrs(filter) == filter->ce_mask) + && cache->hashtable) + return __cache_fast_lookup(cache, filter); + + nl_list_for_each_entry(obj, &cache->c_items, ce_list) { + if (nl_object_match_filter(obj, filter)) { + nl_object_get(obj); + return obj; + } + } + + return NULL; +} + +/** + * Mark all objects of a cache + * @arg cache Cache + * + * Marks all objects of a cache by calling nl_object_mark() on each + * object associated with the cache. + */ +void nl_cache_mark_all(struct nl_cache *cache) +{ + struct nl_object *obj; + + NL_DBG(2, "Marking all objects in cache %p <%s>\n", + cache, nl_cache_name(cache)); + + nl_list_for_each_entry(obj, &cache->c_items, ce_list) + nl_object_mark(obj); +} + +/** @} */ + +/** + * @name Dumping + * @{ + */ + +/** + * Dump all elements of a cache. + * @arg cache cache to dump + * @arg params dumping parameters + * + * Dumps all elements of the \a cache to the file descriptor \a fd. + */ +void nl_cache_dump(struct nl_cache *cache, struct nl_dump_params *params) +{ + nl_cache_dump_filter(cache, params, NULL); +} + +/** + * Dump all elements of a cache (filtered). + * @arg cache cache to dump + * @arg params dumping parameters + * @arg filter filter object + * + * Dumps all elements of the \a cache to the file descriptor \a fd + * given they match the given filter \a filter. + */ +void nl_cache_dump_filter(struct nl_cache *cache, + struct nl_dump_params *params, + struct nl_object *filter) +{ + struct nl_dump_params params_copy; + struct nl_object_ops *ops; + struct nl_object *obj; + int type; + + NL_DBG(2, "Dumping cache %p <%s> with filter %p\n", + cache, nl_cache_name(cache), filter); + + if (!params) { + /* It doesn't really make sense that @params is an optional parameter. In the + * past, nl_cache_dump() was documented that the @params would be optional, so + * try to save it. + * + * Note that this still isn't useful, because we don't set any dump option. + * It only exists not to crash applications that wrongly pass %NULL here. */ + _nl_assert_not_reached (); + params_copy = (struct nl_dump_params) { + .dp_type = NL_DUMP_DETAILS, + }; + params = ¶ms_copy; + } + + type = params->dp_type; + + if (type > NL_DUMP_MAX || type < 0) + BUG(); + + if (cache->c_ops == NULL) + BUG(); + + ops = cache->c_ops->co_obj_ops; + if (!ops->oo_dump[type]) + return; + + if (params->dp_buf) + memset(params->dp_buf, 0, params->dp_buflen); + + nl_list_for_each_entry(obj, &cache->c_items, ce_list) { + if (filter && !nl_object_match_filter(obj, filter)) + continue; + + NL_DBG(4, "Dumping object %p...\n", obj); + dump_from_ops(obj, params); + } +} + +/** @} */ + +/** + * @name Iterators + * @{ + */ + +/** + * Call a callback on each element of the cache. + * @arg cache cache to iterate on + * @arg cb callback function + * @arg arg argument passed to callback function + * + * Calls a callback function \a cb on each element of the \a cache. + * The argument \a arg is passed on the callback function. + */ +void nl_cache_foreach(struct nl_cache *cache, + void (*cb)(struct nl_object *, void *), void *arg) +{ + nl_cache_foreach_filter(cache, NULL, cb, arg); +} + +/** + * Call a callback on each element of the cache (filtered). + * @arg cache cache to iterate on + * @arg filter filter object + * @arg cb callback function + * @arg arg argument passed to callback function + * + * Calls a callback function \a cb on each element of the \a cache + * that matches the \a filter. The argument \a arg is passed on + * to the callback function. + */ +void nl_cache_foreach_filter(struct nl_cache *cache, struct nl_object *filter, + void (*cb)(struct nl_object *, void *), void *arg) +{ + struct nl_object *obj, *tmp; + + if (cache->c_ops == NULL) + BUG(); + + nl_list_for_each_entry_safe(obj, tmp, &cache->c_items, ce_list) { + if (filter) { + int diff = nl_object_match_filter(obj, filter); + + NL_DBG(3, "%p<->%p object difference: %x\n", + obj, filter, diff); + + if (!diff) + continue; + } + + /* Caller may hold obj for a long time */ + nl_object_get(obj); + + cb(obj, arg); + + nl_object_put(obj); + } +} + +/** @} */ + +/** @} */ diff --git a/libnl/lib/cache_mngr.c b/libnl/lib/cache_mngr.c new file mode 100644 index 0000000..04a164e --- /dev/null +++ b/libnl/lib/cache_mngr.c @@ -0,0 +1,636 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +/** + * @ingroup cache_mngt + * @defgroup cache_mngr Manager + * @brief Manager keeping caches up to date automatically. + * + * The cache manager keeps caches up to date automatically by listening to + * netlink notifications and integrating the received information into the + * existing cache. + * + * @note This functionality is still considered experimental. + * + * Related sections in the development guide: + * - @core_doc{_cache_manager,Cache Manager} + * + * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define NASSOC_INIT 16 +#define NASSOC_EXPAND 8 +/** @endcond */ + +static int include_cb(struct nl_object *obj, struct nl_parser_param *p) +{ + struct nl_cache_assoc *ca = p->pp_arg; + struct nl_cache_ops *ops = ca->ca_cache->c_ops; + + NL_DBG(2, "Including object %p into cache %p\n", obj, ca->ca_cache); +#ifdef NL_DEBUG + if (nl_debug >= 4) + nl_object_dump(obj, &nl_debug_dp); +#endif + + if (ops->co_event_filter) + if (ops->co_event_filter(ca->ca_cache, obj) != NL_OK) + return 0; + + if (ops->co_include_event) + return ops->co_include_event(ca->ca_cache, obj, ca->ca_change, + ca->ca_change_v2, + ca->ca_change_data); + else { + if (ca->ca_change_v2) + return nl_cache_include_v2(ca->ca_cache, obj, ca->ca_change_v2, ca->ca_change_data); + else + return nl_cache_include(ca->ca_cache, obj, ca->ca_change, ca->ca_change_data); + } + +} + +static int event_input(struct nl_msg *msg, void *arg) +{ + struct nl_cache_mngr *mngr = arg; + int protocol = nlmsg_get_proto(msg); + int type = nlmsg_hdr(msg)->nlmsg_type; + struct nl_cache_ops *ops; + int i, n; + struct nl_parser_param p = { + .pp_cb = include_cb, + }; + + NL_DBG(2, "Cache manager %p, handling new message %p as event\n", + mngr, msg); +#ifdef NL_DEBUG + if (nl_debug >= 4) + nl_msg_dump(msg, stderr); +#endif + + if (mngr->cm_protocol != protocol) + BUG(); + + for (i = 0; i < mngr->cm_nassocs; i++) { + if (mngr->cm_assocs[i].ca_cache) { + ops = mngr->cm_assocs[i].ca_cache->c_ops; + for (n = 0; ops->co_msgtypes[n].mt_id >= 0; n++) + if (ops->co_msgtypes[n].mt_id == type) + goto found; + } + } + + return NL_SKIP; + +found: + NL_DBG(2, "Associated message %p to cache %p\n", + msg, mngr->cm_assocs[i].ca_cache); + p.pp_arg = &mngr->cm_assocs[i]; + + return nl_cache_parse(ops, NULL, nlmsg_hdr(msg), &p); +} + +/** + * Allocate new cache manager + * @arg sk Netlink socket or NULL to auto allocate + * @arg protocol Netlink protocol this manager is used for + * @arg flags Flags (\c NL_AUTO_PROVIDE) + * @arg result Result pointer + * + * Allocates a new cache manager for the specified netlink protocol. + * + * 1. If sk is not specified (\c NULL) a netlink socket matching the + * specified protocol will be automatically allocated. + * + * 2. The socket will be put in non-blocking mode and sequence checking + * will be disabled regardless of whether the socket was provided by + * the caller or automatically allocated. + * + * 3. The socket will be connected. + * + * If the flag \c NL_AUTO_PROVIDE is specified, any cache added to the + * manager will automatically be made available to other users using + * nl_cache_mngt_provide(). + * + * @note If the socket is provided by the caller, it is NOT recommended + * to use the socket for anything else besides receiving netlink + * notifications. + * + * @return 0 on success or a negative error code. + */ +int nl_cache_mngr_alloc(struct nl_sock *sk, int protocol, int flags, + struct nl_cache_mngr **result) +{ + struct nl_cache_mngr *mngr; + int err = -NLE_NOMEM; + + /* Catch abuse of flags */ + if (flags & NL_ALLOCATED_SOCK) + BUG(); + + mngr = calloc(1, sizeof(*mngr)); + if (!mngr) + return -NLE_NOMEM; + + if (!sk) { + if (!(sk = nl_socket_alloc())) + goto errout; + + flags |= NL_ALLOCATED_SOCK; + } + + mngr->cm_sock = sk; + mngr->cm_nassocs = NASSOC_INIT; + mngr->cm_protocol = protocol; + mngr->cm_flags = flags; + mngr->cm_assocs = calloc(mngr->cm_nassocs, + sizeof(struct nl_cache_assoc)); + if (!mngr->cm_assocs) + goto errout; + + /* Required to receive async event notifications */ + nl_socket_disable_seq_check(mngr->cm_sock); + + if ((err = nl_connect(mngr->cm_sock, protocol)) < 0) + goto errout; + + if ((err = nl_socket_set_nonblocking(mngr->cm_sock)) < 0) + goto errout; + + /* Create and allocate socket for sync cache fills */ + mngr->cm_sync_sock = nl_socket_alloc(); + if (!mngr->cm_sync_sock) { + err = -NLE_NOMEM; + goto errout; + } + if ((err = nl_connect(mngr->cm_sync_sock, protocol)) < 0) + goto errout_free_sync_sock; + + NL_DBG(1, "Allocated cache manager %p, protocol %d, %d caches\n", + mngr, protocol, mngr->cm_nassocs); + + *result = mngr; + return 0; + +errout_free_sync_sock: + nl_socket_free(mngr->cm_sync_sock); +errout: + nl_cache_mngr_free(mngr); + return err; +} + +/** + * Set change_func_v2 for cache manager + * @arg mngr Cache manager. + * @arg cache Cache associated with the callback + * @arg cb Function to be called upon changes. + * @arg data Argument passed on to change callback + * + * Adds callback change_func_v2 to a registered cache. This callback provides + * in like the standard change_func the added or remove netlink object. In case + * of a change the old and the new object is provided as well as the according + * diff. If this callback is registered this has a higher priority then the + * change_func registered during cache registration. Hence only one callback is + * executed. + * + * The first netlink object in the callback is refering to the old object and + * the second to the new. This means on NL_ACT_CHANGE the first is the previous + * object in the cache and the second the updated version. On NL_ACT_DEL the + * first is the deleted object the second is NULL. On NL_ACT_NEW the first is + * NULL and the second the new netlink object. + * + * The user is responsible for calling nl_cache_mngr_poll() or monitor + * the socket and call nl_cache_mngr_data_ready() to allow the library + * to process netlink notification events. + * + * @see nl_cache_mngr_poll() + * @see nl_cache_mngr_data_ready() + * + * @return 0 on success or a negative error code. + * @return -NLE_PROTO_MISMATCH Protocol mismatch between cache manager and + * cache type + * @return -NLE_OPNOTSUPP Cache type does not support updates + * @return -NLE_RANGE Cache of this type is not registered + */ +static int nl_cache_mngr_set_change_func_v2(struct nl_cache_mngr *mngr, + struct nl_cache *cache, + change_func_v2_t cb, void *data) +{ + struct nl_cache_ops *ops; + int i; + + ops = cache->c_ops; + if (!ops) + return -NLE_INVAL; + + if (ops->co_protocol != mngr->cm_protocol) + return -NLE_PROTO_MISMATCH; + + if (ops->co_groups == NULL) + return -NLE_OPNOTSUPP; + + for (i = 0; i < mngr->cm_nassocs; i++) + if (mngr->cm_assocs[i].ca_cache == cache) + break; + + if (i >= mngr->cm_nassocs) { + return -NLE_RANGE; + } + + mngr->cm_assocs[i].ca_change_v2 = cb; + mngr->cm_assocs[i].ca_change_data = data; + + return 0; +} + +/** + * Add cache to cache manager + * @arg mngr Cache manager. + * @arg cache Cache to be added to cache manager + * @arg cb Function to be called upon changes. + * @arg data Argument passed on to change callback + * + * Adds cache to the manager. The operation will trigger a full + * dump request from the kernel to initially fill the contents + * of the cache. The manager will subscribe to the notification group + * of the cache and keep track of any further changes. + * + * The user is responsible for calling nl_cache_mngr_poll() or monitor + * the socket and call nl_cache_mngr_data_ready() to allow the library + * to process netlink notification events. + * + * @see nl_cache_mngr_poll() + * @see nl_cache_mngr_data_ready() + * + * @return 0 on success or a negative error code. + * @return -NLE_PROTO_MISMATCH Protocol mismatch between cache manager and + * cache type + * @return -NLE_OPNOTSUPP Cache type does not support updates + * @return -NLE_EXIST Cache of this type already being managed + */ +int nl_cache_mngr_add_cache(struct nl_cache_mngr *mngr, struct nl_cache *cache, + change_func_t cb, void *data) +{ + struct nl_cache_ops *ops; + struct nl_af_group *grp; + int err, i; + + ops = cache->c_ops; + if (!ops) + return -NLE_INVAL; + + if (ops->co_protocol != mngr->cm_protocol) + return -NLE_PROTO_MISMATCH; + + if (ops->co_groups == NULL) + return -NLE_OPNOTSUPP; + + for (i = 0; i < mngr->cm_nassocs; i++) + if (mngr->cm_assocs[i].ca_cache && + mngr->cm_assocs[i].ca_cache->c_ops == ops) + return -NLE_EXIST; + + for (i = 0; i < mngr->cm_nassocs; i++) + if (!mngr->cm_assocs[i].ca_cache) + break; + + if (i >= mngr->cm_nassocs) { + struct nl_cache_assoc *cm_assocs; + int cm_nassocs = mngr->cm_nassocs + NASSOC_EXPAND; + + cm_assocs = realloc(mngr->cm_assocs, + cm_nassocs * sizeof(struct nl_cache_assoc)); + if (cm_assocs == NULL) + return -NLE_NOMEM; + + memset(cm_assocs + mngr->cm_nassocs, 0, + NASSOC_EXPAND * sizeof(struct nl_cache_assoc)); + mngr->cm_assocs = cm_assocs; + mngr->cm_nassocs = cm_nassocs; + + NL_DBG(1, "Increased capacity of cache manager %p " \ + "to %d\n", mngr, mngr->cm_nassocs); + } + + for (grp = ops->co_groups; grp->ag_group; grp++) { + err = nl_socket_add_membership(mngr->cm_sock, grp->ag_group); + if (err < 0) + return err; + } + + err = nl_cache_refill(mngr->cm_sync_sock, cache); + if (err < 0) + goto errout_drop_membership; + + mngr->cm_assocs[i].ca_cache = cache; + mngr->cm_assocs[i].ca_change = cb; + mngr->cm_assocs[i].ca_change_data = data; + + if (mngr->cm_flags & NL_AUTO_PROVIDE) + nl_cache_mngt_provide(cache); + + NL_DBG(1, "Added cache %p <%s> to cache manager %p\n", + cache, nl_cache_name(cache), mngr); + + return 0; + +errout_drop_membership: + for (grp = ops->co_groups; grp->ag_group; grp++) + nl_socket_drop_membership(mngr->cm_sock, grp->ag_group); + + return err; +} + +/** + * Add cache to cache manager + * @arg mngr Cache manager. + * @arg cache Cache to be added to cache manager + * @arg cb V2 function to be called upon changes. + * @arg data Argument passed on to change callback + * + * Adds cache to the manager. The operation will trigger a full + * dump request from the kernel to initially fill the contents + * of the cache. The manager will subscribe to the notification group + * of the cache and keep track of any further changes. + * + * The user is responsible for calling nl_cache_mngr_poll() or monitor + * the socket and call nl_cache_mngr_data_ready() to allow the library + * to process netlink notification events. + * + * @see nl_cache_mngr_poll() + * @see nl_cache_mngr_data_ready() + * + * @return 0 on success or a negative error code. + * @return -NLE_PROTO_MISMATCH Protocol mismatch between cache manager and + * cache type + * @return -NLE_OPNOTSUPP Cache type does not support updates + * @return -NLE_EXIST Cache of this type already being managed + */ +int nl_cache_mngr_add_cache_v2(struct nl_cache_mngr *mngr, struct nl_cache *cache, + change_func_v2_t cb, void *data) { + int err; + err = nl_cache_mngr_add_cache(mngr, cache, NULL, NULL); + if (err < 0) + return err; + + return nl_cache_mngr_set_change_func_v2(mngr, cache, cb, data); +} + +/** + * Add cache to cache manager + * @arg mngr Cache manager. + * @arg name Name of cache to keep track of + * @arg cb Function to be called upon changes. + * @arg data Argument passed on to change callback + * @arg result Pointer to store added cache (optional) + * + * Allocates a new cache of the specified type and adds it to the manager. + * The operation will trigger a full dump request from the kernel to + * initially fill the contents of the cache. The manager will subscribe + * to the notification group of the cache and keep track of any further + * changes. + * + * The user is responsible for calling nl_cache_mngr_poll() or monitor + * the socket and call nl_cache_mngr_data_ready() to allow the library + * to process netlink notification events. + * + * @note Versions up to 3.4.0 actually required the result argument, preventing + * NULL to be passed. + * + * @see nl_cache_mngr_poll() + * @see nl_cache_mngr_data_ready() + * + * @return 0 on success or a negative error code. + * @return -NLE_NOCACHE Unknown cache type + * @return -NLE_PROTO_MISMATCH Protocol mismatch between cache manager and + * cache type + * @return -NLE_OPNOTSUPP Cache type does not support updates + * @return -NLE_EXIST Cache of this type already being managed + */ +int nl_cache_mngr_add(struct nl_cache_mngr *mngr, const char *name, + change_func_t cb, void *data, struct nl_cache **result) +{ + struct nl_cache_ops *ops; + struct nl_cache *cache; + int err; + + ops = nl_cache_ops_lookup_safe(name); + if (!ops) + return -NLE_NOCACHE; + + cache = nl_cache_alloc(ops); + nl_cache_ops_put(ops); + if (!cache) + return -NLE_NOMEM; + + err = nl_cache_mngr_add_cache(mngr, cache, cb, data); + if (err < 0) + goto errout_free_cache; + + if (result) + *result = cache; + return 0; + +errout_free_cache: + nl_cache_free(cache); + + return err; +} + +/** + * Get socket file descriptor + * @arg mngr Cache Manager + * + * Get the file descriptor of the socket associated with the manager. + * + * @note Do not use the socket for anything besides receiving + * notifications. + */ +int nl_cache_mngr_get_fd(struct nl_cache_mngr *mngr) +{ + return nl_socket_get_fd(mngr->cm_sock); +} + +/** + * Check for event notifications + * @arg mngr Cache Manager + * @arg timeout Upper limit poll() will block, in milliseconds. + * + * Causes poll() to be called to check for new event notifications + * being available. Calls nl_cache_mngr_data_ready() to process + * available data. + * + * This functionally is ideally called regularly during an idle + * period. + * + * A timeout can be specified in milliseconds to limit the time the + * function will wait for updates. + * + * @see nl_cache_mngr_data_ready() + * + * @return The number of messages processed or a negative error code. + */ +int nl_cache_mngr_poll(struct nl_cache_mngr *mngr, int timeout) +{ + int ret; + struct pollfd fds = { + .fd = nl_socket_get_fd(mngr->cm_sock), + .events = POLLIN, + }; + + NL_DBG(3, "Cache manager %p, poll() fd %d\n", mngr, fds.fd); + ret = poll(&fds, 1, timeout); + NL_DBG(3, "Cache manager %p, poll() returned %d\n", mngr, ret); + if (ret < 0) { + NL_DBG(4, "nl_cache_mngr_poll(%p): poll() failed with %d (%s)\n", + mngr, errno, nl_strerror_l(errno)); + return -nl_syserr2nlerr(errno); + } + + /* No events, return */ + if (ret == 0) + return 0; + + return nl_cache_mngr_data_ready(mngr); +} + +/** + * Receive available event notifications + * @arg mngr Cache manager + * + * This function can be called if the socket associated to the manager + * contains updates to be received. This function should only be used + * if nl_cache_mngr_poll() is not used. + * + * The function will process messages until there is no more data to + * be read from the socket. + * + * @see nl_cache_mngr_poll() + * + * @return The number of messages processed or a negative error code. + */ +int nl_cache_mngr_data_ready(struct nl_cache_mngr *mngr) +{ + int err, nread = 0; + struct nl_cb *cb; + + NL_DBG(2, "Cache manager %p, reading new data from fd %d\n", + mngr, nl_socket_get_fd(mngr->cm_sock)); + + cb = nl_cb_clone(mngr->cm_sock->s_cb); + if (cb == NULL) + return -NLE_NOMEM; + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, event_input, mngr); + + while ((err = nl_recvmsgs_report(mngr->cm_sock, cb)) > 0) { + NL_DBG(2, "Cache manager %p, recvmsgs read %d messages\n", + mngr, err); + nread += err; + } + + nl_cb_put(cb); + if (err < 0 && err != -NLE_AGAIN) + return err; + + return nread; +} + +/** + * Print information about cache manager + * @arg mngr Cache manager + * @arg p Dumping parameters + * + * Prints information about the cache manager including all managed caches. + * + * @note This is a debugging function. + */ +void nl_cache_mngr_info(struct nl_cache_mngr *mngr, struct nl_dump_params *p) +{ + char buf[128]; + int i; + + nl_dump_line(p, "cache-manager <%p>\n", mngr); + nl_dump_line(p, " .protocol = %s\n", + nl_nlfamily2str(mngr->cm_protocol, buf, sizeof(buf))); + nl_dump_line(p, " .flags = %#x\n", mngr->cm_flags); + nl_dump_line(p, " .nassocs = %u\n", mngr->cm_nassocs); + nl_dump_line(p, " .sock = <%p>\n", mngr->cm_sock); + + for (i = 0; i < mngr->cm_nassocs; i++) { + struct nl_cache_assoc *assoc = &mngr->cm_assocs[i]; + + if (assoc->ca_cache) { + nl_dump_line(p, " .cache[%d] = <%p> {\n", i, assoc->ca_cache); + nl_dump_line(p, " .name = %s\n", assoc->ca_cache->c_ops->co_name); + nl_dump_line(p, " .change_func = <%p>\n", assoc->ca_change); + nl_dump_line(p, " .change_data = <%p>\n", assoc->ca_change_data); + nl_dump_line(p, " .nitems = %u\n", nl_cache_nitems(assoc->ca_cache)); + nl_dump_line(p, " .objects = {\n"); + + p->dp_prefix += 6; + nl_cache_dump(assoc->ca_cache, p); + p->dp_prefix -= 6; + + nl_dump_line(p, " }\n"); + nl_dump_line(p, " }\n"); + } + } +} + +/** + * Free cache manager and all caches. + * @arg mngr Cache manager. + * + * Release all resources held by a cache manager. + */ +void nl_cache_mngr_free(struct nl_cache_mngr *mngr) +{ + int i; + + if (!mngr) + return; + + if (mngr->cm_sock) + nl_close(mngr->cm_sock); + + if (mngr->cm_sync_sock) { + nl_close(mngr->cm_sync_sock); + nl_socket_free(mngr->cm_sync_sock); + } + + if (mngr->cm_flags & NL_ALLOCATED_SOCK) + nl_socket_free(mngr->cm_sock); + + for (i = 0; i < mngr->cm_nassocs; i++) { + if (mngr->cm_assocs[i].ca_cache) { + nl_cache_mngt_unprovide(mngr->cm_assocs[i].ca_cache); + nl_cache_free(mngr->cm_assocs[i].ca_cache); + } + } + + free(mngr->cm_assocs); + + NL_DBG(1, "Cache manager %p freed\n", mngr); + + free(mngr); +} + +/** @} */ diff --git a/libnl/lib/cache_mngt.c b/libnl/lib/cache_mngt.c new file mode 100644 index 0000000..8dee485 --- /dev/null +++ b/libnl/lib/cache_mngt.c @@ -0,0 +1,438 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +/** + * @ingroup core + * @defgroup cache_mngt Caching System + * + * Related sections in the development guide: + * - @core_doc{core_cache, Caching System} + * + * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include + +static struct nl_cache_ops *cache_ops; +static NL_RW_LOCK(cache_ops_lock); + +/** + * @name Cache Operations Sets + * @{ + */ + +static struct nl_cache_ops *__nl_cache_ops_lookup(const char *name) +{ + struct nl_cache_ops *ops; + + for (ops = cache_ops; ops; ops = ops->co_next) + if (!strcmp(ops->co_name, name)) + return ops; + + return NULL; +} + +/** + * Increment reference counter + * @arg ops Cache operations + */ +void nl_cache_ops_get(struct nl_cache_ops *ops) +{ + ops->co_refcnt++; +} + +/** + * Decrement reference counter + * @arg ops Cache operations + */ +void nl_cache_ops_put(struct nl_cache_ops *ops) +{ + ops->co_refcnt--; +} + +/** + * Lookup cache operations by name + * @arg name name of the cache type + * + * @attention This function is not safe, it does not increment the reference + * counter. Please use nl_cache_ops_lookup_safe(). + * + * @return The cache operations or NULL if not found. + */ +struct nl_cache_ops *nl_cache_ops_lookup(const char *name) +{ + struct nl_cache_ops *ops; + + nl_read_lock(&cache_ops_lock); + ops = __nl_cache_ops_lookup(name); + nl_read_unlock(&cache_ops_lock); + + return ops; +} + +/** + * Lookup cache operations by name + * @arg name name of the cache type + * + * @note The reference counter of the returned cache operation is incremented + * and must be decremented after use with nl_cache_ops_put(). + * + * @return The cache operations or NULL if not found. + */ +struct nl_cache_ops *nl_cache_ops_lookup_safe(const char *name) +{ + struct nl_cache_ops *ops; + + nl_write_lock(&cache_ops_lock); + if ((ops = __nl_cache_ops_lookup(name))) + nl_cache_ops_get(ops); + nl_write_unlock(&cache_ops_lock); + + return ops; +} + +static struct nl_cache_ops *__cache_ops_associate(int protocol, int msgtype) +{ + int i; + struct nl_cache_ops *ops; + + for (ops = cache_ops; ops; ops = ops->co_next) { + if (ops->co_protocol != protocol) + continue; + + for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++) + if (ops->co_msgtypes[i].mt_id == msgtype) + return ops; + } + + return NULL; +} + +/** + * Associate protocol and message type to cache operations + * @arg protocol netlink protocol + * @arg msgtype netlink message type + * + * @attention This function is not safe, it does not increment the reference + * counter. Please use nl_cache_ops_associate_safe(). + * + * @see nl_cache_ops_associate_safe() + * + * @return The cache operations or NULL if no match found. + */ +struct nl_cache_ops *nl_cache_ops_associate(int protocol, int msgtype) +{ + struct nl_cache_ops *ops; + + nl_read_lock(&cache_ops_lock); + ops = __cache_ops_associate(protocol, msgtype); + nl_read_unlock(&cache_ops_lock); + + return ops; +} + +/** + * Associate protocol and message type to cache operations + * @arg protocol netlink protocol + * @arg msgtype netlink message type + * + * Searches the registered cache operations for a matching protocol + * and message type. + * + * @note The reference counter of the returned cache operation is incremented + * and must be decremented after use with nl_cache_ops_put(). + * + * @return The cache operations or NULL if no no match was found. + */ +struct nl_cache_ops *nl_cache_ops_associate_safe(int protocol, int msgtype) +{ + struct nl_cache_ops *ops; + + nl_write_lock(&cache_ops_lock); + if ((ops = __cache_ops_associate(protocol, msgtype))) + nl_cache_ops_get(ops); + nl_write_unlock(&cache_ops_lock); + + return ops; +} + +/** + * Lookup message type cache association + * @arg ops cache operations + * @arg msgtype netlink message type + * + * Searches for a matching message type association ing the specified + * cache operations. + * + * @attention The guranteed lifetime of the returned message type is bound + * to the lifetime of the underlying cache operations. + * + * @return A message type association or NULL. + */ +struct nl_msgtype *nl_msgtype_lookup(struct nl_cache_ops *ops, int msgtype) +{ + int i; + + for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++) + if (ops->co_msgtypes[i].mt_id == msgtype) + return &ops->co_msgtypes[i]; + + return NULL; +} + +/* Must hold cache_ops_lock */ +static struct nl_cache_ops *cache_ops_lookup_for_obj(struct nl_object_ops *obj_ops) +{ + struct nl_cache_ops *ops; + + for (ops = cache_ops; ops; ops = ops->co_next) + if (ops->co_obj_ops == obj_ops) + return ops; + + return NULL; + +} + +/** + * Call a function for each registered cache operation + * @arg cb Callback function to be called + * @arg arg User specific argument. + */ +void nl_cache_ops_foreach(void (*cb)(struct nl_cache_ops *, void *), void *arg) +{ + struct nl_cache_ops *ops; + + nl_read_lock(&cache_ops_lock); + for (ops = cache_ops; ops; ops = ops->co_next) + cb(ops, arg); + nl_read_unlock(&cache_ops_lock); +} + +/** + * Set default flags for caches of this type + * @arg ops Cache ops + * @arg flags Flags to set + * + * The cache operation flags will be derived to all caches allocates + * based on this set of cache operations. + */ +void nl_cache_ops_set_flags(struct nl_cache_ops *ops, unsigned int flags) +{ + nl_write_lock(&cache_ops_lock); + ops->co_flags |= flags; + nl_write_unlock(&cache_ops_lock); +} + +/** + * Register a set of cache operations + * @arg ops cache operations + * + * Called by users of caches to announce the avaibility of + * a certain cache type. + * + * @return 0 on success or a negative error code. + */ +int nl_cache_mngt_register(struct nl_cache_ops *ops) +{ + if (!ops->co_name || !ops->co_obj_ops) + return -NLE_INVAL; + + /* oo_keygen() also needs oo_compare() */ + BUG_ON (ops->co_obj_ops->oo_keygen && !ops->co_obj_ops->oo_compare); + + nl_write_lock(&cache_ops_lock); + if (__nl_cache_ops_lookup(ops->co_name)) { + nl_write_unlock(&cache_ops_lock); + return -NLE_EXIST; + } + + ops->co_refcnt = 0; + ops->co_next = cache_ops; + cache_ops = ops; + nl_write_unlock(&cache_ops_lock); + + NL_DBG(1, "Registered cache operations %s\n", ops->co_name); + + return 0; +} + +/** + * Unregister a set of cache operations + * @arg ops cache operations + * + * Called by users of caches to announce a set of + * cache operations is no longer available. The + * specified cache operations must have been registered + * previously using nl_cache_mngt_register() + * + * @return 0 on success or a negative error code + */ +int nl_cache_mngt_unregister(struct nl_cache_ops *ops) +{ + struct nl_cache_ops *t, **tp; + int err = 0; + + nl_write_lock(&cache_ops_lock); + + if (ops->co_refcnt > 0) { + err = -NLE_BUSY; + goto errout; + } + + for (tp = &cache_ops; (t=*tp) != NULL; tp = &t->co_next) + if (t == ops) + break; + + if (!t) { + err = -NLE_NOCACHE; + goto errout; + } + + NL_DBG(1, "Unregistered cache operations %s\n", ops->co_name); + + *tp = t->co_next; +errout: + nl_write_unlock(&cache_ops_lock); + + return err; +} + +/** @} */ + +/** + * @name Global Cache Provisioning/Requiring + * @{ + */ + +/** + * Provide a cache for global use + * @arg cache cache to provide + * + * Offers the specified cache to be used by other modules. + * Only one cache per type may be shared at a time, + * a previsouly provided caches will be overwritten. + */ +void nl_cache_mngt_provide(struct nl_cache *cache) +{ + struct nl_cache_ops *ops; + + nl_write_lock(&cache_ops_lock); + + ops = cache_ops_lookup_for_obj(cache->c_ops->co_obj_ops); + if (!ops) + BUG(); + else { + nl_cache_get(cache); + + /* + * Hold a reference to the cache operations to ensure the + * ops don't go away while we use it to store the cache pointer. + */ + if (!ops->co_major_cache) + nl_cache_ops_get(ops); + + ops->co_major_cache = cache; + } + + nl_write_unlock(&cache_ops_lock); +} + +/** + * Unprovide a cache for global use + * @arg cache cache to unprovide + * + * Cancels the offer to use a cache globally. The + * cache will no longer be returned via lookups but + * may still be in use. + */ +void nl_cache_mngt_unprovide(struct nl_cache *cache) +{ + struct nl_cache_ops *ops; + + nl_write_lock(&cache_ops_lock); + + ops = cache_ops_lookup_for_obj(cache->c_ops->co_obj_ops); + if (!ops) + BUG(); + else if (ops->co_major_cache == cache) { + nl_cache_free(ops->co_major_cache); + nl_cache_ops_put(ops); + ops->co_major_cache = NULL; + } + + nl_write_unlock(&cache_ops_lock); +} + +struct nl_cache *__nl_cache_mngt_require(const char *name) +{ + struct nl_cache_ops *ops; + struct nl_cache *cache = NULL; + + ops = nl_cache_ops_lookup_safe(name); + if (ops) { + cache = ops->co_major_cache; + nl_cache_ops_put(ops); + } + + return cache; +} + +/** + * Return cache previously provided via nl_cache_mngt_provide() + * @arg name Name of cache to lookup + * + * @attention This function is not safe, it does not increment the reference + * counter. Please use nl_cache_mngt_require_safe(). + * + * @see nl_cache_mngt_require_safe() + * + * @return Pointer to cache or NULL if none registered + */ +struct nl_cache *nl_cache_mngt_require(const char *name) +{ + struct nl_cache *cache; + + if (!(cache = __nl_cache_mngt_require(name))) + NL_DBG(1, "Application BUG: Your application must " + "call nl_cache_mngt_provide() and\nprovide a valid " + "%s cache to be used for internal lookups.\nSee the " + " API documentation for more details.\n", name); + + return cache; +} + +/** + * Return cache previously provided via nl_cache_mngt_provide() + * @arg name Name of cache to lookup + * + * @note The reference counter of the returned cache is incremented + * and must be decremented after use with nl_cache_put(). + * + * @return Pointer to cache or NULL if none registered + */ +struct nl_cache *nl_cache_mngt_require_safe(const char *name) +{ + struct nl_cache *cache; + + if ((cache = nl_cache_mngt_require(name))) + nl_cache_get(cache); + + return cache; +} + +/** @} */ + +/** @} */ diff --git a/libnl/lib/data.c b/libnl/lib/data.c new file mode 100644 index 0000000..2b14bdc --- /dev/null +++ b/libnl/lib/data.c @@ -0,0 +1,194 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +/** + * @ingroup core_types + * @defgroup data Abstract Data + * + * Abstract data type representing a binary data blob. + * + * Related sections in the development guide: + * - @core_doc{_abstract_data, Abstract Data} + * + * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include + +/** + * @name General + * @{ + */ + +/** + * Allocate a new abstract data object. + * @arg buf Data buffer containing the actual data. + * @arg size Size of data buffer. + * + * Allocates a new abstract data and copies the specified data + * buffer into the new handle. + * + * @return Newly allocated data handle or NULL + */ +struct nl_data *nl_data_alloc(const void *buf, size_t size) +{ + struct nl_data *data; + + data = calloc(1, sizeof(*data)); + if (!data) + goto errout; + + data->d_data = calloc(1, size); + if (!data->d_data) { + free(data); + goto errout; + } + + data->d_size = size; + + if (buf) + memcpy(data->d_data, buf, size); + + return data; +errout: + return NULL; +} + +/** + * Allocate abstract data object based on netlink attribute. + * @arg nla Netlink attribute of unspecific type. + * + * Allocates a new abstract data and copies the payload of the + * attribute to the abstract data object. + * + * @see nla_data_alloc + * @return Newly allocated data handle or NULL + */ +struct nl_data *nl_data_alloc_attr(const struct nlattr *nla) +{ + return nl_data_alloc(nla_data(nla), nla_len(nla)); +} + +/** + * Clone an abstract data object. + * @arg src Abstract data object + * + * @return Cloned object or NULL + */ +struct nl_data *nl_data_clone(const struct nl_data *src) +{ + return nl_data_alloc(src->d_data, src->d_size); +} + +/** + * Append data to an abstract data object. + * @arg data Abstract data object. + * @arg buf Data buffer containing the data to be appended. + * @arg size Size of data to be apppended. + * + * Reallocates an abstract data and copies the specified data + * buffer into the new handle. + * + * @return 0 on success or a negative error code + */ +int nl_data_append(struct nl_data *data, const void *buf, size_t size) +{ + if (size > 0) { + char *d_data = realloc(data->d_data, data->d_size + size); + if (!d_data) + return -NLE_NOMEM; + + if (buf) + memcpy(d_data + data->d_size, buf, size); + else + memset(d_data + data->d_size, 0, size); + + data->d_data = d_data; + data->d_size += size; + } + + return 0; +} + +/** + * Free an abstract data object. + * @arg data Abstract data object. + */ +void nl_data_free(struct nl_data *data) +{ + if (data) + free(data->d_data); + + free(data); +} + +/** @} */ + +/** + * @name Attribute Access + * @{ + */ + +/** + * Get data buffer of abstract data object. + * @arg data Abstract data object. + * @return Data buffer or NULL if empty. + */ +void *nl_data_get(const struct nl_data *data) +{ + if (data->d_size > 0) + return (void*)data->d_data; + return NULL; +} + +/** + * Get size of data buffer of abstract data object. + * @arg data Abstract data object. + * @return Size of data buffer. + */ +size_t nl_data_get_size(const struct nl_data *data) +{ + return data->d_size; +} + +/** @} */ + +/** + * @name Misc + * @{ + */ + +/** + * Compare two abstract data objects. + * @arg a Abstract data object. + * @arg b Another abstract data object. + * @return An integer less than, equal to, or greater than zero if + * a is found, respectively, to be less than, to match, or + * be greater than b. + */ +int nl_data_cmp(const struct nl_data *a, const struct nl_data *b) +{ + const void *a_ = nl_data_get(a); + const void *b_ = nl_data_get(b); + + if (a_ && b_) + return memcmp(a_, b_, nl_data_get_size(a)); + else + return -1; +} + +/** @} */ +/** @} */ diff --git a/libnl/lib/error.c b/libnl/lib/error.c new file mode 100644 index 0000000..9a3de90 --- /dev/null +++ b/libnl/lib/error.c @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008 Thomas Graf + */ + +#define _GNU_SOURCE + +#include +#include + +static const char *errmsg[NLE_MAX+1] = { +[NLE_SUCCESS] = "Success", +[NLE_FAILURE] = "Unspecific failure", +[NLE_INTR] = "Interrupted system call", +[NLE_BAD_SOCK] = "Bad socket", +[NLE_AGAIN] = "Try again", +[NLE_NOMEM] = "Out of memory", +[NLE_EXIST] = "Object exists", +[NLE_INVAL] = "Invalid input data or parameter", +[NLE_RANGE] = "Input data out of range", +[NLE_MSGSIZE] = "Message size not sufficient", +[NLE_OPNOTSUPP] = "Operation not supported", +[NLE_AF_NOSUPPORT] = "Address family not supported", +[NLE_OBJ_NOTFOUND] = "Object not found", +[NLE_NOATTR] = "Attribute not available", +[NLE_MISSING_ATTR] = "Missing attribute", +[NLE_AF_MISMATCH] = "Address family mismatch", +[NLE_SEQ_MISMATCH] = "Message sequence number mismatch", +[NLE_MSG_OVERFLOW] = "Kernel reported message overflow", +[NLE_MSG_TRUNC] = "Kernel reported truncated message", +[NLE_NOADDR] = "Invalid address for specified address family", +[NLE_SRCRT_NOSUPPORT] = "Source based routing not supported", +[NLE_MSG_TOOSHORT] = "Netlink message is too short", +[NLE_MSGTYPE_NOSUPPORT] = "Netlink message type is not supported", +[NLE_OBJ_MISMATCH] = "Object type does not match cache", +[NLE_NOCACHE] = "Unknown or invalid cache type", +[NLE_BUSY] = "Object busy", +[NLE_PROTO_MISMATCH] = "Protocol mismatch", +[NLE_NOACCESS] = "No Access", +[NLE_PERM] = "Operation not permitted", +[NLE_PKTLOC_FILE] = "Unable to open packet location file", +[NLE_PARSE_ERR] = "Unable to parse object", +[NLE_NODEV] = "No such device", +[NLE_IMMUTABLE] = "Immutable attribute", +[NLE_DUMP_INTR] = "Dump inconsistency detected, interrupted", +[NLE_ATTRSIZE] = "Attribute max length exceeded", +}; + +/** + * Return error message for an error code + * @return error message + */ +const char *nl_geterror(int error) +{ + error = abs(error); + + if (error > NLE_MAX) + error = NLE_FAILURE; + + return errmsg[error]; +} + +/** + * Print a libnl error message + * @arg s error message prefix + * + * Prints the error message of the call that failed last. + * + * If s is not NULL and *s is not a null byte the argument + * string is printed, followed by a colon and a blank. Then + * the error message and a new-line. + */ +void nl_perror(int error, const char *s) +{ + if (s && *s) + fprintf(stderr, "%s: %s\n", s, nl_geterror(error)); + else + fprintf(stderr, "%s\n", nl_geterror(error)); +} + +int nl_syserr2nlerr(int error) +{ + error = abs(error); + + switch (error) { + case EBADF: return NLE_BAD_SOCK; + case EADDRINUSE: return NLE_EXIST; + case EEXIST: return NLE_EXIST; + case EADDRNOTAVAIL: return NLE_NOADDR; + case ESRCH: /* fall through */ + case ENOENT: return NLE_OBJ_NOTFOUND; + case EINTR: return NLE_INTR; + case EAGAIN: return NLE_AGAIN; + case ENOTSOCK: return NLE_BAD_SOCK; + case ENOPROTOOPT: return NLE_INVAL; + case EFAULT: return NLE_INVAL; + case EACCES: return NLE_NOACCESS; + case EINVAL: return NLE_INVAL; + case ENOBUFS: return NLE_NOMEM; + case ENOMEM: return NLE_NOMEM; + case EAFNOSUPPORT: return NLE_AF_NOSUPPORT; + case EPROTONOSUPPORT: return NLE_PROTO_MISMATCH; + case EOPNOTSUPP: return NLE_OPNOTSUPP; + case EPERM: return NLE_PERM; + case EBUSY: return NLE_BUSY; + case ERANGE: return NLE_RANGE; + case ENODEV: return NLE_NODEV; + default: return NLE_FAILURE; + } +} + +/** @} */ + diff --git a/libnl/lib/genl/ctrl.c b/libnl/lib/genl/ctrl.c new file mode 100644 index 0000000..1838142 --- /dev/null +++ b/libnl/lib/genl/ctrl.c @@ -0,0 +1,543 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +/** + * @ingroup genl + * @defgroup genl_ctrl Controller (Resolver) + * + * Resolves Generic Netlink family names to numeric identifiers. + * + * The controller is a component in the kernel that resolves Generic Netlink + * family names to their numeric identifiers. This module provides functions + * to query the controller to access the resolving functionality. + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define CTRL_VERSION 0x0001 + +static struct nl_cache_ops genl_ctrl_ops; + +static int ctrl_request_update(struct nl_cache *c, struct nl_sock *h) +{ + return genl_send_simple(h, GENL_ID_CTRL, CTRL_CMD_GETFAMILY, + CTRL_VERSION, NLM_F_DUMP); +} + +static struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] = { + [CTRL_ATTR_FAMILY_ID] = { .type = NLA_U16 }, + [CTRL_ATTR_FAMILY_NAME] = { .type = NLA_STRING, + .maxlen = GENL_NAMSIZ }, + [CTRL_ATTR_VERSION] = { .type = NLA_U32 }, + [CTRL_ATTR_HDRSIZE] = { .type = NLA_U32 }, + [CTRL_ATTR_MAXATTR] = { .type = NLA_U32 }, + [CTRL_ATTR_OPS] = { .type = NLA_NESTED }, + [CTRL_ATTR_MCAST_GROUPS] = { .type = NLA_NESTED }, +}; + +static struct nla_policy family_op_policy[CTRL_ATTR_OP_MAX+1] = { + [CTRL_ATTR_OP_ID] = { .type = NLA_U32 }, + [CTRL_ATTR_OP_FLAGS] = { .type = NLA_U32 }, +}; + +static struct nla_policy family_grp_policy[CTRL_ATTR_MCAST_GRP_MAX+1] = { + [CTRL_ATTR_MCAST_GRP_NAME] = { .type = NLA_STRING }, + [CTRL_ATTR_MCAST_GRP_ID] = { .type = NLA_U32 }, +}; + +static int parse_mcast_grps(struct genl_family *family, struct nlattr *grp_attr) +{ + struct nlattr *nla; + int remaining, err; + + if (!grp_attr) + BUG(); + + nla_for_each_nested(nla, grp_attr, remaining) { + struct nlattr *tb[CTRL_ATTR_MCAST_GRP_MAX+1]; + int id; + const char * name; + + err = nla_parse_nested(tb, CTRL_ATTR_MCAST_GRP_MAX, nla, + family_grp_policy); + if (err < 0) + goto errout; + + if (tb[CTRL_ATTR_MCAST_GRP_ID] == NULL) { + err = -NLE_MISSING_ATTR; + goto errout; + } + id = nla_get_u32(tb[CTRL_ATTR_MCAST_GRP_ID]); + + if (tb[CTRL_ATTR_MCAST_GRP_NAME] == NULL) { + err = -NLE_MISSING_ATTR; + goto errout; + } + name = nla_get_string(tb[CTRL_ATTR_MCAST_GRP_NAME]); + + err = genl_family_add_grp(family, id, name); + if (err < 0) + goto errout; + } + + err = 0; + +errout: + return err; +} + +static int ctrl_msg_parser(struct nl_cache_ops *ops, struct genl_cmd *cmd, + struct genl_info *info, void *arg) +{ + struct genl_family *family; + struct nl_parser_param *pp = arg; + int err; + + family = genl_family_alloc(); + if (family == NULL) { + err = -NLE_NOMEM; + goto errout; + } + + if (info->attrs[CTRL_ATTR_FAMILY_NAME] == NULL) { + err = -NLE_MISSING_ATTR; + goto errout; + } + + if (info->attrs[CTRL_ATTR_FAMILY_ID] == NULL) { + err = -NLE_MISSING_ATTR; + goto errout; + } + + family->ce_msgtype = info->nlh->nlmsg_type; + genl_family_set_id(family, + nla_get_u16(info->attrs[CTRL_ATTR_FAMILY_ID])); + genl_family_set_name(family, + nla_get_string(info->attrs[CTRL_ATTR_FAMILY_NAME])); + + if (info->attrs[CTRL_ATTR_VERSION]) { + uint32_t version = nla_get_u32(info->attrs[CTRL_ATTR_VERSION]); + genl_family_set_version(family, version); + } + + if (info->attrs[CTRL_ATTR_HDRSIZE]) { + uint32_t hdrsize = nla_get_u32(info->attrs[CTRL_ATTR_HDRSIZE]); + genl_family_set_hdrsize(family, hdrsize); + } + + if (info->attrs[CTRL_ATTR_MAXATTR]) { + uint32_t maxattr = nla_get_u32(info->attrs[CTRL_ATTR_MAXATTR]); + genl_family_set_maxattr(family, maxattr); + } + + if (info->attrs[CTRL_ATTR_OPS]) { + struct nlattr *nla, *nla_ops; + int remaining; + + nla_ops = info->attrs[CTRL_ATTR_OPS]; + nla_for_each_nested(nla, nla_ops, remaining) { + struct nlattr *tb[CTRL_ATTR_OP_MAX+1]; + int flags = 0, id; + + err = nla_parse_nested(tb, CTRL_ATTR_OP_MAX, nla, + family_op_policy); + if (err < 0) + goto errout; + + if (tb[CTRL_ATTR_OP_ID] == NULL) { + err = -NLE_MISSING_ATTR; + goto errout; + } + + id = nla_get_u32(tb[CTRL_ATTR_OP_ID]); + + if (tb[CTRL_ATTR_OP_FLAGS]) + flags = nla_get_u32(tb[CTRL_ATTR_OP_FLAGS]); + + err = genl_family_add_op(family, id, flags); + if (err < 0) + goto errout; + + } + } + + if (info->attrs[CTRL_ATTR_MCAST_GROUPS]) { + err = parse_mcast_grps(family, info->attrs[CTRL_ATTR_MCAST_GROUPS]); + if (err < 0) + goto errout; + } + + err = pp->pp_cb((struct nl_object *) family, pp); +errout: + genl_family_put(family); + return err; +} + +/** + * process responses from from the query sent by genl_ctrl_probe_by_name + * @arg nl_msg Returned message. + * @arg name genl_family structure to fill out. + * + * Process returned messages, filling out the missing informatino in the + * genl_family structure + * + * @return Indicator to keep processing frames or not + * + */ +static int probe_response(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[CTRL_ATTR_MAX+1]; + struct nlmsghdr *nlh = nlmsg_hdr(msg); + struct genl_family *ret = (struct genl_family *)arg; + + if (genlmsg_parse(nlh, 0, tb, CTRL_ATTR_MAX, ctrl_policy)) + return NL_SKIP; + + if (tb[CTRL_ATTR_FAMILY_ID]) + genl_family_set_id(ret, nla_get_u16(tb[CTRL_ATTR_FAMILY_ID])); + + if (tb[CTRL_ATTR_MCAST_GROUPS]) + if (parse_mcast_grps(ret, tb[CTRL_ATTR_MCAST_GROUPS]) < 0) + return NL_SKIP; + + return NL_STOP; +} + +/** + * Look up generic netlink family by family name querying the kernel directly + * @arg sk Socket. + * @arg name Family name. + * + * Directly query's the kernel for a given family name. The caller will own a + * reference on the returned object which needsd to be given back after usage + * using genl_family_put. + * + * Note: This API call differs from genl_ctrl_search_by_name in that it querys + * the kernel directly, alowing for module autoload to take place to resolve the + * family request. Using an nl_cache prevents that operation + * + * @return Generic netlink family object or NULL if no match was found. + */ +static struct genl_family *genl_ctrl_probe_by_name(struct nl_sock *sk, + const char *name) +{ + struct nl_msg *msg; + struct genl_family *ret; + struct nl_cb *cb, *orig; + int rc; + + ret = genl_family_alloc(); + if (!ret) + goto out; + + genl_family_set_name(ret, name); + + msg = nlmsg_alloc(); + if (!msg) + goto out_fam_free; + + if (!(orig = nl_socket_get_cb(sk))) + goto out_msg_free; + + cb = nl_cb_clone(orig); + nl_cb_put(orig); + if (!cb) + goto out_msg_free; + + if (!genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, GENL_ID_CTRL, + 0, 0, CTRL_CMD_GETFAMILY, 1)) { + BUG(); + goto out_cb_free; + } + + if (nla_put_string(msg, CTRL_ATTR_FAMILY_NAME, name) < 0) + goto out_cb_free; + + rc = nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, probe_response, + (void *) ret); + if (rc < 0) + goto out_cb_free; + + rc = nl_send_auto_complete(sk, msg); + if (rc < 0) + goto out_cb_free; + + rc = nl_recvmsgs(sk, cb); + if (rc < 0) + goto out_cb_free; + + /* If search was successful, request may be ACKed after data */ + rc = wait_for_ack(sk); + if (rc < 0) + goto out_cb_free; + + if (genl_family_get_id(ret) != 0) { + nlmsg_free(msg); + nl_cb_put(cb); + return ret; + } + +out_cb_free: + nl_cb_put(cb); +out_msg_free: + nlmsg_free(msg); +out_fam_free: + genl_family_put(ret); + ret = NULL; +out: + return ret; +} + + +/** @endcond */ + +/** + * @name Controller Cache + * + * The controller cache allows to keep a local copy of the list of all + * kernel side registered Generic Netlink families to quickly resolve + * multiple Generic Netlink family names without requiring to communicate + * with the kernel for each resolving iteration. + * + * @{ + */ + +/** + * Allocate a new controller cache + * @arg sk Generic Netlink socket + * @arg result Pointer to store resulting cache + * + * Allocates a new cache mirroring the state of the controller and stores it + * in \c *result. The allocated cache will contain a list of all currently + * registered kernel side Generic Netlink families. The cache is meant to be + * used to resolve family names locally. + * + * @return 0 on success or a negative error code. + */ +int genl_ctrl_alloc_cache(struct nl_sock *sk, struct nl_cache **result) +{ + return nl_cache_alloc_and_fill(&genl_ctrl_ops, sk, result); +} + +/** + * Search controller cache for a numeric address match + * @arg cache Controller cache + * @arg id Numeric family identifier. + * + * Searches a previously allocated controller cache and looks for an entry + * that matches the specified numeric family identifier \c id. If a match + * is found successfully, the reference count of the matching object is + * increased by one before the objet is returned. + * + * @see genl_ctrl_alloc_cache() + * @see genl_ctrl_search_by_name() + * @see genl_family_put() + * + * @return Generic Netlink family object or NULL if no match was found. + */ +struct genl_family *genl_ctrl_search(struct nl_cache *cache, int id) +{ + struct genl_family *fam; + + if (cache->c_ops != &genl_ctrl_ops) + BUG(); + + nl_list_for_each_entry(fam, &cache->c_items, ce_list) { + if (fam->gf_id == id) { + nl_object_get((struct nl_object *) fam); + return fam; + } + } + + return NULL; +} + +/** + * Search controller cache for a family name match + * @arg cache Controller cache + * @arg name Name of Generic Netlink family + * + * Searches a previously allocated controller cache and looks for an entry + * that matches the specified family \c name. If a match is found successfully, + * the reference count of the matching object is increased by one before the + * objet is returned. + * + * @see genl_ctrl_alloc_cache() + * @see genl_ctrl_search() + * @see genl_family_put() + * + * @return Generic Netlink family object or NULL if no match was found. + */ +struct genl_family *genl_ctrl_search_by_name(struct nl_cache *cache, + const char *name) +{ + struct genl_family *fam; + + if (cache->c_ops != &genl_ctrl_ops) + BUG(); + + nl_list_for_each_entry(fam, &cache->c_items, ce_list) { + if (!strcmp(name, fam->gf_name)) { + nl_object_get((struct nl_object *) fam); + return fam; + } + } + + return NULL; +} + +/** @} */ + +/** + * @name Direct Resolvers + * + * These functions communicate directly with the kernel and do not require + * a cache to be kept up to date. + * + * @{ + */ + +/** + * Resolve Generic Netlink family name to numeric identifier + * @arg sk Generic Netlink socket. + * @arg name Name of Generic Netlink family + * + * Resolves the Generic Netlink family name to the corresponding numeric + * family identifier. This function queries the kernel directly, use + * genl_ctrl_search_by_name() if you need to resolve multiple names. + * + * @see genl_ctrl_search_by_name() + * + * @return The numeric family identifier or a negative error code. + */ +int genl_ctrl_resolve(struct nl_sock *sk, const char *name) +{ + struct genl_family *family; + int err; + + family = genl_ctrl_probe_by_name(sk, name); + if (family == NULL) { + err = -NLE_OBJ_NOTFOUND; + goto errout; + } + + err = genl_family_get_id(family); + genl_family_put(family); +errout: + return err; +} + +static int genl_ctrl_grp_by_name(const struct genl_family *family, + const char *grp_name) +{ + struct genl_family_grp *grp; + + nl_list_for_each_entry(grp, &family->gf_mc_grps, list) { + if (!strcmp(grp->name, grp_name)) { + return grp->id; + } + } + + return -NLE_OBJ_NOTFOUND; +} + +/** + * Resolve Generic Netlink family group name + * @arg sk Generic Netlink socket + * @arg family_name Name of Generic Netlink family + * @arg grp_name Name of group to resolve + * + * Looks up the family object and resolves the group name to the numeric + * group identifier. + * + * @return Numeric group identifier or a negative error code. + */ +int genl_ctrl_resolve_grp(struct nl_sock *sk, const char *family_name, + const char *grp_name) +{ + + struct genl_family *family; + int err; + + family = genl_ctrl_probe_by_name(sk, family_name); + if (family == NULL) { + err = -NLE_OBJ_NOTFOUND; + goto errout; + } + + err = genl_ctrl_grp_by_name(family, grp_name); + genl_family_put(family); +errout: + return err; +} + +/** @} */ + +/** @cond SKIP */ +static struct genl_cmd genl_cmds[] = { + { + .c_id = CTRL_CMD_NEWFAMILY, + .c_name = "NEWFAMILY" , + .c_maxattr = CTRL_ATTR_MAX, + .c_attr_policy = ctrl_policy, + .c_msg_parser = ctrl_msg_parser, + }, + { + .c_id = CTRL_CMD_DELFAMILY, + .c_name = "DELFAMILY" , + }, + { + .c_id = CTRL_CMD_GETFAMILY, + .c_name = "GETFAMILY" , + }, + { + .c_id = CTRL_CMD_NEWOPS, + .c_name = "NEWOPS" , + }, + { + .c_id = CTRL_CMD_DELOPS, + .c_name = "DELOPS" , + }, +}; + +static struct genl_ops genl_ops = { + .o_cmds = genl_cmds, + .o_ncmds = ARRAY_SIZE(genl_cmds), +}; + +extern struct nl_object_ops genl_family_ops; + +static struct nl_cache_ops genl_ctrl_ops = { + .co_name = "genl/family", + .co_hdrsize = GENL_HDRSIZE(0), + .co_msgtypes = GENL_FAMILY(GENL_ID_CTRL, "nlctrl"), + .co_genl = &genl_ops, + .co_protocol = NETLINK_GENERIC, + .co_request_update = ctrl_request_update, + .co_obj_ops = &genl_family_ops, +}; + +static void __init ctrl_init(void) +{ + genl_register(&genl_ctrl_ops); +} + +static void __exit ctrl_exit(void) +{ + genl_unregister(&genl_ctrl_ops); +} +/** @endcond */ + +/** @} */ diff --git a/libnl/lib/genl/family.c b/libnl/lib/genl/family.c new file mode 100644 index 0000000..f729f62 --- /dev/null +++ b/libnl/lib/genl/family.c @@ -0,0 +1,404 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +/** + * @ingroup genl_ctrl + * @defgroup genl_family Generic Netlink Family Object + * + * Object representing a kernel side registered Generic Netlink family + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include + +#include "netlink-private/utils.h" + +/** @cond SKIP */ +#define FAMILY_ATTR_ID 0x01 +#define FAMILY_ATTR_NAME 0x02 +#define FAMILY_ATTR_VERSION 0x04 +#define FAMILY_ATTR_HDRSIZE 0x08 +#define FAMILY_ATTR_MAXATTR 0x10 +#define FAMILY_ATTR_OPS 0x20 + +struct nl_object_ops genl_family_ops; + +static void family_constructor(struct nl_object *c) +{ + struct genl_family *family = (struct genl_family *) c; + + nl_init_list_head(&family->gf_ops); + nl_init_list_head(&family->gf_mc_grps); +} + +static void family_free_data(struct nl_object *c) +{ + struct genl_family *family = (struct genl_family *) c; + struct genl_family_op *ops, *tmp; + struct genl_family_grp *grp, *t_grp; + + if (family == NULL) + return; + + nl_list_for_each_entry_safe(ops, tmp, &family->gf_ops, o_list) { + nl_list_del(&ops->o_list); + free(ops); + } + + nl_list_for_each_entry_safe(grp, t_grp, &family->gf_mc_grps, list) { + nl_list_del(&grp->list); + free(grp); + } + +} + +static int family_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct genl_family *dst = nl_object_priv(_dst); + struct genl_family *src = nl_object_priv(_src); + struct genl_family_op *ops; + struct genl_family_grp *grp; + int err; + + nl_list_for_each_entry(ops, &src->gf_ops, o_list) { + err = genl_family_add_op(dst, ops->o_id, ops->o_flags); + if (err < 0) + return err; + } + + nl_list_for_each_entry(grp, &src->gf_mc_grps, list) { + err = genl_family_add_grp(dst, grp->id, grp->name); + if (err < 0) + return err; + } + + + return 0; +} + +static void family_dump_line(struct nl_object *obj, struct nl_dump_params *p) +{ + struct genl_family *family = (struct genl_family *) obj; + + nl_dump(p, "0x%04x %s version %u\n", + family->gf_id, family->gf_name, family->gf_version); +} + +static const struct trans_tbl ops_flags[] = { + __ADD(GENL_ADMIN_PERM, admin_perm), + __ADD(GENL_CMD_CAP_DO, has_doit), + __ADD(GENL_CMD_CAP_DUMP, has_dump), + __ADD(GENL_CMD_CAP_HASPOL, has_policy), +}; + +static char *ops_flags2str(int flags, char *buf, size_t len) +{ + return __flags2str(flags, buf, len, ops_flags, ARRAY_SIZE(ops_flags)); +} + +static void family_dump_details(struct nl_object *obj, struct nl_dump_params *p) +{ + struct genl_family_grp *grp; + struct genl_family *family = (struct genl_family *) obj; + + family_dump_line(obj, p); + nl_dump_line(p, " hdrsize %u maxattr %u\n", + family->gf_hdrsize, family->gf_maxattr); + + if (family->ce_mask & FAMILY_ATTR_OPS) { + struct genl_family_op *op; + char buf[64]; + + nl_list_for_each_entry(op, &family->gf_ops, o_list) { + ops_flags2str(op->o_flags, buf, sizeof(buf)); + + genl_op2name(family->gf_id, op->o_id, buf, sizeof(buf)); + + nl_dump_line(p, " op %s (0x%02x)", buf, op->o_id); + + if (op->o_flags) + nl_dump(p, " <%s>", + ops_flags2str(op->o_flags, buf, + sizeof(buf))); + + nl_dump(p, "\n"); + } + } + + nl_list_for_each_entry(grp, &family->gf_mc_grps, list) { + nl_dump_line(p, " grp %s (0x%02x)\n", grp->name, grp->id); + } + +} + +static void family_dump_stats(struct nl_object *obj, struct nl_dump_params *p) +{ + family_dump_details(obj, p); +} + +static uint64_t family_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + struct genl_family *a = (struct genl_family *) _a; + struct genl_family *b = (struct genl_family *) _b; + uint64_t diff = 0; + +#define FAM_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, FAMILY_ATTR_##ATTR, a, b, EXPR) + + diff |= FAM_DIFF(ID, a->gf_id != b->gf_id); + diff |= FAM_DIFF(VERSION, a->gf_version != b->gf_version); + diff |= FAM_DIFF(HDRSIZE, a->gf_hdrsize != b->gf_hdrsize); + diff |= FAM_DIFF(MAXATTR, a->gf_maxattr != b->gf_maxattr); + diff |= FAM_DIFF(NAME, strcmp(a->gf_name, b->gf_name)); + +#undef FAM_DIFF + + return diff; +} +/** @endcond */ + +/** + * @name Object Allocation + * @{ + */ + +/** + * Allocate new Generic Netlink family object + * + * @return Newly allocated Generic Netlink family object or NULL. + */ +struct genl_family *genl_family_alloc(void) +{ + return (struct genl_family *) nl_object_alloc(&genl_family_ops); +} + +/** + * Release reference on Generic Netlink family object + * @arg family Generic Netlink family object + * + * Reduces the reference counter of a Generic Netlink family object by one. + * The object is freed after the last user has returned its reference. + * + * @see nl_object_put() + */ +void genl_family_put(struct genl_family *family) +{ + nl_object_put((struct nl_object *) family); +} + +/** @} */ + +/** + * @name Numeric Identifier + * @{ + */ + +/** + * Return numeric identifier + * @arg family Generic Netlink family object + * + * @return Numeric identifier or 0 if not available. + */ +unsigned int genl_family_get_id(struct genl_family *family) +{ + if (family->ce_mask & FAMILY_ATTR_ID) + return family->gf_id; + else + return 0; +} + +/** + * Set the numeric identifier + * @arg family Generic Netlink family object + * @arg id New numeric identifier + */ +void genl_family_set_id(struct genl_family *family, unsigned int id) +{ + family->gf_id = id; + family->ce_mask |= FAMILY_ATTR_ID; +} + +/** @} */ + +/** + * @name Human Readable Name + * @{ + */ + +/** + * Return human readable name + * @arg family Generic Netlink family object + * + * @return Name of family or NULL if not available + */ +char *genl_family_get_name(struct genl_family *family) +{ + if (family->ce_mask & FAMILY_ATTR_NAME) + return family->gf_name; + else + return NULL; +} + +/** + * Set human readable name + * @arg family Generic Netlink family object + * @arg name New human readable name + */ +void genl_family_set_name(struct genl_family *family, const char *name) +{ + _nl_strncpy_trunc(family->gf_name, name, GENL_NAMSIZ); + family->ce_mask |= FAMILY_ATTR_NAME; +} + +/** + * @name Interface Version + * @{ + */ + +/** + * Return interface version + * @arg family Generic Netlink family object + * + * @return Interface version or 0 if not available. + */ +uint8_t genl_family_get_version(struct genl_family *family) +{ + if (family->ce_mask & FAMILY_ATTR_VERSION) + return family->gf_version; + else + return 0; +} + +/** + * Set interface version + * @arg family Generic Netlink family object + * @arg version New interface version + */ +void genl_family_set_version(struct genl_family *family, uint8_t version) +{ + family->gf_version = version; + family->ce_mask |= FAMILY_ATTR_VERSION; +} + +/** @} */ + +/** + * @name Header Size + * @{ + */ + +/** + * Return user header size expected by kernel component + * @arg family Generic Netlink family object + * + * @return Expected header length or 0 if not available. + */ +uint32_t genl_family_get_hdrsize(struct genl_family *family) +{ + if (family->ce_mask & FAMILY_ATTR_HDRSIZE) + return family->gf_hdrsize; + else + return 0; +} + +void genl_family_set_hdrsize(struct genl_family *family, uint32_t hdrsize) +{ + family->gf_hdrsize = hdrsize; + family->ce_mask |= FAMILY_ATTR_HDRSIZE; +} + +/** @} */ + +/** + * @name Maximum Expected Attribute + * @{ + */ + +uint32_t genl_family_get_maxattr(struct genl_family *family) +{ + if (family->ce_mask & FAMILY_ATTR_MAXATTR) + return family->gf_maxattr; + else + return 0; +} + +void genl_family_set_maxattr(struct genl_family *family, uint32_t maxattr) +{ + family->gf_maxattr = maxattr; + family->ce_mask |= FAMILY_ATTR_MAXATTR; +} + +/** @} */ + +/** + * @name Operations + * @{ + */ + +int genl_family_add_op(struct genl_family *family, int id, int flags) +{ + struct genl_family_op *op; + + op = calloc(1, sizeof(*op)); + if (op == NULL) + return -NLE_NOMEM; + + op->o_id = id; + op->o_flags = flags; + + nl_list_add_tail(&op->o_list, &family->gf_ops); + family->ce_mask |= FAMILY_ATTR_OPS; + + return 0; +} + +int genl_family_add_grp(struct genl_family *family, uint32_t id, + const char *name) +{ + struct genl_family_grp *grp; + + if ( !name + || strlen (name) >= GENL_NAMSIZ) + return -NLE_INVAL; + + grp = calloc(1, sizeof(*grp)); + if (grp == NULL) + return -NLE_NOMEM; + + grp->id = id; + _nl_strncpy_assert(grp->name, name, GENL_NAMSIZ); + + nl_list_add_tail(&grp->list, &family->gf_mc_grps); + + return 0; +} + +/** @} */ + +/** @cond SKIP */ +struct nl_object_ops genl_family_ops = { + .oo_name = "genl/family", + .oo_size = sizeof(struct genl_family), + .oo_constructor = family_constructor, + .oo_free_data = family_free_data, + .oo_clone = family_clone, + .oo_dump = { + [NL_DUMP_LINE] = family_dump_line, + [NL_DUMP_DETAILS] = family_dump_details, + [NL_DUMP_STATS] = family_dump_stats, + }, + .oo_compare = family_compare, + .oo_id_attrs = FAMILY_ATTR_ID, +}; +/** @endcond */ + +/** @} */ diff --git a/libnl/lib/genl/genl.c b/libnl/lib/genl/genl.c new file mode 100644 index 0000000..794d051 --- /dev/null +++ b/libnl/lib/genl/genl.c @@ -0,0 +1,388 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +/** + * @defgroup genl Generic Netlink Library (libnl-genl) + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include + +/** + * @name Generic Netlink Socket + * @{ + */ + +/** + * Connect a Generic Netlink socket + * @arg sk Unconnected Netlink socket + * + * This function expects a struct nl_socket object previously allocated via + * nl_socket_alloc(). It calls nl_connect() to create the local socket file + * descriptor and binds the socket to the \c NETLINK_GENERIC Netlink protocol. + * + * Using this function is equivalent to: + * @code + * nl_connect(sk, NETLINK_GENERIC); + * @endcode + * + * @see nl_connect() + * + * @return 0 on success or a negative error code. + */ +int genl_connect(struct nl_sock *sk) +{ + return nl_connect(sk, NETLINK_GENERIC); +} + +/** @} */ + +/** + * @name Sending Data + * @{ + */ + +/** + * Send a Generic Netlink message consisting only of a header + * @arg sk Generic Netlink socket + * @arg family Numeric family identifier + * @arg cmd Numeric command identifier + * @arg version Interface version + * @arg flags Additional Netlink message flags (optional) + * + * This function is a shortcut for sending a Generic Netlink message without + * any message payload. The message will only consist of the Netlink and + * Generic Netlink headers. The header is constructed based on the specified + * parameters and passed on to nl_send_simple() to send it on the specified + * socket. + * + * @par Example: + * @code + * #include + * #include + * + * err = genl_send_simple(sk, GENL_ID_CTRL, CTRL_CMD_GETFAMILY, CTRL_VERSION, + * NLM_F_DUMP); + * @endcode + * + * @see nl_send_simple() + * + * @return 0 on success or a negative error code. Due to a bug, this function + * returns the number of bytes sent. Treat any non-negative number as success. + */ +int genl_send_simple(struct nl_sock *sk, int family, int cmd, + int version, int flags) +{ + struct genlmsghdr hdr = { + .cmd = cmd, + .version = version, + }; + + return nl_send_simple(sk, family, flags, &hdr, sizeof(hdr)); +} + +/** @} */ + +/** + * @name Message Parsing + * @{ + */ + +/** + * Validate Generic Netlink message headers + * @arg nlh Pointer to Netlink message header + * @arg hdrlen Length of user header + * + * Verifies the integrity of the Netlink and Generic Netlink headers by + * enforcing the following requirements: + * - Valid Netlink message header (nlmsg_valid_hdr()) + * - Presence of a complete Generic Netlink header + * - At least \c hdrlen bytes of payload included after the generic + * netlink header. + * + * @return A positive integer (true) if the headers are valid or + * 0 (false) if not. + */ +int genlmsg_valid_hdr(struct nlmsghdr *nlh, int hdrlen) +{ + struct genlmsghdr *ghdr; + + if (!nlmsg_valid_hdr(nlh, GENL_HDRLEN)) + return 0; + + ghdr = nlmsg_data(nlh); + if (genlmsg_len(ghdr) < NLMSG_ALIGN(hdrlen)) + return 0; + + return 1; +} + +/** + * Validate Generic Netlink message including attributes + * @arg nlh Pointer to Netlink message header + * @arg hdrlen Length of user header + * @arg maxtype Maximum attribtue id expected + * @arg policy Attribute validation policy + * + * Verifies the validity of the Netlink and Generic Netlink headers using + * genlmsg_valid_hdr() and calls nla_validate() on the message payload to + * verify the integrity of eventual attributes. + * + * @note You may call genlmsg_parse() directly to perform validation and + * parsing in a single step. + * + * @see genlmsg_valid_hdr() + * @see nla_validate() + * @see genlmsg_parse() + * + * @return 0 on success or a negative error code. + */ +int genlmsg_validate(struct nlmsghdr *nlh, int hdrlen, int maxtype, + const struct nla_policy *policy) +{ + struct genlmsghdr *ghdr; + + if (!genlmsg_valid_hdr(nlh, hdrlen)) + return -NLE_MSG_TOOSHORT; + + ghdr = nlmsg_data(nlh); + return nla_validate(genlmsg_attrdata(ghdr, hdrlen), + genlmsg_attrlen(ghdr, hdrlen), maxtype, policy); +} + +/** + * Parse Generic Netlink message including attributes + * @arg nlh Pointer to Netlink message header + * @arg hdrlen Length of user header + * @arg tb Array to store parsed attributes + * @arg maxtype Maximum attribute id expected + * @arg policy Attribute validation policy + * + * Verifies the validity of the Netlink and Generic Netlink headers using + * genlmsg_valid_hdr() and calls nla_parse() on the message payload to + * parse eventual attributes. + * + * @par Example: + * @code + * struct nlattr *attrs[MY_TYPE_MAX+1]; + * + * if ((err = genlmsg_parse(nlmsg_hdr(msg), sizeof(struct my_hdr), attrs, + * MY_TYPE_MAX, attr_policy)) < 0) + * // ERROR + * @endcode + * + * @see genlmsg_valid_hdr() + * @see genlmsg_validate() + * @see nla_parse() + * + * @return 0 on success or a negative error code. + */ +int genlmsg_parse(struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], + int maxtype, const struct nla_policy *policy) +{ + struct genlmsghdr *ghdr; + + if (!genlmsg_valid_hdr(nlh, hdrlen)) + return -NLE_MSG_TOOSHORT; + + ghdr = nlmsg_data(nlh); + return nla_parse(tb, maxtype, genlmsg_attrdata(ghdr, hdrlen), + genlmsg_attrlen(ghdr, hdrlen), policy); +} + +/** + * Return pointer to Generic Netlink header + * @arg nlh Netlink message header + * + * @return Pointer to Generic Netlink message header + */ +struct genlmsghdr *genlmsg_hdr(struct nlmsghdr *nlh) +{ + return nlmsg_data(nlh); +} + +/** + * Return length of message payload including user header + * @arg gnlh Generic Netlink message header + * + * @see genlmsg_data() + * + * @return Length of user payload including an eventual user header in + * number of bytes. + */ +int genlmsg_len(const struct genlmsghdr *gnlh) +{ + const struct nlmsghdr *nlh; + + nlh = (const struct nlmsghdr *)((const unsigned char *) gnlh - NLMSG_HDRLEN); + return (nlh->nlmsg_len - GENL_HDRLEN - NLMSG_HDRLEN); +} + + +/** + * Return pointer to user header + * @arg gnlh Generic Netlink message header + * + * Calculates the pointer to the user header based on the pointer to + * the Generic Netlink message header. + * + * @return Pointer to the user header + */ +void *genlmsg_user_hdr(const struct genlmsghdr *gnlh) +{ + return genlmsg_data(gnlh); +} + +/** + * Return pointer to user data + * @arg gnlh Generic netlink message header + * @arg hdrlen Length of user header + * + * Calculates the pointer to the user data based on the pointer to + * the Generic Netlink message header. + * + * @see genlmsg_user_datalen() + * + * @return Pointer to the user data + */ +void *genlmsg_user_data(const struct genlmsghdr *gnlh, const int hdrlen) +{ + return (char *) genlmsg_user_hdr(gnlh) + NLMSG_ALIGN(hdrlen); +} + +/** + * Return length of user data + * @arg gnlh Generic Netlink message header + * @arg hdrlen Length of user header + * + * @see genlmsg_user_data() + * + * @return Length of user data in bytes + */ +int genlmsg_user_datalen(const struct genlmsghdr *gnlh, const int hdrlen) +{ + return genlmsg_len(gnlh) - NLMSG_ALIGN(hdrlen); +} + +/** + * Return pointer to message attributes + * @arg gnlh Generic Netlink message header + * @arg hdrlen Length of user header + * + * @see genlmsg_attrlen() + * + * @return Pointer to the start of the message's attributes section. + */ +struct nlattr *genlmsg_attrdata(const struct genlmsghdr *gnlh, int hdrlen) +{ + return genlmsg_user_data(gnlh, hdrlen); +} + +/** + * Return length of message attributes + * @arg gnlh Generic Netlink message header + * @arg hdrlen Length of user header + * + * @see genlmsg_attrdata() + * + * @return Length of the message section containing attributes in number + * of bytes. + */ +int genlmsg_attrlen(const struct genlmsghdr *gnlh, int hdrlen) +{ + return genlmsg_len(gnlh) - NLMSG_ALIGN(hdrlen); +} + +/** @} */ + +/** + * @name Message Construction + * @{ + */ + +/** + * Add Generic Netlink headers to Netlink message + * @arg msg Netlink message object + * @arg port Netlink port or NL_AUTO_PORT + * @arg seq Sequence number of message or NL_AUTO_SEQ + * @arg family Numeric family identifier + * @arg hdrlen Length of user header + * @arg flags Additional Netlink message flags (optional) + * @arg cmd Numeric command identifier + * @arg version Interface version + * + * Calls nlmsg_put() on the specified message object to reserve space for + * the Netlink header, the Generic Netlink header, and a user header of + * specified length. Fills out the header fields with the specified + * parameters. + * + * @par Example: + * @code + * struct nl_msg *msg; + * struct my_hdr *user_hdr; + * + * if (!(msg = nlmsg_alloc())) + * // ERROR + * + * user_hdr = genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, family_id, + * sizeof(struct my_hdr), 0, MY_CMD_FOO, 0); + * if (!user_hdr) + * // ERROR + * @endcode + * + * @see nlmsg_put() + * + * Returns Pointer to user header or NULL if an error occurred. + */ +void *genlmsg_put(struct nl_msg *msg, uint32_t port, uint32_t seq, int family, + int hdrlen, int flags, uint8_t cmd, uint8_t version) +{ + struct nlmsghdr *nlh; + struct genlmsghdr hdr = { + .cmd = cmd, + .version = version, + }; + + nlh = nlmsg_put(msg, port, seq, family, GENL_HDRLEN + hdrlen, flags); + if (nlh == NULL) + return NULL; + + memcpy(nlmsg_data(nlh), &hdr, sizeof(hdr)); + NL_DBG(2, "msg %p: Added generic netlink header cmd=%d version=%d\n", + msg, cmd, version); + + return (char *) nlmsg_data(nlh) + GENL_HDRLEN; +} + +/** @} */ + +/** + * @name Deprecated + * @{ + */ + +/** + * Return pointer to message payload + * @arg gnlh Generic Netlink message header + * + * @deprecated This function has been deprecated due to inability to specify + * the length of the user header. Use genlmsg_user_hdr() + * respectively genlmsg_user_data(). + * + * @return Pointer to payload section + */ +void *genlmsg_data(const struct genlmsghdr *gnlh) +{ + return ((unsigned char *) gnlh + GENL_HDRLEN); +} + +/** @} */ +/** @} */ diff --git a/libnl/lib/genl/mngt.c b/libnl/lib/genl/mngt.c new file mode 100644 index 0000000..687dcd9 --- /dev/null +++ b/libnl/lib/genl/mngt.c @@ -0,0 +1,401 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +/** + * @ingroup genl + * @defgroup genl_mngt Family and Command Registration + * + * Registering Generic Netlink Families and Commands + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +#include "netlink-private/utils.h" + +/** @cond SKIP */ + +static NL_LIST_HEAD(genl_ops_list); + +static struct genl_cmd *lookup_cmd(struct genl_ops *ops, int cmd_id) +{ + struct genl_cmd *cmd; + int i; + + for (i = 0; i < ops->o_ncmds; i++) { + cmd = &ops->o_cmds[i]; + if (cmd->c_id == cmd_id) + return cmd; + } + + return NULL; +} + +static int cmd_msg_parser(struct sockaddr_nl *who, struct nlmsghdr *nlh, + struct genl_ops *ops, struct nl_cache_ops *cache_ops, void *arg) +{ + _nl_auto_free struct nlattr **tb_free = NULL; + int err; + struct genlmsghdr *ghdr; + struct genl_cmd *cmd; + struct nlattr **tb; + + ghdr = genlmsg_hdr(nlh); + + if (!(cmd = lookup_cmd(ops, ghdr->cmd))) + return -NLE_MSGTYPE_NOSUPPORT; + + if (cmd->c_msg_parser == NULL) + return -NLE_OPNOTSUPP; + + tb = _nl_malloc_maybe_a (300, (((size_t) cmd->c_maxattr) + 1u) * sizeof (struct nlattr *), &tb_free); + if (!tb) + return -NLE_NOMEM; + + err = nlmsg_parse(nlh, + GENL_HDRSIZE(ops->o_hdrsize), + tb, + cmd->c_maxattr, + cmd->c_attr_policy); + if (err < 0) + return err; + + { + struct genl_info info = { + .who = who, + .nlh = nlh, + .genlhdr = ghdr, + .userhdr = genlmsg_user_hdr(ghdr), + .attrs = tb, + }; + + return cmd->c_msg_parser(cache_ops, cmd, &info, arg); + } +} + +static int genl_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, struct nl_parser_param *pp) +{ + if (ops->co_genl == NULL) + BUG(); + + return cmd_msg_parser(who, nlh, ops->co_genl, ops, pp); +} + +static struct genl_ops *lookup_family(int family) +{ + struct genl_ops *ops; + + nl_list_for_each_entry(ops, &genl_ops_list, o_list) { + if (ops->o_id == family) + return ops; + } + + return NULL; +} + +static struct genl_ops *lookup_family_by_name(const char *name) +{ + struct genl_ops *ops; + + nl_list_for_each_entry(ops, &genl_ops_list, o_list) { + if (!strcmp(ops->o_name, name)) + return ops; + } + + return NULL; +} + +char *genl_op2name(int family, int op, char *buf, size_t len) +{ + struct genl_ops *ops; + int i; + + if ((ops = lookup_family(family))) { + for (i = 0; i < ops->o_ncmds; i++) { + struct genl_cmd *cmd; + cmd = &ops->o_cmds[i]; + + if (cmd->c_id == op) { + _nl_strncpy_trunc(buf, cmd->c_name, len); + return buf; + } + } + } + + _nl_strncpy_trunc(buf, "unknown", len); + return NULL; +} + +/** @endcond */ + +/** + * @name Registration + * @{ + */ + +/** + * Register Generic Netlink family and associated commands + * @arg ops Generic Netlink family definition + * + * Registers the specified Generic Netlink family definition together with + * all associated commands. After registration, received Generic Netlink + * messages can be passed to genl_handle_msg() which will validate the + * messages, look for a matching command and call the respective callback + * function automatically. + * + * @note Consider using genl_register() if the family is used to implement a + * cacheable type. + * + * @see genl_unregister_family(); + * @see genl_register(); + * + * @return 0 on success or a negative error code. + */ +int genl_register_family(struct genl_ops *ops) +{ + if (!ops->o_name) + return -NLE_INVAL; + + if (ops->o_cmds && ops->o_ncmds <= 0) + return -NLE_INVAL; + + if (ops->o_id && lookup_family(ops->o_id)) + return -NLE_EXIST; + + if (lookup_family_by_name(ops->o_name)) + return -NLE_EXIST; + + nl_list_add_tail(&ops->o_list, &genl_ops_list); + + return 0; +} + +/** + * Unregister Generic Netlink family + * @arg ops Generic Netlink family definition + * + * Unregisters a family and all associated commands that were previously + * registered using genl_register_family(). + * + * @see genl_register_family() + * + * @return 0 on success or a negative error code. + */ +int genl_unregister_family(struct genl_ops *ops) +{ + nl_list_del(&ops->o_list); + + return 0; +} + +/** + * Run a received message through the demultiplexer + * @arg msg Generic Netlink message + * @arg arg Argument passed on to the message handler callback + * + * @return 0 on success or a negative error code. + */ +int genl_handle_msg(struct nl_msg *msg, void *arg) +{ + struct nlmsghdr *nlh = nlmsg_hdr(msg); + struct genl_ops *ops; + + if (!genlmsg_valid_hdr(nlh, 0)) + return -NLE_INVAL; + + if (!(ops = lookup_family(nlh->nlmsg_type))) + return -NLE_MSGTYPE_NOSUPPORT; + + return cmd_msg_parser(nlmsg_get_src(msg), nlh, ops, NULL, arg); +} + +/** @} */ + +/** + * @name Registration of Cache Operations + * @{ + */ + +/** + * Register Generic Netlink family backed cache + * @arg ops Cache operations definition + * + * Same as genl_register_family() but additionally registers the specified + * cache operations using nl_cache_mngt_register() and associates it with + * the Generic Netlink family. + * + * @see genl_register_family() + * + * @return 0 on success or a negative error code. + */ +int genl_register(struct nl_cache_ops *ops) +{ + int err; + + if (ops->co_protocol != NETLINK_GENERIC) { + err = -NLE_PROTO_MISMATCH; + goto errout; + } + + if (ops->co_hdrsize < GENL_HDRSIZE(0)) { + err = -NLE_INVAL; + goto errout; + } + + if (ops->co_genl == NULL) { + err = -NLE_INVAL; + goto errout; + } + + ops->co_genl->o_cache_ops = ops; + ops->co_genl->o_hdrsize = ops->co_hdrsize - GENL_HDRLEN; + ops->co_genl->o_name = ops->co_msgtypes[0].mt_name; + ops->co_genl->o_id = ops->co_msgtypes[0].mt_id; + ops->co_msg_parser = genl_msg_parser; + + if ((err = genl_register_family(ops->co_genl)) < 0) + goto errout; + + err = nl_cache_mngt_register(ops); +errout: + return err; +} + +/** + * Unregister cache based Generic Netlink family + * @arg ops Cache operations definition + */ +void genl_unregister(struct nl_cache_ops *ops) +{ + if (!ops) + return; + + nl_cache_mngt_unregister(ops); + + genl_unregister_family(ops->co_genl); +} + +/** @} */ + +/** @cond SKIP */ +static int __genl_ops_resolve(struct nl_cache *ctrl, struct genl_ops *ops) +{ + struct genl_family *family; + + family = genl_ctrl_search_by_name(ctrl, ops->o_name); + if (family != NULL) { + ops->o_id = genl_family_get_id(family); + + if (ops->o_cache_ops) + ops->o_cache_ops->co_msgtypes[0].mt_id = ops->o_id; + + genl_family_put(family); + + return 0; + } + + return -NLE_OBJ_NOTFOUND; +} + +int genl_resolve_id(struct genl_ops *ops) +{ + struct nl_sock *sk; + int err = 0; + + /* Check if resolved already */ + if (ops->o_id != 0) + return 0; + + if (!ops->o_name) + return -NLE_INVAL; + + if (!(sk = nl_socket_alloc())) + return -NLE_NOMEM; + + if ((err = genl_connect(sk)) < 0) + goto errout_free; + + err = genl_ops_resolve(sk, ops); + +errout_free: + nl_socket_free(sk); + + return err; +} +/** @endcond */ + +/** + * @name Resolving the name of registered families + * @{ + */ + +/** + * Resolve a single Generic Netlink family + * @arg sk Generic Netlink socket + * @arg ops Generic Netlink family definition + * + * Resolves the family name to its numeric identifier. + * + * @return 0 on success or a negative error code. + */ +int genl_ops_resolve(struct nl_sock *sk, struct genl_ops *ops) +{ + struct nl_cache *ctrl; + int err; + + if ((err = genl_ctrl_alloc_cache(sk, &ctrl)) < 0) + goto errout; + + err = __genl_ops_resolve(ctrl, ops); + + nl_cache_free(ctrl); +errout: + return err; +} + +/** + * Resolve all registered Generic Netlink families + * @arg sk Generic Netlink socket + * + * Walks through all local Generic Netlink families that have been registered + * using genl_register() and resolves the name of each family to the + * corresponding numeric identifier. + * + * @see genl_register() + * @see genl_ops_resolve() + * + * @return 0 on success or a negative error code. + */ +int genl_mngt_resolve(struct nl_sock *sk) +{ + struct nl_cache *ctrl; + struct genl_ops *ops; + int err = 0; + + if ((err = genl_ctrl_alloc_cache(sk, &ctrl)) < 0) + goto errout; + + nl_list_for_each_entry(ops, &genl_ops_list, o_list) { + err = __genl_ops_resolve(ctrl, ops); + } + + nl_cache_free(ctrl); +errout: + return err; +} + +/** @} */ + +/** @} */ diff --git a/libnl/lib/handlers.c b/libnl/lib/handlers.c new file mode 100644 index 0000000..a7e0a8f --- /dev/null +++ b/libnl/lib/handlers.c @@ -0,0 +1,399 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + */ + +/** + * @ingroup core + * @defgroup cb Callbacks/Customization + * + * Related sections in the development guide: + * - @core_doc{core_cb, Callback Configuration} + * + * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + +static void print_header_content(FILE *ofd, struct nlmsghdr *n) +{ + char flags[128]; + char type[32]; + + fprintf(ofd, "type=%s length=%u flags=<%s> sequence-nr=%u pid=%u", + nl_nlmsgtype2str(n->nlmsg_type, type, sizeof(type)), + n->nlmsg_len, nl_nlmsg_flags2str(n->nlmsg_flags, flags, + sizeof(flags)), n->nlmsg_seq, n->nlmsg_pid); +} + +static int nl_valid_handler_verbose(struct nl_msg *msg, void *arg) +{ + FILE *ofd = arg ? arg : stdout; + + fprintf(ofd, "-- Warning: unhandled valid message: "); + print_header_content(ofd, nlmsg_hdr(msg)); + fprintf(ofd, "\n"); + + return NL_OK; +} + +static int nl_invalid_handler_verbose(struct nl_msg *msg, void *arg) +{ + FILE *ofd = arg ? arg : stderr; + + fprintf(ofd, "-- Error: Invalid message: "); + print_header_content(ofd, nlmsg_hdr(msg)); + fprintf(ofd, "\n"); + + return NL_STOP; +} + +static int nl_overrun_handler_verbose(struct nl_msg *msg, void *arg) +{ + FILE *ofd = arg ? arg : stderr; + + fprintf(ofd, "-- Error: Netlink Overrun: "); + print_header_content(ofd, nlmsg_hdr(msg)); + fprintf(ofd, "\n"); + + return NL_STOP; +} + +static int nl_error_handler_verbose(struct sockaddr_nl *who, + struct nlmsgerr *e, void *arg) +{ + FILE *ofd = arg ? arg : stderr; + + fprintf(ofd, "-- Error received: %s\n-- Original message: ", + nl_strerror_l(-e->error)); + print_header_content(ofd, &e->msg); + fprintf(ofd, "\n"); + + return -nl_syserr2nlerr(e->error); +} + +static int nl_valid_handler_debug(struct nl_msg *msg, void *arg) +{ + FILE *ofd = arg ? arg : stderr; + + fprintf(ofd, "-- Debug: Unhandled Valid message: "); + print_header_content(ofd, nlmsg_hdr(msg)); + fprintf(ofd, "\n"); + + return NL_OK; +} + +static int nl_finish_handler_debug(struct nl_msg *msg, void *arg) +{ + FILE *ofd = arg ? arg : stderr; + + fprintf(ofd, "-- Debug: End of multipart message block: "); + print_header_content(ofd, nlmsg_hdr(msg)); + fprintf(ofd, "\n"); + + return NL_STOP; +} + +static int nl_msg_in_handler_debug(struct nl_msg *msg, void *arg) +{ + FILE *ofd = arg ? arg : stderr; + + fprintf(ofd, "-- Debug: Received Message:\n"); + nl_msg_dump(msg, ofd); + + return NL_OK; +} + +static int nl_msg_out_handler_debug(struct nl_msg *msg, void *arg) +{ + FILE *ofd = arg ? arg : stderr; + + fprintf(ofd, "-- Debug: Sent Message:\n"); + nl_msg_dump(msg, ofd); + + return NL_OK; +} + +static int nl_skipped_handler_debug(struct nl_msg *msg, void *arg) +{ + FILE *ofd = arg ? arg : stderr; + + fprintf(ofd, "-- Debug: Skipped message: "); + print_header_content(ofd, nlmsg_hdr(msg)); + fprintf(ofd, "\n"); + + return NL_SKIP; +} + +static int nl_ack_handler_debug(struct nl_msg *msg, void *arg) +{ + FILE *ofd = arg ? arg : stderr; + + fprintf(ofd, "-- Debug: ACK: "); + print_header_content(ofd, nlmsg_hdr(msg)); + fprintf(ofd, "\n"); + + return NL_STOP; +} + +static nl_recvmsg_msg_cb_t cb_def[NL_CB_TYPE_MAX+1][NL_CB_KIND_MAX+1] = { + [NL_CB_VALID] = { + [NL_CB_VERBOSE] = nl_valid_handler_verbose, + [NL_CB_DEBUG] = nl_valid_handler_debug, + }, + [NL_CB_FINISH] = { + [NL_CB_DEBUG] = nl_finish_handler_debug, + }, + [NL_CB_INVALID] = { + [NL_CB_VERBOSE] = nl_invalid_handler_verbose, + [NL_CB_DEBUG] = nl_invalid_handler_verbose, + }, + [NL_CB_MSG_IN] = { + [NL_CB_DEBUG] = nl_msg_in_handler_debug, + }, + [NL_CB_MSG_OUT] = { + [NL_CB_DEBUG] = nl_msg_out_handler_debug, + }, + [NL_CB_OVERRUN] = { + [NL_CB_VERBOSE] = nl_overrun_handler_verbose, + [NL_CB_DEBUG] = nl_overrun_handler_verbose, + }, + [NL_CB_SKIPPED] = { + [NL_CB_DEBUG] = nl_skipped_handler_debug, + }, + [NL_CB_ACK] = { + [NL_CB_DEBUG] = nl_ack_handler_debug, + }, +}; + +static nl_recvmsg_err_cb_t cb_err_def[NL_CB_KIND_MAX+1] = { + [NL_CB_VERBOSE] = nl_error_handler_verbose, + [NL_CB_DEBUG] = nl_error_handler_verbose, +}; + +/** + * @name Callback Handle Management + * @{ + */ + +/** + * Allocate a new callback handle + * @arg kind callback kind to be used for initialization + * @return Newly allocated callback handle or NULL + */ +struct nl_cb *nl_cb_alloc(enum nl_cb_kind kind) +{ + int i; + struct nl_cb *cb; + + if ((unsigned int) kind > NL_CB_KIND_MAX) + return NULL; + + cb = calloc(1, sizeof(*cb)); + if (!cb) + return NULL; + + cb->cb_refcnt = 1; + cb->cb_active = NL_CB_TYPE_MAX + 1; + + for (i = 0; i <= NL_CB_TYPE_MAX; i++) + nl_cb_set(cb, i, kind, NULL, NULL); + + nl_cb_err(cb, kind, NULL, NULL); + + return cb; +} + +/** + * Clone an existing callback handle + * @arg orig original callback handle + * @return Newly allocated callback handle being a duplicate of + * orig or NULL + */ +struct nl_cb *nl_cb_clone(struct nl_cb *orig) +{ + struct nl_cb *cb; + + cb = nl_cb_alloc(NL_CB_DEFAULT); + if (!cb) + return NULL; + + memcpy(cb, orig, sizeof(*orig)); + cb->cb_refcnt = 1; + + return cb; +} + +struct nl_cb *nl_cb_get(struct nl_cb *cb) +{ + cb->cb_refcnt++; + + return cb; +} + +void nl_cb_put(struct nl_cb *cb) +{ + if (!cb) + return; + + cb->cb_refcnt--; + + if (cb->cb_refcnt < 0) + BUG(); + + if (cb->cb_refcnt <= 0) + free(cb); +} + +/** + * Obtain type of current active callback + * @arg cb callback to query + * + * @return type or __NL_CB_TYPE_MAX if none active + */ +enum nl_cb_type nl_cb_active_type(struct nl_cb *cb) +{ + return cb->cb_active; +} + +/** @} */ + +/** + * @name Callback Setup + * @{ + */ + +/** + * Set up a callback + * @arg cb callback set + * @arg type callback to modify + * @arg kind kind of implementation + * @arg func callback function (NL_CB_CUSTOM) + * @arg arg argument passed to callback + * + * @return 0 on success or a negative error code + */ +int nl_cb_set(struct nl_cb *cb, enum nl_cb_type type, enum nl_cb_kind kind, + nl_recvmsg_msg_cb_t func, void *arg) +{ + if ((unsigned int) type > NL_CB_TYPE_MAX) + return -NLE_RANGE; + + if ((unsigned int) kind > NL_CB_KIND_MAX) + return -NLE_RANGE; + + if (kind == NL_CB_CUSTOM) { + cb->cb_set[type] = func; + cb->cb_args[type] = arg; + } else { + cb->cb_set[type] = cb_def[type][kind]; + cb->cb_args[type] = arg; + } + + return 0; +} + +/** + * Set up a all callbacks + * @arg cb callback set + * @arg kind kind of callback + * @arg func callback function + * @arg arg argument to be passwd to callback function + * + * @return 0 on success or a negative error code + */ +int nl_cb_set_all(struct nl_cb *cb, enum nl_cb_kind kind, + nl_recvmsg_msg_cb_t func, void *arg) +{ + int i, err; + + for (i = 0; i <= NL_CB_TYPE_MAX; i++) { + err = nl_cb_set(cb, i, kind, func, arg); + if (err < 0) + return err; + } + + return 0; +} + +/** + * Set up an error callback + * @arg cb callback set + * @arg kind kind of callback + * @arg func callback function + * @arg arg argument to be passed to callback function + */ +int nl_cb_err(struct nl_cb *cb, enum nl_cb_kind kind, + nl_recvmsg_err_cb_t func, void *arg) +{ + if ((unsigned int) kind > NL_CB_KIND_MAX) + return -NLE_RANGE; + + if (kind == NL_CB_CUSTOM) { + cb->cb_err = func; + cb->cb_err_arg = arg; + } else { + cb->cb_err = cb_err_def[kind]; + cb->cb_err_arg = arg; + } + + return 0; +} + +/** @} */ + +/** + * @name Overwriting + * @{ + */ + +/** + * Overwrite internal calls to nl_recvmsgs() + * @arg cb callback set + * @arg func replacement callback for nl_recvmsgs() + */ +void nl_cb_overwrite_recvmsgs(struct nl_cb *cb, + int (*func)(struct nl_sock *, struct nl_cb *)) +{ + cb->cb_recvmsgs_ow = func; +} + +/** + * Overwrite internal calls to nl_recv() + * @arg cb callback set + * @arg func replacement callback for nl_recv() + */ +void nl_cb_overwrite_recv(struct nl_cb *cb, + int (*func)(struct nl_sock *, struct sockaddr_nl *, + unsigned char **, struct ucred **)) +{ + cb->cb_recv_ow = func; +} + +/** + * Overwrite internal calls to nl_send() + * @arg cb callback set + * @arg func replacement callback for nl_send() + */ +void nl_cb_overwrite_send(struct nl_cb *cb, + int (*func)(struct nl_sock *, struct nl_msg *)) +{ + cb->cb_send_ow = func; +} + +/** @} */ + +/** @} */ diff --git a/libnl/lib/hash.c b/libnl/lib/hash.c new file mode 100644 index 0000000..17b5c8f --- /dev/null +++ b/libnl/lib/hash.c @@ -0,0 +1,483 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * This code was taken from http://ccodearchive.net/info/hash.html + * The original file was modified to remove unwanted code + * and some changes to fit the current build environment + */ +/* +------------------------------------------------------------------------------- +lookup3.c, by Bob Jenkins, May 2006, Public Domain. + +These are functions for producing 32-bit hashes for hash table lookup. +hash_word(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() +are externally useful functions. Routines to test the hash are included +if SELF_TEST is defined. You can use this free for any purpose. It's in +the public domain. It has no warranty. + +You probably want to use hashlittle(). hashlittle() and hashbig() +hash byte arrays. hashlittle() is is faster than hashbig() on +little-endian machines. Intel and AMD are little-endian machines. +On second thought, you probably want hashlittle2(), which is identical to +hashlittle() except it returns two 32-bit hashes for the price of one. +You could implement hashbig2() if you wanted but I haven't bothered here. + +If you want to find a hash of, say, exactly 7 integers, do + a = i1; b = i2; c = i3; + mix(a,b,c); + a += i4; b += i5; c += i6; + mix(a,b,c); + a += i7; + final(a,b,c); +then use c as the hash value. If you have a variable length array of +4-byte integers to hash, use hash_word(). If you have a byte array (like +a character string), use hashlittle(). If you have several byte arrays, or +a mix of things, see the comments above hashlittle(). + +Why is this so big? I read 12 bytes at a time into 3 4-byte integers, +then mix those integers. This is fast (you can do a lot more thorough +mixing with 12*3 instructions on 3 integers than you can with 3 instructions +on 1 byte), but shoehorning those bytes into integers efficiently is messy. +------------------------------------------------------------------------------- +*/ +#include + +#if HAVE_LITTLE_ENDIAN +#define HASH_LITTLE_ENDIAN 1 +#define HASH_BIG_ENDIAN 0 +#elif HAVE_BIG_ENDIAN +#define HASH_LITTLE_ENDIAN 0 +#define HASH_BIG_ENDIAN 1 +#else +#error Unknown endian +#endif + +#define hashsize(n) ((uint32_t)1<<(n)) +#define hashmask(n) (hashsize(n)-1) +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) + +/* +------------------------------------------------------------------------------- +mix -- mix 3 32-bit values reversibly. + +This is reversible, so any information in (a,b,c) before mix() is +still in (a,b,c) after mix(). + +If four pairs of (a,b,c) inputs are run through mix(), or through +mix() in reverse, there are at least 32 bits of the output that +are sometimes the same for one pair and different for another pair. +This was tested for: +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that +satisfy this are + 4 6 8 16 19 4 + 9 15 3 18 27 15 + 14 9 3 7 17 3 +Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing +for "differ" defined as + with a one-bit base and a two-bit delta. I +used http://burtleburtle.net/bob/hash/avalanche.html to choose +the operations, constants, and arrangements of the variables. + +This does not achieve avalanche. There are input bits of (a,b,c) +that fail to affect some output bits of (a,b,c), especially of a. The +most thoroughly mixed value is c, but it doesn't really even achieve +avalanche in c. + +This allows some parallelism. Read-after-writes are good at doubling +the number of bits affected, so the goal of mixing pulls in the opposite +direction as the goal of parallelism. I did what I could. Rotates +seem to cost as much as shifts on every machine I could lay my hands +on, and rotates are much kinder to the top and bottom bits, so I used +rotates. +------------------------------------------------------------------------------- +*/ +#define mix(a,b,c) \ +{ \ + a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c,16); c += b; \ + b -= a; b ^= rot(a,19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ +} + +/* +------------------------------------------------------------------------------- +final -- final mixing of 3 32-bit values (a,b,c) into c + +Pairs of (a,b,c) values differing in only a few bits will usually +produce values of c that look totally different. This was tested for +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +These constants passed: + 14 11 25 16 4 14 24 + 12 14 25 16 4 14 24 +and these came close: + 4 8 15 26 3 22 24 + 10 8 15 26 3 22 24 + 11 8 15 26 3 22 24 +------------------------------------------------------------------------------- +*/ +#define final(a,b,c) \ +{ \ + c ^= b; c -= rot(b,14); \ + a ^= c; a -= rot(c,11); \ + b ^= a; b -= rot(a,25); \ + c ^= b; c -= rot(b,16); \ + a ^= c; a -= rot(c,4); \ + b ^= a; b -= rot(a,14); \ + c ^= b; c -= rot(b,24); \ +} + +/* +------------------------------------------------------------------------------- +hashlittle() -- hash a variable-length key into a 32-bit value + k : the key (the unaligned variable-length array of bytes) + length : the length of the key, counting by bytes + val2 : IN: can be any 4-byte value OUT: second 32 bit hash. +Returns a 32-bit value. Every bit of the key affects every bit of +the return value. Two keys differing by one or two bits will have +totally different hash values. Note that the return value is better +mixed than val2, so use that first. + +The best hash table sizes are powers of 2. There is no need to do +mod a prime (mod is sooo slow!). If you need less than 32 bits, +use a bitmask. For example, if you need only 10 bits, do + h = (h & hashmask(10)); +In which case, the hash table should have hashsize(10) elements. + +If you are hashing n strings (uint8_t **)k, do it like this: + for (i=0, h=0; i 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]&0xffffff" actually reads beyond the end of the string, but + * then masks off the part it's not allowed to read. Because the + * string is aligned, the masked-off tail is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + * + * Not on my testing with gcc 4.5 on an intel i5 CPU, at least --RR. + */ +#if 0 + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff; a+=k[0]; break; + case 6 : b+=k[1]&0xffff; a+=k[0]; break; + case 5 : b+=k[1]&0xff; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff; break; + case 2 : a+=k[0]&0xffff; break; + case 1 : a+=k[0]&0xff; break; + case 0 : return c; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ + case 1 : a+=k8[0]; break; + case 0 : return c; + } + +#endif /* !valgrind */ + + } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { + const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ + const uint8_t *k8; + + /*--------------- all but last block: aligned reads and different mixing */ + while (length > 12) + { + a += k[0] + (((uint32_t)k[1])<<16); + b += k[2] + (((uint32_t)k[3])<<16); + c += k[4] + (((uint32_t)k[5])<<16); + mix(a,b,c); + length -= 12; + k += 6; + } + + /*----------------------------- handle the last (probably partial) block */ + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[4]+(((uint32_t)k[5])<<16); + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=k[4]; + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=k[2]; + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=k[0]; + break; + case 1 : a+=k8[0]; + break; + case 0 : return c; /* zero length requires no mixing */ + } + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + a += ((uint32_t)k[1])<<8; + a += ((uint32_t)k[2])<<16; + a += ((uint32_t)k[3])<<24; + b += k[4]; + b += ((uint32_t)k[5])<<8; + b += ((uint32_t)k[6])<<16; + b += ((uint32_t)k[7])<<24; + c += k[8]; + c += ((uint32_t)k[9])<<8; + c += ((uint32_t)k[10])<<16; + c += ((uint32_t)k[11])<<24; + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=((uint32_t)k[11])<<24; /* fall through */ + case 11: c+=((uint32_t)k[10])<<16; /* fall through */ + case 10: c+=((uint32_t)k[9])<<8; /* fall through */ + case 9 : c+=k[8]; /* fall through */ + case 8 : b+=((uint32_t)k[7])<<24; /* fall through */ + case 7 : b+=((uint32_t)k[6])<<16; /* fall through */ + case 6 : b+=((uint32_t)k[5])<<8; /* fall through */ + case 5 : b+=k[4]; /* fall through */ + case 4 : a+=((uint32_t)k[3])<<24; /* fall through */ + case 3 : a+=((uint32_t)k[2])<<16; /* fall through */ + case 2 : a+=((uint32_t)k[1])<<8; /* fall through */ + case 1 : a+=k[0]; + break; + case 0 : return c; + } + } + + final(a,b,c); + *val2 = b; + return c; +} + +/* + * hashbig(): + * This is the same as hash_word() on big-endian machines. It is different + * from hashlittle() on all machines. hashbig() takes advantage of + * big-endian byte ordering. + */ +static uint32_t hashbig( const void *key, size_t length, uint32_t *val2) +{ + uint32_t a,b,c; + union { const void *ptr; size_t i; } u; /* to cast key to (size_t) happily */ + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t)length) + *val2; + + u.ptr = key; + if (HASH_BIG_ENDIAN && ((u.i & 0x3) == 0)) { + const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */ + const uint8_t *k8; + + /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]<<8" actually reads beyond the end of the string, but + * then shifts out the part it's not allowed to read. Because the + * string is aligned, the illegal read is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + * + * Not on my testing with gcc 4.5 on an intel i5 CPU, at least --RR. + */ +#if 0 + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff00; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff0000; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff000000; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff00; a+=k[0]; break; + case 6 : b+=k[1]&0xffff0000; a+=k[0]; break; + case 5 : b+=k[1]&0xff000000; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff00; break; + case 2 : a+=k[0]&0xffff0000; break; + case 1 : a+=k[0]&0xff000000; break; + case 0 : return c; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + k8 = (const uint8_t *)k; + switch(length) /* all the case statements fall through */ + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<8; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<16; /* fall through */ + case 9 : c+=((uint32_t)k8[8])<<24; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<8; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<16; /* fall through */ + case 5 : b+=((uint32_t)k8[4])<<24; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<8; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<16; /* fall through */ + case 1 : a+=((uint32_t)k8[0])<<24; break; + case 0 : return c; + } + +#endif /* !VALGRIND */ + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += ((uint32_t)k[0])<<24; + a += ((uint32_t)k[1])<<16; + a += ((uint32_t)k[2])<<8; + a += ((uint32_t)k[3]); + b += ((uint32_t)k[4])<<24; + b += ((uint32_t)k[5])<<16; + b += ((uint32_t)k[6])<<8; + b += ((uint32_t)k[7]); + c += ((uint32_t)k[8])<<24; + c += ((uint32_t)k[9])<<16; + c += ((uint32_t)k[10])<<8; + c += ((uint32_t)k[11]); + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=k[11]; /* fall through */ + case 11: c+=((uint32_t)k[10])<<8; /* fall through */ + case 10: c+=((uint32_t)k[9])<<16; /* fall through */ + case 9 : c+=((uint32_t)k[8])<<24; /* fall through */ + case 8 : b+=k[7]; /* fall through */ + case 7 : b+=((uint32_t)k[6])<<8; /* fall through */ + case 6 : b+=((uint32_t)k[5])<<16; /* fall through */ + case 5 : b+=((uint32_t)k[4])<<24; /* fall through */ + case 4 : a+=k[3]; /* fall through */ + case 3 : a+=((uint32_t)k[2])<<8; /* fall through */ + case 2 : a+=((uint32_t)k[1])<<16; /* fall through */ + case 1 : a+=((uint32_t)k[0])<<24; /* fall through */ + break; + case 0 : return c; + } + } + + final(a,b,c); + *val2 = b; + return c; +} + +uint32_t nl_hash_any(const void *key, size_t length, uint32_t base) +{ + if (HASH_BIG_ENDIAN) + return hashbig(key, length, &base); + else + return hashlittle(key, length, &base); +} diff --git a/libnl/lib/hashtable.c b/libnl/lib/hashtable.c new file mode 100644 index 0000000..ae3b606 --- /dev/null +++ b/libnl/lib/hashtable.c @@ -0,0 +1,194 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2012 Cumulus Networks, Inc + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include + +/** + * @ingroup core_types + * @defgroup hashtable Hashtable + * @{ + */ + +/** + * Allocate hashtable + * @arg size Size of hashtable in number of elements + * + * @return Allocated hashtable or NULL. + */ +nl_hash_table_t *nl_hash_table_alloc(int size) +{ + nl_hash_table_t *ht; + + ht = calloc(1, sizeof (*ht)); + if (!ht) + goto errout; + + ht->nodes = calloc(size, sizeof (*ht->nodes)); + if (!ht->nodes) { + free(ht); + goto errout; + } + + ht->size = size; + + return ht; +errout: + return NULL; +} + +/** + * Free hashtable including all nodes + * @arg ht Hashtable + * + * @note Reference counter of all objects in the hashtable will be decremented. + */ +void nl_hash_table_free(nl_hash_table_t *ht) +{ + int i; + + for(i = 0; i < ht->size; i++) { + nl_hash_node_t *node = ht->nodes[i]; + nl_hash_node_t *saved_node; + + while (node) { + saved_node = node; + node = node->next; + nl_object_put(saved_node->obj); + free(saved_node); + } + } + + free(ht->nodes); + free(ht); +} + +/** + * Lookup identical object in hashtable + * @arg ht Hashtable + * @arg obj Object to lookup + * + * Generates hashkey for `obj` and traverses the corresponding chain calling + * `nl_object_identical()` on each trying to find a match. + * + * @return Pointer to object if match was found or NULL. + */ +struct nl_object* nl_hash_table_lookup(nl_hash_table_t *ht, + struct nl_object *obj) +{ + nl_hash_node_t *node; + uint32_t key_hash; + + nl_object_keygen(obj, &key_hash, ht->size); + node = ht->nodes[key_hash]; + + while (node) { + if (nl_object_identical(node->obj, obj)) + return node->obj; + node = node->next; + } + + return NULL; +} + +/** + * Add object to hashtable + * @arg ht Hashtable + * @arg obj Object to add + * + * Adds `obj` to the hashtable. Object type must support hashing, otherwise all + * objects will be added to the chain `0`. + * + * @note The reference counter of the object is incremented. + * + * @return 0 on success or a negative error code + * @retval -NLE_EXIST Identical object already present in hashtable + */ +int nl_hash_table_add(nl_hash_table_t *ht, struct nl_object *obj) +{ + nl_hash_node_t *node; + uint32_t key_hash; + + nl_object_keygen(obj, &key_hash, ht->size); + node = ht->nodes[key_hash]; + + while (node) { + if (nl_object_identical(node->obj, obj)) { + NL_DBG(2, "Warning: Add to hashtable found duplicate...\n"); + return -NLE_EXIST; + } + node = node->next; + } + + NL_DBG (5, "adding cache entry of obj %p in table %p, with hash 0x%x\n", + obj, ht, key_hash); + + node = malloc(sizeof(nl_hash_node_t)); + if (!node) + return -NLE_NOMEM; + nl_object_get(obj); + node->obj = obj; + node->key = key_hash; + node->key_size = sizeof(uint32_t); + node->next = ht->nodes[key_hash]; + ht->nodes[key_hash] = node; + + return 0; +} + +/** + * Remove object from hashtable + * @arg ht Hashtable + * @arg obj Object to remove + * + * Remove `obj` from hashtable if it exists. + * + * @note Reference counter of object will be decremented. + * + * @return 0 on success or a negative error code. + * @retval -NLE_OBJ_NOTFOUND Object not present in hashtable. + */ +int nl_hash_table_del(nl_hash_table_t *ht, struct nl_object *obj) +{ + nl_hash_node_t *node, *prev; + uint32_t key_hash; + + nl_object_keygen(obj, &key_hash, ht->size); + prev = node = ht->nodes[key_hash]; + + while (node) { + if (nl_object_identical(node->obj, obj)) { + nl_object_put(obj); + + NL_DBG (5, "deleting cache entry of obj %p in table %p, with" + " hash 0x%x\n", obj, ht, key_hash); + + if (node == ht->nodes[key_hash]) + ht->nodes[key_hash] = node->next; + else + prev->next = node->next; + + free(node); + + return 0; + } + prev = node; + node = node->next; + } + + return -NLE_OBJ_NOTFOUND; +} + +uint32_t nl_hash(void *k, size_t length, uint32_t initval) +{ + return(__nl_hash((char *) k, length, initval)); +} + +/** @} */ diff --git a/libnl/lib/idiag/idiag.c b/libnl/lib/idiag/idiag.c new file mode 100644 index 0000000..16436df --- /dev/null +++ b/libnl/lib/idiag/idiag.c @@ -0,0 +1,274 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Sassano Systems LLC + */ + +/** + * @defgroup idiag Inet Diag library (libnl-idiag) + * @brief + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include + +/** + * @name Socket Creation + * @{ + */ + +/** + * Create and connect idiag netlink socket. + * @arg sk Netlink socket. + * + * Creates a NETLINK_INET_DIAG socket, binds the socket, and issues a connection + * attemp. + * + * @see nl_connect() + * + * @return 0 on success or a negative error code. + */ +int idiagnl_connect(struct nl_sock *sk) +{ + return nl_connect(sk, NETLINK_INET_DIAG); +} + +/** @} */ + +/** + * @name Sending + * @{ + */ + +/** + * Send trivial idiag netlink message + * @arg sk Netlink socket. + * @arg flags Message flags + * @arg family Address family + * @arg states Socket states to query + * @arg ext Inet Diag attribute extensions to query. Note that this only supports + * 8 bit arguments. Flags outside uint8_t range are silently ignored. + * + * @return 0 on success or a negative error code. Due to a bug, this function + * returns the number of bytes sent. Treat any non-negative number as success. + */ +int idiagnl_send_simple(struct nl_sock *sk, int flags, uint8_t family, + uint16_t states, uint16_t ext) +{ + struct inet_diag_req req; + memset(&req, 0, sizeof(req)); + + flags |= NLM_F_ROOT; + + req.idiag_family = family; + req.idiag_states = states; + req.idiag_ext = ext; + + return nl_send_simple(sk, TCPDIAG_GETSOCK, flags, &req, sizeof(req)); +} + +/** @} */ + +/** + * @name Inet Diag flag and attribute conversions + * @{ + */ + +static const struct trans_tbl idiag_states[] = { + __ADD(TCP_ESTABLISHED, established), + __ADD(TCP_SYN_SENT, syn_sent), + __ADD(TCP_SYN_RECV, syn_recv), + __ADD(TCP_FIN_WAIT1, fin_wait), + __ADD(TCP_FIN_WAIT2, fin_wait2), + __ADD(TCP_TIME_WAIT, time_wait), + __ADD(TCP_CLOSE, close), + __ADD(TCP_CLOSE_WAIT, close_wait), + __ADD(TCP_LAST_ACK, last_ack), + __ADD(TCP_LISTEN, listen), + __ADD(TCP_CLOSING, closing), +}; + +/** + * Convert inet diag socket states to strings. + * @arg state inetdiag socket state (e.g., TCP_ESTABLISHED) + * @arg buf output buffer which will hold string result + * @arg len length in bytes of the output buffer + * + * @return string representation of the inetdiag socket state or an empty + * string. + */ +char * idiagnl_state2str(int state, char *buf, size_t len) +{ + return __type2str(state, buf, len, idiag_states, + ARRAY_SIZE(idiag_states)); +} + +/** + * Convert inet diag socket state string to int. + * @arg name inetdiag socket state string + * + * @return the int representation of the socket state strign or a negative error + * code. + */ +int idiagnl_str2state(const char *name) +{ + return __str2type(name, idiag_states, ARRAY_SIZE(idiag_states)); +} + +static const struct trans_tbl idiag_timers[] = { + __ADD(IDIAGNL_TIMER_OFF, off), + __ADD(IDIAGNL_TIMER_ON, on), + __ADD(IDIAGNL_TIMER_KEEPALIVE, keepalive), + __ADD(IDIAGNL_TIMER_TIMEWAIT, timewait), + __ADD(IDIAGNL_TIMER_PERSIST, persist), + __ADD(IDIAGNL_TIMER_UNKNOWN, unknown), +}; + +/** + * Convert inet diag timer types to strings. + * @arg timer inetdiag timer (e.g., IDIAGNL_TIMER_ON) + * @arg buf output buffer which will hold string result + * @arg len length in bytes of the output buffer + * + * @return string representation of the inetdiag timer type or an empty string. + */ +char * idiagnl_timer2str(int timer, char *buf, size_t len) +{ + return __type2str(timer, buf, len, idiag_timers, + ARRAY_SIZE(idiag_timers)); +} + +/** + * Convert inet diag timer string to int. + * @arg name inetdiag timer string + * + * @return the int representation of the timer string or a negative error code. + */ +int idiagnl_str2timer(const char *name) +{ + return __str2type(name, idiag_timers, ARRAY_SIZE(idiag_timers)); +} + +static const struct trans_tbl idiag_attrs[] = { + __ADD(INET_DIAG_NONE, none), + __ADD(INET_DIAG_MEMINFO, meminfo), + __ADD(INET_DIAG_INFO, info), + __ADD(INET_DIAG_VEGASINFO, vegasinfo), + __ADD(INET_DIAG_CONG, congestion), + __ADD(INET_DIAG_TOS, tos), + __ADD(INET_DIAG_TCLASS, tclass), + __ADD(INET_DIAG_SKMEMINFO, skmeminfo), + __ADD(INET_DIAG_SHUTDOWN, shutdown), +}; + +/** + * Convert inet diag extension type to a string. + * @arg attrs inet diag extension type (e.g. INET_DIAG_MEMINFO) + * @arg buf output buffer which will hold string result + * @arg len length in bytes of the output buffer + * + * @return string representation of inet diag extension type or an empty string. + * @deprecated: don't use this function. It is not very useful and should + * never have been exposed as public API. + */ +char *idiagnl_attrs2str(int attrs, char *buf, size_t len) +{ + return __type2str(attrs, buf, len, idiag_attrs, ARRAY_SIZE(idiag_attrs)); +} + +static const struct trans_tbl idiag_exts[] = { + __ADD((1 << (INET_DIAG_MEMINFO - 1)), meminfo), + __ADD((1 << (INET_DIAG_INFO - 1)), info), + __ADD((1 << (INET_DIAG_VEGASINFO - 1)), vegasinfo), + __ADD((1 << (INET_DIAG_CONG - 1)), congestion), + __ADD((1 << (INET_DIAG_TOS - 1)), tos), + __ADD((1 << (INET_DIAG_TCLASS - 1)), tclass), + __ADD((1 << (INET_DIAG_SKMEMINFO - 1)), skmeminfo), + __ADD((1 << (INET_DIAG_SHUTDOWN - 1)), shutdown), +}; + +/** + * Convert inet diag extension flags to a string. + * @arg attrs inet diag extension flags (e.g. + * ( (1<<(INET_DIAG_MEMINFO-1)) | (1<<(INET_DIAG_CONG-1)) | (1<<(INET_DIAG_TOS-1)) ) ) + * @arg buf Output buffer to hold string representation + * @arg len length in bytes of the output buffer + */ +char *idiagnl_exts2str(uint8_t attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, idiag_exts, ARRAY_SIZE(idiag_exts)); +} + +static const struct trans_tbl idiagnl_tcpstates[] = { + __ADD(TCP_CA_Open, open), + __ADD(TCP_CA_Disorder, disorder), + __ADD(TCP_CA_CWR, cwr), + __ADD(TCP_CA_Recovery, recovery), + __ADD(TCP_CA_Loss, loss), +}; + +/** + * Convert inetdiag tcp states to strings. + * @arg state TCP state (e.g., TCP_CA_Open) + * @arg buf output buffer which will hold string result + * @arg len length in bytes of the output buffer + */ +char *idiagnl_tcpstate2str(uint8_t state, char *buf, size_t len) +{ + return __type2str(state, buf, len, idiagnl_tcpstates, + ARRAY_SIZE(idiagnl_tcpstates)); +} + +static const struct trans_tbl idiagnl_tcpopt_attrs[] = { + __ADD(TCPI_OPT_TIMESTAMPS, timestamps), + __ADD(TCPI_OPT_SACK, sACK), + __ADD(TCPI_OPT_WSCALE, wscale), + __ADD(TCPI_OPT_ECN, ecn), +}; + +/** + * Convert TCP option attributes to string + * @arg attrs TCP option attributes to convert (e.g., TCPI_OPT_SACK | + * TCPI_OPT_WSCALE) + * @arg buf Output buffer for string + * @arg len Length in bytes of output buffer + * + * @return buffer with string representation or empty string + */ +char *idiagnl_tcpopts2str(uint8_t attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, idiagnl_tcpopt_attrs, + ARRAY_SIZE(idiagnl_tcpopt_attrs)); +} + +/** + * Convert shutdown state to string. + * @arg shutdown Shutdown state (e.g., idiag_msg->shutdown) + * @arg buf Ouput buffer to hold string representation + * @arg len Length in bytes of output buffer + * + * @return string representation of shutdown state or NULL + */ +char * idiagnl_shutdown2str(uint8_t shutdown, char *buf, size_t len) +{ + if (shutdown == 0) { + snprintf(buf, len, " "); + return buf; + } else if (shutdown == 1) { + snprintf(buf, len, "receive shutdown"); + return buf; + } else if (shutdown == 2) { + snprintf(buf, len, "send shutdown"); + return buf; + } + + return NULL; +} + +/** @} */ +/** @} */ diff --git a/libnl/lib/idiag/idiag_meminfo_obj.c b/libnl/lib/idiag/idiag_meminfo_obj.c new file mode 100644 index 0000000..194e3e0 --- /dev/null +++ b/libnl/lib/idiag/idiag_meminfo_obj.c @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Sassano Systems LLC + */ + +#define _GNU_SOURCE + +#include +#include + +/** + * @ingroup idiag + * @defgroup idiagnl_meminfo Inet Diag Memory Info + * + * @details + * @idiagnl_doc{idiagnl_meminfo, Inet Diag Memory Info Documentation} + * @{ + */ +struct idiagnl_meminfo *idiagnl_meminfo_alloc(void) +{ + return (struct idiagnl_meminfo *) nl_object_alloc(&idiagnl_meminfo_obj_ops); +} + +void idiagnl_meminfo_get(struct idiagnl_meminfo *minfo) +{ + nl_object_get((struct nl_object *) minfo); +} + +void idiagnl_meminfo_put(struct idiagnl_meminfo *minfo) +{ + nl_object_put((struct nl_object *) minfo); +} + +/** + * @name Attributes + * @{ + */ +uint32_t idiagnl_meminfo_get_rmem(const struct idiagnl_meminfo *minfo) +{ + return minfo->idiag_rmem; +} + +void idiagnl_meminfo_set_rmem(struct idiagnl_meminfo *minfo, uint32_t rmem) +{ + minfo->idiag_rmem = rmem; +} + +uint32_t idiagnl_meminfo_get_wmem(const struct idiagnl_meminfo *minfo) +{ + return minfo->idiag_wmem; +} + +void idiagnl_meminfo_set_wmem(struct idiagnl_meminfo *minfo, uint32_t wmem) +{ + minfo->idiag_wmem = wmem; +} + +uint32_t idiagnl_meminfo_get_fmem(const struct idiagnl_meminfo *minfo) +{ + return minfo->idiag_fmem; +} + +void idiagnl_meminfo_set_fmem(struct idiagnl_meminfo *minfo, uint32_t fmem) +{ + minfo->idiag_fmem = fmem; +} + +uint32_t idiagnl_meminfo_get_tmem(const struct idiagnl_meminfo *minfo) +{ + return minfo->idiag_tmem; +} + +void idiagnl_meminfo_set_tmem(struct idiagnl_meminfo *minfo, uint32_t tmem) +{ + minfo->idiag_tmem = tmem; +} +/** @} */ + +/** @cond SKIP */ +static uint64_t idiagnl_meminfo_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + struct idiagnl_meminfo *a = (struct idiagnl_meminfo *) _a; + struct idiagnl_meminfo *b = (struct idiagnl_meminfo *) _b; + + /* meminfo is a very simple object. It has no attribe flags (ce_mask), + * hence compare just returns 0 or 1, not a bit mask of attributes. */ + return a->idiag_rmem != b->idiag_rmem || + a->idiag_wmem != b->idiag_wmem || + a->idiag_fmem != b->idiag_fmem || + a->idiag_tmem != b->idiag_tmem; +} + +struct nl_object_ops idiagnl_meminfo_obj_ops = { + .oo_name = "idiag/idiag_meminfo", + .oo_size = sizeof(struct idiagnl_meminfo), + .oo_compare = idiagnl_meminfo_compare, +}; +/** @endcond */ +/** @} */ diff --git a/libnl/lib/idiag/idiag_msg_obj.c b/libnl/lib/idiag/idiag_msg_obj.c new file mode 100644 index 0000000..a000b2f --- /dev/null +++ b/libnl/lib/idiag/idiag_msg_obj.c @@ -0,0 +1,955 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Sassano Systems LLC + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + + +/** @cond SKIP */ +#define IDIAGNL_ATTR_FAMILY (0x1 << 1) +#define IDIAGNL_ATTR_STATE (0x1 << 2) +#define IDIAGNL_ATTR_TIMER (0x1 << 3) +#define IDIAGNL_ATTR_RETRANS (0x1 << 4) +#define IDIAGNL_ATTR_SPORT (0x1 << 5) +#define IDIAGNL_ATTR_DPORT (0x1 << 6) +#define IDIAGNL_ATTR_SRC (0x1 << 7) +#define IDIAGNL_ATTR_DST (0x1 << 8) +#define IDIAGNL_ATTR_IFINDEX (0x1 << 9) +#define IDIAGNL_ATTR_EXPIRES (0x1 << 10) +#define IDIAGNL_ATTR_RQUEUE (0x1 << 11) +#define IDIAGNL_ATTR_WQUEUE (0x1 << 12) +#define IDIAGNL_ATTR_UID (0x1 << 13) +#define IDIAGNL_ATTR_INODE (0x1 << 14) +#define IDIAGNL_ATTR_TOS (0x1 << 15) +#define IDIAGNL_ATTR_TCLASS (0x1 << 16) +#define IDIAGNL_ATTR_SHUTDOWN (0x1 << 17) +#define IDIAGNL_ATTR_CONG (0x1 << 18) +#define IDIAGNL_ATTR_MEMINFO (0x1 << 19) +#define IDIAGNL_ATTR_VEGASINFO (0x1 << 20) +#define IDIAGNL_ATTR_TCPINFO (0x1 << 21) +#define IDIAGNL_ATTR_SKMEMINFO (0x1 << 22) + +#define _INET_DIAG_ALL ((1<<(INET_DIAG_MAX+1))-1) +/** @endcond */ + +/** + * @ingroup idiag + * @defgroup idiagnl_msg Inet Diag Messages + * + * @details + * @idiagnl_doc{idiagnl_msg, Inet Diag Message Documentation} + * @{ + */ +struct idiagnl_msg *idiagnl_msg_alloc(void) +{ + return (struct idiagnl_msg *) nl_object_alloc(&idiagnl_msg_obj_ops); +} + +void idiagnl_msg_get(struct idiagnl_msg *msg) +{ + nl_object_get((struct nl_object *) msg); +} + +void idiagnl_msg_put(struct idiagnl_msg *msg) +{ + nl_object_put((struct nl_object *) msg); +} + +static struct nl_cache_ops idiagnl_msg_ops; + +static int idiagnl_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, struct nl_parser_param *pp) +{ + struct idiagnl_msg *msg = NULL; + int err = 0; + + if ((err = idiagnl_msg_parse(nlh, &msg)) < 0) + return err; + + err = pp->pp_cb((struct nl_object *) msg, pp); + idiagnl_msg_put(msg); + + return err; +} + +static int idiagnl_request_update(struct nl_cache *cache, struct nl_sock *sk) +{ + int family = cache->c_iarg1; + int states = cache->c_iarg2; + + /* idiagnl_send_simple()'s "ext" argument is u16, which is too small for _INET_DIAG_ALL, + * which is more than 16 bits on recent kernels. + * + * Actually, internally idiagnl_send_simple() sets "struct inet_diag_req"'s "idiag_ext" + * field, which is only 8 bits. So, it's even worse. + * + * FIXME: this probably should be fixed (by adding idiagnl_send_simple2() function), but for + * the moment it means we cannot request more than 0xFF. + */ + + return idiagnl_send_simple(sk, 0, family, states, (uint16_t) _INET_DIAG_ALL); +} + +static struct nl_cache_ops idiagnl_msg_ops = { + .co_name = "idiag/idiag", + .co_hdrsize = sizeof(struct inet_diag_msg), + .co_msgtypes = { + { TCPDIAG_GETSOCK, NL_ACT_NEW, "new" }, + { DCCPDIAG_GETSOCK, NL_ACT_NEW, "new" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_INET_DIAG, + .co_request_update = idiagnl_request_update, + .co_msg_parser = idiagnl_msg_parser, + .co_obj_ops = &idiagnl_msg_obj_ops, +}; + +static void __init idiagnl_init(void) +{ + nl_cache_mngt_register(&idiagnl_msg_ops); +} + +static void __exit idiagnl_exit(void) +{ + nl_cache_mngt_unregister(&idiagnl_msg_ops); +} + +/** + * @name Cache Management + * @{ + */ + +/** + * Build an inetdiag cache to hold socket state information. + * @arg sk Netlink socket + * @arg family The address family to query + * @arg states Socket states to query + * @arg result Result pointer + * + * @note The caller is responsible for destroying and free the cache after using + * it. + * @return 0 on success of a negative error code. + */ +int idiagnl_msg_alloc_cache(struct nl_sock *sk, int family, int states, + struct nl_cache **result) +{ + struct nl_cache *cache = NULL; + int err; + + if (!(cache = nl_cache_alloc(&idiagnl_msg_ops))) + return -NLE_NOMEM; + + cache->c_iarg1 = family; + cache->c_iarg2 = states; + + if (sk && (err = nl_cache_refill(sk, cache)) < 0) { + free(cache); + return err; + } + + *result = cache; + return 0; +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +uint8_t idiagnl_msg_get_family(const struct idiagnl_msg *msg) +{ + return msg->idiag_family; +} + +void idiagnl_msg_set_family(struct idiagnl_msg *msg, uint8_t family) +{ + msg->idiag_family = family; + msg->ce_mask |= IDIAGNL_ATTR_FAMILY; +} + +uint8_t idiagnl_msg_get_state(const struct idiagnl_msg *msg) +{ + return msg->idiag_state; +} + +void idiagnl_msg_set_state(struct idiagnl_msg *msg, uint8_t state) +{ + msg->idiag_state = state; + msg->ce_mask |= IDIAGNL_ATTR_STATE; +} + +uint8_t idiagnl_msg_get_timer(const struct idiagnl_msg *msg) +{ + return msg->idiag_timer; +} + +void idiagnl_msg_set_timer(struct idiagnl_msg *msg, uint8_t timer) +{ + msg->idiag_timer = timer; + msg->ce_mask |= IDIAGNL_ATTR_TIMER; +} + +uint8_t idiagnl_msg_get_retrans(const struct idiagnl_msg *msg) +{ + return msg->idiag_retrans; +} + +void idiagnl_msg_set_retrans(struct idiagnl_msg *msg, uint8_t retrans) +{ + msg->idiag_retrans = retrans; + msg->ce_mask |= IDIAGNL_ATTR_RETRANS; +} + +uint16_t idiagnl_msg_get_sport(struct idiagnl_msg *msg) +{ + return msg->idiag_sport; +} + +void idiagnl_msg_set_sport(struct idiagnl_msg *msg, uint16_t port) +{ + msg->idiag_sport = port; + msg->ce_mask |= IDIAGNL_ATTR_SPORT; +} + +uint16_t idiagnl_msg_get_dport(struct idiagnl_msg *msg) +{ + return msg->idiag_dport; +} + +void idiagnl_msg_set_dport(struct idiagnl_msg *msg, uint16_t port) +{ + msg->idiag_dport = port; + msg->ce_mask |= IDIAGNL_ATTR_DPORT; +} + +struct nl_addr *idiagnl_msg_get_src(const struct idiagnl_msg *msg) +{ + return msg->idiag_src; +} + +int idiagnl_msg_set_src(struct idiagnl_msg *msg, struct nl_addr *addr) +{ + if (msg->idiag_src) + nl_addr_put(msg->idiag_src); + + nl_addr_get(addr); + msg->idiag_src = addr; + msg->ce_mask |= IDIAGNL_ATTR_SRC; + + return 0; +} + +struct nl_addr *idiagnl_msg_get_dst(const struct idiagnl_msg *msg) +{ + return msg->idiag_dst; +} + +int idiagnl_msg_set_dst(struct idiagnl_msg *msg, struct nl_addr *addr) +{ + if (msg->idiag_dst) + nl_addr_put(msg->idiag_dst); + + nl_addr_get(addr); + msg->idiag_dst = addr; + msg->ce_mask |= IDIAGNL_ATTR_DST; + + return 0; +} + +uint32_t idiagnl_msg_get_ifindex(const struct idiagnl_msg *msg) +{ + return msg->idiag_ifindex; +} + +void idiagnl_msg_set_ifindex(struct idiagnl_msg *msg, uint32_t ifindex) +{ + msg->idiag_ifindex = ifindex; + msg->ce_mask |= IDIAGNL_ATTR_IFINDEX; +} + +uint32_t idiagnl_msg_get_expires(const struct idiagnl_msg *msg) +{ + return msg->idiag_expires; +} + +void idiagnl_msg_set_expires(struct idiagnl_msg *msg, uint32_t expires) +{ + msg->idiag_expires = expires; + msg->ce_mask |= IDIAGNL_ATTR_EXPIRES; +} + +uint32_t idiagnl_msg_get_rqueue(const struct idiagnl_msg *msg) +{ + return msg->idiag_rqueue; +} + +void idiagnl_msg_set_rqueue(struct idiagnl_msg *msg, uint32_t rqueue) +{ + msg->idiag_rqueue = rqueue; + msg->ce_mask |= IDIAGNL_ATTR_RQUEUE; +} + +uint32_t idiagnl_msg_get_wqueue(const struct idiagnl_msg *msg) +{ + return msg->idiag_wqueue; +} + +void idiagnl_msg_set_wqueue(struct idiagnl_msg *msg, uint32_t wqueue) +{ + msg->idiag_wqueue = wqueue; + msg->ce_mask |= IDIAGNL_ATTR_WQUEUE; +} + +uint32_t idiagnl_msg_get_uid(const struct idiagnl_msg *msg) +{ + return msg->idiag_uid; +} + +void idiagnl_msg_set_uid(struct idiagnl_msg *msg, uint32_t uid) +{ + msg->idiag_uid = uid; + msg->ce_mask |= IDIAGNL_ATTR_UID; +} + +uint32_t idiagnl_msg_get_inode(const struct idiagnl_msg *msg) +{ + return msg->idiag_inode; +} + +void idiagnl_msg_set_inode(struct idiagnl_msg *msg, uint32_t inode) +{ + msg->idiag_inode = inode; + msg->ce_mask |= IDIAGNL_ATTR_INODE; +} + +uint8_t idiagnl_msg_get_tos(const struct idiagnl_msg *msg) +{ + return msg->idiag_tos; +} + +void idiagnl_msg_set_tos(struct idiagnl_msg *msg, uint8_t tos) +{ + msg->idiag_tos = tos; + msg->ce_mask |= IDIAGNL_ATTR_TOS; +} + +uint8_t idiagnl_msg_get_tclass(const struct idiagnl_msg *msg) +{ + return msg->idiag_tclass; +} + +void idiagnl_msg_set_tclass(struct idiagnl_msg *msg, uint8_t tclass) +{ + msg->idiag_tclass = tclass; + msg->ce_mask |= IDIAGNL_ATTR_TCLASS; +} + +uint8_t idiagnl_msg_get_shutdown(const struct idiagnl_msg *msg) +{ + return msg->idiag_shutdown; +} + +void idiagnl_msg_set_shutdown(struct idiagnl_msg *msg, uint8_t shutdown) +{ + msg->idiag_shutdown = shutdown; + msg->ce_mask |= IDIAGNL_ATTR_SHUTDOWN; +} + +char *idiagnl_msg_get_cong(const struct idiagnl_msg *msg) +{ + return msg->idiag_cong; +} + +void idiagnl_msg_set_cong(struct idiagnl_msg *msg, char *cong) +{ + free (msg->idiag_cong); + msg->idiag_cong = strdup(cong); + msg->ce_mask |= IDIAGNL_ATTR_CONG; +} + +struct idiagnl_meminfo *idiagnl_msg_get_meminfo(const struct idiagnl_msg *msg) +{ + return msg->idiag_meminfo; +} + +void idiagnl_msg_set_meminfo(struct idiagnl_msg *msg, struct idiagnl_meminfo *minfo) +{ + if (msg->idiag_meminfo) + idiagnl_meminfo_put(msg->idiag_meminfo); + + idiagnl_meminfo_get(minfo); + msg->idiag_meminfo = minfo; + msg->ce_mask |= IDIAGNL_ATTR_MEMINFO; +} + +struct idiagnl_vegasinfo *idiagnl_msg_get_vegasinfo(const struct idiagnl_msg *msg) +{ + return msg->idiag_vegasinfo; +} + +void idiagnl_msg_set_vegasinfo(struct idiagnl_msg *msg, struct idiagnl_vegasinfo *vinfo) +{ + if (msg->idiag_vegasinfo) + idiagnl_vegasinfo_put(msg->idiag_vegasinfo); + + idiagnl_vegasinfo_get(vinfo); + msg->idiag_vegasinfo = vinfo; + msg->ce_mask |= IDIAGNL_ATTR_VEGASINFO; +} + +struct tcp_info idiagnl_msg_get_tcpinfo(const struct idiagnl_msg *msg) +{ + return msg->idiag_tcpinfo; +} + +void idiagnl_msg_set_tcpinfo(struct idiagnl_msg *msg, struct tcp_info *tinfo) +{ + memcpy(&msg->idiag_tcpinfo, tinfo, sizeof(struct tcp_info)); + msg->ce_mask |= IDIAGNL_ATTR_TCPINFO; +} + +/** @} */ + +static void idiag_msg_dump_line(struct nl_object *a, struct nl_dump_params *p) +{ + struct idiagnl_msg *msg = (struct idiagnl_msg *) a; + char buf[64] = { 0 }; + + nl_dump_line(p, "family: %s ", nl_af2str(msg->idiag_family, buf, sizeof(buf))); + nl_dump(p, "src: %s:%d ", nl_addr2str(msg->idiag_src, buf, sizeof(buf)), + ntohs(msg->idiag_sport)); + nl_dump(p, "dst: %s:%d ", nl_addr2str(msg->idiag_dst, buf, sizeof(buf)), + ntohs(msg->idiag_dport)); + nl_dump(p, "iif: %d ", msg->idiag_ifindex); + nl_dump(p, "\n"); +} + +static void idiag_msg_dump_details(struct nl_object *a, struct nl_dump_params *p) +{ + struct idiagnl_msg *msg = (struct idiagnl_msg *) a; + char buf[64], buf2[64]; + + nl_dump(p, "\nfamily: %s\n", nl_af2str(msg->idiag_family, buf, sizeof(buf))); + nl_dump(p, "state: %s\n", + idiagnl_state2str(msg->idiag_state, buf, sizeof(buf))); + nl_dump(p, "timer (%s, %s, retransmits: %d)\n", + idiagnl_timer2str(msg->idiag_timer, buf, sizeof(buf)), + nl_msec2str(msg->idiag_expires, buf2, sizeof(buf2)), + msg->idiag_retrans); + + nl_dump(p, "source: %s:%d\n", nl_addr2str(msg->idiag_src, buf, sizeof(buf)), + ntohs(msg->idiag_sport)); + nl_dump(p, "destination: %s:%d\n", nl_addr2str(msg->idiag_dst, buf, sizeof(buf)), + ntohs(msg->idiag_dport)); + + nl_dump(p, "ifindex: %d\n", msg->idiag_ifindex); + nl_dump(p, "rqueue: %-6d wqueue: %-6d\n", msg->idiag_rqueue, msg->idiag_wqueue); + nl_dump(p, "uid %d\n", msg->idiag_uid); + nl_dump(p, "inode %d\n", msg->idiag_inode); + if (msg->idiag_shutdown) { + nl_dump(p, "socket shutdown: %s\n", + idiagnl_shutdown2str(msg->idiag_shutdown, + buf, sizeof(buf))); + } + + nl_dump(p, "tos: 0x%x\n", msg->idiag_tos); + nl_dump(p, "traffic class: %d\n", msg->idiag_tclass); + nl_dump(p, "congestion algorithm: %s\n", msg->idiag_cong ? msg->idiag_cong : ""); +} + +static void idiag_msg_dump_stats(struct nl_object *obj, struct nl_dump_params *p) +{ + struct idiagnl_msg *msg = (struct idiagnl_msg *) obj; + char buf[64]; + + idiag_msg_dump_details(obj, p); + + nl_dump(p, "tcp info: [\n"); + nl_dump(p, "\tsocket state: %s\n", + idiagnl_state2str(msg->idiag_tcpinfo.tcpi_state, + buf, sizeof(buf))); + nl_dump(p, "\ttcp state: %s\n", + idiagnl_tcpstate2str(msg->idiag_tcpinfo.tcpi_ca_state, + buf, sizeof(buf))); + nl_dump(p, "\tretransmits: %d\n", + msg->idiag_tcpinfo.tcpi_retransmits); + nl_dump(p, "\tprobes: %d\n", + msg->idiag_tcpinfo.tcpi_probes); + nl_dump(p, "\tbackoff: %d\n", + msg->idiag_tcpinfo.tcpi_backoff); + nl_dump(p, "\toptions: %s\n", + idiagnl_tcpopts2str(msg->idiag_tcpinfo.tcpi_options, + buf, sizeof(buf))); + nl_dump(p, "\tsnd_wscale: %d\n", msg->idiag_tcpinfo.tcpi_snd_wscale); + nl_dump(p, "\trcv_wscale: %d\n", msg->idiag_tcpinfo.tcpi_rcv_wscale); + nl_dump(p, "\trto: %d\n", msg->idiag_tcpinfo.tcpi_rto); + nl_dump(p, "\tato: %d\n", msg->idiag_tcpinfo.tcpi_ato); + nl_dump(p, "\tsnd_mss: %s\n", nl_size2str(msg->idiag_tcpinfo.tcpi_snd_mss, + buf, sizeof(buf))); + nl_dump(p, "\trcv_mss: %s\n", nl_size2str(msg->idiag_tcpinfo.tcpi_rcv_mss, + buf, sizeof(buf))); + nl_dump(p, "\tunacked: %d\n", msg->idiag_tcpinfo.tcpi_unacked); + nl_dump(p, "\tsacked: %d\n", msg->idiag_tcpinfo.tcpi_sacked); + + nl_dump(p, "\tlost: %d\n", msg->idiag_tcpinfo.tcpi_lost); + nl_dump(p, "\tretransmit segments: %d\n", + msg->idiag_tcpinfo.tcpi_retrans); + nl_dump(p, "\tfackets: %d\n", + msg->idiag_tcpinfo.tcpi_fackets); + nl_dump(p, "\tlast data sent: %s\n", + nl_msec2str(msg->idiag_tcpinfo.tcpi_last_data_sent, buf, + sizeof(buf))); + nl_dump(p, "\tlast ack sent: %s\n", + nl_msec2str(msg->idiag_tcpinfo.tcpi_last_ack_sent, buf, sizeof(buf))); + nl_dump(p, "\tlast data recv: %s\n", + nl_msec2str(msg->idiag_tcpinfo.tcpi_last_data_recv, buf, + sizeof(buf))); + nl_dump(p, "\tlast ack recv: %s\n", + nl_msec2str(msg->idiag_tcpinfo.tcpi_last_ack_recv, buf, + sizeof(buf))); + nl_dump(p, "\tpath mtu: %s\n", + nl_size2str(msg->idiag_tcpinfo.tcpi_pmtu, buf, + sizeof(buf))); + nl_dump(p, "\trcv ss threshold: %d\n", + msg->idiag_tcpinfo.tcpi_rcv_ssthresh); + nl_dump(p, "\tsmoothed round trip time: %d\n", + msg->idiag_tcpinfo.tcpi_rtt); + nl_dump(p, "\tround trip time variation: %d\n", + msg->idiag_tcpinfo.tcpi_rttvar); + nl_dump(p, "\tsnd ss threshold: %s\n", + nl_size2str(msg->idiag_tcpinfo.tcpi_snd_ssthresh, buf, + sizeof(buf))); + nl_dump(p, "\tsend congestion window: %d\n", + msg->idiag_tcpinfo.tcpi_snd_cwnd); + nl_dump(p, "\tadvertised mss: %s\n", + nl_size2str(msg->idiag_tcpinfo.tcpi_advmss, buf, + sizeof(buf))); + nl_dump(p, "\treordering: %d\n", + msg->idiag_tcpinfo.tcpi_reordering); + nl_dump(p, "\trcv rround trip time: %d\n", + msg->idiag_tcpinfo.tcpi_rcv_rtt); + nl_dump(p, "\treceive queue space: %s\n", + nl_size2str(msg->idiag_tcpinfo.tcpi_rcv_space, buf, + sizeof(buf))); + nl_dump(p, "\ttotal retransmits: %d\n", + msg->idiag_tcpinfo.tcpi_total_retrans); + nl_dump(p, "]\n"); + + if (msg->idiag_meminfo) { + nl_dump(p, "meminfo: [\n"); + nl_dump(p, "\trmem: %s\n", + nl_size2str(msg->idiag_meminfo->idiag_rmem, + buf, + sizeof(buf))); + nl_dump(p, "\twmem: %s\n", + nl_size2str(msg->idiag_meminfo->idiag_wmem, + buf, + sizeof(buf))); + nl_dump(p, "\tfmem: %s\n", + nl_size2str(msg->idiag_meminfo->idiag_fmem, + buf, + sizeof(buf))); + nl_dump(p, "\ttmem: %s\n", + nl_size2str(msg->idiag_meminfo->idiag_tmem, + buf, + sizeof(buf))); + nl_dump(p, "]\n"); + } + + if (msg->idiag_vegasinfo) { + nl_dump(p, "vegasinfo: [\n"); + nl_dump(p, "\tvegas enabled: %d\n", + msg->idiag_vegasinfo->tcpv_enabled); + if (msg->idiag_vegasinfo->tcpv_enabled) { + nl_dump(p, "\trtt cnt: %d", + msg->idiag_vegasinfo->tcpv_rttcnt); + nl_dump(p, "\trtt (propagation delay): %d", + msg->idiag_vegasinfo->tcpv_rtt); + nl_dump(p, "\tmin rtt: %d", + msg->idiag_vegasinfo->tcpv_minrtt); + } + nl_dump(p, "]\n"); + } + + if (msg->ce_mask & IDIAGNL_ATTR_MEMINFO) { + nl_dump(p, "skmeminfo: [\n"); + nl_dump(p, "\trmem alloc: %d\n", + msg->idiag_skmeminfo[SK_MEMINFO_RMEM_ALLOC]); + nl_dump(p, "\trcv buf: %s\n", + nl_size2str(msg->idiag_skmeminfo[SK_MEMINFO_RCVBUF], + buf, sizeof(buf))); + nl_dump(p, "\twmem alloc: %d\n", + msg->idiag_skmeminfo[SK_MEMINFO_WMEM_ALLOC]); + nl_dump(p, "\tsnd buf: %s\n", + nl_size2str(msg->idiag_skmeminfo[SK_MEMINFO_SNDBUF], + buf, sizeof(buf))); + nl_dump(p, "\tfwd alloc: %d\n", + msg->idiag_skmeminfo[SK_MEMINFO_FWD_ALLOC]); + nl_dump(p, "\twmem queued: %s\n", + nl_size2str(msg->idiag_skmeminfo[SK_MEMINFO_WMEM_QUEUED], + buf, sizeof(buf))); + nl_dump(p, "\topt mem: %d\n", + msg->idiag_skmeminfo[SK_MEMINFO_OPTMEM]); + nl_dump(p, "\tbacklog: %d\n", + msg->idiag_skmeminfo[SK_MEMINFO_BACKLOG]); + nl_dump(p, "]\n\n"); + } +} + +static void idiagnl_msg_free(struct nl_object *a) +{ + struct idiagnl_msg *msg = (struct idiagnl_msg *) a; + if (a == NULL) + return; + + free(msg->idiag_cong); + nl_addr_put(msg->idiag_src); + nl_addr_put(msg->idiag_dst); + idiagnl_meminfo_put(msg->idiag_meminfo); + idiagnl_vegasinfo_put(msg->idiag_vegasinfo); +} + +static int idiagnl_msg_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct idiagnl_msg *dst = (struct idiagnl_msg *) _dst; + struct idiagnl_msg *src = (struct idiagnl_msg *) _src; + + dst->idiag_cong = NULL; + dst->idiag_src = NULL; + dst->idiag_dst = NULL; + dst->idiag_meminfo = NULL; + dst->idiag_vegasinfo = NULL; + dst->ce_mask &= ~(IDIAGNL_ATTR_CONG | + IDIAGNL_ATTR_SRC | + IDIAGNL_ATTR_DST | + IDIAGNL_ATTR_MEMINFO | + IDIAGNL_ATTR_VEGASINFO); + + if (src->idiag_cong) { + if (!(dst->idiag_cong = strdup(src->idiag_cong))) + return -NLE_NOMEM; + dst->ce_mask |= IDIAGNL_ATTR_CONG; + } + + if (src->idiag_src) { + if (!(dst->idiag_src = nl_addr_clone(src->idiag_src))) + return -NLE_NOMEM; + dst->ce_mask |= IDIAGNL_ATTR_SRC; + } + + if (src->idiag_dst) { + if (!(dst->idiag_dst = nl_addr_clone(src->idiag_dst))) + return -NLE_NOMEM; + dst->ce_mask |= IDIAGNL_ATTR_DST; + } + + if (src->idiag_meminfo) { + if (!(dst->idiag_meminfo = (struct idiagnl_meminfo *) nl_object_clone((struct nl_object *) src->idiag_meminfo))) + return -NLE_NOMEM; + dst->ce_mask |= IDIAGNL_ATTR_MEMINFO; + } + + if (src->idiag_vegasinfo) { + if (!(dst->idiag_vegasinfo = (struct idiagnl_vegasinfo *) nl_object_clone((struct nl_object *) src->idiag_vegasinfo))) + return -NLE_NOMEM; + dst->ce_mask |= IDIAGNL_ATTR_VEGASINFO; + } + + return 0; +} + +static struct nla_policy ext_policy[INET_DIAG_MAX+1] = { + [INET_DIAG_MEMINFO] = { .minlen = sizeof(struct inet_diag_meminfo) }, + [INET_DIAG_INFO] = { .minlen = sizeof(struct tcp_info) }, + [INET_DIAG_VEGASINFO] = { .minlen = sizeof(struct tcpvegas_info) }, + [INET_DIAG_CONG] = { .type = NLA_STRING }, + [INET_DIAG_TOS] = { .type = NLA_U8 }, + [INET_DIAG_TCLASS] = { .type = NLA_U8 }, + /* Older kernel doesn't have SK_MEMINFO_BACKLOG */ + [INET_DIAG_SKMEMINFO] = { .minlen = (sizeof(uint32_t) * (SK_MEMINFO_OPTMEM + 1)) }, + [INET_DIAG_SHUTDOWN] = { .type = NLA_U8 }, +}; + +int idiagnl_msg_parse(struct nlmsghdr *nlh, struct idiagnl_msg **result) +{ + struct idiagnl_msg *msg = NULL; + struct inet_diag_msg *raw_msg = NULL; + struct nl_addr *src = NULL, *dst = NULL; + struct nlattr *tb[INET_DIAG_MAX+1]; + int err = 0; + + msg = idiagnl_msg_alloc(); + if (!msg) + goto errout_nomem; + + err = nlmsg_parse(nlh, sizeof(struct inet_diag_msg), tb, INET_DIAG_MAX, + ext_policy); + if (err < 0) + goto errout; + + raw_msg = nlmsg_data(nlh); + msg->idiag_family = raw_msg->idiag_family; + msg->idiag_state = raw_msg->idiag_state; + msg->idiag_timer = raw_msg->idiag_timer; + msg->idiag_retrans = raw_msg->idiag_retrans; + msg->idiag_expires = raw_msg->idiag_expires; + msg->idiag_rqueue = raw_msg->idiag_rqueue; + msg->idiag_wqueue = raw_msg->idiag_wqueue; + msg->idiag_uid = raw_msg->idiag_uid; + msg->idiag_inode = raw_msg->idiag_inode; + msg->idiag_sport = raw_msg->id.idiag_sport; + msg->idiag_dport = raw_msg->id.idiag_dport; + msg->idiag_ifindex = raw_msg->id.idiag_if; + + msg->ce_mask = (IDIAGNL_ATTR_FAMILY | + IDIAGNL_ATTR_STATE | + IDIAGNL_ATTR_TIMER | + IDIAGNL_ATTR_RETRANS | + IDIAGNL_ATTR_EXPIRES | + IDIAGNL_ATTR_RQUEUE | + IDIAGNL_ATTR_WQUEUE | + IDIAGNL_ATTR_UID | + IDIAGNL_ATTR_INODE | + IDIAGNL_ATTR_SPORT | + IDIAGNL_ATTR_DPORT | + IDIAGNL_ATTR_IFINDEX); + + dst = nl_addr_build(raw_msg->idiag_family, raw_msg->id.idiag_dst, + sizeof(raw_msg->id.idiag_dst)); + if (!dst) + goto errout_nomem; + + err = idiagnl_msg_set_dst(msg, dst); + if (err < 0) + goto errout; + + nl_addr_put(dst); + + src = nl_addr_build(raw_msg->idiag_family, raw_msg->id.idiag_src, + sizeof(raw_msg->id.idiag_src)); + if (!src) + goto errout_nomem; + + err = idiagnl_msg_set_src(msg, src); + if (err < 0) + goto errout; + + nl_addr_put(src); + + if (tb[INET_DIAG_TOS]) { + msg->idiag_tos = nla_get_u8(tb[INET_DIAG_TOS]); + msg->ce_mask |= IDIAGNL_ATTR_TOS; + } + + if (tb[INET_DIAG_TCLASS]) { + msg->idiag_tclass = nla_get_u8(tb[INET_DIAG_TCLASS]); + msg->ce_mask |= IDIAGNL_ATTR_TCLASS; + } + + if (tb[INET_DIAG_SHUTDOWN]) { + msg->idiag_shutdown = nla_get_u8(tb[INET_DIAG_SHUTDOWN]); + msg->ce_mask |= IDIAGNL_ATTR_SHUTDOWN; + } + + if (tb[INET_DIAG_CONG]) { + msg->idiag_cong = nla_strdup(tb[INET_DIAG_CONG]); + msg->ce_mask |= IDIAGNL_ATTR_CONG; + } + + if (tb[INET_DIAG_INFO]) { + nla_memcpy(&msg->idiag_tcpinfo, tb[INET_DIAG_INFO], + sizeof(msg->idiag_tcpinfo)); + msg->ce_mask |= IDIAGNL_ATTR_TCPINFO; + } + + if (tb[INET_DIAG_MEMINFO]) { + struct idiagnl_meminfo *minfo = idiagnl_meminfo_alloc(); + struct inet_diag_meminfo *raw_minfo = NULL; + + if (!minfo) + goto errout_nomem; + + raw_minfo = (struct inet_diag_meminfo *) + nla_data(tb[INET_DIAG_MEMINFO]); + + idiagnl_meminfo_set_rmem(minfo, raw_minfo->idiag_rmem); + idiagnl_meminfo_set_wmem(minfo, raw_minfo->idiag_wmem); + idiagnl_meminfo_set_fmem(minfo, raw_minfo->idiag_fmem); + idiagnl_meminfo_set_tmem(minfo, raw_minfo->idiag_tmem); + + msg->idiag_meminfo = minfo; + msg->ce_mask |= IDIAGNL_ATTR_MEMINFO; + } + + if (tb[INET_DIAG_VEGASINFO]) { + struct idiagnl_vegasinfo *vinfo = idiagnl_vegasinfo_alloc(); + struct tcpvegas_info *raw_vinfo = NULL; + + if (!vinfo) + goto errout_nomem; + + raw_vinfo = (struct tcpvegas_info *) + nla_data(tb[INET_DIAG_VEGASINFO]); + + idiagnl_vegasinfo_set_enabled(vinfo, raw_vinfo->tcpv_enabled); + idiagnl_vegasinfo_set_rttcnt(vinfo, raw_vinfo->tcpv_rttcnt); + idiagnl_vegasinfo_set_rtt(vinfo, raw_vinfo->tcpv_rtt); + idiagnl_vegasinfo_set_minrtt(vinfo, raw_vinfo->tcpv_minrtt); + + msg->idiag_vegasinfo = vinfo; + msg->ce_mask |= IDIAGNL_ATTR_VEGASINFO; + } + + if (tb[INET_DIAG_SKMEMINFO]) { + nla_memcpy(&msg->idiag_skmeminfo, tb[INET_DIAG_SKMEMINFO], + sizeof(msg->idiag_skmeminfo)); + msg->ce_mask |= IDIAGNL_ATTR_SKMEMINFO; + } + + *result = msg; + return 0; + +errout: + idiagnl_msg_put(msg); + return err; + +errout_nomem: + err = -NLE_NOMEM; + goto errout; +} + +static const struct trans_tbl idiagnl_attrs[] = { + __ADD(IDIAGNL_ATTR_FAMILY, family), + __ADD(IDIAGNL_ATTR_STATE, state), + __ADD(IDIAGNL_ATTR_TIMER, timer), + __ADD(IDIAGNL_ATTR_RETRANS, retrans), + __ADD(IDIAGNL_ATTR_SPORT, sport), + __ADD(IDIAGNL_ATTR_DPORT, dport), + __ADD(IDIAGNL_ATTR_SRC, src), + __ADD(IDIAGNL_ATTR_DST, dst), + __ADD(IDIAGNL_ATTR_IFINDEX, ifindex), + __ADD(IDIAGNL_ATTR_EXPIRES, expires), + __ADD(IDIAGNL_ATTR_RQUEUE, rqueue), + __ADD(IDIAGNL_ATTR_WQUEUE, wqueue), + __ADD(IDIAGNL_ATTR_UID, uid), + __ADD(IDIAGNL_ATTR_INODE, inode), + __ADD(IDIAGNL_ATTR_TOS, tos), + __ADD(IDIAGNL_ATTR_TCLASS, tclass), + __ADD(IDIAGNL_ATTR_SHUTDOWN, shutdown), + __ADD(IDIAGNL_ATTR_CONG, cong), + __ADD(IDIAGNL_ATTR_MEMINFO, meminfo), + __ADD(IDIAGNL_ATTR_VEGASINFO, vegasinfo), + __ADD(IDIAGNL_ATTR_TCPINFO, tcpinfo), + __ADD(IDIAGNL_ATTR_SKMEMINFO, skmeminfo), +}; + +static char *_idiagnl_attrs2str(int attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, idiagnl_attrs, + ARRAY_SIZE(idiagnl_attrs)); +} + +static uint64_t idiagnl_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + struct idiagnl_msg *a = (struct idiagnl_msg *) _a; + struct idiagnl_msg *b = (struct idiagnl_msg *) _b; + uint64_t diff = 0; + +#define _DIFF(ATTR, EXPR) ATTR_DIFF(attrs, IDIAGNL_ATTR_##ATTR, a, b, EXPR) + diff |= _DIFF(FAMILY, a->idiag_family != b->idiag_family); + diff |= _DIFF(STATE, a->idiag_state != b->idiag_state); + diff |= _DIFF(TIMER, a->idiag_timer != b->idiag_timer); + diff |= _DIFF(RETRANS, a->idiag_retrans != b->idiag_retrans); + diff |= _DIFF(SPORT, a->idiag_sport != b->idiag_sport); + diff |= _DIFF(DPORT, a->idiag_dport != b->idiag_dport); + diff |= _DIFF(SRC, nl_addr_cmp (a->idiag_src, b->idiag_src)); + diff |= _DIFF(DST, nl_addr_cmp (a->idiag_dst, b->idiag_dst)); + diff |= _DIFF(IFINDEX, a->idiag_ifindex != b->idiag_ifindex); + diff |= _DIFF(EXPIRES, a->idiag_expires != b->idiag_expires); + diff |= _DIFF(RQUEUE, a->idiag_rqueue != b->idiag_rqueue); + diff |= _DIFF(WQUEUE, a->idiag_wqueue != b->idiag_wqueue); + diff |= _DIFF(UID, a->idiag_uid != b->idiag_uid); + diff |= _DIFF(INODE, a->idiag_inode != b->idiag_inode); + diff |= _DIFF(TOS, a->idiag_tos != b->idiag_tos); + diff |= _DIFF(TCLASS, a->idiag_tclass != b->idiag_tclass); + diff |= _DIFF(SHUTDOWN, a->idiag_shutdown != b->idiag_shutdown); + diff |= _DIFF(CONG, strcmp(a->idiag_cong, b->idiag_cong)); + diff |= _DIFF(MEMINFO, nl_object_diff((struct nl_object *) a->idiag_meminfo, (struct nl_object *) b->idiag_meminfo)); + diff |= _DIFF(VEGASINFO, nl_object_diff((struct nl_object *) a->idiag_vegasinfo, (struct nl_object *) b->idiag_vegasinfo)); + diff |= _DIFF(TCPINFO, memcmp(&a->idiag_tcpinfo, &b->idiag_tcpinfo, sizeof(a->idiag_tcpinfo))); + diff |= _DIFF(SKMEMINFO, memcmp(a->idiag_skmeminfo, b->idiag_skmeminfo, sizeof(a->idiag_skmeminfo))); +#undef _DIFF + return diff; +} + +static void idiagnl_keygen(struct nl_object *obj, uint32_t *hashkey, + uint32_t table_sz) +{ + struct idiagnl_msg *msg = (struct idiagnl_msg *)obj; + unsigned int key_sz; + struct idiagnl_hash_key { + uint8_t family; + uint32_t src_hash; + uint32_t dst_hash; + uint16_t sport; + uint16_t dport; + } __attribute__((packed)) key; + + key_sz = sizeof(key); + key.family = msg->idiag_family; + key.src_hash = 0; + key.dst_hash = 0; + key.sport = msg->idiag_sport; + key.dport = msg->idiag_dport; + + if (msg->idiag_src) { + key.src_hash = nl_hash (nl_addr_get_binary_addr(msg->idiag_src), + nl_addr_get_len(msg->idiag_src), 0); + } + if (msg->idiag_dst) { + key.dst_hash = nl_hash (nl_addr_get_binary_addr(msg->idiag_dst), + nl_addr_get_len(msg->idiag_dst), 0); + } + + *hashkey = nl_hash(&key, key_sz, 0) % table_sz; + + NL_DBG(5, "idiagnl %p key (fam %d src_hash %d dst_hash %d sport %d dport %d) keysz %d, hash 0x%x\n", + msg, key.family, key.src_hash, key.dst_hash, key.sport, key.dport, key_sz, *hashkey); + + return; +} + +/** @cond SKIP */ +struct nl_object_ops idiagnl_msg_obj_ops = { + .oo_name = "idiag/idiag_msg", + .oo_size = sizeof(struct idiagnl_msg), + .oo_free_data = idiagnl_msg_free, + .oo_clone = idiagnl_msg_clone, + .oo_dump = { + [NL_DUMP_LINE] = idiag_msg_dump_line, + [NL_DUMP_DETAILS] = idiag_msg_dump_details, + [NL_DUMP_STATS] = idiag_msg_dump_stats, + }, + .oo_compare = idiagnl_compare, + .oo_keygen = idiagnl_keygen, + .oo_attrs2str = _idiagnl_attrs2str, + .oo_id_attrs = (IDIAGNL_ATTR_FAMILY | + IDIAGNL_ATTR_SRC | + IDIAGNL_ATTR_DST | + IDIAGNL_ATTR_SPORT | + IDIAGNL_ATTR_DPORT), +}; +/** @endcond */ + +/** @} */ diff --git a/libnl/lib/idiag/idiag_req_obj.c b/libnl/lib/idiag/idiag_req_obj.c new file mode 100644 index 0000000..f897b68 --- /dev/null +++ b/libnl/lib/idiag/idiag_req_obj.c @@ -0,0 +1,251 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Sassano Systems LLC + */ + +#define _GNU_SOURCE + +#include +#include +#include + +/** + * @ingroup idiag + * @defgroup idiagnl_req Inet Diag Requests + * + * @details + * @idiagnl_doc{idiagnl_req, Inet Diag Request Documentation} + * @{ + */ +struct idiagnl_req *idiagnl_req_alloc(void) +{ + return (struct idiagnl_req *) nl_object_alloc(&idiagnl_req_obj_ops); +} + +void idiagnl_req_get(struct idiagnl_req *req) +{ + nl_object_get((struct nl_object *) req); +} + +void idiagnl_req_put(struct idiagnl_req *req) +{ + nl_object_put((struct nl_object *) req); +} + +/** + * @name Attributes + * @{ + */ + +uint8_t idiagnl_req_get_family(const struct idiagnl_req *req) +{ + return req->idiag_family; +} + +void idiagnl_req_set_family(struct idiagnl_req *req, uint8_t family) +{ + req->idiag_family = family; +} + +uint8_t idiagnl_req_get_ext(const struct idiagnl_req *req) +{ + return req->idiag_ext; +} + +void idiagnl_req_set_ext(struct idiagnl_req *req, uint8_t ext) +{ + req->idiag_ext = ext; +} + +uint32_t idiagnl_req_get_ifindex(const struct idiagnl_req *req) +{ + return req->idiag_ifindex; +} + +void idiagnl_req_set_ifindex(struct idiagnl_req *req, uint32_t ifindex) +{ + req->idiag_ifindex = ifindex; +} + +uint32_t idiagnl_req_get_states(const struct idiagnl_req *req) +{ + return req->idiag_states; +} + +void idiagnl_req_set_states(struct idiagnl_req *req, uint32_t states) +{ + req->idiag_states = states; +} + +uint32_t idiagnl_req_get_dbs(const struct idiagnl_req *req) +{ + return req->idiag_dbs; +} + +void idiagnl_req_set_dbs(struct idiagnl_req *req, uint32_t dbs) +{ + req->idiag_dbs = dbs; +} + +struct nl_addr *idiagnl_req_get_src(const struct idiagnl_req *req) +{ + return req->idiag_src; +} + +int idiagnl_req_set_src(struct idiagnl_req *req, struct nl_addr *addr) +{ + if (req->idiag_src) + nl_addr_put(req->idiag_src); + + nl_addr_get(addr); + req->idiag_src = addr; + + return 0; +} + +struct nl_addr *idiagnl_req_get_dst(const struct idiagnl_req *req) +{ + return req->idiag_dst; +} + +int idiagnl_req_set_dst(struct idiagnl_req *req, struct nl_addr *addr) +{ + if (req->idiag_dst) + nl_addr_put(req->idiag_dst); + + nl_addr_get(addr); + req->idiag_dst = addr; + + return 0; +} + +/** @} */ + +static void idiag_req_dump_line(struct nl_object *a, struct nl_dump_params *p) +{ + struct idiagnl_req *req = (struct idiagnl_req *) a; + char buf[64] = { 0 }; + + nl_dump_line(p, "%s ", nl_af2str(req->idiag_family, buf, sizeof(buf))); + nl_dump(p, "src %s ", nl_addr2str(req->idiag_src, buf, sizeof(buf))); + nl_dump(p, "dst %s ", nl_addr2str(req->idiag_dst, buf, sizeof(buf))); + nl_dump(p, "iif %d ", req->idiag_ifindex); + nl_dump(p, "\n"); +} + +static void idiag_req_dump_details(struct nl_object *a, struct nl_dump_params *p) +{ + struct idiagnl_req *req = (struct idiagnl_req *) a; + char buf[64]; + + nl_dump_line(p, " "); + nl_dump(p, "%s ", nl_af2str(req->idiag_family, buf, sizeof(buf))); + nl_dump(p, "exts %s ", + idiagnl_exts2str(req->idiag_ext, buf, sizeof(buf))); + nl_dump(p, "src %s ", nl_addr2str(req->idiag_src, buf, sizeof(buf))); + nl_dump(p, "dst %s ", nl_addr2str(req->idiag_dst, buf, sizeof(buf))); + nl_dump(p, "iif %d ", req->idiag_ifindex); + nl_dump(p, "states %s ", idiagnl_state2str(req->idiag_states, buf, + sizeof(buf))); + nl_dump(p, "dbs %d", req->idiag_dbs); + nl_dump(p, "\n"); +} + +static void idiag_req_dump_stats(struct nl_object *obj, struct nl_dump_params *p) +{ + idiag_req_dump_details(obj, p); +} + +static void idiagnl_req_free(struct nl_object *a) +{ + struct idiagnl_req *req = (struct idiagnl_req *) a; + if (a == NULL) + return; + + nl_addr_put(req->idiag_src); + nl_addr_put(req->idiag_dst); +} + +static int idiagnl_req_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct idiagnl_req *dst = (struct idiagnl_req *) _dst; + struct idiagnl_req *src = (struct idiagnl_req *) _src; + + if (src->idiag_src) + if (!(dst->idiag_src = nl_addr_clone(src->idiag_src))) + return -NLE_NOMEM; + + if (src->idiag_dst) + if (!(dst->idiag_dst = nl_addr_clone(src->idiag_dst))) + return -NLE_NOMEM; + + return 0; +} + +int idiagnl_req_parse(struct nlmsghdr *nlh, struct idiagnl_req **result) +{ + struct idiagnl_req *req = NULL; + struct inet_diag_req *raw_req = NULL; + struct nl_addr *src = NULL, *dst = NULL; + int err = 0; + + req = idiagnl_req_alloc(); + if (!req) + goto errout_nomem; + + raw_req = nlmsg_data(nlh); + req->idiag_family = raw_req->idiag_family; + req->idiag_ext = raw_req->idiag_ext; + req->idiag_states = raw_req->idiag_states; + req->idiag_dbs = raw_req->idiag_dbs; + req->idiag_ifindex = raw_req->id.idiag_if; + + dst = nl_addr_build(raw_req->idiag_family, raw_req->id.idiag_dst, + sizeof(raw_req->id.idiag_dst)); + if (!dst) + goto errout_nomem; + + err = idiagnl_req_set_dst(req, dst); + if (err < 0) + goto errout; + + nl_addr_put(dst); + + src = nl_addr_build(raw_req->idiag_family, raw_req->id.idiag_src, + sizeof(raw_req->id.idiag_src)); + if (!src) + goto errout_nomem; + + err = idiagnl_req_set_src(req, src); + if (err < 0) + goto errout; + + nl_addr_put(src); + + *result = req; + return 0; + +errout: + idiagnl_req_put(req); + return err; + +errout_nomem: + err = -NLE_NOMEM; + goto errout; +} + +/** @cond SKIP */ +struct nl_object_ops idiagnl_req_obj_ops = { + .oo_name = "idiag/idiag_req", + .oo_size = sizeof(struct idiagnl_req), + .oo_free_data = idiagnl_req_free, + .oo_clone = idiagnl_req_clone, + .oo_dump = { + [NL_DUMP_LINE] = idiag_req_dump_line, + [NL_DUMP_DETAILS] = idiag_req_dump_details, + [NL_DUMP_STATS] = idiag_req_dump_stats, + }, +}; +/** @endcond */ + +/** @} */ diff --git a/libnl/lib/idiag/idiag_vegasinfo_obj.c b/libnl/lib/idiag/idiag_vegasinfo_obj.c new file mode 100644 index 0000000..1c01d43 --- /dev/null +++ b/libnl/lib/idiag/idiag_vegasinfo_obj.c @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Sassano Systems LLC + */ + +#define _GNU_SOURCE + +#include +#include + +/** + * @ingroup idiag + * @defgroup idiagnl_vegasinfo Inet Diag TCP Vegas Info + * + * @details + * @idiagnl_doc{idiagnl_vegasinfo, Inet Diag TCP Vegas Info Documentation} + * @{ + */ +struct idiagnl_vegasinfo *idiagnl_vegasinfo_alloc(void) +{ + return (struct idiagnl_vegasinfo *) nl_object_alloc(&idiagnl_vegasinfo_obj_ops); +} + +void idiagnl_vegasinfo_get(struct idiagnl_vegasinfo *vinfo) +{ + nl_object_get((struct nl_object *) vinfo); +} + +void idiagnl_vegasinfo_put(struct idiagnl_vegasinfo *vinfo) +{ + nl_object_put((struct nl_object *) vinfo); +} + +/** + * @name Attributes + * @{ + */ +uint32_t idiagnl_vegasinfo_get_enabled(const struct idiagnl_vegasinfo *vinfo) +{ + return vinfo->tcpv_enabled; +} + +void idiagnl_vegasinfo_set_enabled(struct idiagnl_vegasinfo *vinfo, uint32_t + enabled) +{ + vinfo->tcpv_enabled = enabled; +} + +uint32_t idiagnl_vegasinfo_get_rttcnt(const struct idiagnl_vegasinfo *vinfo) +{ + return vinfo->tcpv_rttcnt; +} + +void idiagnl_vegasinfo_set_rttcnt(struct idiagnl_vegasinfo *vinfo, uint32_t + rttcnt) +{ + vinfo->tcpv_rttcnt = rttcnt; +} + +uint32_t idiagnl_vegasinfo_get_rtt(const struct idiagnl_vegasinfo *vinfo) +{ + return vinfo->tcpv_rtt; +} + +void idiagnl_vegasinfo_set_rtt(struct idiagnl_vegasinfo *vinfo, uint32_t rtt) +{ + vinfo->tcpv_rtt = rtt; +} + +uint32_t idiagnl_vegasinfo_get_minrtt(const struct idiagnl_vegasinfo *vinfo) +{ + return vinfo->tcpv_minrtt; +} + +void idiagnl_vegasinfo_set_minrtt(struct idiagnl_vegasinfo *vinfo, uint32_t + minrtt) +{ + vinfo->tcpv_minrtt = minrtt; +} +/** @} */ + +/** @cond SKIP */ +static uint64_t idiagnl_vegasinfo_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + struct idiagnl_vegasinfo *a = (struct idiagnl_vegasinfo *) _a; + struct idiagnl_vegasinfo *b = (struct idiagnl_vegasinfo *) _b; + + /* vegasinfo is a very simple object. It has no attribe flags (ce_mask), + * hence compare just returns 0 or 1, not a bit mask of attributes. */ + return a->tcpv_enabled != b->tcpv_enabled || + a->tcpv_rttcnt != b->tcpv_rttcnt || + a->tcpv_rtt != b->tcpv_rtt || + a->tcpv_minrtt != b->tcpv_minrtt; +} + +struct nl_object_ops idiagnl_vegasinfo_obj_ops = { + .oo_name = "idiag/idiag_vegasinfo", + .oo_size = sizeof(struct idiagnl_vegasinfo), + .oo_compare = idiagnl_vegasinfo_compare, +}; +/** @endcond */ +/** @} */ diff --git a/libnl/lib/mpls.c b/libnl/lib/mpls.c new file mode 100644 index 0000000..6d0e493 --- /dev/null +++ b/libnl/lib/mpls.c @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Adapted from mpls_ntop and mpls_pton copied from iproute2, + * lib/mpls_ntop.c and lib/mpls_pton.c + */ +#include +#include +#include +#include +#include +#include +#include + +static const char *mpls_ntop1(const struct mpls_label *addr, + char *buf, size_t buflen) +{ + size_t destlen = buflen; + char *dest = buf; + int count = 0; + + while (1) { + uint32_t entry = ntohl(addr[count++].entry); + uint32_t label = (entry & MPLS_LS_LABEL_MASK) >> MPLS_LS_LABEL_SHIFT; + int len = snprintf(dest, destlen, "%u", label); + + if (len >= destlen) + break; + + /* Is this the end? */ + if (entry & MPLS_LS_S_MASK) + return buf; + + dest += len; + destlen -= len; + if (destlen) { + *dest = '/'; + dest++; + destlen--; + } + } + errno = E2BIG; + + return NULL; +} + +const char *mpls_ntop(int af, const void *addr, char *buf, size_t buflen) +{ + switch(af) { + case AF_MPLS: + errno = 0; + return mpls_ntop1((struct mpls_label *)addr, buf, buflen); + } + + errno = EINVAL; + return NULL; +} + +static int mpls_pton1(const char *name, struct mpls_label *addr, + unsigned int maxlabels) +{ + char *endp; + unsigned count; + + for (count = 0; count < maxlabels; count++) { + unsigned long label; + + label = strtoul(name, &endp, 0); + /* Fail when the label value is out or range */ + if (label >= (1 << 20)) + return 0; + + if (endp == name) /* no digits */ + return 0; + + addr->entry = htonl(label << MPLS_LS_LABEL_SHIFT); + if (*endp == '\0') { + addr->entry |= htonl(1 << MPLS_LS_S_SHIFT); + return (count + 1) * sizeof(struct mpls_label); + } + + /* Bad character in the address */ + if (*endp != '/') + return 0; + + name = endp + 1; + addr += 1; + } + + /* The address was too long */ + return 0; +} + +int mpls_pton(int af, const char *src, void *addr, size_t alen) +{ + unsigned int maxlabels = alen / sizeof(struct mpls_label); + int err; + + switch(af) { + case AF_MPLS: + errno = 0; + err = mpls_pton1(src, (struct mpls_label *)addr, maxlabels); + break; + default: + errno = EAFNOSUPPORT; + err = -1; + } + + return err; +} diff --git a/libnl/lib/msg.c b/libnl/lib/msg.c new file mode 100644 index 0000000..630a87e --- /dev/null +++ b/libnl/lib/msg.c @@ -0,0 +1,992 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +/** + * @ingroup core + * @defgroup msg Message Construction & Parsing + * Netlink Message Construction/Parsing Interface + * + * Related sections in the development guide: + * - @core_doc{_message_parsing_amp_construction,Message Parsing & Construction} + * + * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +static size_t default_msg_size; + +static void __init init_msg_size(void) +{ + default_msg_size = getpagesize(); +} + +/** + * @name Size Calculations + * @{ + */ + +/** + * Calculates size of netlink message based on payload length. + * @arg payload Length of payload + * + * @return size of netlink message without padding. + */ +int nlmsg_size(int payload) +{ + return NLMSG_HDRLEN + payload; +} + +static int nlmsg_msg_size(int payload) +{ + return nlmsg_size(payload); +} + +/** + * Calculates size of netlink message including padding based on payload length + * @arg payload Length of payload + * + * This function is idential to nlmsg_size() + nlmsg_padlen(). + * + * @return Size of netlink message including padding. + */ +int nlmsg_total_size(int payload) +{ + return NLMSG_ALIGN(nlmsg_msg_size(payload)); +} + +/** + * Size of padding that needs to be added at end of message + * @arg payload Length of payload + * + * Calculates the number of bytes of padding which is required to be added to + * the end of the message to ensure that the next netlink message header begins + * properly aligned to NLMSG_ALIGNTO. + * + * @return Number of bytes of padding needed. + */ +int nlmsg_padlen(int payload) +{ + return nlmsg_total_size(payload) - nlmsg_msg_size(payload); +} + +/** @} */ + +/** + * @name Access to Message Payload + * @{ + */ + +/** + * Return pointer to message payload + * @arg nlh Netlink message header + * + * @return Pointer to start of message payload. + */ +void *nlmsg_data(const struct nlmsghdr *nlh) +{ + return (unsigned char *) nlh + NLMSG_HDRLEN; +} + +void *nlmsg_tail(const struct nlmsghdr *nlh) +{ + return (unsigned char *) nlh + NLMSG_ALIGN(nlh->nlmsg_len); +} + +/** + * Return length of message payload + * @arg nlh Netlink message header + * + * @return Length of message payload in bytes. + */ +int nlmsg_datalen(const struct nlmsghdr *nlh) +{ + return nlh->nlmsg_len - NLMSG_HDRLEN; +} + +static int nlmsg_len(const struct nlmsghdr *nlh) +{ + return nlmsg_datalen(nlh); +} + +/** @} */ + +/** + * @name Attribute Access + * @{ + */ + +/** + * head of attributes data + * @arg nlh netlink message header + * @arg hdrlen length of family specific header + */ +struct nlattr *nlmsg_attrdata(const struct nlmsghdr *nlh, int hdrlen) +{ + unsigned char *data = nlmsg_data(nlh); + return (struct nlattr *) (data + NLMSG_ALIGN(hdrlen)); +} + +/** + * length of attributes data + * @arg nlh netlink message header + * @arg hdrlen length of family specific header + */ +int nlmsg_attrlen(const struct nlmsghdr *nlh, int hdrlen) +{ + return max_t(int, nlmsg_len(nlh) - NLMSG_ALIGN(hdrlen), 0); +} + +/** @} */ + +/** + * @name Message Parsing + * @{ + */ + +int nlmsg_valid_hdr(const struct nlmsghdr *nlh, int hdrlen) +{ + if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen)) + return 0; + + return 1; +} + +/** + * check if the netlink message fits into the remaining bytes + * @arg nlh netlink message header + * @arg remaining number of bytes remaining in message stream + */ +int nlmsg_ok(const struct nlmsghdr *nlh, int remaining) +{ + return (remaining >= (int)sizeof(struct nlmsghdr) && + nlh->nlmsg_len >= sizeof(struct nlmsghdr) && + nlh->nlmsg_len <= remaining); +} + +/** + * next netlink message in message stream + * @arg nlh netlink message header + * @arg remaining number of bytes remaining in message stream + * + * @returns the next netlink message in the message stream and + * decrements remaining by the size of the current message. + */ +struct nlmsghdr *nlmsg_next(struct nlmsghdr *nlh, int *remaining) +{ + int totlen = NLMSG_ALIGN(nlh->nlmsg_len); + + *remaining -= totlen; + + return (struct nlmsghdr *) ((unsigned char *) nlh + totlen); +} + +/** + * parse attributes of a netlink message + * @arg nlh netlink message header + * @arg hdrlen length of family specific header + * @arg tb destination array with maxtype+1 elements + * @arg maxtype maximum attribute type to be expected + * @arg policy validation policy + * + * See nla_parse() + */ +int nlmsg_parse(struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], + int maxtype, const struct nla_policy *policy) +{ + if (!nlmsg_valid_hdr(nlh, hdrlen)) + return -NLE_MSG_TOOSHORT; + + return nla_parse(tb, maxtype, nlmsg_attrdata(nlh, hdrlen), + nlmsg_attrlen(nlh, hdrlen), policy); +} + +/** + * nlmsg_find_attr - find a specific attribute in a netlink message + * @arg nlh netlink message header + * @arg hdrlen length of familiy specific header + * @arg attrtype type of attribute to look for + * + * Returns the first attribute which matches the specified type. + */ +struct nlattr *nlmsg_find_attr(struct nlmsghdr *nlh, int hdrlen, int attrtype) +{ + return nla_find(nlmsg_attrdata(nlh, hdrlen), + nlmsg_attrlen(nlh, hdrlen), attrtype); +} + +/** + * nlmsg_validate - validate a netlink message including attributes + * @arg nlh netlinket message header + * @arg hdrlen length of familiy specific header + * @arg maxtype maximum attribute type to be expected + * @arg policy validation policy + */ +int nlmsg_validate(struct nlmsghdr *nlh, int hdrlen, int maxtype, + const struct nla_policy *policy) +{ + if (!nlmsg_valid_hdr(nlh, hdrlen)) + return -NLE_MSG_TOOSHORT; + + return nla_validate(nlmsg_attrdata(nlh, hdrlen), + nlmsg_attrlen(nlh, hdrlen), maxtype, policy); +} + +/** @} */ + +/** + * @name Message Building/Access + * @{ + */ + +static struct nl_msg *__nlmsg_alloc(size_t len) +{ + struct nl_msg *nm; + + if (len < sizeof(struct nlmsghdr)) + len = sizeof(struct nlmsghdr); + + nm = calloc(1, sizeof(*nm)); + if (!nm) + goto errout; + + nm->nm_refcnt = 1; + + nm->nm_nlh = calloc(1, len); + if (!nm->nm_nlh) + goto errout; + + nm->nm_protocol = -1; + nm->nm_size = len; + nm->nm_nlh->nlmsg_len = nlmsg_total_size(0); + + NL_DBG(2, "msg %p: Allocated new message, maxlen=%zu\n", nm, len); + + return nm; +errout: + free(nm); + return NULL; +} + +/** + * Allocate a new netlink message with the default maximum payload size. + * + * Allocates a new netlink message without any further payload. The + * maximum payload size defaults to PAGESIZE or as otherwise specified + * with nlmsg_set_default_size(). + * + * @return Newly allocated netlink message or NULL. + */ +struct nl_msg *nlmsg_alloc(void) +{ + return __nlmsg_alloc(default_msg_size); +} + +/** + * Allocate a new netlink message with maximum payload size specified. + */ +struct nl_msg *nlmsg_alloc_size(size_t max) +{ + return __nlmsg_alloc(max); +} + +/** + * Allocate a new netlink message and inherit netlink message header + * @arg hdr Netlink message header template + * + * Allocates a new netlink message and inherits the original message + * header. If \a hdr is not NULL it will be used as a template for + * the netlink message header, otherwise the header is left blank. + * + * @return Newly allocated netlink message or NULL + */ +struct nl_msg *nlmsg_inherit(struct nlmsghdr *hdr) +{ + struct nl_msg *nm; + + nm = nlmsg_alloc(); + if (nm && hdr) { + struct nlmsghdr *new = nm->nm_nlh; + + new->nlmsg_type = hdr->nlmsg_type; + new->nlmsg_flags = hdr->nlmsg_flags; + new->nlmsg_seq = hdr->nlmsg_seq; + new->nlmsg_pid = hdr->nlmsg_pid; + } + + return nm; +} + +/** + * Allocate a new netlink message + * @arg nlmsgtype Netlink message type + * @arg flags Message flags. + * + * @return Newly allocated netlink message or NULL. + */ +struct nl_msg *nlmsg_alloc_simple(int nlmsgtype, int flags) +{ + struct nl_msg *msg; + struct nlmsghdr nlh = { + .nlmsg_type = nlmsgtype, + .nlmsg_flags = flags, + .nlmsg_seq = NL_AUTO_SEQ, + .nlmsg_pid = NL_AUTO_PID, + }; + + msg = nlmsg_inherit(&nlh); + if (msg) + NL_DBG(2, "msg %p: Allocated new simple message\n", msg); + + return msg; +} + +/** + * Set the default maximum message payload size for allocated messages + * @arg max Size of payload in bytes. + */ +void nlmsg_set_default_size(size_t max) +{ + if (max < nlmsg_total_size(0)) + max = nlmsg_total_size(0); + + default_msg_size = max; +} + +/** + * Convert a netlink message received from a netlink socket to a nl_msg + * @arg hdr Netlink message received from netlink socket. + * + * Allocates a new netlink message and copies all of the data pointed to + * by \a hdr into the new message object. + * + * @return Newly allocated netlink message or NULL. + */ +struct nl_msg *nlmsg_convert(struct nlmsghdr *hdr) +{ + struct nl_msg *nm; + + nm = __nlmsg_alloc(NLMSG_ALIGN(hdr->nlmsg_len)); + if (!nm) + return NULL; + + memcpy(nm->nm_nlh, hdr, hdr->nlmsg_len); + + return nm; +} + +/** + * Reserve room for additional data in a netlink message + * @arg n netlink message + * @arg len length of additional data to reserve room for + * @arg pad number of bytes to align data to + * + * Reserves room for additional data at the tail of the an + * existing netlink message. Eventual padding required will + * be zeroed out. + * + * @return Pointer to start of additional data tailroom or NULL. + */ +void *nlmsg_reserve(struct nl_msg *n, size_t len, int pad) +{ + char *buf = (char *) n->nm_nlh; + size_t nlmsg_len = n->nm_nlh->nlmsg_len; + size_t tlen; + + if (len > n->nm_size) + return NULL; + + tlen = pad ? ((len + (pad - 1)) & ~(pad - 1)) : len; + + if ((tlen + nlmsg_len) > n->nm_size) + return NULL; + + buf += nlmsg_len; + n->nm_nlh->nlmsg_len += tlen; + + if (tlen > len) + memset(buf + len, 0, tlen - len); + + NL_DBG(2, "msg %p: Reserved %zu (%zu) bytes, pad=%d, nlmsg_len=%d\n", + n, tlen, len, pad, n->nm_nlh->nlmsg_len); + + return buf; +} + +/** + * Append data to tail of a netlink message + * @arg n netlink message + * @arg data data to add + * @arg len length of data + * @arg pad Number of bytes to align data to. + * + * Extends the netlink message as needed and appends the data of given + * length to the message. + * + * @return 0 on success or a negative error code + */ +int nlmsg_append(struct nl_msg *n, void *data, size_t len, int pad) +{ + void *tmp; + + tmp = nlmsg_reserve(n, len, pad); + if (tmp == NULL) + return -NLE_NOMEM; + + memcpy(tmp, data, len); + NL_DBG(2, "msg %p: Appended %zu bytes with padding %d\n", n, len, pad); + + return 0; +} + +/** + * Expand maximum payload size of a netlink message + * @arg n Netlink message. + * @arg newlen New maximum payload size. + * + * Reallocates the payload section of a netlink message and increases + * the maximum payload size of the message. + * + * @note Any pointers pointing to old payload block will be stale and + * need to be refetched. Therfore, do not expand while constructing + * nested attributes or while reserved data blocks are held. + * + * @return 0 on success or a negative error code. + */ +int nlmsg_expand(struct nl_msg *n, size_t newlen) +{ + void *tmp; + + if (newlen <= n->nm_size) + return -NLE_INVAL; + + tmp = realloc(n->nm_nlh, newlen); + if (tmp == NULL) + return -NLE_NOMEM; + + n->nm_nlh = tmp; + n->nm_size = newlen; + + return 0; +} + +/** + * Add a netlink message header to a netlink message + * @arg n netlink message + * @arg pid netlink process id or NL_AUTO_PID + * @arg seq sequence number of message or NL_AUTO_SEQ + * @arg type message type + * @arg payload length of message payload + * @arg flags message flags + * + * Adds or overwrites the netlink message header in an existing message + * object. If \a payload is greater-than zero additional room will be + * reserved, f.e. for family specific headers. It can be accesed via + * nlmsg_data(). + * + * @return A pointer to the netlink message header or NULL. + */ +struct nlmsghdr *nlmsg_put(struct nl_msg *n, uint32_t pid, uint32_t seq, + int type, int payload, int flags) +{ + struct nlmsghdr *nlh; + + if (n->nm_nlh->nlmsg_len < NLMSG_HDRLEN) + BUG(); + + nlh = (struct nlmsghdr *) n->nm_nlh; + nlh->nlmsg_type = type; + nlh->nlmsg_flags = flags; + nlh->nlmsg_pid = pid; + nlh->nlmsg_seq = seq; + + NL_DBG(2, "msg %p: Added netlink header type=%d, flags=%d, pid=%d, " + "seq=%d\n", n, type, flags, pid, seq); + + if (payload > 0 && + nlmsg_reserve(n, payload, NLMSG_ALIGNTO) == NULL) + return NULL; + + return nlh; +} + +/** + * Return actual netlink message + * @arg n netlink message + * + * Returns the actual netlink message casted to the type of the netlink + * message header. + * + * @return A pointer to the netlink message. + */ +struct nlmsghdr *nlmsg_hdr(struct nl_msg *n) +{ + return n->nm_nlh; +} + +/** + * Acquire a reference on a netlink message + * @arg msg message to acquire reference from + */ +void nlmsg_get(struct nl_msg *msg) +{ + msg->nm_refcnt++; + NL_DBG(4, "New reference to message %p, total %d\n", + msg, msg->nm_refcnt); +} + +/** + * Release a reference from an netlink message + * @arg msg message to release reference from + * + * Frees memory after the last reference has been released. + */ +void nlmsg_free(struct nl_msg *msg) +{ + if (!msg) + return; + + msg->nm_refcnt--; + NL_DBG(4, "Returned message reference %p, %d remaining\n", + msg, msg->nm_refcnt); + + if (msg->nm_refcnt < 0) + BUG(); + + if (msg->nm_refcnt <= 0) { + free(msg->nm_nlh); + NL_DBG(2, "msg %p: Freed\n", msg); + free(msg); + } +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +void nlmsg_set_proto(struct nl_msg *msg, int protocol) +{ + msg->nm_protocol = protocol; +} + +int nlmsg_get_proto(struct nl_msg *msg) +{ + return msg->nm_protocol; +} + +size_t nlmsg_get_max_size(struct nl_msg *msg) +{ + return msg->nm_size; +} + +void nlmsg_set_src(struct nl_msg *msg, struct sockaddr_nl *addr) +{ + memcpy(&msg->nm_src, addr, sizeof(*addr)); +} + +struct sockaddr_nl *nlmsg_get_src(struct nl_msg *msg) +{ + return &msg->nm_src; +} + +void nlmsg_set_dst(struct nl_msg *msg, struct sockaddr_nl *addr) +{ + memcpy(&msg->nm_dst, addr, sizeof(*addr)); +} + +struct sockaddr_nl *nlmsg_get_dst(struct nl_msg *msg) +{ + return &msg->nm_dst; +} + +void nlmsg_set_creds(struct nl_msg *msg, struct ucred *creds) +{ + memcpy(&msg->nm_creds, creds, sizeof(*creds)); + msg->nm_flags |= NL_MSG_CRED_PRESENT; +} + +struct ucred *nlmsg_get_creds(struct nl_msg *msg) +{ + if (msg->nm_flags & NL_MSG_CRED_PRESENT) + return &msg->nm_creds; + return NULL; +} + +/** @} */ + +/** + * @name Netlink Message Type Translations + * @{ + */ + +static const struct trans_tbl nl_msgtypes[] = { + __ADD(NLMSG_NOOP,NOOP), + __ADD(NLMSG_ERROR,ERROR), + __ADD(NLMSG_DONE,DONE), + __ADD(NLMSG_OVERRUN,OVERRUN), +}; + +char *nl_nlmsgtype2str(int type, char *buf, size_t size) +{ + return __type2str(type, buf, size, nl_msgtypes, + ARRAY_SIZE(nl_msgtypes)); +} + +int nl_str2nlmsgtype(const char *name) +{ + return __str2type(name, nl_msgtypes, ARRAY_SIZE(nl_msgtypes)); +} + +/** @} */ + +/** + * @name Netlink Message Flags Translations + * @{ + */ + +char *nl_nlmsg_flags2str(int flags, char *buf, size_t len) +{ + memset(buf, 0, len); + +#define PRINT_FLAG(f) \ + if (flags & NLM_F_##f) { \ + flags &= ~NLM_F_##f; \ + strncat(buf, #f, len - strlen(buf) - 1); \ + if (flags) \ + strncat(buf, ",", len - strlen(buf) - 1); \ + } + + PRINT_FLAG(REQUEST); + PRINT_FLAG(MULTI); + PRINT_FLAG(ACK); + PRINT_FLAG(ECHO); + PRINT_FLAG(ROOT); + PRINT_FLAG(MATCH); + PRINT_FLAG(ATOMIC); + PRINT_FLAG(REPLACE); + PRINT_FLAG(EXCL); + PRINT_FLAG(CREATE); + PRINT_FLAG(APPEND); + + if (flags) { + char s[32]; + snprintf(s, sizeof(s), "0x%x", flags); + strncat(buf, s, len - strlen(buf) - 1); + } +#undef PRINT_FLAG + + return buf; +} + +/** @} */ + +/** + * @name Direct Parsing + * @{ + */ + +/** @cond SKIP */ +struct dp_xdata { + void (*cb)(struct nl_object *, void *); + void *arg; +}; +/** @endcond */ + +static int parse_cb(struct nl_object *obj, struct nl_parser_param *p) +{ + struct dp_xdata *x = p->pp_arg; + + x->cb(obj, x->arg); + return 0; +} + +int nl_msg_parse(struct nl_msg *msg, void (*cb)(struct nl_object *, void *), + void *arg) +{ + struct nl_cache_ops *ops; + struct nl_parser_param p = { + .pp_cb = parse_cb + }; + struct dp_xdata x = { + .cb = cb, + .arg = arg, + }; + int err; + + ops = nl_cache_ops_associate_safe(nlmsg_get_proto(msg), + nlmsg_hdr(msg)->nlmsg_type); + if (ops == NULL) + return -NLE_MSGTYPE_NOSUPPORT; + p.pp_arg = &x; + + err = nl_cache_parse(ops, NULL, nlmsg_hdr(msg), &p); + nl_cache_ops_put(ops); + + return err; +} + +/** @} */ + +/** + * @name Dumping + * @{ + */ + +static void prefix_line(FILE *ofd, int prefix) +{ + int i; + + for (i = 0; i < prefix; i++) + fprintf(ofd, " "); +} + +static inline void dump_hex(FILE *ofd, char *start, int len, int prefix) +{ + int i, a, c, limit; + char ascii[21] = {0}; + + limit = 16 - (prefix * 2); + prefix_line(ofd, prefix); + fprintf(ofd, " "); + + for (i = 0, a = 0, c = 0; i < len; i++) { + int v = *(uint8_t *) (start + i); + + fprintf(ofd, "%02x ", v); + ascii[a++] = isprint(v) ? v : '.'; + + if (++c >= limit) { + fprintf(ofd, "%s\n", ascii); + if (i < (len - 1)) { + prefix_line(ofd, prefix); + fprintf(ofd, " "); + } + a = c = 0; + memset(ascii, 0, sizeof(ascii)); + } + } + + if (c != 0) { + for (i = 0; i < (limit - c); i++) + fprintf(ofd, " "); + fprintf(ofd, "%s\n", ascii); + } +} + +static void print_hdr(FILE *ofd, struct nl_msg *msg) +{ + struct nlmsghdr *nlh = nlmsg_hdr(msg); + struct nl_cache_ops *ops; + struct nl_msgtype *mt; + char buf[128]; + + fprintf(ofd, " .nlmsg_len = %d\n", nlh->nlmsg_len); + + ops = nl_cache_ops_associate_safe(nlmsg_get_proto(msg), nlh->nlmsg_type); + if (ops) { + mt = nl_msgtype_lookup(ops, nlh->nlmsg_type); + if (!mt) + BUG(); + + snprintf(buf, sizeof(buf), "%s::%s", ops->co_name, mt->mt_name); + nl_cache_ops_put(ops); + } else + nl_nlmsgtype2str(nlh->nlmsg_type, buf, sizeof(buf)); + + fprintf(ofd, " .type = %d <%s>\n", nlh->nlmsg_type, buf); + fprintf(ofd, " .flags = %d <%s>\n", nlh->nlmsg_flags, + nl_nlmsg_flags2str(nlh->nlmsg_flags, buf, sizeof(buf))); + fprintf(ofd, " .seq = %d\n", nlh->nlmsg_seq); + fprintf(ofd, " .port = %d\n", nlh->nlmsg_pid); + +} + +static void print_genl_hdr(FILE *ofd, void *start) +{ + struct genlmsghdr *ghdr = start; + + fprintf(ofd, " [GENERIC NETLINK HEADER] %zu octets\n", GENL_HDRLEN); + fprintf(ofd, " .cmd = %u\n", ghdr->cmd); + fprintf(ofd, " .version = %u\n", ghdr->version); + fprintf(ofd, " .unused = %#x\n", ghdr->reserved); +} + +static void *print_genl_msg(struct nl_msg *msg, FILE *ofd, struct nlmsghdr *hdr, + struct nl_cache_ops *ops, int *payloadlen) +{ + char *data = nlmsg_data(hdr); + + if (*payloadlen < GENL_HDRLEN) + return data; + + print_genl_hdr(ofd, data); + + *payloadlen -= GENL_HDRLEN; + data += GENL_HDRLEN; + + if (ops) { + int hdrsize = ops->co_hdrsize - GENL_HDRLEN; + + if (hdrsize > 0) { + if (*payloadlen < hdrsize) + return data; + + fprintf(ofd, " [HEADER] %d octets\n", hdrsize); + dump_hex(ofd, data, hdrsize, 0); + + *payloadlen -= hdrsize; + data += hdrsize; + } + } + + return data; +} + +static void dump_attr(FILE *ofd, struct nlattr *attr, int prefix) +{ + int len = nla_len(attr); + + dump_hex(ofd, nla_data(attr), len, prefix); +} + +static void dump_attrs(FILE *ofd, struct nlattr *attrs, int attrlen, + int prefix) +{ + int rem; + struct nlattr *nla; + + nla_for_each_attr(nla, attrs, attrlen, rem) { + int padlen, alen = nla_len(nla); + + prefix_line(ofd, prefix); + + if (nla->nla_type == 0) + fprintf(ofd, " [ATTR PADDING] %d octets\n", alen); + else + fprintf(ofd, " [ATTR %02d%s] %d octets\n", nla_type(nla), + nla_is_nested(nla) ? " NESTED" : "", + alen); + + if (nla_is_nested(nla)) + dump_attrs(ofd, nla_data(nla), alen, prefix+1); + else + dump_attr(ofd, nla, prefix); + + padlen = nla_padlen(alen); + if (padlen > 0) { + prefix_line(ofd, prefix); + fprintf(ofd, " [PADDING] %d octets\n", + padlen); + dump_hex(ofd, (char *) nla_data(nla) + alen, + padlen, prefix); + } + } + + if (rem) { + prefix_line(ofd, prefix); + fprintf(ofd, " [LEFTOVER] %d octets\n", rem); + } +} + +static void dump_error_msg(struct nl_msg *msg, FILE *ofd) +{ + struct nlmsghdr *hdr = nlmsg_hdr(msg); + struct nlmsgerr *err = nlmsg_data(hdr); + + fprintf(ofd, " [ERRORMSG] %zu octets\n", sizeof(*err)); + + if (nlmsg_len(hdr) >= sizeof(*err)) { + struct nl_msg *errmsg; + + fprintf(ofd, " .error = %d \"%s\"\n", err->error, + nl_strerror_l(-err->error)); + fprintf(ofd, " [ORIGINAL MESSAGE] %zu octets\n", sizeof(*hdr)); + + errmsg = nlmsg_inherit(&err->msg); + print_hdr(ofd, errmsg); + nlmsg_free(errmsg); + } +} + +static void print_msg(struct nl_msg *msg, FILE *ofd, struct nlmsghdr *hdr) +{ + struct nl_cache_ops *ops; + int payloadlen = nlmsg_len(hdr); + int attrlen = 0; + void *data; + + data = nlmsg_data(hdr); + ops = nl_cache_ops_associate_safe(nlmsg_get_proto(msg), + hdr->nlmsg_type); + if (ops) { + attrlen = nlmsg_attrlen(hdr, ops->co_hdrsize); + payloadlen -= attrlen; + } + + if (msg->nm_protocol == NETLINK_GENERIC) + data = print_genl_msg(msg, ofd, hdr, ops, &payloadlen); + + if (payloadlen) { + fprintf(ofd, " [PAYLOAD] %d octets\n", payloadlen); + dump_hex(ofd, data, payloadlen, 0); + } + + if (attrlen) { + struct nlattr *attrs; + int attrlen; + + attrs = nlmsg_attrdata(hdr, ops->co_hdrsize); + attrlen = nlmsg_attrlen(hdr, ops->co_hdrsize); + dump_attrs(ofd, attrs, attrlen, 0); + } + + if (ops) + nl_cache_ops_put(ops); +} + +/** + * Dump message in human readable format to file descriptor + * @arg msg Message to print + * @arg ofd File descriptor. + */ +void nl_msg_dump(struct nl_msg *msg, FILE *ofd) +{ + struct nlmsghdr *hdr = nlmsg_hdr(msg); + + fprintf(ofd, + "-------------------------- BEGIN NETLINK MESSAGE ---------------------------\n"); + + fprintf(ofd, " [NETLINK HEADER] %zu octets\n", sizeof(struct nlmsghdr)); + print_hdr(ofd, msg); + + if (hdr->nlmsg_type == NLMSG_ERROR) + dump_error_msg(msg, ofd); + else if (nlmsg_len(hdr) > 0) + print_msg(msg, ofd, hdr); + + fprintf(ofd, + "--------------------------- END NETLINK MESSAGE ---------------------------\n"); +} + +/** @} */ + +/** @} */ diff --git a/libnl/lib/netfilter/ct.c b/libnl/lib/netfilter/ct.c new file mode 100644 index 0000000..97545eb --- /dev/null +++ b/libnl/lib/netfilter/ct.c @@ -0,0 +1,674 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + * Copyright (c) 2008 Patrick McHardy + */ + +/** + * @ingroup nfnl + * @defgroup ct Conntrack + * @brief + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include + +#include +#include +#include +#include +#include + +static struct nl_cache_ops nfnl_ct_ops; + + +static struct nla_policy ct_policy[CTA_MAX+1] = { + [CTA_TUPLE_ORIG] = { .type = NLA_NESTED }, + [CTA_TUPLE_REPLY] = { .type = NLA_NESTED }, + [CTA_STATUS] = { .type = NLA_U32 }, + [CTA_PROTOINFO] = { .type = NLA_NESTED }, + //[CTA_HELP] + //[CTA_NAT_SRC] + [CTA_TIMEOUT] = { .type = NLA_U32 }, + [CTA_MARK] = { .type = NLA_U32 }, + [CTA_COUNTERS_ORIG] = { .type = NLA_NESTED }, + [CTA_COUNTERS_REPLY] = { .type = NLA_NESTED }, + [CTA_USE] = { .type = NLA_U32 }, + [CTA_ID] = { .type = NLA_U32 }, + [CTA_ZONE] = { .type = NLA_U16 }, + //[CTA_NAT_DST] +}; + +static struct nla_policy ct_tuple_policy[CTA_TUPLE_MAX+1] = { + [CTA_TUPLE_IP] = { .type = NLA_NESTED }, + [CTA_TUPLE_PROTO] = { .type = NLA_NESTED }, +}; + +static struct nla_policy ct_ip_policy[CTA_IP_MAX+1] = { + [CTA_IP_V4_SRC] = { .type = NLA_U32 }, + [CTA_IP_V4_DST] = { .type = NLA_U32 }, + [CTA_IP_V6_SRC] = { .minlen = 16 }, + [CTA_IP_V6_DST] = { .minlen = 16 }, +}; + +static struct nla_policy ct_proto_policy[CTA_PROTO_MAX+1] = { + [CTA_PROTO_NUM] = { .type = NLA_U8 }, + [CTA_PROTO_SRC_PORT] = { .type = NLA_U16 }, + [CTA_PROTO_DST_PORT] = { .type = NLA_U16 }, + [CTA_PROTO_ICMP_ID] = { .type = NLA_U16 }, + [CTA_PROTO_ICMP_TYPE] = { .type = NLA_U8 }, + [CTA_PROTO_ICMP_CODE] = { .type = NLA_U8 }, + [CTA_PROTO_ICMPV6_ID] = { .type = NLA_U16 }, + [CTA_PROTO_ICMPV6_TYPE] = { .type = NLA_U8 }, + [CTA_PROTO_ICMPV6_CODE] = { .type = NLA_U8 }, +}; + +static struct nla_policy ct_protoinfo_policy[CTA_PROTOINFO_MAX+1] = { + [CTA_PROTOINFO_TCP] = { .type = NLA_NESTED }, +}; + +static struct nla_policy ct_protoinfo_tcp_policy[CTA_PROTOINFO_TCP_MAX+1] = { + [CTA_PROTOINFO_TCP_STATE] = { .type = NLA_U8 }, + [CTA_PROTOINFO_TCP_WSCALE_ORIGINAL] = { .type = NLA_U8 }, + [CTA_PROTOINFO_TCP_WSCALE_REPLY] = { .type = NLA_U8 }, + [CTA_PROTOINFO_TCP_FLAGS_ORIGINAL] = { .minlen = 2 }, + [CTA_PROTOINFO_TCP_FLAGS_REPLY] = { .minlen = 2 }, + +}; + +static struct nla_policy ct_counters_policy[CTA_COUNTERS_MAX+1] = { + [CTA_COUNTERS_PACKETS] = { .type = NLA_U64 }, + [CTA_COUNTERS_BYTES] = { .type = NLA_U64 }, + [CTA_COUNTERS32_PACKETS]= { .type = NLA_U32 }, + [CTA_COUNTERS32_BYTES] = { .type = NLA_U32 }, +}; + +static struct nla_policy ct_timestamp_policy[CTA_TIMESTAMP_MAX + 1] = { + [CTA_TIMESTAMP_START] = { .type = NLA_U64 }, + [CTA_TIMESTAMP_STOP] = { .type = NLA_U64 }, +}; + +static int ct_parse_ip(struct nfnl_ct *ct, int repl, struct nlattr *attr) +{ + struct nlattr *tb[CTA_IP_MAX+1]; + struct nl_addr *addr; + int err; + + err = nla_parse_nested(tb, CTA_IP_MAX, attr, ct_ip_policy); + if (err < 0) + goto errout; + + if (tb[CTA_IP_V4_SRC]) { + addr = nl_addr_alloc_attr(tb[CTA_IP_V4_SRC], AF_INET); + if (addr == NULL) + goto errout_enomem; + err = nfnl_ct_set_src(ct, repl, addr); + nl_addr_put(addr); + if (err < 0) + goto errout; + } + if (tb[CTA_IP_V4_DST]) { + addr = nl_addr_alloc_attr(tb[CTA_IP_V4_DST], AF_INET); + if (addr == NULL) + goto errout_enomem; + err = nfnl_ct_set_dst(ct, repl, addr); + nl_addr_put(addr); + if (err < 0) + goto errout; + } + if (tb[CTA_IP_V6_SRC]) { + addr = nl_addr_alloc_attr(tb[CTA_IP_V6_SRC], AF_INET6); + if (addr == NULL) + goto errout_enomem; + err = nfnl_ct_set_src(ct, repl, addr); + nl_addr_put(addr); + if (err < 0) + goto errout; + } + if (tb[CTA_IP_V6_DST]) { + addr = nl_addr_alloc_attr(tb[CTA_IP_V6_DST], AF_INET6); + if (addr == NULL) + goto errout_enomem; + err = nfnl_ct_set_dst(ct, repl, addr); + nl_addr_put(addr); + if (err < 0) + goto errout; + } + + return 0; + +errout_enomem: + err = -NLE_NOMEM; +errout: + return err; +} + +static int ct_parse_proto(struct nfnl_ct *ct, int repl, struct nlattr *attr) +{ + struct nlattr *tb[CTA_PROTO_MAX+1]; + int err; + + err = nla_parse_nested(tb, CTA_PROTO_MAX, attr, ct_proto_policy); + if (err < 0) + return err; + + if (!repl && tb[CTA_PROTO_NUM]) + nfnl_ct_set_proto(ct, nla_get_u8(tb[CTA_PROTO_NUM])); + if (tb[CTA_PROTO_SRC_PORT]) + nfnl_ct_set_src_port(ct, repl, + ntohs(nla_get_u16(tb[CTA_PROTO_SRC_PORT]))); + if (tb[CTA_PROTO_DST_PORT]) + nfnl_ct_set_dst_port(ct, repl, + ntohs(nla_get_u16(tb[CTA_PROTO_DST_PORT]))); + + if (ct->ct_family == AF_INET) { + if (tb[CTA_PROTO_ICMP_ID]) + nfnl_ct_set_icmp_id(ct, repl, + ntohs(nla_get_u16(tb[CTA_PROTO_ICMP_ID]))); + if (tb[CTA_PROTO_ICMP_TYPE]) + nfnl_ct_set_icmp_type(ct, repl, + nla_get_u8(tb[CTA_PROTO_ICMP_TYPE])); + if (tb[CTA_PROTO_ICMP_CODE]) + nfnl_ct_set_icmp_code(ct, repl, + nla_get_u8(tb[CTA_PROTO_ICMP_CODE])); + } else if (ct->ct_family == AF_INET6) { + if (tb[CTA_PROTO_ICMPV6_ID]) + nfnl_ct_set_icmp_id(ct, repl, + ntohs(nla_get_u16(tb[CTA_PROTO_ICMPV6_ID]))); + if (tb[CTA_PROTO_ICMPV6_TYPE]) + nfnl_ct_set_icmp_type(ct, repl, + nla_get_u8(tb[CTA_PROTO_ICMPV6_TYPE])); + if (tb[CTA_PROTO_ICMPV6_CODE]) + nfnl_ct_set_icmp_code(ct, repl, + nla_get_u8(tb[CTA_PROTO_ICMPV6_CODE])); + } + + return 0; +} + +static int ct_parse_tuple(struct nfnl_ct *ct, int repl, struct nlattr *attr) +{ + struct nlattr *tb[CTA_TUPLE_MAX+1]; + int err; + + err = nla_parse_nested(tb, CTA_TUPLE_MAX, attr, ct_tuple_policy); + if (err < 0) + return err; + + if (tb[CTA_TUPLE_IP]) { + err = ct_parse_ip(ct, repl, tb[CTA_TUPLE_IP]); + if (err < 0) + return err; + } + + if (tb[CTA_TUPLE_PROTO]) { + err = ct_parse_proto(ct, repl, tb[CTA_TUPLE_PROTO]); + if (err < 0) + return err; + } + + return 0; +} + +static int ct_parse_protoinfo_tcp(struct nfnl_ct *ct, struct nlattr *attr) +{ + struct nlattr *tb[CTA_PROTOINFO_TCP_MAX+1]; + int err; + + err = nla_parse_nested(tb, CTA_PROTOINFO_TCP_MAX, attr, + ct_protoinfo_tcp_policy); + if (err < 0) + return err; + + if (tb[CTA_PROTOINFO_TCP_STATE]) + nfnl_ct_set_tcp_state(ct, + nla_get_u8(tb[CTA_PROTOINFO_TCP_STATE])); + + return 0; +} + +static int ct_parse_protoinfo(struct nfnl_ct *ct, struct nlattr *attr) +{ + struct nlattr *tb[CTA_PROTOINFO_MAX+1]; + int err; + + err = nla_parse_nested(tb, CTA_PROTOINFO_MAX, attr, + ct_protoinfo_policy); + if (err < 0) + return err; + + if (tb[CTA_PROTOINFO_TCP]) { + err = ct_parse_protoinfo_tcp(ct, tb[CTA_PROTOINFO_TCP]); + if (err < 0) + return err; + } + + return 0; +} + +static int ct_parse_counters(struct nfnl_ct *ct, int repl, struct nlattr *attr) +{ + struct nlattr *tb[CTA_COUNTERS_MAX+1]; + int err; + + err = nla_parse_nested(tb, CTA_COUNTERS_MAX, attr, ct_counters_policy); + if (err < 0) + return err; + + if (tb[CTA_COUNTERS_PACKETS]) + nfnl_ct_set_packets(ct, repl, + ntohll(nla_get_u64(tb[CTA_COUNTERS_PACKETS]))); + if (tb[CTA_COUNTERS32_PACKETS]) + nfnl_ct_set_packets(ct, repl, + ntohl(nla_get_u32(tb[CTA_COUNTERS32_PACKETS]))); + if (tb[CTA_COUNTERS_BYTES]) + nfnl_ct_set_bytes(ct, repl, + ntohll(nla_get_u64(tb[CTA_COUNTERS_BYTES]))); + if (tb[CTA_COUNTERS32_BYTES]) + nfnl_ct_set_bytes(ct, repl, + ntohl(nla_get_u32(tb[CTA_COUNTERS32_BYTES]))); + + return 0; +} + +int nfnlmsg_ct_group(struct nlmsghdr *nlh) +{ + switch (nfnlmsg_subtype(nlh)) { + case IPCTNL_MSG_CT_NEW: + if (nlh->nlmsg_flags & (NLM_F_CREATE|NLM_F_EXCL)) + return NFNLGRP_CONNTRACK_NEW; + else + return NFNLGRP_CONNTRACK_UPDATE; + case IPCTNL_MSG_CT_DELETE: + return NFNLGRP_CONNTRACK_DESTROY; + default: + return NFNLGRP_NONE; + } +} + +static int ct_parse_timestamp(struct nfnl_ct *ct, struct nlattr *attr) +{ + struct nlattr *tb[CTA_TIMESTAMP_MAX + 1]; + int err; + + err = nla_parse_nested(tb, CTA_TIMESTAMP_MAX, attr, + ct_timestamp_policy); + if (err < 0) + return err; + + if (tb[CTA_TIMESTAMP_START] && tb[CTA_TIMESTAMP_STOP]) + nfnl_ct_set_timestamp(ct, + ntohll(nla_get_u64(tb[CTA_TIMESTAMP_START])), + ntohll(nla_get_u64(tb[CTA_TIMESTAMP_STOP]))); + + return 0; +} + +int nfnlmsg_ct_parse(struct nlmsghdr *nlh, struct nfnl_ct **result) +{ + struct nfnl_ct *ct; + struct nlattr *tb[CTA_MAX+1]; + int err; + + ct = nfnl_ct_alloc(); + if (!ct) + return -NLE_NOMEM; + + ct->ce_msgtype = nlh->nlmsg_type; + + err = nlmsg_parse(nlh, sizeof(struct nfgenmsg), tb, CTA_MAX, + ct_policy); + if (err < 0) + goto errout; + + nfnl_ct_set_family(ct, nfnlmsg_family(nlh)); + + if (tb[CTA_TUPLE_ORIG]) { + err = ct_parse_tuple(ct, 0, tb[CTA_TUPLE_ORIG]); + if (err < 0) + goto errout; + } + if (tb[CTA_TUPLE_REPLY]) { + err = ct_parse_tuple(ct, 1, tb[CTA_TUPLE_REPLY]); + if (err < 0) + goto errout; + } + + if (tb[CTA_PROTOINFO]) { + err = ct_parse_protoinfo(ct, tb[CTA_PROTOINFO]); + if (err < 0) + goto errout; + } + + if (tb[CTA_STATUS]) + nfnl_ct_set_status(ct, ntohl(nla_get_u32(tb[CTA_STATUS]))); + if (tb[CTA_TIMEOUT]) + nfnl_ct_set_timeout(ct, ntohl(nla_get_u32(tb[CTA_TIMEOUT]))); + if (tb[CTA_MARK]) + nfnl_ct_set_mark(ct, ntohl(nla_get_u32(tb[CTA_MARK]))); + if (tb[CTA_USE]) + nfnl_ct_set_use(ct, ntohl(nla_get_u32(tb[CTA_USE]))); + if (tb[CTA_ID]) + nfnl_ct_set_id(ct, ntohl(nla_get_u32(tb[CTA_ID]))); + if (tb[CTA_ZONE]) + nfnl_ct_set_zone(ct, ntohs(nla_get_u16(tb[CTA_ZONE]))); + + if (tb[CTA_COUNTERS_ORIG]) { + err = ct_parse_counters(ct, 0, tb[CTA_COUNTERS_ORIG]); + if (err < 0) + goto errout; + } + + if (tb[CTA_COUNTERS_REPLY]) { + err = ct_parse_counters(ct, 1, tb[CTA_COUNTERS_REPLY]); + if (err < 0) + goto errout; + } + + if (tb[CTA_TIMESTAMP]) { + err = ct_parse_timestamp(ct, tb[CTA_TIMESTAMP]); + if (err < 0) + goto errout; + } + + *result = ct; + return 0; + +errout: + nfnl_ct_put(ct); + return err; +} + +static int ct_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, struct nl_parser_param *pp) +{ + struct nfnl_ct *ct; + int err; + + if ((err = nfnlmsg_ct_parse(nlh, &ct)) < 0) + return err; + + err = pp->pp_cb((struct nl_object *) ct, pp); + nfnl_ct_put(ct); + return err; +} + +/** + * Send nfnl ct dump request + * @arg sk Netlink socket. + * + * @return 0 on success or a negative error code. Due to a bug, this function + * returns the number of bytes sent. Treat any non-negative number as success. + */ +int nfnl_ct_dump_request(struct nl_sock *sk) +{ + return nfnl_send_simple(sk, NFNL_SUBSYS_CTNETLINK, IPCTNL_MSG_CT_GET, + NLM_F_DUMP, AF_UNSPEC, 0); +} + +static int ct_request_update(struct nl_cache *cache, struct nl_sock *sk) +{ + return nfnl_ct_dump_request(sk); +} + +static int nfnl_ct_build_tuple(struct nl_msg *msg, const struct nfnl_ct *ct, + int repl) +{ + struct nlattr *tuple, *ip, *proto; + struct nl_addr *addr; + int family; + + family = nfnl_ct_get_family(ct); + + tuple = nla_nest_start(msg, repl ? CTA_TUPLE_REPLY : CTA_TUPLE_ORIG); + if (!tuple) + goto nla_put_failure; + + ip = nla_nest_start(msg, CTA_TUPLE_IP); + if (!ip) + goto nla_put_failure; + + addr = nfnl_ct_get_src(ct, repl); + if (addr) + NLA_PUT_ADDR(msg, + family == AF_INET ? CTA_IP_V4_SRC : CTA_IP_V6_SRC, + addr); + + addr = nfnl_ct_get_dst(ct, repl); + if (addr) + NLA_PUT_ADDR(msg, + family == AF_INET ? CTA_IP_V4_DST : CTA_IP_V6_DST, + addr); + + nla_nest_end(msg, ip); + + proto = nla_nest_start(msg, CTA_TUPLE_PROTO); + if (!proto) + goto nla_put_failure; + + if (nfnl_ct_test_proto(ct)) + NLA_PUT_U8(msg, CTA_PROTO_NUM, nfnl_ct_get_proto(ct)); + + if (nfnl_ct_test_src_port(ct, repl)) + NLA_PUT_U16(msg, CTA_PROTO_SRC_PORT, + htons(nfnl_ct_get_src_port(ct, repl))); + + if (nfnl_ct_test_dst_port(ct, repl)) + NLA_PUT_U16(msg, CTA_PROTO_DST_PORT, + htons(nfnl_ct_get_dst_port(ct, repl))); + + if (family == AF_INET) { + if (nfnl_ct_test_icmp_id(ct, repl)) + NLA_PUT_U16(msg, CTA_PROTO_ICMP_ID, + htons(nfnl_ct_get_icmp_id(ct, repl))); + + if (nfnl_ct_test_icmp_type(ct, repl)) + NLA_PUT_U8(msg, CTA_PROTO_ICMP_TYPE, + nfnl_ct_get_icmp_type(ct, repl)); + + if (nfnl_ct_test_icmp_code(ct, repl)) + NLA_PUT_U8(msg, CTA_PROTO_ICMP_CODE, + nfnl_ct_get_icmp_code(ct, repl)); + } else if (family == AF_INET6) { + if (nfnl_ct_test_icmp_id(ct, repl)) + NLA_PUT_U16(msg, CTA_PROTO_ICMPV6_ID, + htons(nfnl_ct_get_icmp_id(ct, repl))); + + if (nfnl_ct_test_icmp_type(ct, repl)) + NLA_PUT_U8(msg, CTA_PROTO_ICMPV6_TYPE, + nfnl_ct_get_icmp_type(ct, repl)); + + if (nfnl_ct_test_icmp_code(ct, repl)) + NLA_PUT_U8(msg, CTA_PROTO_ICMPV6_CODE, + nfnl_ct_get_icmp_code(ct, repl)); + } + + nla_nest_end(msg, proto); + + nla_nest_end(msg, tuple); + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +static int nfnl_ct_build_message(const struct nfnl_ct *ct, int cmd, int flags, + struct nl_msg **result) +{ + struct nl_msg *msg; + int err; + + msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_CTNETLINK, cmd, flags, + nfnl_ct_get_family(ct), 0); + if (msg == NULL) + return -NLE_NOMEM; + + if ((err = nfnl_ct_build_tuple(msg, ct, 0)) < 0) + goto err_out; + + /* REPLY tuple is optional, dont add unless at least src/dst specified */ + + if ( nfnl_ct_get_src(ct, 1) && nfnl_ct_get_dst(ct, 1) ) + if ((err = nfnl_ct_build_tuple(msg, ct, 1)) < 0) + goto err_out; + + if (nfnl_ct_test_status(ct)) + NLA_PUT_U32(msg, CTA_STATUS, htonl(nfnl_ct_get_status(ct))); + + if (nfnl_ct_test_timeout(ct)) + NLA_PUT_U32(msg, CTA_TIMEOUT, htonl(nfnl_ct_get_timeout(ct))); + + if (nfnl_ct_test_mark(ct)) + NLA_PUT_U32(msg, CTA_MARK, htonl(nfnl_ct_get_mark(ct))); + + if (nfnl_ct_test_id(ct)) + NLA_PUT_U32(msg, CTA_ID, htonl(nfnl_ct_get_id(ct))); + + if (nfnl_ct_test_zone(ct)) + NLA_PUT_U16(msg, CTA_ZONE, htons(nfnl_ct_get_zone(ct))); + + *result = msg; + return 0; + +nla_put_failure: +err_out: + nlmsg_free(msg); + return err; +} + +int nfnl_ct_build_add_request(const struct nfnl_ct *ct, int flags, + struct nl_msg **result) +{ + return nfnl_ct_build_message(ct, IPCTNL_MSG_CT_NEW, flags, result); +} + +int nfnl_ct_add(struct nl_sock *sk, const struct nfnl_ct *ct, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_ct_build_add_request(ct, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +int nfnl_ct_build_delete_request(const struct nfnl_ct *ct, int flags, + struct nl_msg **result) +{ + return nfnl_ct_build_message(ct, IPCTNL_MSG_CT_DELETE, flags, result); +} + +int nfnl_ct_del(struct nl_sock *sk, const struct nfnl_ct *ct, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_ct_build_delete_request(ct, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +int nfnl_ct_build_query_request(const struct nfnl_ct *ct, int flags, + struct nl_msg **result) +{ + return nfnl_ct_build_message(ct, IPCTNL_MSG_CT_GET, flags, result); +} + +int nfnl_ct_query(struct nl_sock *sk, const struct nfnl_ct *ct, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_ct_build_query_request(ct, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +/** + * @name Cache Management + * @{ + */ + +/** + * Build a conntrack cache holding all conntrack currently in the kernel + * @arg sk Netlink socket. + * @arg result Pointer to store resulting cache. + * + * Allocates a new cache, initializes it properly and updates it to + * contain all conntracks currently in the kernel. + * + * @return 0 on success or a negative error code. + */ +int nfnl_ct_alloc_cache(struct nl_sock *sk, struct nl_cache **result) +{ + return nl_cache_alloc_and_fill(&nfnl_ct_ops, sk, result); +} + +/** @} */ + +/** + * @name Conntrack Addition + * @{ + */ + +/** @} */ + +static struct nl_af_group ct_groups[] = { + { AF_UNSPEC, NFNLGRP_CONNTRACK_NEW }, + { AF_UNSPEC, NFNLGRP_CONNTRACK_UPDATE }, + { AF_UNSPEC, NFNLGRP_CONNTRACK_DESTROY }, + { END_OF_GROUP_LIST }, +}; + +#define NFNLMSG_CT_TYPE(type) NFNLMSG_TYPE(NFNL_SUBSYS_CTNETLINK, (type)) +static struct nl_cache_ops nfnl_ct_ops = { + .co_name = "netfilter/ct", + .co_hdrsize = NFNL_HDRLEN, + .co_msgtypes = { + { NFNLMSG_CT_TYPE(IPCTNL_MSG_CT_NEW), NL_ACT_NEW, "new" }, + { NFNLMSG_CT_TYPE(IPCTNL_MSG_CT_GET), NL_ACT_GET, "get" }, + { NFNLMSG_CT_TYPE(IPCTNL_MSG_CT_DELETE), NL_ACT_DEL, "del" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_NETFILTER, + .co_groups = ct_groups, + .co_request_update = ct_request_update, + .co_msg_parser = ct_msg_parser, + .co_obj_ops = &ct_obj_ops, +}; + +static void __init ct_init(void) +{ + nl_cache_mngt_register(&nfnl_ct_ops); +} + +static void __exit ct_exit(void) +{ + nl_cache_mngt_unregister(&nfnl_ct_ops); +} + +/** @} */ diff --git a/libnl/lib/netfilter/ct_obj.c b/libnl/lib/netfilter/ct_obj.c new file mode 100644 index 0000000..6c4e4fb --- /dev/null +++ b/libnl/lib/netfilter/ct_obj.c @@ -0,0 +1,846 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include + +#include +#include +#include + +/** @cond SKIP */ +#define CT_ATTR_FAMILY (1UL << 0) +#define CT_ATTR_PROTO (1UL << 1) + +#define CT_ATTR_TCP_STATE (1UL << 2) + +#define CT_ATTR_STATUS (1UL << 3) +#define CT_ATTR_TIMEOUT (1UL << 4) +#define CT_ATTR_MARK (1UL << 5) +#define CT_ATTR_USE (1UL << 6) +#define CT_ATTR_ID (1UL << 7) + +#define CT_ATTR_ORIG_SRC (1UL << 8) +#define CT_ATTR_ORIG_DST (1UL << 9) +#define CT_ATTR_ORIG_SRC_PORT (1UL << 10) +#define CT_ATTR_ORIG_DST_PORT (1UL << 11) +#define CT_ATTR_ORIG_ICMP_ID (1UL << 12) +#define CT_ATTR_ORIG_ICMP_TYPE (1UL << 13) +#define CT_ATTR_ORIG_ICMP_CODE (1UL << 14) +#define CT_ATTR_ORIG_PACKETS (1UL << 15) +#define CT_ATTR_ORIG_BYTES (1UL << 16) + +#define CT_ATTR_REPL_SRC (1UL << 17) +#define CT_ATTR_REPL_DST (1UL << 18) +#define CT_ATTR_REPL_SRC_PORT (1UL << 19) +#define CT_ATTR_REPL_DST_PORT (1UL << 20) +#define CT_ATTR_REPL_ICMP_ID (1UL << 21) +#define CT_ATTR_REPL_ICMP_TYPE (1UL << 22) +#define CT_ATTR_REPL_ICMP_CODE (1UL << 23) +#define CT_ATTR_REPL_PACKETS (1UL << 24) +#define CT_ATTR_REPL_BYTES (1UL << 25) +#define CT_ATTR_TIMESTAMP (1UL << 26) +#define CT_ATTR_ZONE (1UL << 27) +/** @endcond */ + +static void ct_free_data(struct nl_object *c) +{ + struct nfnl_ct *ct = (struct nfnl_ct *) c; + + if (ct == NULL) + return; + + nl_addr_put(ct->ct_orig.src); + nl_addr_put(ct->ct_orig.dst); + nl_addr_put(ct->ct_repl.src); + nl_addr_put(ct->ct_repl.dst); +} + +static int ct_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct nfnl_ct *dst = (struct nfnl_ct *) _dst; + struct nfnl_ct *src = (struct nfnl_ct *) _src; + struct nl_addr *addr; + + if (src->ct_orig.src) { + addr = nl_addr_clone(src->ct_orig.src); + if (!addr) + return -NLE_NOMEM; + dst->ct_orig.src = addr; + } + + if (src->ct_orig.dst) { + addr = nl_addr_clone(src->ct_orig.dst); + if (!addr) + return -NLE_NOMEM; + dst->ct_orig.dst = addr; + } + + if (src->ct_repl.src) { + addr = nl_addr_clone(src->ct_repl.src); + if (!addr) + return -NLE_NOMEM; + dst->ct_repl.src = addr; + } + + if (src->ct_repl.dst) { + addr = nl_addr_clone(src->ct_repl.dst); + if (!addr) + return -NLE_NOMEM; + dst->ct_repl.dst = addr; + } + + return 0; +} + +static void dump_addr(struct nl_dump_params *p, struct nl_addr *addr, int port) +{ + char buf[64]; + + if (addr) + nl_dump(p, "%s", nl_addr2str(addr, buf, sizeof(buf))); + + if (port) + nl_dump(p, ":%u ", port); + else if (addr) + nl_dump(p, " "); +} + +static void dump_icmp(struct nl_dump_params *p, struct nfnl_ct *ct, int reply) +{ + if (nfnl_ct_test_icmp_type(ct, reply)) + nl_dump(p, "icmp type %d ", nfnl_ct_get_icmp_type(ct, reply)); + + if (nfnl_ct_test_icmp_code(ct, reply)) + nl_dump(p, "code %d ", nfnl_ct_get_icmp_code(ct, reply)); + + if (nfnl_ct_test_icmp_id(ct, reply)) + nl_dump(p, "id %d ", nfnl_ct_get_icmp_id(ct, reply)); +} + +static void ct_dump_tuples(struct nfnl_ct *ct, struct nl_dump_params *p) +{ + struct nl_addr *orig_src, *orig_dst, *reply_src, *reply_dst; + int orig_sport = 0, orig_dport = 0, reply_sport = 0, reply_dport = 0; + int sync = 0; + + orig_src = nfnl_ct_get_src(ct, 0); + orig_dst = nfnl_ct_get_dst(ct, 0); + reply_src = nfnl_ct_get_src(ct, 1); + reply_dst = nfnl_ct_get_dst(ct, 1); + + if (nfnl_ct_test_src_port(ct, 0)) + orig_sport = nfnl_ct_get_src_port(ct, 0); + + if (nfnl_ct_test_dst_port(ct, 0)) + orig_dport = nfnl_ct_get_dst_port(ct, 0); + + if (nfnl_ct_test_src_port(ct, 1)) + reply_sport = nfnl_ct_get_src_port(ct, 1); + + if (nfnl_ct_test_dst_port(ct, 1)) + reply_dport = nfnl_ct_get_dst_port(ct, 1); + + if (orig_src && orig_dst && reply_src && reply_dst && + orig_sport == reply_dport && orig_dport == reply_sport && + !nl_addr_cmp(orig_src, reply_dst) && + !nl_addr_cmp(orig_dst, reply_src)) + sync = 1; + + dump_addr(p, orig_src, orig_sport); + nl_dump(p, sync ? "<-> " : "-> "); + dump_addr(p, orig_dst, orig_dport); + dump_icmp(p, ct, 0); + + if (!sync) { + dump_addr(p, reply_src, reply_sport); + nl_dump(p, "<- "); + dump_addr(p, reply_dst, reply_dport); + dump_icmp(p, ct, 1); + } +} + +/* Compatible with /proc/net/nf_conntrack */ +static void ct_dump_line(struct nl_object *a, struct nl_dump_params *p) +{ + struct nfnl_ct *ct = (struct nfnl_ct *) a; + char buf[64]; + + nl_new_line(p); + + if (nfnl_ct_test_proto(ct)) + nl_dump(p, "%s ", + nl_ip_proto2str(nfnl_ct_get_proto(ct), buf, sizeof(buf))); + + if (nfnl_ct_test_tcp_state(ct)) + nl_dump(p, "%s ", + nfnl_ct_tcp_state2str(nfnl_ct_get_tcp_state(ct), + buf, sizeof(buf))); + + ct_dump_tuples(ct, p); + + if (nfnl_ct_test_mark(ct) && nfnl_ct_get_mark(ct)) + nl_dump(p, "mark %u ", nfnl_ct_get_mark(ct)); + + if (nfnl_ct_test_zone(ct)) + nl_dump(p, "zone %hu ", nfnl_ct_get_zone(ct)); + + if (nfnl_ct_test_timestamp(ct)) { + const struct nfnl_ct_timestamp *tstamp = nfnl_ct_get_timestamp(ct); + int64_t delta_time = tstamp->stop - tstamp->start; + + if (delta_time > 0) + delta_time /= NSEC_PER_SEC; + else + delta_time = 0; + nl_dump(p, "delta-time %llu ", delta_time); + } + + nl_dump(p, "\n"); +} + +static void ct_dump_details(struct nl_object *a, struct nl_dump_params *p) +{ + struct nfnl_ct *ct = (struct nfnl_ct *) a; + char buf[64]; + int fp = 0; + + ct_dump_line(a, p); + + nl_dump(p, " id 0x%x ", ct->ct_id); + nl_dump_line(p, "family %s ", + nl_af2str(ct->ct_family, buf, sizeof(buf))); + + if (nfnl_ct_test_use(ct)) + nl_dump(p, "refcnt %u ", nfnl_ct_get_use(ct)); + + if (nfnl_ct_test_timeout(ct)) { + uint64_t timeout_ms = nfnl_ct_get_timeout(ct) * 1000UL; + nl_dump(p, "timeout %s ", + nl_msec2str(timeout_ms, buf, sizeof(buf))); + } + + if (ct->ct_status) + nl_dump(p, "<"); + +#define PRINT_FLAG(str) \ + { nl_dump(p, "%s%s", fp++ ? "," : "", (str)); } + + if (ct->ct_status & IPS_EXPECTED) + PRINT_FLAG("EXPECTED"); + if (!(ct->ct_status & IPS_SEEN_REPLY)) + PRINT_FLAG("NOREPLY"); + if (ct->ct_status & IPS_ASSURED) + PRINT_FLAG("ASSURED"); + if (!(ct->ct_status & IPS_CONFIRMED)) + PRINT_FLAG("NOTSENT"); + if (ct->ct_status & IPS_SRC_NAT) + PRINT_FLAG("SNAT"); + if (ct->ct_status & IPS_DST_NAT) + PRINT_FLAG("DNAT"); + if (ct->ct_status & IPS_SEQ_ADJUST) + PRINT_FLAG("SEQADJUST"); + if (!(ct->ct_status & IPS_SRC_NAT_DONE)) + PRINT_FLAG("SNAT_INIT"); + if (!(ct->ct_status & IPS_DST_NAT_DONE)) + PRINT_FLAG("DNAT_INIT"); + if (ct->ct_status & IPS_DYING) + PRINT_FLAG("DYING"); + if (ct->ct_status & IPS_FIXED_TIMEOUT) + PRINT_FLAG("FIXED_TIMEOUT"); +#undef PRINT_FLAG + + if (ct->ct_status) + nl_dump(p, ">"); + nl_dump(p, "\n"); +} + +static void ct_dump_stats(struct nl_object *a, struct nl_dump_params *p) +{ + struct nfnl_ct *ct = (struct nfnl_ct *) a; + double res; + char *unit; + uint64_t packets; + const char * const names[] = {"rx", "tx"}; + int i; + + ct_dump_details(a, p); + + if (!nfnl_ct_test_bytes(ct, 0) || + !nfnl_ct_test_packets(ct, 0) || + !nfnl_ct_test_bytes(ct, 1) || + !nfnl_ct_test_packets(ct, 1)) + { + nl_dump_line(p, " Statistics are not available.\n"); + nl_dump_line(p, " Please set sysctl net.netfilter.nf_conntrack_acct=1\n"); + nl_dump_line(p, " (Require kernel 2.6.27)\n"); + return; + } + + nl_dump_line(p, " # packets volume\n"); + for (i=0; i<=1; i++) { + res = nl_cancel_down_bytes(nfnl_ct_get_bytes(ct, i), &unit); + packets = nfnl_ct_get_packets(ct, i); + nl_dump_line(p, " %s %10" PRIu64 " %7.2f %s\n", names[i], packets, res, unit); + } +} + +static uint64_t ct_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + struct nfnl_ct *a = (struct nfnl_ct *) _a; + struct nfnl_ct *b = (struct nfnl_ct *) _b; + uint64_t diff = 0; + +#define CT_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, CT_ATTR_##ATTR, a, b, EXPR) +#define CT_DIFF_VAL(ATTR, FIELD) CT_DIFF(ATTR, a->FIELD != b->FIELD) +#define CT_DIFF_ADDR(ATTR, FIELD) \ + ((flags & LOOSE_COMPARISON) \ + ? CT_DIFF(ATTR, nl_addr_cmp_prefix(a->FIELD, b->FIELD)) \ + : CT_DIFF(ATTR, nl_addr_cmp(a->FIELD, b->FIELD))) + + diff |= CT_DIFF_VAL(FAMILY, ct_family); + diff |= CT_DIFF_VAL(PROTO, ct_proto); + diff |= CT_DIFF_VAL(TCP_STATE, ct_protoinfo.tcp.state); + diff |= CT_DIFF_VAL(TIMEOUT, ct_timeout); + diff |= CT_DIFF_VAL(MARK, ct_mark); + diff |= CT_DIFF_VAL(USE, ct_use); + diff |= CT_DIFF_VAL(ID, ct_id); + diff |= CT_DIFF_ADDR(ORIG_SRC, ct_orig.src); + diff |= CT_DIFF_ADDR(ORIG_DST, ct_orig.dst); + diff |= CT_DIFF_VAL(ORIG_SRC_PORT, ct_orig.proto.port.src); + diff |= CT_DIFF_VAL(ORIG_DST_PORT, ct_orig.proto.port.dst); + diff |= CT_DIFF_VAL(ORIG_ICMP_ID, ct_orig.proto.icmp.id); + diff |= CT_DIFF_VAL(ORIG_ICMP_TYPE, ct_orig.proto.icmp.type); + diff |= CT_DIFF_VAL(ORIG_ICMP_CODE, ct_orig.proto.icmp.code); + diff |= CT_DIFF_VAL(ORIG_PACKETS, ct_orig.packets); + diff |= CT_DIFF_VAL(ORIG_BYTES, ct_orig.bytes); + diff |= CT_DIFF_ADDR(REPL_SRC, ct_repl.src); + diff |= CT_DIFF_ADDR(REPL_DST, ct_repl.dst); + diff |= CT_DIFF_VAL(REPL_SRC_PORT, ct_repl.proto.port.src); + diff |= CT_DIFF_VAL(REPL_DST_PORT, ct_repl.proto.port.dst); + diff |= CT_DIFF_VAL(REPL_ICMP_ID, ct_repl.proto.icmp.id); + diff |= CT_DIFF_VAL(REPL_ICMP_TYPE, ct_repl.proto.icmp.type); + diff |= CT_DIFF_VAL(REPL_ICMP_CODE, ct_repl.proto.icmp.code); + diff |= CT_DIFF_VAL(REPL_PACKETS, ct_repl.packets); + diff |= CT_DIFF_VAL(REPL_BYTES, ct_repl.bytes); + + if (flags & LOOSE_COMPARISON) + diff |= CT_DIFF(STATUS, (a->ct_status ^ b->ct_status) & + b->ct_status_mask); + else + diff |= CT_DIFF(STATUS, a->ct_status != b->ct_status); + +#undef CT_DIFF +#undef CT_DIFF_VAL +#undef CT_DIFF_ADDR + + return diff; +} + +static const struct trans_tbl ct_attrs[] = { + __ADD(CT_ATTR_FAMILY, family), + __ADD(CT_ATTR_PROTO, proto), + __ADD(CT_ATTR_TCP_STATE, tcpstate), + __ADD(CT_ATTR_STATUS, status), + __ADD(CT_ATTR_TIMEOUT, timeout), + __ADD(CT_ATTR_MARK, mark), + __ADD(CT_ATTR_USE, use), + __ADD(CT_ATTR_ID, id), + __ADD(CT_ATTR_ORIG_SRC, origsrc), + __ADD(CT_ATTR_ORIG_DST, origdst), + __ADD(CT_ATTR_ORIG_SRC_PORT, origsrcport), + __ADD(CT_ATTR_ORIG_DST_PORT, origdstport), + __ADD(CT_ATTR_ORIG_ICMP_ID, origicmpid), + __ADD(CT_ATTR_ORIG_ICMP_TYPE, origicmptype), + __ADD(CT_ATTR_ORIG_ICMP_CODE, origicmpcode), + __ADD(CT_ATTR_ORIG_PACKETS, origpackets), + __ADD(CT_ATTR_ORIG_BYTES, origbytes), + __ADD(CT_ATTR_REPL_SRC, replysrc), + __ADD(CT_ATTR_REPL_DST, replydst), + __ADD(CT_ATTR_REPL_SRC_PORT, replysrcport), + __ADD(CT_ATTR_REPL_DST_PORT, replydstport), + __ADD(CT_ATTR_REPL_ICMP_ID, replyicmpid), + __ADD(CT_ATTR_REPL_ICMP_TYPE, replyicmptype), + __ADD(CT_ATTR_REPL_ICMP_CODE, replyicmpcode), + __ADD(CT_ATTR_REPL_PACKETS, replypackets), + __ADD(CT_ATTR_REPL_BYTES, replybytes), +}; + +static char *ct_attrs2str(int attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, ct_attrs, ARRAY_SIZE(ct_attrs)); +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct nfnl_ct *nfnl_ct_alloc(void) +{ + return (struct nfnl_ct *) nl_object_alloc(&ct_obj_ops); +} + +void nfnl_ct_get(struct nfnl_ct *ct) +{ + nl_object_get((struct nl_object *) ct); +} + +void nfnl_ct_put(struct nfnl_ct *ct) +{ + nl_object_put((struct nl_object *) ct); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +void nfnl_ct_set_family(struct nfnl_ct *ct, uint8_t family) +{ + ct->ct_family = family; + ct->ce_mask |= CT_ATTR_FAMILY; +} + +uint8_t nfnl_ct_get_family(const struct nfnl_ct *ct) +{ + if (ct->ce_mask & CT_ATTR_FAMILY) + return ct->ct_family; + else + return AF_UNSPEC; +} + +void nfnl_ct_set_proto(struct nfnl_ct *ct, uint8_t proto) +{ + ct->ct_proto = proto; + ct->ce_mask |= CT_ATTR_PROTO; +} + +int nfnl_ct_test_proto(const struct nfnl_ct *ct) +{ + return !!(ct->ce_mask & CT_ATTR_PROTO); +} + +uint8_t nfnl_ct_get_proto(const struct nfnl_ct *ct) +{ + return ct->ct_proto; +} + +void nfnl_ct_set_tcp_state(struct nfnl_ct *ct, uint8_t state) +{ + ct->ct_protoinfo.tcp.state = state; + ct->ce_mask |= CT_ATTR_TCP_STATE; +} + +int nfnl_ct_test_tcp_state(const struct nfnl_ct *ct) +{ + return !!(ct->ce_mask & CT_ATTR_TCP_STATE); +} + +uint8_t nfnl_ct_get_tcp_state(const struct nfnl_ct *ct) +{ + return ct->ct_protoinfo.tcp.state; +} + +static const struct trans_tbl tcp_states[] = { + __ADD(TCP_CONNTRACK_NONE,NONE), + __ADD(TCP_CONNTRACK_SYN_SENT,SYN_SENT), + __ADD(TCP_CONNTRACK_SYN_RECV,SYN_RECV), + __ADD(TCP_CONNTRACK_ESTABLISHED,ESTABLISHED), + __ADD(TCP_CONNTRACK_FIN_WAIT,FIN_WAIT), + __ADD(TCP_CONNTRACK_CLOSE_WAIT,CLOSE_WAIT), + __ADD(TCP_CONNTRACK_LAST_ACK,LAST_ACK), + __ADD(TCP_CONNTRACK_TIME_WAIT,TIME_WAIT), + __ADD(TCP_CONNTRACK_CLOSE,CLOSE), + __ADD(TCP_CONNTRACK_LISTEN,LISTEN), +}; + +char *nfnl_ct_tcp_state2str(uint8_t state, char *buf, size_t len) +{ + return __type2str(state, buf, len, tcp_states, ARRAY_SIZE(tcp_states)); +} + +int nfnl_ct_str2tcp_state(const char *name) +{ + return __str2type(name, tcp_states, ARRAY_SIZE(tcp_states)); +} + +void nfnl_ct_set_status(struct nfnl_ct *ct, uint32_t status) +{ + ct->ct_status_mask |= status; + ct->ct_status |= status; + ct->ce_mask |= CT_ATTR_STATUS; +} + +void nfnl_ct_unset_status(struct nfnl_ct *ct, uint32_t status) +{ + ct->ct_status_mask |= status; + ct->ct_status &= ~status; + ct->ce_mask |= CT_ATTR_STATUS; +} + +int nfnl_ct_test_status(const struct nfnl_ct *ct) +{ + return !!(ct->ce_mask & CT_ATTR_STATUS); +} + +uint32_t nfnl_ct_get_status(const struct nfnl_ct *ct) +{ + return ct->ct_status; +} + +static const struct trans_tbl status_flags[] = { + __ADD(IPS_EXPECTED, expected), + __ADD(IPS_SEEN_REPLY, seen_reply), + __ADD(IPS_ASSURED, assured), + __ADD(IPS_CONFIRMED, confirmed), + __ADD(IPS_SRC_NAT, snat), + __ADD(IPS_DST_NAT, dnat), + __ADD(IPS_SEQ_ADJUST, seqadjust), + __ADD(IPS_SRC_NAT_DONE, snat_done), + __ADD(IPS_DST_NAT_DONE, dnat_done), + __ADD(IPS_DYING, dying), + __ADD(IPS_FIXED_TIMEOUT, fixed_timeout), +}; + +char * nfnl_ct_status2str(int flags, char *buf, size_t len) +{ + return __flags2str(flags, buf, len, status_flags, + ARRAY_SIZE(status_flags)); +} + +int nfnl_ct_str2status(const char *name) +{ + return __str2flags(name, status_flags, ARRAY_SIZE(status_flags)); +} + +void nfnl_ct_set_timeout(struct nfnl_ct *ct, uint32_t timeout) +{ + ct->ct_timeout = timeout; + ct->ce_mask |= CT_ATTR_TIMEOUT; +} + +int nfnl_ct_test_timeout(const struct nfnl_ct *ct) +{ + return !!(ct->ce_mask & CT_ATTR_TIMEOUT); +} + +uint32_t nfnl_ct_get_timeout(const struct nfnl_ct *ct) +{ + return ct->ct_timeout; +} + +void nfnl_ct_set_mark(struct nfnl_ct *ct, uint32_t mark) +{ + ct->ct_mark = mark; + ct->ce_mask |= CT_ATTR_MARK; +} + +int nfnl_ct_test_mark(const struct nfnl_ct *ct) +{ + return !!(ct->ce_mask & CT_ATTR_MARK); +} + +uint32_t nfnl_ct_get_mark(const struct nfnl_ct *ct) +{ + return ct->ct_mark; +} + +void nfnl_ct_set_use(struct nfnl_ct *ct, uint32_t use) +{ + ct->ct_use = use; + ct->ce_mask |= CT_ATTR_USE; +} + +int nfnl_ct_test_use(const struct nfnl_ct *ct) +{ + return !!(ct->ce_mask & CT_ATTR_USE); +} + +uint32_t nfnl_ct_get_use(const struct nfnl_ct *ct) +{ + return ct->ct_use; +} + +void nfnl_ct_set_id(struct nfnl_ct *ct, uint32_t id) +{ + ct->ct_id = id; + ct->ce_mask |= CT_ATTR_ID; +} + +int nfnl_ct_test_id(const struct nfnl_ct *ct) +{ + return !!(ct->ce_mask & CT_ATTR_ID); +} + +uint32_t nfnl_ct_get_id(const struct nfnl_ct *ct) +{ + return ct->ct_id; +} + +void nfnl_ct_set_zone(struct nfnl_ct *ct, uint16_t zone) +{ + ct->ct_zone = zone; + ct->ce_mask |= CT_ATTR_ZONE; +} + +int nfnl_ct_test_zone(const struct nfnl_ct *ct) +{ + return !!(ct->ce_mask & CT_ATTR_ZONE); +} + +uint16_t nfnl_ct_get_zone(const struct nfnl_ct *ct) +{ + return ct->ct_zone; +} + +static int ct_set_addr(struct nfnl_ct *ct, struct nl_addr *addr, + int attr, struct nl_addr ** ct_addr) +{ + if (ct->ce_mask & CT_ATTR_FAMILY) { + if (addr->a_family != ct->ct_family) + return -NLE_AF_MISMATCH; + } else + nfnl_ct_set_family(ct, addr->a_family); + + if (*ct_addr) + nl_addr_put(*ct_addr); + + nl_addr_get(addr); + *ct_addr = addr; + ct->ce_mask |= attr; + + return 0; +} + +int nfnl_ct_set_src(struct nfnl_ct *ct, int repl, struct nl_addr *addr) +{ + struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + int attr = repl ? CT_ATTR_REPL_SRC : CT_ATTR_ORIG_SRC; + return ct_set_addr(ct, addr, attr, &dir->src); +} + +int nfnl_ct_set_dst(struct nfnl_ct *ct, int repl, struct nl_addr *addr) +{ + struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + int attr = repl ? CT_ATTR_REPL_DST : CT_ATTR_ORIG_DST; + return ct_set_addr(ct, addr, attr, &dir->dst); +} + +struct nl_addr *nfnl_ct_get_src(const struct nfnl_ct *ct, int repl) +{ + const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + int attr = repl ? CT_ATTR_REPL_SRC : CT_ATTR_ORIG_SRC; + if (!(ct->ce_mask & attr)) + return NULL; + return dir->src; +} + +struct nl_addr *nfnl_ct_get_dst(const struct nfnl_ct *ct, int repl) +{ + const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + int attr = repl ? CT_ATTR_REPL_DST : CT_ATTR_ORIG_DST; + if (!(ct->ce_mask & attr)) + return NULL; + return dir->dst; +} + +void nfnl_ct_set_src_port(struct nfnl_ct *ct, int repl, uint16_t port) +{ + struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + int attr = repl ? CT_ATTR_REPL_SRC_PORT : CT_ATTR_ORIG_SRC_PORT; + + dir->proto.port.src = port; + ct->ce_mask |= attr; +} + +int nfnl_ct_test_src_port(const struct nfnl_ct *ct, int repl) +{ + int attr = repl ? CT_ATTR_REPL_SRC_PORT : CT_ATTR_ORIG_SRC_PORT; + return !!(ct->ce_mask & attr); +} + +uint16_t nfnl_ct_get_src_port(const struct nfnl_ct *ct, int repl) +{ + const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + + return dir->proto.port.src; +} + +void nfnl_ct_set_dst_port(struct nfnl_ct *ct, int repl, uint16_t port) +{ + struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + int attr = repl ? CT_ATTR_REPL_DST_PORT : CT_ATTR_ORIG_DST_PORT; + + dir->proto.port.dst = port; + ct->ce_mask |= attr; +} + +int nfnl_ct_test_dst_port(const struct nfnl_ct *ct, int repl) +{ + int attr = repl ? CT_ATTR_REPL_DST_PORT : CT_ATTR_ORIG_DST_PORT; + return !!(ct->ce_mask & attr); +} + +uint16_t nfnl_ct_get_dst_port(const struct nfnl_ct *ct, int repl) +{ + const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + + return dir->proto.port.dst; +} + +void nfnl_ct_set_icmp_id(struct nfnl_ct *ct, int repl, uint16_t id) +{ + struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + int attr = repl ? CT_ATTR_REPL_ICMP_ID : CT_ATTR_ORIG_ICMP_ID; + + dir->proto.icmp.id = id; + ct->ce_mask |= attr; +} + +int nfnl_ct_test_icmp_id(const struct nfnl_ct *ct, int repl) +{ + int attr = repl ? CT_ATTR_REPL_ICMP_ID : CT_ATTR_ORIG_ICMP_ID; + return !!(ct->ce_mask & attr); +} + +uint16_t nfnl_ct_get_icmp_id(const struct nfnl_ct *ct, int repl) +{ + const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + + return dir->proto.icmp.id; +} + +void nfnl_ct_set_icmp_type(struct nfnl_ct *ct, int repl, uint8_t type) +{ + struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + int attr = repl ? CT_ATTR_REPL_ICMP_TYPE : CT_ATTR_ORIG_ICMP_TYPE; + + dir->proto.icmp.type = type; + ct->ce_mask |= attr; +} + +int nfnl_ct_test_icmp_type(const struct nfnl_ct *ct, int repl) +{ + int attr = repl ? CT_ATTR_REPL_ICMP_TYPE : CT_ATTR_ORIG_ICMP_TYPE; + return !!(ct->ce_mask & attr); +} + +uint8_t nfnl_ct_get_icmp_type(const struct nfnl_ct *ct, int repl) +{ + const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + + return dir->proto.icmp.type; +} + +void nfnl_ct_set_icmp_code(struct nfnl_ct *ct, int repl, uint8_t code) +{ + struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + int attr = repl ? CT_ATTR_REPL_ICMP_CODE : CT_ATTR_ORIG_ICMP_CODE; + + dir->proto.icmp.code = code; + ct->ce_mask |= attr; +} + +int nfnl_ct_test_icmp_code(const struct nfnl_ct *ct, int repl) +{ + int attr = repl ? CT_ATTR_REPL_ICMP_CODE : CT_ATTR_ORIG_ICMP_CODE; + return !!(ct->ce_mask & attr); +} + +uint8_t nfnl_ct_get_icmp_code(const struct nfnl_ct *ct, int repl) +{ + const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + + return dir->proto.icmp.code; +} + +void nfnl_ct_set_packets(struct nfnl_ct *ct, int repl, uint64_t packets) +{ + struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + int attr = repl ? CT_ATTR_REPL_PACKETS : CT_ATTR_ORIG_PACKETS; + + dir->packets = packets; + ct->ce_mask |= attr; +} + +int nfnl_ct_test_packets(const struct nfnl_ct *ct, int repl) +{ + int attr = repl ? CT_ATTR_REPL_PACKETS : CT_ATTR_ORIG_PACKETS; + return !!(ct->ce_mask & attr); +} + +uint64_t nfnl_ct_get_packets(const struct nfnl_ct *ct, int repl) +{ + const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + + return dir->packets; +} + +void nfnl_ct_set_bytes(struct nfnl_ct *ct, int repl, uint64_t bytes) +{ + struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + int attr = repl ? CT_ATTR_REPL_BYTES : CT_ATTR_ORIG_BYTES; + + dir->bytes = bytes; + ct->ce_mask |= attr; +} + +int nfnl_ct_test_bytes(const struct nfnl_ct *ct, int repl) +{ + int attr = repl ? CT_ATTR_REPL_BYTES : CT_ATTR_ORIG_BYTES; + return !!(ct->ce_mask & attr); +} + +uint64_t nfnl_ct_get_bytes(const struct nfnl_ct *ct, int repl) +{ + const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + + return dir->bytes; +} + +void nfnl_ct_set_timestamp(struct nfnl_ct *ct, uint64_t start, uint64_t stop) +{ + ct->ct_tstamp.start = start; + ct->ct_tstamp.stop = stop; + ct->ce_mask |= CT_ATTR_TIMESTAMP; +} + +int nfnl_ct_test_timestamp(const struct nfnl_ct *ct) +{ + return !!(ct->ce_mask & CT_ATTR_TIMESTAMP); +} + +const struct nfnl_ct_timestamp *nfnl_ct_get_timestamp(const struct nfnl_ct *ct) +{ + return &ct->ct_tstamp; +} + +/** @} */ + +struct nl_object_ops ct_obj_ops = { + .oo_name = "netfilter/ct", + .oo_size = sizeof(struct nfnl_ct), + .oo_free_data = ct_free_data, + .oo_clone = ct_clone, + .oo_dump = { + [NL_DUMP_LINE] = ct_dump_line, + [NL_DUMP_DETAILS] = ct_dump_details, + [NL_DUMP_STATS] = ct_dump_stats, + }, + .oo_compare = ct_compare, + .oo_attrs2str = ct_attrs2str, +}; + +/** @} */ diff --git a/libnl/lib/netfilter/exp.c b/libnl/lib/netfilter/exp.c new file mode 100644 index 0000000..3e45390 --- /dev/null +++ b/libnl/lib/netfilter/exp.c @@ -0,0 +1,626 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + * Copyright (c) 2008 Patrick McHardy + * Copyright (c) 2012 Rich Fought + */ + +/** + * @ingroup nfnl + * @defgroup exp Expectation + * @brief + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include + +#include +#include +#include +#include + +static struct nl_cache_ops nfnl_exp_ops; + +static struct nla_policy exp_policy[CTA_EXPECT_MAX+1] = { + [CTA_EXPECT_MASTER] = { .type = NLA_NESTED }, + [CTA_EXPECT_TUPLE] = { .type = NLA_NESTED }, + [CTA_EXPECT_MASK] = { .type = NLA_NESTED }, + [CTA_EXPECT_TIMEOUT] = { .type = NLA_U32 }, + [CTA_EXPECT_ID] = { .type = NLA_U32 }, + [CTA_EXPECT_HELP_NAME] = { .type = NLA_STRING }, + [CTA_EXPECT_ZONE] = { .type = NLA_U16 }, + [CTA_EXPECT_FLAGS] = { .type = NLA_U32 }, // Added in kernel 2.6.37 + [CTA_EXPECT_CLASS] = { .type = NLA_U32 }, // Added in kernel 3.5 + [CTA_EXPECT_NAT] = { .type = NLA_NESTED }, // Added in kernel 3.5 + [CTA_EXPECT_FN] = { .type = NLA_STRING }, // Added in kernel 3.5 +}; + +static struct nla_policy exp_tuple_policy[CTA_TUPLE_MAX+1] = { + [CTA_TUPLE_IP] = { .type = NLA_NESTED }, + [CTA_TUPLE_PROTO] = { .type = NLA_NESTED }, +}; + +static struct nla_policy exp_ip_policy[CTA_IP_MAX+1] = { + [CTA_IP_V4_SRC] = { .type = NLA_U32 }, + [CTA_IP_V4_DST] = { .type = NLA_U32 }, + [CTA_IP_V6_SRC] = { .minlen = 16 }, + [CTA_IP_V6_DST] = { .minlen = 16 }, +}; + +static struct nla_policy exp_proto_policy[CTA_PROTO_MAX+1] = { + [CTA_PROTO_NUM] = { .type = NLA_U8 }, + [CTA_PROTO_SRC_PORT] = { .type = NLA_U16 }, + [CTA_PROTO_DST_PORT] = { .type = NLA_U16 }, + [CTA_PROTO_ICMP_ID] = { .type = NLA_U16 }, + [CTA_PROTO_ICMP_TYPE] = { .type = NLA_U8 }, + [CTA_PROTO_ICMP_CODE] = { .type = NLA_U8 }, + [CTA_PROTO_ICMPV6_ID] = { .type = NLA_U16 }, + [CTA_PROTO_ICMPV6_TYPE] = { .type = NLA_U8 }, + [CTA_PROTO_ICMPV6_CODE] = { .type = NLA_U8 }, +}; + +static struct nla_policy exp_nat_policy[CTA_EXPECT_NAT_MAX+1] = { + [CTA_EXPECT_NAT_DIR] = { .type = NLA_U32 }, + [CTA_EXPECT_NAT_TUPLE] = { .type = NLA_NESTED }, +}; + +static int exp_parse_ip(struct nfnl_exp *exp, int tuple, struct nlattr *attr) +{ + struct nlattr *tb[CTA_IP_MAX+1]; + struct nl_addr *addr; + int err; + + err = nla_parse_nested(tb, CTA_IP_MAX, attr, exp_ip_policy); + if (err < 0) + goto errout; + + if (tb[CTA_IP_V4_SRC]) { + addr = nl_addr_alloc_attr(tb[CTA_IP_V4_SRC], AF_INET); + if (addr == NULL) + goto errout_enomem; + err = nfnl_exp_set_src(exp, tuple, addr); + nl_addr_put(addr); + if (err < 0) + goto errout; + } + if (tb[CTA_IP_V4_DST]) { + addr = nl_addr_alloc_attr(tb[CTA_IP_V4_DST], AF_INET); + if (addr == NULL) + goto errout_enomem; + err = nfnl_exp_set_dst(exp, tuple, addr); + nl_addr_put(addr); + if (err < 0) + goto errout; + } + if (tb[CTA_IP_V6_SRC]) { + addr = nl_addr_alloc_attr(tb[CTA_IP_V6_SRC], AF_INET6); + if (addr == NULL) + goto errout_enomem; + err = nfnl_exp_set_src(exp, tuple, addr); + nl_addr_put(addr); + if (err < 0) + goto errout; + } + if (tb[CTA_IP_V6_DST]) { + addr = nl_addr_alloc_attr(tb[CTA_IP_V6_DST], AF_INET6); + if (addr == NULL) + goto errout_enomem; + err = nfnl_exp_set_dst(exp, tuple, addr); + nl_addr_put(addr); + if (err < 0) + goto errout; + } + + return 0; + +errout_enomem: + err = -NLE_NOMEM; +errout: + return err; +} + +static int exp_parse_proto(struct nfnl_exp *exp, int tuple, struct nlattr *attr) +{ + struct nlattr *tb[CTA_PROTO_MAX+1]; + int err; + uint16_t srcport = 0, dstport = 0, icmpid = 0; + uint8_t icmptype = 0, icmpcode = 0; + + err = nla_parse_nested(tb, CTA_PROTO_MAX, attr, exp_proto_policy); + if (err < 0) + return err; + + if (tb[CTA_PROTO_NUM]) + nfnl_exp_set_l4protonum(exp, tuple, nla_get_u8(tb[CTA_PROTO_NUM])); + + if (tb[CTA_PROTO_SRC_PORT]) + srcport = ntohs(nla_get_u16(tb[CTA_PROTO_SRC_PORT])); + if (tb[CTA_PROTO_DST_PORT]) + dstport = ntohs(nla_get_u16(tb[CTA_PROTO_DST_PORT])); + if (tb[CTA_PROTO_SRC_PORT] || tb[CTA_PROTO_DST_PORT]) + nfnl_exp_set_ports(exp, tuple, srcport, dstport); + + if (tb[CTA_PROTO_ICMP_ID]) + icmpid = ntohs(nla_get_u16(tb[CTA_PROTO_ICMP_ID])); + if (tb[CTA_PROTO_ICMP_TYPE]) + icmptype = nla_get_u8(tb[CTA_PROTO_ICMP_TYPE]); + if (tb[CTA_PROTO_ICMP_CODE]) + icmpcode = nla_get_u8(tb[CTA_PROTO_ICMP_CODE]); + if (tb[CTA_PROTO_ICMP_ID] || tb[CTA_PROTO_ICMP_TYPE] || tb[CTA_PROTO_ICMP_CODE]) + nfnl_exp_set_icmp(exp, tuple, icmpid, icmptype, icmpcode); + return 0; +} + +static int exp_parse_tuple(struct nfnl_exp *exp, int tuple, struct nlattr *attr) +{ + struct nlattr *tb[CTA_TUPLE_MAX+1]; + int err; + + err = nla_parse_nested(tb, CTA_TUPLE_MAX, attr, exp_tuple_policy); + if (err < 0) + return err; + + if (tb[CTA_TUPLE_IP]) { + err = exp_parse_ip(exp, tuple, tb[CTA_TUPLE_IP]); + if (err < 0) + return err; + } + + if (tb[CTA_TUPLE_PROTO]) { + err = exp_parse_proto(exp, tuple, tb[CTA_TUPLE_PROTO]); + if (err < 0) + return err; + } + + return 0; +} + +static int exp_parse_nat(struct nfnl_exp *exp, struct nlattr *attr) +{ + struct nlattr *tb[CTA_EXPECT_NAT_MAX+1]; + int err; + + err = nla_parse_nested(tb, CTA_EXPECT_NAT_MAX, attr, exp_nat_policy); + if (err < 0) + return err; + + if (tb[CTA_EXPECT_NAT_DIR]) + nfnl_exp_set_nat_dir(exp, nla_get_u32(tb[CTA_EXPECT_NAT_DIR])); + + if (tb[CTA_EXPECT_NAT_TUPLE]) { + err = exp_parse_tuple(exp, NFNL_EXP_TUPLE_NAT, tb[CTA_EXPECT_NAT_TUPLE]); + if (err < 0) + return err; + } + + return 0; +} + +int nfnlmsg_exp_group(struct nlmsghdr *nlh) +{ + switch (nfnlmsg_subtype(nlh)) { + case IPCTNL_MSG_EXP_NEW: + if (nlh->nlmsg_flags & (NLM_F_CREATE|NLM_F_EXCL)) + return NFNLGRP_CONNTRACK_EXP_NEW; + else + return NFNLGRP_CONNTRACK_EXP_UPDATE; + case IPCTNL_MSG_EXP_DELETE: + return NFNLGRP_CONNTRACK_EXP_DESTROY; + default: + return NFNLGRP_NONE; + } +} + +int nfnlmsg_exp_parse(struct nlmsghdr *nlh, struct nfnl_exp **result) +{ + struct nfnl_exp *exp; + struct nlattr *tb[CTA_MAX+1]; + int err; + + exp = nfnl_exp_alloc(); + if (!exp) + return -NLE_NOMEM; + + exp->ce_msgtype = nlh->nlmsg_type; + + err = nlmsg_parse(nlh, sizeof(struct nfgenmsg), tb, CTA_EXPECT_MAX, + exp_policy); + if (err < 0) + goto errout; + + nfnl_exp_set_family(exp, nfnlmsg_family(nlh)); + + if (tb[CTA_EXPECT_TUPLE]) { + err = exp_parse_tuple(exp, NFNL_EXP_TUPLE_EXPECT, tb[CTA_EXPECT_TUPLE]); + if (err < 0) + goto errout; + } + if (tb[CTA_EXPECT_MASTER]) { + err = exp_parse_tuple(exp, NFNL_EXP_TUPLE_MASTER, tb[CTA_EXPECT_MASTER]); + if (err < 0) + goto errout; + } + if (tb[CTA_EXPECT_MASK]) { + err = exp_parse_tuple(exp, NFNL_EXP_TUPLE_MASK, tb[CTA_EXPECT_MASK]); + if (err < 0) + goto errout; + } + + if (tb[CTA_EXPECT_NAT]) { + err = exp_parse_nat(exp, tb[CTA_EXPECT_MASK]); + if (err < 0) + goto errout; + } + + if (tb[CTA_EXPECT_CLASS]) + nfnl_exp_set_class(exp, ntohl(nla_get_u32(tb[CTA_EXPECT_CLASS]))); + + if (tb[CTA_EXPECT_FN]) + nfnl_exp_set_fn(exp, nla_data(tb[CTA_EXPECT_FN])); + + if (tb[CTA_EXPECT_TIMEOUT]) + nfnl_exp_set_timeout(exp, ntohl(nla_get_u32(tb[CTA_EXPECT_TIMEOUT]))); + + if (tb[CTA_EXPECT_ID]) + nfnl_exp_set_id(exp, ntohl(nla_get_u32(tb[CTA_EXPECT_ID]))); + + if (tb[CTA_EXPECT_HELP_NAME]) + nfnl_exp_set_helper_name(exp, nla_data(tb[CTA_EXPECT_HELP_NAME])); + + if (tb[CTA_EXPECT_ZONE]) + nfnl_exp_set_zone(exp, ntohs(nla_get_u16(tb[CTA_EXPECT_ZONE]))); + + if (tb[CTA_EXPECT_FLAGS]) + nfnl_exp_set_flags(exp, ntohl(nla_get_u32(tb[CTA_EXPECT_FLAGS]))); + + *result = exp; + return 0; + +errout: + nfnl_exp_put(exp); + return err; +} + +static int exp_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, struct nl_parser_param *pp) +{ + struct nfnl_exp *exp; + int err; + + if ((err = nfnlmsg_exp_parse(nlh, &exp)) < 0) + return err; + + err = pp->pp_cb((struct nl_object *) exp, pp); + nfnl_exp_put(exp); + return err; +} + +/** + * Send nfnl exp dump request + * @arg sk Netlink socket. + * + * @return 0 on success or a negative error code. Due to a bug, this function + * returns the number of bytes sent. Treat any non-negative number as success. + */ +int nfnl_exp_dump_request(struct nl_sock *sk) +{ + return nfnl_send_simple(sk, NFNL_SUBSYS_CTNETLINK_EXP, IPCTNL_MSG_EXP_GET, + NLM_F_DUMP, AF_UNSPEC, 0); +} + +static int exp_request_update(struct nl_cache *cache, struct nl_sock *sk) +{ + return nfnl_exp_dump_request(sk); +} + +static int exp_get_tuple_attr(int tuple) +{ + int attr = 0; + + switch (tuple) { + case CTA_EXPECT_MASTER: + attr = NFNL_EXP_TUPLE_MASTER; + break; + case CTA_EXPECT_MASK: + attr = NFNL_EXP_TUPLE_MASK; + break; + case CTA_EXPECT_NAT: + attr = NFNL_EXP_TUPLE_NAT; + break; + case CTA_EXPECT_TUPLE: + default : + attr = NFNL_EXP_TUPLE_EXPECT; + break; + } + + return attr; +} + +static int nfnl_exp_build_tuple(struct nl_msg *msg, const struct nfnl_exp *exp, + int cta) +{ + struct nlattr *tuple, *ip, *proto; + struct nl_addr *addr; + int family; + int type; + + family = nfnl_exp_get_family(exp); + + type = exp_get_tuple_attr(cta); + + if (cta == CTA_EXPECT_NAT) + tuple = nla_nest_start(msg, CTA_EXPECT_NAT_TUPLE); + else + tuple = nla_nest_start(msg, cta); + + if (!tuple) + goto nla_put_failure; + + ip = nla_nest_start(msg, CTA_TUPLE_IP); + if (!ip) + goto nla_put_failure; + + addr = nfnl_exp_get_src(exp, type); + if (addr) + NLA_PUT_ADDR(msg, + family == AF_INET ? CTA_IP_V4_SRC : CTA_IP_V6_SRC, + addr); + + addr = nfnl_exp_get_dst(exp, type); + if (addr) + NLA_PUT_ADDR(msg, + family == AF_INET ? CTA_IP_V4_DST : CTA_IP_V6_DST, + addr); + + nla_nest_end(msg, ip); + + proto = nla_nest_start(msg, CTA_TUPLE_PROTO); + if (!proto) + goto nla_put_failure; + + if (nfnl_exp_test_l4protonum(exp, type)) + NLA_PUT_U8(msg, CTA_PROTO_NUM, nfnl_exp_get_l4protonum(exp, type)); + + if (nfnl_exp_test_ports(exp, type)) { + NLA_PUT_U16(msg, CTA_PROTO_SRC_PORT, + htons(nfnl_exp_get_src_port(exp, type))); + + NLA_PUT_U16(msg, CTA_PROTO_DST_PORT, + htons(nfnl_exp_get_dst_port(exp, type))); + } + + if (nfnl_exp_test_icmp(exp, type)) { + NLA_PUT_U16(msg, CTA_PROTO_ICMP_ID, + htons(nfnl_exp_get_icmp_id(exp, type))); + + NLA_PUT_U8(msg, CTA_PROTO_ICMP_TYPE, + nfnl_exp_get_icmp_type(exp, type)); + + NLA_PUT_U8(msg, CTA_PROTO_ICMP_CODE, + nfnl_exp_get_icmp_code(exp, type)); + } + + nla_nest_end(msg, proto); + + nla_nest_end(msg, tuple); + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +static int nfnl_exp_build_nat(struct nl_msg *msg, const struct nfnl_exp *exp) +{ + struct nlattr *nat; + int err; + + nat = nla_nest_start(msg, CTA_EXPECT_NAT); + + if (nfnl_exp_test_nat_dir(exp)) { + NLA_PUT_U32(msg, CTA_EXPECT_NAT_DIR, + nfnl_exp_get_nat_dir(exp)); + } + + if ((err = nfnl_exp_build_tuple(msg, exp, CTA_EXPECT_NAT)) < 0) + goto nla_put_failure; + + nla_nest_end(msg, nat); + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +static int nfnl_exp_build_message(const struct nfnl_exp *exp, int cmd, int flags, + struct nl_msg **result) +{ + struct nl_msg *msg; + int err; + + msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_CTNETLINK_EXP, cmd, flags, + nfnl_exp_get_family(exp), 0); + if (msg == NULL) + return -NLE_NOMEM; + + if ((err = nfnl_exp_build_tuple(msg, exp, CTA_EXPECT_TUPLE)) < 0) + goto err_out; + + if ((err = nfnl_exp_build_tuple(msg, exp, CTA_EXPECT_MASTER)) < 0) + goto err_out; + + if ((err = nfnl_exp_build_tuple(msg, exp, CTA_EXPECT_MASK)) < 0) + goto err_out; + + if (nfnl_exp_test_src(exp, NFNL_EXP_TUPLE_NAT)) { + if ((err = nfnl_exp_build_nat(msg, exp)) < 0) + goto err_out; + } + + if (nfnl_exp_test_class(exp)) + NLA_PUT_U32(msg, CTA_EXPECT_CLASS, htonl(nfnl_exp_get_class(exp))); + + if (nfnl_exp_test_fn(exp)) + NLA_PUT_STRING(msg, CTA_EXPECT_FN, nfnl_exp_get_fn(exp)); + + if (nfnl_exp_test_id(exp)) + NLA_PUT_U32(msg, CTA_EXPECT_ID, htonl(nfnl_exp_get_id(exp))); + + if (nfnl_exp_test_timeout(exp)) + NLA_PUT_U32(msg, CTA_EXPECT_TIMEOUT, htonl(nfnl_exp_get_timeout(exp))); + + if (nfnl_exp_test_helper_name(exp)) + NLA_PUT_STRING(msg, CTA_EXPECT_HELP_NAME, nfnl_exp_get_helper_name(exp)); + + if (nfnl_exp_test_zone(exp)) + NLA_PUT_U16(msg, CTA_EXPECT_ZONE, htons(nfnl_exp_get_zone(exp))); + + if (nfnl_exp_test_flags(exp)) + NLA_PUT_U32(msg, CTA_EXPECT_FLAGS, htonl(nfnl_exp_get_flags(exp))); + + *result = msg; + return 0; + +nla_put_failure: + err = -NLE_NOMEM; + +err_out: + nlmsg_free(msg); + return err; +} + +int nfnl_exp_build_add_request(const struct nfnl_exp *exp, int flags, + struct nl_msg **result) +{ + return nfnl_exp_build_message(exp, IPCTNL_MSG_EXP_NEW, flags, result); +} + +int nfnl_exp_add(struct nl_sock *sk, const struct nfnl_exp *exp, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_exp_build_add_request(exp, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +int nfnl_exp_build_delete_request(const struct nfnl_exp *exp, int flags, + struct nl_msg **result) +{ + return nfnl_exp_build_message(exp, IPCTNL_MSG_EXP_DELETE, flags, result); +} + +int nfnl_exp_del(struct nl_sock *sk, const struct nfnl_exp *exp, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_exp_build_delete_request(exp, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +int nfnl_exp_build_query_request(const struct nfnl_exp *exp, int flags, + struct nl_msg **result) +{ + return nfnl_exp_build_message(exp, IPCTNL_MSG_EXP_GET, flags, result); +} + +int nfnl_exp_query(struct nl_sock *sk, const struct nfnl_exp *exp, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_exp_build_query_request(exp, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +/** + * @name Cache Management + * @{ + */ + +/** + * Build a expectation cache holding all expectations currently in the kernel + * @arg sk Netlink socket. + * @arg result Pointer to store resulting cache. + * + * Allocates a new cache, initializes it properly and updates it to + * contain all expectations currently in the kernel. + * + * @return 0 on success or a negative error code. + */ +int nfnl_exp_alloc_cache(struct nl_sock *sk, struct nl_cache **result) +{ + return nl_cache_alloc_and_fill(&nfnl_exp_ops, sk, result); +} + +/** @} */ + +/** + * @name Expectation Addition + * @{ + */ + +/** @} */ + +static struct nl_af_group exp_groups[] = { + { AF_UNSPEC, NFNLGRP_CONNTRACK_EXP_NEW }, + { AF_UNSPEC, NFNLGRP_CONNTRACK_EXP_UPDATE }, + { AF_UNSPEC, NFNLGRP_CONNTRACK_EXP_DESTROY }, + { END_OF_GROUP_LIST }, +}; + +#define NFNLMSG_EXP_TYPE(type) NFNLMSG_TYPE(NFNL_SUBSYS_CTNETLINK_EXP, (type)) +static struct nl_cache_ops nfnl_exp_ops = { + .co_name = "netfilter/exp", + .co_hdrsize = NFNL_HDRLEN, + .co_msgtypes = { + { NFNLMSG_EXP_TYPE(IPCTNL_MSG_EXP_NEW), NL_ACT_NEW, "new" }, + { NFNLMSG_EXP_TYPE(IPCTNL_MSG_EXP_GET), NL_ACT_GET, "get" }, + { NFNLMSG_EXP_TYPE(IPCTNL_MSG_EXP_DELETE), NL_ACT_DEL, "del" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_NETFILTER, + .co_groups = exp_groups, + .co_request_update = exp_request_update, + .co_msg_parser = exp_msg_parser, + .co_obj_ops = &exp_obj_ops, +}; + +static void __init exp_init(void) +{ + nl_cache_mngt_register(&nfnl_exp_ops); +} + +static void __exit exp_exit(void) +{ + nl_cache_mngt_unregister(&nfnl_exp_ops); +} + +/** @} */ diff --git a/libnl/lib/netfilter/exp_obj.c b/libnl/lib/netfilter/exp_obj.c new file mode 100644 index 0000000..406d7aa --- /dev/null +++ b/libnl/lib/netfilter/exp_obj.c @@ -0,0 +1,884 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + * Copyright (c) 2012 Rich Fought + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include + +#include +#include +#include + +// The 32-bit attribute mask in the common object header isn't +// big enough to handle all attributes of an expectation. So +// we'll for sure specify optional attributes + parent attributes +// that are required for valid object comparison. Comparison of +// these parent attributes will include nested attributes. + +/** @cond SKIP */ +#define EXP_ATTR_FAMILY (1UL << 0) // 8-bit +#define EXP_ATTR_TIMEOUT (1UL << 1) // 32-bit +#define EXP_ATTR_ID (1UL << 2) // 32-bit +#define EXP_ATTR_HELPER_NAME (1UL << 3) // string +#define EXP_ATTR_ZONE (1UL << 4) // 16-bit +#define EXP_ATTR_FLAGS (1UL << 5) // 32-bit +#define EXP_ATTR_CLASS (1UL << 6) // 32-bit +#define EXP_ATTR_FN (1UL << 7) // String +// Tuples +#define EXP_ATTR_EXPECT_IP_SRC (1UL << 8) +#define EXP_ATTR_EXPECT_IP_DST (1UL << 9) +#define EXP_ATTR_EXPECT_L4PROTO_NUM (1UL << 10) +#define EXP_ATTR_EXPECT_L4PROTO_PORTS (1UL << 11) +#define EXP_ATTR_EXPECT_L4PROTO_ICMP (1UL << 12) +#define EXP_ATTR_MASTER_IP_SRC (1UL << 13) +#define EXP_ATTR_MASTER_IP_DST (1UL << 14) +#define EXP_ATTR_MASTER_L4PROTO_NUM (1UL << 15) +#define EXP_ATTR_MASTER_L4PROTO_PORTS (1UL << 16) +#define EXP_ATTR_MASTER_L4PROTO_ICMP (1UL << 17) +#define EXP_ATTR_MASK_IP_SRC (1UL << 18) +#define EXP_ATTR_MASK_IP_DST (1UL << 19) +#define EXP_ATTR_MASK_L4PROTO_NUM (1UL << 20) +#define EXP_ATTR_MASK_L4PROTO_PORTS (1UL << 21) +#define EXP_ATTR_MASK_L4PROTO_ICMP (1UL << 22) +#define EXP_ATTR_NAT_IP_SRC (1UL << 23) +#define EXP_ATTR_NAT_IP_DST (1UL << 24) +#define EXP_ATTR_NAT_L4PROTO_NUM (1UL << 25) +#define EXP_ATTR_NAT_L4PROTO_PORTS (1UL << 26) +#define EXP_ATTR_NAT_L4PROTO_ICMP (1UL << 27) +#define EXP_ATTR_NAT_DIR (1UL << 28) +/** @endcond */ + +static void exp_free_data(struct nl_object *c) +{ + struct nfnl_exp *exp = (struct nfnl_exp *) c; + + if (exp == NULL) + return; + + nl_addr_put(exp->exp_expect.src); + nl_addr_put(exp->exp_expect.dst); + nl_addr_put(exp->exp_master.src); + nl_addr_put(exp->exp_master.dst); + nl_addr_put(exp->exp_mask.src); + nl_addr_put(exp->exp_mask.dst); + nl_addr_put(exp->exp_nat.src); + nl_addr_put(exp->exp_nat.dst); + + free(exp->exp_fn); + free(exp->exp_helper_name); +} + +static int exp_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct nfnl_exp *dst = (struct nfnl_exp *) _dst; + struct nfnl_exp *src = (struct nfnl_exp *) _src; + struct nl_addr *addr; + + // Expectation + if (src->exp_expect.src) { + addr = nl_addr_clone(src->exp_expect.src); + if (!addr) + return -NLE_NOMEM; + dst->exp_expect.src = addr; + } + + if (src->exp_expect.dst) { + addr = nl_addr_clone(src->exp_expect.dst); + if (!addr) + return -NLE_NOMEM; + dst->exp_expect.dst = addr; + } + + // Master CT + if (src->exp_master.src) { + addr = nl_addr_clone(src->exp_master.src); + if (!addr) + return -NLE_NOMEM; + dst->exp_master.src = addr; + } + + if (src->exp_master.dst) { + addr = nl_addr_clone(src->exp_master.dst); + if (!addr) + return -NLE_NOMEM; + dst->exp_master.dst = addr; + } + + // Mask + if (src->exp_mask.src) { + addr = nl_addr_clone(src->exp_mask.src); + if (!addr) + return -NLE_NOMEM; + dst->exp_mask.src = addr; + } + + if (src->exp_mask.dst) { + addr = nl_addr_clone(src->exp_mask.dst); + if (!addr) + return -NLE_NOMEM; + dst->exp_mask.dst = addr; + } + + // NAT + if (src->exp_nat.src) { + addr = nl_addr_clone(src->exp_nat.src); + if (!addr) + return -NLE_NOMEM; + dst->exp_nat.src = addr; + } + + if (src->exp_nat.dst) { + addr = nl_addr_clone(src->exp_nat.dst); + if (!addr) + return -NLE_NOMEM; + dst->exp_nat.dst = addr; + } + + if (src->exp_fn) + dst->exp_fn = strdup(src->exp_fn); + + if (src->exp_helper_name) + dst->exp_helper_name = strdup(src->exp_helper_name); + + return 0; +} + +static void dump_addr(struct nl_dump_params *p, struct nl_addr *addr, int port) +{ + char buf[64]; + + if (addr) + nl_dump(p, "%s", nl_addr2str(addr, buf, sizeof(buf))); + + if (port) + nl_dump(p, ":%u ", port); + else if (addr) + nl_dump(p, " "); +} + +static void dump_icmp(struct nl_dump_params *p, struct nfnl_exp *exp, int tuple) +{ + if (nfnl_exp_test_icmp(exp, tuple)) { + + nl_dump(p, "icmp type %d ", nfnl_exp_get_icmp_type(exp, tuple)); + + nl_dump(p, "code %d ", nfnl_exp_get_icmp_code(exp, tuple)); + + nl_dump(p, "id %d ", nfnl_exp_get_icmp_id(exp, tuple)); + } +} + +static void exp_dump_tuples(struct nfnl_exp *exp, struct nl_dump_params *p) +{ + struct nl_addr *tuple_src, *tuple_dst; + int tuple_sport, tuple_dport; + int i = 0; + char buf[64]; + + for (i = NFNL_EXP_TUPLE_EXPECT; i < NFNL_EXP_TUPLE_MAX; i++) { + tuple_src = NULL; + tuple_dst = NULL; + tuple_sport = 0; + tuple_dport = 0; + + // Test needed for NAT case + if (nfnl_exp_test_src(exp, i)) + tuple_src = nfnl_exp_get_src(exp, i); + if (nfnl_exp_test_dst(exp, i)) + tuple_dst = nfnl_exp_get_dst(exp, i); + + // Don't have tests for individual ports/types/codes/ids, + if (nfnl_exp_test_l4protonum(exp, i)) { + nl_dump(p, "%s ", + nl_ip_proto2str(nfnl_exp_get_l4protonum(exp, i), buf, sizeof(buf))); + } + + if (nfnl_exp_test_ports(exp, i)) { + tuple_sport = nfnl_exp_get_src_port(exp, i); + tuple_dport = nfnl_exp_get_dst_port(exp, i); + } + + dump_addr(p, tuple_src, tuple_sport); + dump_addr(p, tuple_dst, tuple_dport); + dump_icmp(p, exp, 0); + } + + if (nfnl_exp_test_nat_dir(exp)) + nl_dump(p, "nat dir %s ", exp->exp_nat_dir); + +} + +/* FIXME Compatible with /proc/net/nf_conntrack */ +static void exp_dump_line(struct nl_object *a, struct nl_dump_params *p) +{ + struct nfnl_exp *exp = (struct nfnl_exp *) a; + + nl_new_line(p); + + exp_dump_tuples(exp, p); + + nl_dump(p, "\n"); +} + +static void exp_dump_details(struct nl_object *a, struct nl_dump_params *p) +{ + struct nfnl_exp *exp = (struct nfnl_exp *) a; + char buf[64]; + int fp = 0; + + exp_dump_line(a, p); + + nl_dump(p, " id 0x%x ", exp->exp_id); + nl_dump_line(p, "family %s ", + nl_af2str(exp->exp_family, buf, sizeof(buf))); + + if (nfnl_exp_test_timeout(exp)) { + uint64_t timeout_ms = nfnl_exp_get_timeout(exp) * 1000UL; + nl_dump(p, "timeout %s ", + nl_msec2str(timeout_ms, buf, sizeof(buf))); + } + + if (nfnl_exp_test_helper_name(exp)) + nl_dump(p, "helper %s ", exp->exp_helper_name); + + if (nfnl_exp_test_fn(exp)) + nl_dump(p, "fn %s ", exp->exp_fn); + + if (nfnl_exp_test_class(exp)) + nl_dump(p, "class %u ", nfnl_exp_get_class(exp)); + + if (nfnl_exp_test_zone(exp)) + nl_dump(p, "zone %u ", nfnl_exp_get_zone(exp)); + + if (nfnl_exp_test_flags(exp)) + nl_dump(p, "<"); +#define PRINT_FLAG(str) \ + { nl_dump(p, "%s%s", fp++ ? "," : "", (str)); } + + if (exp->exp_flags & NF_CT_EXPECT_PERMANENT) + PRINT_FLAG("PERMANENT"); + if (exp->exp_flags & NF_CT_EXPECT_INACTIVE) + PRINT_FLAG("INACTIVE"); + if (exp->exp_flags & NF_CT_EXPECT_USERSPACE) + PRINT_FLAG("USERSPACE"); +#undef PRINT_FLAG + + if (nfnl_exp_test_flags(exp)) + nl_dump(p, ">"); + + nl_dump(p, "\n"); +} + +static int exp_cmp_l4proto_ports (union nfnl_exp_protodata *a, union nfnl_exp_protodata *b) { + // Must return 0 for match, 1 for mismatch + int d = 0; + d = ( (a->port.src != b->port.src) || + (a->port.dst != b->port.dst) ); + + return d; +} + +static int exp_cmp_l4proto_icmp (union nfnl_exp_protodata *a, union nfnl_exp_protodata *b) { + // Must return 0 for match, 1 for mismatch + int d = 0; + d = ( (a->icmp.code != b->icmp.code) || + (a->icmp.type != b->icmp.type) || + (a->icmp.id != b->icmp.id) ); + + return d; +} + +static uint64_t exp_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + struct nfnl_exp *a = (struct nfnl_exp *) _a; + struct nfnl_exp *b = (struct nfnl_exp *) _b; + uint64_t diff = 0; + +#define EXP_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, EXP_ATTR_##ATTR, a, b, EXPR) +#define EXP_DIFF_VAL(ATTR, FIELD) EXP_DIFF(ATTR, a->FIELD != b->FIELD) +#define EXP_DIFF_STRING(ATTR, FIELD) EXP_DIFF(ATTR, (strcmp(a->FIELD, b->FIELD) != 0)) +#define EXP_DIFF_ADDR(ATTR, FIELD) \ + ((flags & LOOSE_COMPARISON) \ + ? EXP_DIFF(ATTR, nl_addr_cmp_prefix(a->FIELD, b->FIELD)) \ + : EXP_DIFF(ATTR, nl_addr_cmp(a->FIELD, b->FIELD))) +#define EXP_DIFF_L4PROTO_PORTS(ATTR, FIELD) \ + EXP_DIFF(ATTR, exp_cmp_l4proto_ports(&(a->FIELD), &(b->FIELD))) +#define EXP_DIFF_L4PROTO_ICMP(ATTR, FIELD) \ + EXP_DIFF(ATTR, exp_cmp_l4proto_icmp(&(a->FIELD), &(b->FIELD))) + + diff |= EXP_DIFF_VAL(FAMILY, exp_family); + diff |= EXP_DIFF_VAL(TIMEOUT, exp_timeout); + diff |= EXP_DIFF_VAL(ID, exp_id); + diff |= EXP_DIFF_VAL(ZONE, exp_zone); + diff |= EXP_DIFF_VAL(CLASS, exp_class); + diff |= EXP_DIFF_VAL(FLAGS, exp_flags); + diff |= EXP_DIFF_VAL(NAT_DIR, exp_nat_dir); + + diff |= EXP_DIFF_STRING(FN, exp_fn); + diff |= EXP_DIFF_STRING(HELPER_NAME, exp_helper_name); + + diff |= EXP_DIFF_ADDR(EXPECT_IP_SRC, exp_expect.src); + diff |= EXP_DIFF_ADDR(EXPECT_IP_DST, exp_expect.dst); + diff |= EXP_DIFF_VAL(EXPECT_L4PROTO_NUM, exp_expect.proto.l4protonum); + diff |= EXP_DIFF_L4PROTO_PORTS(EXPECT_L4PROTO_PORTS, exp_expect.proto.l4protodata); + diff |= EXP_DIFF_L4PROTO_ICMP(EXPECT_L4PROTO_ICMP, exp_expect.proto.l4protodata); + + diff |= EXP_DIFF_ADDR(MASTER_IP_SRC, exp_master.src); + diff |= EXP_DIFF_ADDR(MASTER_IP_DST, exp_master.dst); + diff |= EXP_DIFF_VAL(MASTER_L4PROTO_NUM, exp_master.proto.l4protonum); + diff |= EXP_DIFF_L4PROTO_PORTS(MASTER_L4PROTO_PORTS, exp_master.proto.l4protodata); + diff |= EXP_DIFF_L4PROTO_ICMP(MASTER_L4PROTO_ICMP, exp_master.proto.l4protodata); + + diff |= EXP_DIFF_ADDR(MASK_IP_SRC, exp_mask.src); + diff |= EXP_DIFF_ADDR(MASK_IP_DST, exp_mask.dst); + diff |= EXP_DIFF_VAL(MASK_L4PROTO_NUM, exp_mask.proto.l4protonum); + diff |= EXP_DIFF_L4PROTO_PORTS(MASK_L4PROTO_PORTS, exp_mask.proto.l4protodata); + diff |= EXP_DIFF_L4PROTO_ICMP(MASK_L4PROTO_ICMP, exp_mask.proto.l4protodata); + + diff |= EXP_DIFF_ADDR(NAT_IP_SRC, exp_nat.src); + diff |= EXP_DIFF_ADDR(NAT_IP_DST, exp_nat.dst); + diff |= EXP_DIFF_VAL(NAT_L4PROTO_NUM, exp_nat.proto.l4protonum); + diff |= EXP_DIFF_L4PROTO_PORTS(NAT_L4PROTO_PORTS, exp_nat.proto.l4protodata); + diff |= EXP_DIFF_L4PROTO_ICMP(NAT_L4PROTO_ICMP, exp_nat.proto.l4protodata); + +#undef EXP_DIFF +#undef EXP_DIFF_VAL +#undef EXP_DIFF_STRING +#undef EXP_DIFF_ADDR +#undef EXP_DIFF_L4PROTO_PORTS +#undef EXP_DIFF_L4PROTO_ICMP + + return diff; +} + +// CLI arguments? +static const struct trans_tbl exp_attrs[] = { + __ADD(EXP_ATTR_FAMILY, family), + __ADD(EXP_ATTR_TIMEOUT, timeout), + __ADD(EXP_ATTR_ID, id), + __ADD(EXP_ATTR_HELPER_NAME, helpername), + __ADD(EXP_ATTR_ZONE, zone), + __ADD(EXP_ATTR_CLASS, class), + __ADD(EXP_ATTR_FLAGS, flags), + __ADD(EXP_ATTR_FN, function), + __ADD(EXP_ATTR_EXPECT_IP_SRC, expectipsrc), + __ADD(EXP_ATTR_EXPECT_IP_DST, expectipdst), + __ADD(EXP_ATTR_EXPECT_L4PROTO_NUM, expectprotonum), + __ADD(EXP_ATTR_EXPECT_L4PROTO_PORTS, expectports), + __ADD(EXP_ATTR_EXPECT_L4PROTO_ICMP, expecticmp), + __ADD(EXP_ATTR_MASTER_IP_SRC, masteripsrc), + __ADD(EXP_ATTR_MASTER_IP_DST, masteripdst), + __ADD(EXP_ATTR_MASTER_L4PROTO_NUM, masterprotonum), + __ADD(EXP_ATTR_MASTER_L4PROTO_PORTS, masterports), + __ADD(EXP_ATTR_MASTER_L4PROTO_ICMP, mastericmp), + __ADD(EXP_ATTR_MASK_IP_SRC, maskipsrc), + __ADD(EXP_ATTR_MASK_IP_DST, maskipdst), + __ADD(EXP_ATTR_MASK_L4PROTO_NUM, maskprotonum), + __ADD(EXP_ATTR_MASK_L4PROTO_PORTS, maskports), + __ADD(EXP_ATTR_MASK_L4PROTO_ICMP, maskicmp), + __ADD(EXP_ATTR_NAT_IP_SRC, natipsrc), + __ADD(EXP_ATTR_NAT_IP_DST, natipdst), + __ADD(EXP_ATTR_NAT_L4PROTO_NUM, natprotonum), + __ADD(EXP_ATTR_NAT_L4PROTO_PORTS, natports), + __ADD(EXP_ATTR_NAT_L4PROTO_ICMP, naticmp), + __ADD(EXP_ATTR_NAT_DIR, natdir), +}; + +static char *exp_attrs2str(int attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, exp_attrs, ARRAY_SIZE(exp_attrs)); +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct nfnl_exp *nfnl_exp_alloc(void) +{ + return (struct nfnl_exp *) nl_object_alloc(&exp_obj_ops); +} + +void nfnl_exp_get(struct nfnl_exp *exp) +{ + nl_object_get((struct nl_object *) exp); +} + +void nfnl_exp_put(struct nfnl_exp *exp) +{ + nl_object_put((struct nl_object *) exp); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +void nfnl_exp_set_family(struct nfnl_exp *exp, uint8_t family) +{ + exp->exp_family = family; + exp->ce_mask |= EXP_ATTR_FAMILY; +} + +uint8_t nfnl_exp_get_family(const struct nfnl_exp *exp) +{ + if (exp->ce_mask & EXP_ATTR_FAMILY) + return exp->exp_family; + else + return AF_UNSPEC; +} + +void nfnl_exp_set_flags(struct nfnl_exp *exp, uint32_t flags) +{ + exp->exp_flags |= flags; + exp->ce_mask |= EXP_ATTR_FLAGS; +} + +int nfnl_exp_test_flags(const struct nfnl_exp *exp) +{ + return !!(exp->ce_mask & EXP_ATTR_FLAGS); +} + +void nfnl_exp_unset_flags(struct nfnl_exp *exp, uint32_t flags) +{ + exp->exp_flags &= ~flags; + exp->ce_mask |= EXP_ATTR_FLAGS; +} + +uint32_t nfnl_exp_get_flags(const struct nfnl_exp *exp) +{ + return exp->exp_flags; +} + +static const struct trans_tbl flag_table[] = { + __ADD(IPS_EXPECTED, expected), + __ADD(IPS_SEEN_REPLY, seen_reply), + __ADD(IPS_ASSURED, assured), +}; + +char * nfnl_exp_flags2str(int flags, char *buf, size_t len) +{ + return __flags2str(flags, buf, len, flag_table, + ARRAY_SIZE(flag_table)); +} + +int nfnl_exp_str2flags(const char *name) +{ + return __str2flags(name, flag_table, ARRAY_SIZE(flag_table)); +} + +void nfnl_exp_set_timeout(struct nfnl_exp *exp, uint32_t timeout) +{ + exp->exp_timeout = timeout; + exp->ce_mask |= EXP_ATTR_TIMEOUT; +} + +int nfnl_exp_test_timeout(const struct nfnl_exp *exp) +{ + return !!(exp->ce_mask & EXP_ATTR_TIMEOUT); +} + +uint32_t nfnl_exp_get_timeout(const struct nfnl_exp *exp) +{ + return exp->exp_timeout; +} + +void nfnl_exp_set_id(struct nfnl_exp *exp, uint32_t id) +{ + exp->exp_id = id; + exp->ce_mask |= EXP_ATTR_ID; +} + +int nfnl_exp_test_id(const struct nfnl_exp *exp) +{ + return !!(exp->ce_mask & EXP_ATTR_ID); +} + +uint32_t nfnl_exp_get_id(const struct nfnl_exp *exp) +{ + return exp->exp_id; +} + +int nfnl_exp_set_helper_name(struct nfnl_exp *exp, void *name) +{ + free(exp->exp_helper_name); + exp->exp_helper_name = strdup(name); + if (!exp->exp_helper_name) + return -NLE_NOMEM; + + exp->ce_mask |= EXP_ATTR_HELPER_NAME; + return 0; +} + +int nfnl_exp_test_helper_name(const struct nfnl_exp *exp) +{ + return !!(exp->ce_mask & EXP_ATTR_HELPER_NAME); +} + +const char * nfnl_exp_get_helper_name(const struct nfnl_exp *exp) +{ + return exp->exp_helper_name; +} + +void nfnl_exp_set_zone(struct nfnl_exp *exp, uint16_t zone) +{ + exp->exp_zone = zone; + exp->ce_mask |= EXP_ATTR_ZONE; +} + +int nfnl_exp_test_zone(const struct nfnl_exp *exp) +{ + return !!(exp->ce_mask & EXP_ATTR_ZONE); +} + +uint16_t nfnl_exp_get_zone(const struct nfnl_exp *exp) +{ + return exp->exp_zone; +} + +void nfnl_exp_set_class(struct nfnl_exp *exp, uint32_t class) +{ + exp->exp_class = class; + exp->ce_mask |= EXP_ATTR_CLASS; +} + +int nfnl_exp_test_class(const struct nfnl_exp *exp) +{ + return !!(exp->ce_mask & EXP_ATTR_CLASS); +} + +uint32_t nfnl_exp_get_class(const struct nfnl_exp *exp) +{ + return exp->exp_class; +} + +int nfnl_exp_set_fn(struct nfnl_exp *exp, void *fn) +{ + free(exp->exp_fn); + exp->exp_fn = strdup(fn); + if (!exp->exp_fn) + return -NLE_NOMEM; + + exp->ce_mask |= EXP_ATTR_FN; + return 0; +} + +int nfnl_exp_test_fn(const struct nfnl_exp *exp) +{ + return !!(exp->ce_mask & EXP_ATTR_FN); +} + +const char * nfnl_exp_get_fn(const struct nfnl_exp *exp) +{ + return exp->exp_fn; +} + +void nfnl_exp_set_nat_dir(struct nfnl_exp *exp, uint8_t nat_dir) +{ + exp->exp_nat_dir = nat_dir; + exp->ce_mask |= EXP_ATTR_NAT_DIR; +} + +int nfnl_exp_test_nat_dir(const struct nfnl_exp *exp) +{ + return !!(exp->ce_mask & EXP_ATTR_NAT_DIR); +} + +uint8_t nfnl_exp_get_nat_dir(const struct nfnl_exp *exp) +{ + return exp->exp_nat_dir; +} + +#define EXP_GET_TUPLE(e, t) \ + (t == NFNL_EXP_TUPLE_MASTER) ? \ + &(e->exp_master) : \ + (t == NFNL_EXP_TUPLE_MASK) ? \ + &(e->exp_mask) : \ + (t == NFNL_EXP_TUPLE_NAT) ? \ + &(e->exp_nat) : &(exp->exp_expect) + +static int exp_get_src_attr(int tuple) +{ + int attr = 0; + + switch (tuple) { + case NFNL_EXP_TUPLE_MASTER: + attr = EXP_ATTR_MASTER_IP_SRC; + break; + case NFNL_EXP_TUPLE_MASK: + attr = EXP_ATTR_MASK_IP_SRC; + break; + case NFNL_EXP_TUPLE_NAT: + attr = EXP_ATTR_NAT_IP_SRC; + break; + case NFNL_EXP_TUPLE_EXPECT: + default : + attr = EXP_ATTR_EXPECT_IP_SRC; + } + + return attr; +} + +static int exp_get_dst_attr(int tuple) +{ + int attr = 0; + + switch (tuple) { + case NFNL_EXP_TUPLE_MASTER: + attr = EXP_ATTR_MASTER_IP_DST; + break; + case NFNL_EXP_TUPLE_MASK: + attr = EXP_ATTR_MASK_IP_DST; + break; + case NFNL_EXP_TUPLE_NAT: + attr = EXP_ATTR_NAT_IP_DST; + break; + case NFNL_EXP_TUPLE_EXPECT: + default : + attr = EXP_ATTR_EXPECT_IP_DST; + } + + return attr; +} + + +static int exp_set_addr(struct nfnl_exp *exp, struct nl_addr *addr, + int attr, struct nl_addr ** exp_addr) +{ + if (exp->ce_mask & EXP_ATTR_FAMILY) { + if (addr->a_family != exp->exp_family) + return -NLE_AF_MISMATCH; + } else + nfnl_exp_set_family(exp, addr->a_family); + + if (*exp_addr) + nl_addr_put(*exp_addr); + + nl_addr_get(addr); + *exp_addr = addr; + exp->ce_mask |= attr; + + return 0; +} + +int nfnl_exp_set_src(struct nfnl_exp *exp, int tuple, struct nl_addr *addr) +{ + struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + + return exp_set_addr(exp, addr, exp_get_src_attr(tuple), &dir->src); +} + +int nfnl_exp_set_dst(struct nfnl_exp *exp, int tuple, struct nl_addr *addr) +{ + struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + + return exp_set_addr(exp, addr, exp_get_dst_attr(tuple), &dir->dst); +} + +int nfnl_exp_test_src(const struct nfnl_exp *exp, int tuple) +{ + return !!(exp->ce_mask & exp_get_src_attr(tuple)); +} + +int nfnl_exp_test_dst(const struct nfnl_exp *exp, int tuple) +{ + return !!(exp->ce_mask & exp_get_dst_attr(tuple)); +} + +struct nl_addr *nfnl_exp_get_src(const struct nfnl_exp *exp, int tuple) +{ + const struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + + if (!(exp->ce_mask & exp_get_src_attr(tuple))) + return NULL; + return dir->src; +} + +struct nl_addr *nfnl_exp_get_dst(const struct nfnl_exp *exp, int tuple) +{ + const struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + + if (!(exp->ce_mask & exp_get_dst_attr(tuple))) + return NULL; + return dir->dst; +} + +static int exp_get_l4protonum_attr(int tuple) +{ + int attr = 0; + + switch (tuple) { + case NFNL_EXP_TUPLE_MASTER: + attr = EXP_ATTR_MASTER_L4PROTO_NUM; + break; + case NFNL_EXP_TUPLE_MASK: + attr = EXP_ATTR_MASK_L4PROTO_NUM; + break; + case NFNL_EXP_TUPLE_NAT: + attr = EXP_ATTR_NAT_L4PROTO_NUM; + break; + case NFNL_EXP_TUPLE_EXPECT: + default : + attr = EXP_ATTR_EXPECT_L4PROTO_NUM; + } + + return attr; +} + +void nfnl_exp_set_l4protonum(struct nfnl_exp *exp, int tuple, uint8_t l4protonum) +{ + struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + + dir->proto.l4protonum = l4protonum; + exp->ce_mask |= exp_get_l4protonum_attr(tuple); +} + +int nfnl_exp_test_l4protonum(const struct nfnl_exp *exp, int tuple) +{ + return !!(exp->ce_mask & exp_get_l4protonum_attr(tuple)); +} + +uint8_t nfnl_exp_get_l4protonum(const struct nfnl_exp *exp, int tuple) +{ + const struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + return dir->proto.l4protonum; +} + +static int exp_get_l4ports_attr(int tuple) +{ + int attr = 0; + + switch (tuple) { + case NFNL_EXP_TUPLE_MASTER: + attr = EXP_ATTR_MASTER_L4PROTO_PORTS; + break; + case NFNL_EXP_TUPLE_MASK: + attr = EXP_ATTR_MASK_L4PROTO_PORTS; + break; + case NFNL_EXP_TUPLE_NAT: + attr = EXP_ATTR_NAT_L4PROTO_PORTS; + break; + case NFNL_EXP_TUPLE_EXPECT: + default : + attr = EXP_ATTR_EXPECT_L4PROTO_PORTS; + } + + return attr; +} + +void nfnl_exp_set_ports(struct nfnl_exp *exp, int tuple, uint16_t srcport, uint16_t dstport) +{ + struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + + dir->proto.l4protodata.port.src = srcport; + dir->proto.l4protodata.port.dst = dstport; + + exp->ce_mask |= exp_get_l4ports_attr(tuple); +} + +int nfnl_exp_test_ports(const struct nfnl_exp *exp, int tuple) +{ + return !!(exp->ce_mask & exp_get_l4ports_attr(tuple)); +} + +uint16_t nfnl_exp_get_src_port(const struct nfnl_exp *exp, int tuple) +{ + const struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + return dir->proto.l4protodata.port.src; +} + +uint16_t nfnl_exp_get_dst_port(const struct nfnl_exp *exp, int tuple) +{ + const struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + + return dir->proto.l4protodata.port.dst; +} + +static int exp_get_l4icmp_attr(int tuple) +{ + int attr = 0; + + switch (tuple) { + case NFNL_EXP_TUPLE_MASTER: + attr = EXP_ATTR_MASTER_L4PROTO_ICMP; + break; + case NFNL_EXP_TUPLE_MASK: + attr = EXP_ATTR_MASK_L4PROTO_ICMP; + break; + case NFNL_EXP_TUPLE_NAT: + attr = EXP_ATTR_NAT_L4PROTO_ICMP; + break; + case NFNL_EXP_TUPLE_EXPECT: + default : + attr = EXP_ATTR_EXPECT_L4PROTO_ICMP; + } + + return attr; +} + +void nfnl_exp_set_icmp(struct nfnl_exp *exp, int tuple, uint16_t id, uint8_t type, uint8_t code) +{ + struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + + dir->proto.l4protodata.icmp.id = id; + dir->proto.l4protodata.icmp.type = type; + dir->proto.l4protodata.icmp.code = code; + + exp->ce_mask |= exp_get_l4icmp_attr(tuple); +} + +int nfnl_exp_test_icmp(const struct nfnl_exp *exp, int tuple) +{ + int attr = exp_get_l4icmp_attr(tuple); + return !!(exp->ce_mask & attr); +} + +uint16_t nfnl_exp_get_icmp_id(const struct nfnl_exp *exp, int tuple) +{ + const struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + + return dir->proto.l4protodata.icmp.id; +} + +uint8_t nfnl_exp_get_icmp_type(const struct nfnl_exp *exp, int tuple) +{ + const struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + + return dir->proto.l4protodata.icmp.type; +} + +uint8_t nfnl_exp_get_icmp_code(const struct nfnl_exp *exp, int tuple) +{ + const struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple); + + return dir->proto.l4protodata.icmp.code; +} + +/** @} */ + +struct nl_object_ops exp_obj_ops = { + .oo_name = "netfilter/exp", + .oo_size = sizeof(struct nfnl_exp), + .oo_free_data = exp_free_data, + .oo_clone = exp_clone, + .oo_dump = { + [NL_DUMP_LINE] = exp_dump_line, + [NL_DUMP_DETAILS] = exp_dump_details, + }, + .oo_compare = exp_compare, + .oo_attrs2str = exp_attrs2str, +}; + +/** @} */ diff --git a/libnl/lib/netfilter/log.c b/libnl/lib/netfilter/log.c new file mode 100644 index 0000000..8cc147f --- /dev/null +++ b/libnl/lib/netfilter/log.c @@ -0,0 +1,247 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + */ + +/** + * @ingroup nfnl + * @defgroup log Log + * @brief + * @{ + */ + +#define _GNU_SOURCE + +#include +#include + +#include +#include +#include +#include + +/** + * @name Log Commands + * @{ + */ + +static int build_log_cmd_request(uint8_t family, uint16_t queuenum, + uint8_t command, struct nl_msg **result) +{ + struct nl_msg *msg; + struct nfulnl_msg_config_cmd cmd; + + msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_ULOG, NFULNL_MSG_CONFIG, 0, + family, queuenum); + if (msg == NULL) + return -NLE_NOMEM; + + cmd.command = command; + if (nla_put(msg, NFULA_CFG_CMD, sizeof(cmd), &cmd) < 0) + goto nla_put_failure; + + *result = msg; + return 0; + +nla_put_failure: + nlmsg_free(msg); + return -NLE_MSGSIZE; +} + +static int send_log_request(struct nl_sock *sk, struct nl_msg *msg) +{ + int err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +int nfnl_log_build_pf_bind(uint8_t pf, struct nl_msg **result) +{ + return build_log_cmd_request(pf, 0, NFULNL_CFG_CMD_PF_BIND, result); +} + +int nfnl_log_pf_bind(struct nl_sock *nlh, uint8_t pf) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_log_build_pf_bind(pf, &msg)) < 0) + return err; + + return send_log_request(nlh, msg); +} + +int nfnl_log_build_pf_unbind(uint8_t pf, struct nl_msg **result) +{ + return build_log_cmd_request(pf, 0, NFULNL_CFG_CMD_PF_UNBIND, result); +} + +int nfnl_log_pf_unbind(struct nl_sock *nlh, uint8_t pf) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_log_build_pf_unbind(pf, &msg)) < 0) + return err; + + return send_log_request(nlh, msg); +} + +static int nfnl_log_build_request(const struct nfnl_log *log, + struct nl_msg **result) +{ + struct nl_msg *msg; + + if (!nfnl_log_test_group(log)) + return -NLE_MISSING_ATTR; + + msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_ULOG, NFULNL_MSG_CONFIG, 0, + 0, nfnl_log_get_group(log)); + if (msg == NULL) + return -NLE_NOMEM; + + /* This sucks. The nfnetlink_log interface always expects both + * parameters to be present. Needs to be done properly. + */ + if (nfnl_log_test_copy_mode(log)) { + struct nfulnl_msg_config_mode mode; + + switch (nfnl_log_get_copy_mode(log)) { + case NFNL_LOG_COPY_NONE: + mode.copy_mode = NFULNL_COPY_NONE; + break; + case NFNL_LOG_COPY_META: + mode.copy_mode = NFULNL_COPY_META; + break; + case NFNL_LOG_COPY_PACKET: + mode.copy_mode = NFULNL_COPY_PACKET; + break; + } + mode.copy_range = htonl(nfnl_log_get_copy_range(log)); + mode._pad = 0; + + if (nla_put(msg, NFULA_CFG_MODE, sizeof(mode), &mode) < 0) + goto nla_put_failure; + } + + if (nfnl_log_test_flush_timeout(log) && + nla_put_u32(msg, NFULA_CFG_TIMEOUT, + htonl(nfnl_log_get_flush_timeout(log))) < 0) + goto nla_put_failure; + + if (nfnl_log_test_alloc_size(log) && + nla_put_u32(msg, NFULA_CFG_NLBUFSIZ, + htonl(nfnl_log_get_alloc_size(log))) < 0) + goto nla_put_failure; + + if (nfnl_log_test_queue_threshold(log) && + nla_put_u32(msg, NFULA_CFG_QTHRESH, + htonl(nfnl_log_get_queue_threshold(log))) < 0) + goto nla_put_failure; + + *result = msg; + return 0; + +nla_put_failure: + nlmsg_free(msg); + return -NLE_MSGSIZE; +} + +int nfnl_log_build_create_request(const struct nfnl_log *log, + struct nl_msg **result) +{ + struct nfulnl_msg_config_cmd cmd; + int err; + + if ((err = nfnl_log_build_request(log, result)) < 0) + return err; + + cmd.command = NFULNL_CFG_CMD_BIND; + + if (nla_put(*result, NFULA_CFG_CMD, sizeof(cmd), &cmd) < 0) + goto nla_put_failure; + + return 0; + +nla_put_failure: + nlmsg_free(*result); + return -NLE_MSGSIZE; +} + +int nfnl_log_create(struct nl_sock *nlh, const struct nfnl_log *log) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_log_build_create_request(log, &msg)) < 0) + return err; + + return send_log_request(nlh, msg); +} + +int nfnl_log_build_change_request(const struct nfnl_log *log, + struct nl_msg **result) +{ + return nfnl_log_build_request(log, result); +} + +int nfnl_log_change(struct nl_sock *nlh, const struct nfnl_log *log) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_log_build_change_request(log, &msg)) < 0) + return err; + + return send_log_request(nlh, msg); +} + +int nfnl_log_build_delete_request(const struct nfnl_log *log, + struct nl_msg **result) +{ + if (!nfnl_log_test_group(log)) + return -NLE_MISSING_ATTR; + + return build_log_cmd_request(0, nfnl_log_get_group(log), + NFULNL_CFG_CMD_UNBIND, result); +} + +int nfnl_log_delete(struct nl_sock *nlh, const struct nfnl_log *log) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_log_build_delete_request(log, &msg)) < 0) + return err; + + return send_log_request(nlh, msg); +} + +/** @} */ + +static struct nl_cache_ops nfnl_log_ops = { + .co_name = "netfilter/log", + .co_obj_ops = &log_obj_ops, + .co_msgtypes = { + END_OF_MSGTYPES_LIST, + }, +}; + +static void __init log_init(void) +{ + nl_cache_mngt_register(&nfnl_log_ops); +} + +static void __exit log_exit(void) +{ + nl_cache_mngt_unregister(&nfnl_log_ops); +} + +/** @} */ diff --git a/libnl/lib/netfilter/log_msg.c b/libnl/lib/netfilter/log_msg.c new file mode 100644 index 0000000..861dc5c --- /dev/null +++ b/libnl/lib/netfilter/log_msg.c @@ -0,0 +1,193 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + * Copyright (c) 2008 Patrick McHardy + */ + +/** + * @ingroup nfnl + * @defgroup log Log + * @brief + * @{ + */ + +#define _GNU_SOURCE + +#include +#include + +#include +#include +#include +#include +#include + +static struct nla_policy log_msg_policy[NFULA_MAX+1] = { + [NFULA_PACKET_HDR] = { + .minlen = sizeof(struct nfulnl_msg_packet_hdr) + }, + [NFULA_MARK] = { .type = NLA_U32 }, + [NFULA_TIMESTAMP] = { + .minlen = sizeof(struct nfulnl_msg_packet_timestamp) + }, + [NFULA_IFINDEX_INDEV] = { .type = NLA_U32 }, + [NFULA_IFINDEX_OUTDEV] = { .type = NLA_U32 }, + [NFULA_IFINDEX_PHYSINDEV] = { .type = NLA_U32 }, + [NFULA_IFINDEX_PHYSOUTDEV] = { .type = NLA_U32 }, + [NFULA_HWADDR] = { + .minlen = sizeof(struct nfulnl_msg_packet_hw) + }, + //[NFULA_PAYLOAD] + [NFULA_PREFIX] = { .type = NLA_STRING, }, + [NFULA_UID] = { .type = NLA_U32 }, + [NFULA_GID] = { .type = NLA_U32 }, + [NFULA_SEQ] = { .type = NLA_U32 }, + [NFULA_SEQ_GLOBAL] = { .type = NLA_U32 }, +}; + +int nfnlmsg_log_msg_parse(struct nlmsghdr *nlh, struct nfnl_log_msg **result) +{ + struct nfnl_log_msg *msg; + struct nlattr *tb[NFULA_MAX+1]; + struct nlattr *attr; + int err; + + msg = nfnl_log_msg_alloc(); + if (!msg) + return -NLE_NOMEM; + + msg->ce_msgtype = nlh->nlmsg_type; + + err = nlmsg_parse(nlh, sizeof(struct nfgenmsg), tb, NFULA_MAX, + log_msg_policy); + if (err < 0) + goto errout; + + nfnl_log_msg_set_family(msg, nfnlmsg_family(nlh)); + + attr = tb[NFULA_PACKET_HDR]; + if (attr) { + struct nfulnl_msg_packet_hdr *hdr = nla_data(attr); + + if (hdr->hw_protocol) + nfnl_log_msg_set_hwproto(msg, hdr->hw_protocol); + nfnl_log_msg_set_hook(msg, hdr->hook); + } + + attr = tb[NFULA_MARK]; + if (attr) + nfnl_log_msg_set_mark(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFULA_TIMESTAMP]; + if (attr) { + struct nfulnl_msg_packet_timestamp *timestamp = nla_data(attr); + struct timeval tv; + + tv.tv_sec = ntohll(timestamp->sec); + tv.tv_usec = ntohll(timestamp->usec); + nfnl_log_msg_set_timestamp(msg, &tv); + } + + attr = tb[NFULA_IFINDEX_INDEV]; + if (attr) + nfnl_log_msg_set_indev(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFULA_IFINDEX_OUTDEV]; + if (attr) + nfnl_log_msg_set_outdev(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFULA_IFINDEX_PHYSINDEV]; + if (attr) + nfnl_log_msg_set_physindev(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFULA_IFINDEX_PHYSOUTDEV]; + if (attr) + nfnl_log_msg_set_physoutdev(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFULA_HWADDR]; + if (attr) { + struct nfulnl_msg_packet_hw *hw = nla_data(attr); + + nfnl_log_msg_set_hwaddr(msg, hw->hw_addr, ntohs(hw->hw_addrlen)); + } + + attr = tb[NFULA_PAYLOAD]; + if (attr) { + err = nfnl_log_msg_set_payload(msg, nla_data(attr), nla_len(attr)); + if (err < 0) + goto errout; + } + + attr = tb[NFULA_PREFIX]; + if (attr) { + err = nfnl_log_msg_set_prefix(msg, nla_data(attr)); + if (err < 0) + goto errout; + } + + attr = tb[NFULA_UID]; + if (attr) + nfnl_log_msg_set_uid(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFULA_GID]; + if (attr) + nfnl_log_msg_set_gid(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFULA_SEQ]; + if (attr) + nfnl_log_msg_set_seq(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFULA_SEQ_GLOBAL]; + if (attr) + nfnl_log_msg_set_seq_global(msg, ntohl(nla_get_u32(attr))); + + *result = msg; + return 0; + +errout: + nfnl_log_msg_put(msg); + return err; +} + +static int log_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, struct nl_parser_param *pp) +{ + struct nfnl_log_msg *msg; + int err; + + if ((err = nfnlmsg_log_msg_parse(nlh, &msg)) < 0) + return err; + + err = pp->pp_cb((struct nl_object *) msg, pp); + nfnl_log_msg_put(msg); + return err; +} + +/** @} */ + +#define NFNLMSG_LOG_TYPE(type) NFNLMSG_TYPE(NFNL_SUBSYS_ULOG, (type)) +static struct nl_cache_ops nfnl_log_msg_ops = { + .co_name = "netfilter/log_msg", + .co_hdrsize = NFNL_HDRLEN, + .co_msgtypes = { + { NFNLMSG_LOG_TYPE(NFULNL_MSG_PACKET), NL_ACT_NEW, "new" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_NETFILTER, + .co_msg_parser = log_msg_parser, + .co_obj_ops = &log_msg_obj_ops, +}; + +static void __init log_msg_init(void) +{ + nl_cache_mngt_register(&nfnl_log_msg_ops); +} + +static void __exit log_msg_exit(void) +{ + nl_cache_mngt_unregister(&nfnl_log_msg_ops); +} + +/** @} */ diff --git a/libnl/lib/netfilter/log_msg_obj.c b/libnl/lib/netfilter/log_msg_obj.c new file mode 100644 index 0000000..19d65cc --- /dev/null +++ b/libnl/lib/netfilter/log_msg_obj.c @@ -0,0 +1,457 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include + +/** @cond SKIP */ +#define LOG_MSG_ATTR_FAMILY (1UL << 0) +#define LOG_MSG_ATTR_HWPROTO (1UL << 1) +#define LOG_MSG_ATTR_HOOK (1UL << 2) +#define LOG_MSG_ATTR_MARK (1UL << 3) +#define LOG_MSG_ATTR_TIMESTAMP (1UL << 4) +#define LOG_MSG_ATTR_INDEV (1UL << 5) +#define LOG_MSG_ATTR_OUTDEV (1UL << 6) +#define LOG_MSG_ATTR_PHYSINDEV (1UL << 7) +#define LOG_MSG_ATTR_PHYSOUTDEV (1UL << 8) +#define LOG_MSG_ATTR_HWADDR (1UL << 9) +#define LOG_MSG_ATTR_PAYLOAD (1UL << 10) +#define LOG_MSG_ATTR_PREFIX (1UL << 11) +#define LOG_MSG_ATTR_UID (1UL << 12) +#define LOG_MSG_ATTR_GID (1UL << 13) +#define LOG_MSG_ATTR_SEQ (1UL << 14) +#define LOG_MSG_ATTR_SEQ_GLOBAL (1UL << 15) +/** @endcond */ + +static void log_msg_free_data(struct nl_object *c) +{ + struct nfnl_log_msg *msg = (struct nfnl_log_msg *) c; + + if (msg == NULL) + return; + + free(msg->log_msg_payload); + free(msg->log_msg_prefix); +} + +static int log_msg_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct nfnl_log_msg *dst = (struct nfnl_log_msg *) _dst; + struct nfnl_log_msg *src = (struct nfnl_log_msg *) _src; + int err; + + if (src->log_msg_payload) { + err = nfnl_log_msg_set_payload(dst, src->log_msg_payload, + src->log_msg_payload_len); + if (err < 0) + goto errout; + } + + if (src->log_msg_prefix) { + err = nfnl_log_msg_set_prefix(dst, src->log_msg_prefix); + if (err < 0) + goto errout; + } + + return 0; +errout: + return err; +} + +static void log_msg_dump(struct nl_object *a, struct nl_dump_params *p) +{ + struct nfnl_log_msg *msg = (struct nfnl_log_msg *) a; + struct nl_cache *link_cache; + char buf[64]; + + link_cache = nl_cache_mngt_require_safe("route/link"); + + nl_new_line(p); + + if (msg->ce_mask & LOG_MSG_ATTR_PREFIX) + nl_dump(p, "%s", msg->log_msg_prefix); + + if (msg->ce_mask & LOG_MSG_ATTR_INDEV) { + if (link_cache) + nl_dump(p, "IN=%s ", + rtnl_link_i2name(link_cache, + msg->log_msg_indev, + buf, sizeof(buf))); + else + nl_dump(p, "IN=%d ", msg->log_msg_indev); + } + + if (msg->ce_mask & LOG_MSG_ATTR_PHYSINDEV) { + if (link_cache) + nl_dump(p, "PHYSIN=%s ", + rtnl_link_i2name(link_cache, + msg->log_msg_physindev, + buf, sizeof(buf))); + else + nl_dump(p, "IN=%d ", msg->log_msg_physindev); + } + + if (msg->ce_mask & LOG_MSG_ATTR_OUTDEV) { + if (link_cache) + nl_dump(p, "OUT=%s ", + rtnl_link_i2name(link_cache, + msg->log_msg_outdev, + buf, sizeof(buf))); + else + nl_dump(p, "OUT=%d ", msg->log_msg_outdev); + } + + if (msg->ce_mask & LOG_MSG_ATTR_PHYSOUTDEV) { + if (link_cache) + nl_dump(p, "PHYSOUT=%s ", + rtnl_link_i2name(link_cache, + msg->log_msg_physoutdev, + buf, sizeof(buf))); + else + nl_dump(p, "PHYSOUT=%d ", msg->log_msg_physoutdev); + } + + if (msg->ce_mask & LOG_MSG_ATTR_HWADDR) { + int i; + + nl_dump(p, "MAC"); + for (i = 0; i < msg->log_msg_hwaddr_len; i++) + nl_dump(p, "%c%02x", i?':':'=', msg->log_msg_hwaddr[i]); + nl_dump(p, " "); + } + + /* FIXME: parse the payload to get iptables LOG compatible format */ + + if (msg->ce_mask & LOG_MSG_ATTR_FAMILY) + nl_dump(p, "FAMILY=%s ", + nl_af2str(msg->log_msg_family, buf, sizeof(buf))); + + if (msg->ce_mask & LOG_MSG_ATTR_HWPROTO) + nl_dump(p, "HWPROTO=%s ", + nl_ether_proto2str(ntohs(msg->log_msg_hwproto), + buf, sizeof(buf))); + + if (msg->ce_mask & LOG_MSG_ATTR_HOOK) + nl_dump(p, "HOOK=%s ", + nfnl_inet_hook2str(msg->log_msg_hook, + buf, sizeof(buf))); + + if (msg->ce_mask & LOG_MSG_ATTR_MARK) + nl_dump(p, "MARK=%u ", msg->log_msg_mark); + + if (msg->ce_mask & LOG_MSG_ATTR_PAYLOAD) + nl_dump(p, "PAYLOADLEN=%d ", msg->log_msg_payload_len); + + if (msg->ce_mask & LOG_MSG_ATTR_UID) + nl_dump(p, "UID=%u ", msg->log_msg_uid); + + if (msg->ce_mask & LOG_MSG_ATTR_GID) + nl_dump(p, "GID=%u ", msg->log_msg_gid); + + if (msg->ce_mask & LOG_MSG_ATTR_SEQ) + nl_dump(p, "SEQ=%d ", msg->log_msg_seq); + + if (msg->ce_mask & LOG_MSG_ATTR_SEQ_GLOBAL) + nl_dump(p, "SEQGLOBAL=%d ", msg->log_msg_seq_global); + + nl_dump(p, "\n"); + + if (link_cache) + nl_cache_put(link_cache); +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct nfnl_log_msg *nfnl_log_msg_alloc(void) +{ + return (struct nfnl_log_msg *) nl_object_alloc(&log_msg_obj_ops); +} + +void nfnl_log_msg_get(struct nfnl_log_msg *msg) +{ + nl_object_get((struct nl_object *) msg); +} + +void nfnl_log_msg_put(struct nfnl_log_msg *msg) +{ + nl_object_put((struct nl_object *) msg); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +void nfnl_log_msg_set_family(struct nfnl_log_msg *msg, uint8_t family) +{ + msg->log_msg_family = family; + msg->ce_mask |= LOG_MSG_ATTR_FAMILY; +} + +uint8_t nfnl_log_msg_get_family(const struct nfnl_log_msg *msg) +{ + if (msg->ce_mask & LOG_MSG_ATTR_FAMILY) + return msg->log_msg_family; + else + return AF_UNSPEC; +} + +void nfnl_log_msg_set_hwproto(struct nfnl_log_msg *msg, uint16_t hwproto) +{ + msg->log_msg_hwproto = hwproto; + msg->ce_mask |= LOG_MSG_ATTR_HWPROTO; +} + +int nfnl_log_msg_test_hwproto(const struct nfnl_log_msg *msg) +{ + return !!(msg->ce_mask & LOG_MSG_ATTR_HWPROTO); +} + +uint16_t nfnl_log_msg_get_hwproto(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_hwproto; +} + +void nfnl_log_msg_set_hook(struct nfnl_log_msg *msg, uint8_t hook) +{ + msg->log_msg_hook = hook; + msg->ce_mask |= LOG_MSG_ATTR_HOOK; +} + +int nfnl_log_msg_test_hook(const struct nfnl_log_msg *msg) +{ + return !!(msg->ce_mask & LOG_MSG_ATTR_HOOK); +} + +uint8_t nfnl_log_msg_get_hook(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_hook; +} + +void nfnl_log_msg_set_mark(struct nfnl_log_msg *msg, uint32_t mark) +{ + msg->log_msg_mark = mark; + msg->ce_mask |= LOG_MSG_ATTR_MARK; +} + +int nfnl_log_msg_test_mark(const struct nfnl_log_msg *msg) +{ + return !!(msg->ce_mask & LOG_MSG_ATTR_MARK); +} + +uint32_t nfnl_log_msg_get_mark(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_mark; +} + +void nfnl_log_msg_set_timestamp(struct nfnl_log_msg *msg, struct timeval *tv) +{ + msg->log_msg_timestamp.tv_sec = tv->tv_sec; + msg->log_msg_timestamp.tv_usec = tv->tv_usec; + msg->ce_mask |= LOG_MSG_ATTR_TIMESTAMP; +} + +const struct timeval *nfnl_log_msg_get_timestamp(const struct nfnl_log_msg *msg) +{ + if (!(msg->ce_mask & LOG_MSG_ATTR_TIMESTAMP)) + return NULL; + return &msg->log_msg_timestamp; +} + +void nfnl_log_msg_set_indev(struct nfnl_log_msg *msg, uint32_t indev) +{ + msg->log_msg_indev = indev; + msg->ce_mask |= LOG_MSG_ATTR_INDEV; +} + +uint32_t nfnl_log_msg_get_indev(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_indev; +} + +void nfnl_log_msg_set_outdev(struct nfnl_log_msg *msg, uint32_t outdev) +{ + msg->log_msg_outdev = outdev; + msg->ce_mask |= LOG_MSG_ATTR_OUTDEV; +} + +uint32_t nfnl_log_msg_get_outdev(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_outdev; +} + +void nfnl_log_msg_set_physindev(struct nfnl_log_msg *msg, uint32_t physindev) +{ + msg->log_msg_physindev = physindev; + msg->ce_mask |= LOG_MSG_ATTR_PHYSINDEV; +} + +uint32_t nfnl_log_msg_get_physindev(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_physindev; +} + +void nfnl_log_msg_set_physoutdev(struct nfnl_log_msg *msg, uint32_t physoutdev) +{ + msg->log_msg_physoutdev = physoutdev; + msg->ce_mask |= LOG_MSG_ATTR_PHYSOUTDEV; +} + +uint32_t nfnl_log_msg_get_physoutdev(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_physoutdev; +} + +void nfnl_log_msg_set_hwaddr(struct nfnl_log_msg *msg, uint8_t *hwaddr, int len) +{ + if (len > sizeof(msg->log_msg_hwaddr)) + len = sizeof(msg->log_msg_hwaddr); + msg->log_msg_hwaddr_len = len; + memcpy(msg->log_msg_hwaddr, hwaddr, len); + msg->ce_mask |= LOG_MSG_ATTR_HWADDR; +} + +const uint8_t *nfnl_log_msg_get_hwaddr(const struct nfnl_log_msg *msg, int *len) +{ + if (!(msg->ce_mask & LOG_MSG_ATTR_HWADDR)) { + *len = 0; + return NULL; + } + + *len = msg->log_msg_hwaddr_len; + return msg->log_msg_hwaddr; +} + +int nfnl_log_msg_set_payload(struct nfnl_log_msg *msg, uint8_t *payload, int len) +{ + free(msg->log_msg_payload); + msg->log_msg_payload = malloc(len); + if (!msg->log_msg_payload) + return -NLE_NOMEM; + + memcpy(msg->log_msg_payload, payload, len); + msg->log_msg_payload_len = len; + msg->ce_mask |= LOG_MSG_ATTR_PAYLOAD; + return 0; +} + +const void *nfnl_log_msg_get_payload(const struct nfnl_log_msg *msg, int *len) +{ + if (!(msg->ce_mask & LOG_MSG_ATTR_PAYLOAD)) { + *len = 0; + return NULL; + } + + *len = msg->log_msg_payload_len; + return msg->log_msg_payload; +} + +int nfnl_log_msg_set_prefix(struct nfnl_log_msg *msg, void *prefix) +{ + free(msg->log_msg_prefix); + msg->log_msg_prefix = strdup(prefix); + if (!msg->log_msg_prefix) + return -NLE_NOMEM; + + msg->ce_mask |= LOG_MSG_ATTR_PREFIX; + return 0; +} + +const char *nfnl_log_msg_get_prefix(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_prefix; +} + +void nfnl_log_msg_set_uid(struct nfnl_log_msg *msg, uint32_t uid) +{ + msg->log_msg_uid = uid; + msg->ce_mask |= LOG_MSG_ATTR_UID; +} + +int nfnl_log_msg_test_uid(const struct nfnl_log_msg *msg) +{ + return !!(msg->ce_mask & LOG_MSG_ATTR_UID); +} + +uint32_t nfnl_log_msg_get_uid(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_uid; +} + +void nfnl_log_msg_set_gid(struct nfnl_log_msg *msg, uint32_t gid) +{ + msg->log_msg_gid = gid; + msg->ce_mask |= LOG_MSG_ATTR_GID; +} + +int nfnl_log_msg_test_gid(const struct nfnl_log_msg *msg) +{ + return !!(msg->ce_mask & LOG_MSG_ATTR_GID); +} + +uint32_t nfnl_log_msg_get_gid(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_gid; +} + + +void nfnl_log_msg_set_seq(struct nfnl_log_msg *msg, uint32_t seq) +{ + msg->log_msg_seq = seq; + msg->ce_mask |= LOG_MSG_ATTR_SEQ; +} + +int nfnl_log_msg_test_seq(const struct nfnl_log_msg *msg) +{ + return !!(msg->ce_mask & LOG_MSG_ATTR_SEQ); +} + +uint32_t nfnl_log_msg_get_seq(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_seq; +} + +void nfnl_log_msg_set_seq_global(struct nfnl_log_msg *msg, uint32_t seq_global) +{ + msg->log_msg_seq_global = seq_global; + msg->ce_mask |= LOG_MSG_ATTR_SEQ_GLOBAL; +} + +int nfnl_log_msg_test_seq_global(const struct nfnl_log_msg *msg) +{ + return !!(msg->ce_mask & LOG_MSG_ATTR_SEQ_GLOBAL); +} + +uint32_t nfnl_log_msg_get_seq_global(const struct nfnl_log_msg *msg) +{ + return msg->log_msg_seq_global; +} + +/** @} */ + +struct nl_object_ops log_msg_obj_ops = { + .oo_name = "netfilter/log_msg", + .oo_size = sizeof(struct nfnl_log_msg), + .oo_free_data = log_msg_free_data, + .oo_clone = log_msg_clone, + .oo_dump = { + [NL_DUMP_LINE] = log_msg_dump, + [NL_DUMP_DETAILS] = log_msg_dump, + [NL_DUMP_STATS] = log_msg_dump, + }, +}; + +/** @} */ diff --git a/libnl/lib/netfilter/log_obj.c b/libnl/lib/netfilter/log_obj.c new file mode 100644 index 0000000..d1f892c --- /dev/null +++ b/libnl/lib/netfilter/log_obj.c @@ -0,0 +1,283 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + * Copyright (c) 2008 Patrick McHardy + */ + +#define _GNU_SOURCE + +#include +#include +#include + +/** @cond SKIP */ +#define LOG_ATTR_GROUP (1UL << 0) +#define LOG_ATTR_COPY_MODE (1UL << 1) +#define LOG_ATTR_COPY_RANGE (1UL << 3) +#define LOG_ATTR_FLUSH_TIMEOUT (1UL << 4) +#define LOG_ATTR_ALLOC_SIZE (1UL << 5) +#define LOG_ATTR_QUEUE_THRESHOLD (1UL << 6) + +/** @endcond */ + +static void nfnl_log_dump(struct nl_object *a, struct nl_dump_params *p) +{ + struct nfnl_log *log = (struct nfnl_log *) a; + char buf[64]; + + nl_new_line(p); + + if (log->ce_mask & LOG_ATTR_GROUP) + nl_dump(p, "group=%u ", log->log_group); + + if (log->ce_mask & LOG_ATTR_COPY_MODE) + nl_dump(p, "copy_mode=%s ", + nfnl_log_copy_mode2str(log->log_copy_mode, + buf, sizeof(buf))); + + if (log->ce_mask & LOG_ATTR_COPY_RANGE) + nl_dump(p, "copy_range=%u ", log->log_copy_range); + + if (log->ce_mask & LOG_ATTR_FLUSH_TIMEOUT) + nl_dump(p, "flush_timeout=%u ", log->log_flush_timeout); + + if (log->ce_mask & LOG_ATTR_ALLOC_SIZE) + nl_dump(p, "alloc_size=%u ", log->log_alloc_size); + + if (log->ce_mask & LOG_ATTR_QUEUE_THRESHOLD) + nl_dump(p, "queue_threshold=%u ", log->log_queue_threshold); + + nl_dump(p, "\n"); +} + +static const struct trans_tbl copy_modes[] = { + __ADD(NFNL_LOG_COPY_NONE, none), + __ADD(NFNL_LOG_COPY_META, meta), + __ADD(NFNL_LOG_COPY_PACKET, packet), +}; + +char *nfnl_log_copy_mode2str(enum nfnl_log_copy_mode copy_mode, char *buf, + size_t len) +{ + return __type2str(copy_mode, buf, len, copy_modes, + ARRAY_SIZE(copy_modes)); +} + +int nfnl_log_str2copy_mode(const char *name) +{ + return __str2type(name, copy_modes, ARRAY_SIZE(copy_modes)); +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct nfnl_log *nfnl_log_alloc(void) +{ + return (struct nfnl_log *) nl_object_alloc(&log_obj_ops); +} + +void nfnl_log_get(struct nfnl_log *log) +{ + nl_object_get((struct nl_object *) log); +} + +void nfnl_log_put(struct nfnl_log *log) +{ + nl_object_put((struct nl_object *) log); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +void nfnl_log_set_group(struct nfnl_log *log, uint16_t group) +{ + log->log_group = group; + log->ce_mask |= LOG_ATTR_GROUP; +} + +int nfnl_log_test_group(const struct nfnl_log *log) +{ + return !!(log->ce_mask & LOG_ATTR_GROUP); +} + +uint16_t nfnl_log_get_group(const struct nfnl_log *log) +{ + return log->log_group; +} + +void nfnl_log_set_copy_mode(struct nfnl_log *log, enum nfnl_log_copy_mode mode) +{ + log->log_copy_mode = mode; + log->ce_mask |= LOG_ATTR_COPY_MODE; +} + +int nfnl_log_test_copy_mode(const struct nfnl_log *log) +{ + return !!(log->ce_mask & LOG_ATTR_COPY_MODE); +} + +enum nfnl_log_copy_mode nfnl_log_get_copy_mode(const struct nfnl_log *log) +{ + return log->log_copy_mode; +} + +void nfnl_log_set_copy_range(struct nfnl_log *log, uint32_t copy_range) +{ + log->log_copy_range = copy_range; + log->ce_mask |= LOG_ATTR_COPY_RANGE; +} + +int nfnl_log_test_copy_range(const struct nfnl_log *log) +{ + return !!(log->ce_mask & LOG_ATTR_COPY_RANGE); +} + +uint32_t nfnl_log_get_copy_range(const struct nfnl_log *log) +{ + return log->log_copy_range; +} + +void nfnl_log_set_flush_timeout(struct nfnl_log *log, uint32_t timeout) +{ + log->log_flush_timeout = timeout; + log->ce_mask |= LOG_ATTR_FLUSH_TIMEOUT; +} + +int nfnl_log_test_flush_timeout(const struct nfnl_log *log) +{ + return !!(log->ce_mask & LOG_ATTR_FLUSH_TIMEOUT); +} + +uint32_t nfnl_log_get_flush_timeout(const struct nfnl_log *log) +{ + return log->log_flush_timeout; +} + +void nfnl_log_set_alloc_size(struct nfnl_log *log, uint32_t alloc_size) +{ + log->log_alloc_size = alloc_size; + log->ce_mask |= LOG_ATTR_ALLOC_SIZE; +} + +int nfnl_log_test_alloc_size(const struct nfnl_log *log) +{ + return !!(log->ce_mask & LOG_ATTR_ALLOC_SIZE); +} + +uint32_t nfnl_log_get_alloc_size(const struct nfnl_log *log) +{ + return log->log_alloc_size; +} + +void nfnl_log_set_queue_threshold(struct nfnl_log *log, uint32_t threshold) +{ + log->log_queue_threshold = threshold; + log->ce_mask |= LOG_ATTR_QUEUE_THRESHOLD; +} + +int nfnl_log_test_queue_threshold(const struct nfnl_log *log) +{ + return !!(log->ce_mask & LOG_ATTR_QUEUE_THRESHOLD); +} + +uint32_t nfnl_log_get_queue_threshold(const struct nfnl_log *log) +{ + return log->log_queue_threshold; +} + +/* We don't actually use the flags for anything yet since the + * nfnetlog_log interface truly sucks - it only contains the + * flag value, but not mask, so we would have to make assumptions + * about the supported flags. + */ +void nfnl_log_set_flags(struct nfnl_log *log, unsigned int flags) +{ + log->log_flags |= flags; + log->log_flag_mask |= flags; +} + +void nfnl_log_unset_flags(struct nfnl_log *log, unsigned int flags) +{ + log->log_flags &= ~flags; + log->log_flag_mask |= flags; +} + +static const struct trans_tbl log_flags[] = { + __ADD(NFNL_LOG_FLAG_SEQ, seq), + __ADD(NFNL_LOG_FLAG_SEQ_GLOBAL, seq_global), +}; + +char *nfnl_log_flags2str(unsigned int flags, char *buf, size_t len) +{ + return __flags2str(flags, buf, len, log_flags, ARRAY_SIZE(log_flags)); +} + +unsigned int nfnl_log_str2flags(const char *name) +{ + return __str2flags(name, log_flags, ARRAY_SIZE(log_flags)); +} + +static uint64_t nfnl_log_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + struct nfnl_log *a = (struct nfnl_log *) _a; + struct nfnl_log *b = (struct nfnl_log *) _b; + uint64_t diff = 0; + +#define NFNL_LOG_DIFF(ATTR, EXPR) \ + ATTR_DIFF(attrs, LOG_ATTR_##ATTR, a, b, EXPR) +#define NFNL_LOG_DIFF_VAL(ATTR, FIELD) \ + NFNL_LOG_DIFF(ATTR, a->FIELD != b->FIELD) + + diff |= NFNL_LOG_DIFF_VAL(GROUP, log_group); + diff |= NFNL_LOG_DIFF_VAL(COPY_MODE, log_copy_mode); + diff |= NFNL_LOG_DIFF_VAL(COPY_RANGE, log_copy_range); + diff |= NFNL_LOG_DIFF_VAL(FLUSH_TIMEOUT, log_flush_timeout); + diff |= NFNL_LOG_DIFF_VAL(ALLOC_SIZE, log_alloc_size); + diff |= NFNL_LOG_DIFF_VAL(QUEUE_THRESHOLD, log_queue_threshold); + +#undef NFNL_LOG_DIFF +#undef NFNL_LOG_DIFF_VAL + + return diff; +} + +static const struct trans_tbl nfnl_log_attrs[] = { + __ADD(LOG_ATTR_GROUP, group), + __ADD(LOG_ATTR_COPY_MODE, copy_mode), + __ADD(LOG_ATTR_COPY_RANGE, copy_range), + __ADD(LOG_ATTR_FLUSH_TIMEOUT, flush_timeout), + __ADD(LOG_ATTR_ALLOC_SIZE, alloc_size), + __ADD(LOG_ATTR_QUEUE_THRESHOLD, queue_threshold), +}; + +static char *nfnl_log_attrs2str(int attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, nfnl_log_attrs, + ARRAY_SIZE(nfnl_log_attrs)); +} + +/** @} */ + +struct nl_object_ops log_obj_ops = { + .oo_name = "netfilter/log", + .oo_size = sizeof(struct nfnl_log), + .oo_dump = { + [NL_DUMP_LINE] = nfnl_log_dump, + [NL_DUMP_DETAILS] = nfnl_log_dump, + [NL_DUMP_STATS] = nfnl_log_dump, + }, + .oo_compare = nfnl_log_compare, + .oo_attrs2str = nfnl_log_attrs2str, + .oo_id_attrs = LOG_ATTR_GROUP, +}; + +/** @} */ diff --git a/libnl/lib/netfilter/netfilter.c b/libnl/lib/netfilter/netfilter.c new file mode 100644 index 0000000..6827e96 --- /dev/null +++ b/libnl/lib/netfilter/netfilter.c @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008 Patrick McHardy + */ + +#define _GNU_SOURCE + +#include +#include +#include + +static const struct trans_tbl nfnl_verdicts[] = { + __ADD(NF_DROP, NF_DROP), + __ADD(NF_ACCEPT, NF_ACCEPT), + __ADD(NF_STOLEN, NF_STOLEN), + __ADD(NF_QUEUE, NF_QUEUE), + __ADD(NF_REPEAT, NF_REPEAT), + __ADD(NF_STOP, NF_STOP), +}; + +char *nfnl_verdict2str(unsigned int verdict, char *buf, size_t len) +{ + return __type2str(verdict, buf, len, nfnl_verdicts, + ARRAY_SIZE(nfnl_verdicts)); +} + +unsigned int nfnl_str2verdict(const char *name) +{ + return __str2type(name, nfnl_verdicts, ARRAY_SIZE(nfnl_verdicts)); +} + +static const struct trans_tbl nfnl_inet_hooks[] = { + __ADD(NF_INET_PRE_ROUTING, NF_INET_PREROUTING), + __ADD(NF_INET_LOCAL_IN, NF_INET_LOCAL_IN), + __ADD(NF_INET_FORWARD, NF_INET_FORWARD), + __ADD(NF_INET_LOCAL_OUT, NF_INET_LOCAL_OUT), + __ADD(NF_INET_POST_ROUTING, NF_INET_POST_ROUTING), +}; + +char *nfnl_inet_hook2str(unsigned int hook, char *buf, size_t len) +{ + return __type2str(hook, buf, len, nfnl_inet_hooks, + ARRAY_SIZE(nfnl_inet_hooks)); +} + +unsigned int nfnl_str2inet_hook(const char *name) +{ + return __str2type(name, nfnl_inet_hooks, ARRAY_SIZE(nfnl_inet_hooks)); +} diff --git a/libnl/lib/netfilter/nfnl.c b/libnl/lib/netfilter/nfnl.c new file mode 100644 index 0000000..a368cec --- /dev/null +++ b/libnl/lib/netfilter/nfnl.c @@ -0,0 +1,244 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + */ + +/** + * @defgroup nfnl Netfilter Library (libnl-nf) + * + * @par Message Format + * @code + * <------- NLMSG_ALIGN(hlen) ------> <---- NLMSG_ALIGN(len) ---> + * +----------------------------+- - -+- - - - - - - - - - -+- - -+ + * | Header | Pad | Payload | Pad | + * | struct nlmsghdr | | | | + * +----------------------------+- - -+- - - - - - - - - - -+- - -+ + * @endcode + * @code + * <-------- NFNL_HDRLEN ---------> + * +--------------------------+- - -+------------+ + * | Netfilter Netlink Header | Pad | Attributes | + * | struct nfgenmsg | | | + * +--------------------------+- - -+------------+ + * nfnlmsg_attrdata(nfg, hdrlen)-----^ + * @endcode + * + * @par 1) Creating a new netfilter netlink message + * @code + * struct nl_msg *msg; + * + * // Create a new empty netlink message + * msg = nlmsg_alloc(); + * + * // Append the netlink and netfilter netlink message header + * hdr = nfnlmsg_put(msg, PID, SEQ, SUBSYS, TYPE, NLM_F_ECHO, + * FAMILY, RES_ID); + * + * // Append the attributes. + * nla_put_u32(msg, 1, 0x10); + * + * // Message is ready to be sent. + * nl_send_auto_complete(sk, msg); + * + * // All done? Free the message. + * nlmsg_free(msg); + * @endcode + * + * @par 2) Sending of trivial messages + * @code + * // For trivial messages not requiring any subsys specific header or + * // attributes, nfnl_send_simple() may be used to send messages directly. + * nfnl_send_simple(sk, SUBSYS, TYPE, 0, FAMILY, RES_ID); + * @endcode + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include + +#include + +/** + * @name Socket Creating + * @{ + */ + +/** + * Create and connect netfilter netlink socket. + * @arg sk Netlink socket. + * + * Creates a NETLINK_NETFILTER netlink socket, binds the socket and + * issues a connection attempt. + * + * @see nl_connect() + * + * @return 0 on success or a negative error code. + */ +int nfnl_connect(struct nl_sock *sk) +{ + return nl_connect(sk, NETLINK_NETFILTER); +} + +/** @} */ + +/** + * @name Sending + * @{ + */ + +/** + * Send trivial netfilter netlink message + * @arg sk Netlink socket. + * @arg subsys_id nfnetlink subsystem + * @arg type nfnetlink message type + * @arg flags message flags + * @arg family nfnetlink address family + * @arg res_id nfnetlink resource id + * + * @return 0 on success or a negative error code. Due to a bug, this function + * returns the number of bytes sent. Treat any non-negative number as success. + */ +int nfnl_send_simple(struct nl_sock *sk, uint8_t subsys_id, uint8_t type, + int flags, uint8_t family, uint16_t res_id) +{ + struct nfgenmsg hdr = { + .nfgen_family = family, + .version = NFNETLINK_V0, + .res_id = htons(res_id), + }; + + return nl_send_simple(sk, NFNLMSG_TYPE(subsys_id, type), flags, + &hdr, sizeof(hdr)); +} + +/** @} */ + +/** + * @name Message Parsing + * @{ + */ + +/** + * Get netfilter subsystem id from message + * @arg nlh netlink messsage header + */ +uint8_t nfnlmsg_subsys(struct nlmsghdr *nlh) +{ + return NFNL_SUBSYS_ID(nlh->nlmsg_type); +} + +/** + * Get netfilter message type from message + * @arg nlh netlink messsage header + */ +uint8_t nfnlmsg_subtype(struct nlmsghdr *nlh) +{ + return NFNL_MSG_TYPE(nlh->nlmsg_type); +} + +/** + * Get netfilter family from message + * @arg nlh netlink messsage header + */ +uint8_t nfnlmsg_family(struct nlmsghdr *nlh) +{ + struct nfgenmsg *nfg = nlmsg_data(nlh); + + return nfg->nfgen_family; +} + +/** + * Get netfilter resource id from message + * @arg nlh netlink messsage header + */ +uint16_t nfnlmsg_res_id(struct nlmsghdr *nlh) +{ + struct nfgenmsg *nfg = nlmsg_data(nlh); + + return ntohs(nfg->res_id); +} + +/** @} */ + +/** + * @name Message Building + * @{ + */ + +static int nfnlmsg_append(struct nl_msg *msg, uint8_t family, uint16_t res_id) +{ + struct nfgenmsg *nfg; + + nfg = nlmsg_reserve(msg, sizeof(*nfg), NLMSG_ALIGNTO); + if (nfg == NULL) + return -NLE_NOMEM; + + nfg->nfgen_family = family; + nfg->version = NFNETLINK_V0; + nfg->res_id = htons(res_id); + NL_DBG(2, "msg %p: Added nfnetlink header family=%d res_id=%d\n", + msg, family, res_id); + return 0; +} + +/** + * Allocate a new netfilter netlink message + * @arg subsys_id nfnetlink subsystem + * @arg type nfnetlink message type + * @arg flags message flags + * @arg family nfnetlink address family + * @arg res_id nfnetlink resource id + * + * @return Newly allocated netlink message or NULL. + */ +struct nl_msg *nfnlmsg_alloc_simple(uint8_t subsys_id, uint8_t type, int flags, + uint8_t family, uint16_t res_id) +{ + struct nl_msg *msg; + + msg = nlmsg_alloc_simple(NFNLMSG_TYPE(subsys_id, type), flags); + if (msg == NULL) + return NULL; + + if (nfnlmsg_append(msg, family, res_id) < 0) + goto nla_put_failure; + + return msg; + +nla_put_failure: + nlmsg_free(msg); + return NULL; +} + +/** + * Add netlink and netfilter netlink headers to netlink message + * @arg msg netlink message + * @arg pid netlink process id + * @arg seq sequence number of message + * @arg subsys_id nfnetlink subsystem + * @arg type nfnetlink message type + * @arg flags message flags + * @arg family nfnetlink address family + * @arg res_id nfnetlink resource id + */ +int nfnlmsg_put(struct nl_msg *msg, uint32_t pid, uint32_t seq, + uint8_t subsys_id, uint8_t type, int flags, uint8_t family, + uint16_t res_id) +{ + struct nlmsghdr *nlh; + + nlh = nlmsg_put(msg, pid, seq, NFNLMSG_TYPE(subsys_id, type), 0, flags); + if (nlh == NULL) + return -NLE_MSGSIZE; + + return nfnlmsg_append(msg, family, res_id); +} + +/** @} */ + +/** @} */ diff --git a/libnl/lib/netfilter/queue.c b/libnl/lib/netfilter/queue.c new file mode 100644 index 0000000..dcf9aa7 --- /dev/null +++ b/libnl/lib/netfilter/queue.c @@ -0,0 +1,247 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2007, 2008 Patrick McHardy + */ + +/** + * @ingroup nfnl + * @defgroup queue Queue + * @brief + * @{ + */ + +#define _GNU_SOURCE + +#include +#include + +#include +#include +#include +#include + +struct nl_sock *nfnl_queue_socket_alloc(void) +{ + struct nl_sock *nlsk; + + nlsk = nl_socket_alloc(); + if (nlsk) + nl_socket_disable_auto_ack(nlsk); + return nlsk; +} + +static int send_queue_request(struct nl_sock *sk, struct nl_msg *msg) +{ + int err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +/** + * @name Queue Commands + * @{ + */ + +static int build_queue_cmd_request(uint8_t family, uint16_t queuenum, + uint8_t command, struct nl_msg **result) +{ + struct nl_msg *msg; + struct nfqnl_msg_config_cmd cmd; + + msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_QUEUE, NFQNL_MSG_CONFIG, 0, + family, queuenum); + if (msg == NULL) + return -NLE_NOMEM; + + cmd.pf = htons(family); + cmd._pad = 0; + cmd.command = command; + if (nla_put(msg, NFQA_CFG_CMD, sizeof(cmd), &cmd) < 0) + goto nla_put_failure; + + *result = msg; + return 0; + +nla_put_failure: + nlmsg_free(msg); + return -NLE_MSGSIZE; +} + +int nfnl_queue_build_pf_bind(uint8_t pf, struct nl_msg **result) +{ + return build_queue_cmd_request(pf, 0, NFQNL_CFG_CMD_PF_BIND, result); +} + +int nfnl_queue_pf_bind(struct nl_sock *nlh, uint8_t pf) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_queue_build_pf_bind(pf, &msg)) < 0) + return err; + + return send_queue_request(nlh, msg); +} + +int nfnl_queue_build_pf_unbind(uint8_t pf, struct nl_msg **result) +{ + return build_queue_cmd_request(pf, 0, NFQNL_CFG_CMD_PF_UNBIND, result); +} + +int nfnl_queue_pf_unbind(struct nl_sock *nlh, uint8_t pf) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_queue_build_pf_unbind(pf, &msg)) < 0) + return err; + + return send_queue_request(nlh, msg); +} + +static int nfnl_queue_build_request(const struct nfnl_queue *queue, + struct nl_msg **result) +{ + struct nl_msg *msg; + + if (!nfnl_queue_test_group(queue)) + return -NLE_MISSING_ATTR; + + msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_QUEUE, NFQNL_MSG_CONFIG, 0, + 0, nfnl_queue_get_group(queue)); + if (msg == NULL) + return -NLE_NOMEM; + + if (nfnl_queue_test_maxlen(queue) && + nla_put_u32(msg, NFQA_CFG_QUEUE_MAXLEN, + htonl(nfnl_queue_get_maxlen(queue))) < 0) + goto nla_put_failure; + + /* This sucks, the nfnetlink_queue interface always expects both + * parameters to be present. Needs to be done properly. + */ + if (nfnl_queue_test_copy_mode(queue)) { + struct nfqnl_msg_config_params params; + + switch (nfnl_queue_get_copy_mode(queue)) { + case NFNL_QUEUE_COPY_NONE: + params.copy_mode = NFQNL_COPY_NONE; + break; + case NFNL_QUEUE_COPY_META: + params.copy_mode = NFQNL_COPY_META; + break; + case NFNL_QUEUE_COPY_PACKET: + params.copy_mode = NFQNL_COPY_PACKET; + break; + } + params.copy_range = htonl(nfnl_queue_get_copy_range(queue)); + + if (nla_put(msg, NFQA_CFG_PARAMS, sizeof(params), ¶ms) < 0) + goto nla_put_failure; + } + + *result = msg; + return 0; + +nla_put_failure: + nlmsg_free(msg); + return -NLE_MSGSIZE; +} + +int nfnl_queue_build_create_request(const struct nfnl_queue *queue, + struct nl_msg **result) +{ + struct nfqnl_msg_config_cmd cmd; + int err; + + if ((err = nfnl_queue_build_request(queue, result)) < 0) + return err; + + cmd.pf = 0; + cmd._pad = 0; + cmd.command = NFQNL_CFG_CMD_BIND; + + NLA_PUT(*result, NFQA_CFG_CMD, sizeof(cmd), &cmd); + + return 0; + +nla_put_failure: + nlmsg_free(*result); + return -NLE_MSGSIZE; +} + +int nfnl_queue_create(struct nl_sock *nlh, const struct nfnl_queue *queue) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_queue_build_create_request(queue, &msg)) < 0) + return err; + + return send_queue_request(nlh, msg); +} + +int nfnl_queue_build_change_request(const struct nfnl_queue *queue, + struct nl_msg **result) +{ + return nfnl_queue_build_request(queue, result); +} + +int nfnl_queue_change(struct nl_sock *nlh, const struct nfnl_queue *queue) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_queue_build_change_request(queue, &msg)) < 0) + return err; + + return send_queue_request(nlh, msg); +} + +int nfnl_queue_build_delete_request(const struct nfnl_queue *queue, + struct nl_msg **result) +{ + if (!nfnl_queue_test_group(queue)) + return -NLE_MISSING_ATTR; + + return build_queue_cmd_request(0, nfnl_queue_get_group(queue), + NFQNL_CFG_CMD_UNBIND, result); +} + +int nfnl_queue_delete(struct nl_sock *nlh, const struct nfnl_queue *queue) +{ + struct nl_msg *msg; + int err; + + if ((err = nfnl_queue_build_delete_request(queue, &msg)) < 0) + return err; + + return send_queue_request(nlh, msg); +} + +/** @} */ + +static struct nl_cache_ops nfnl_queue_ops = { + .co_name = "netfilter/queue", + .co_obj_ops = &queue_obj_ops, + .co_msgtypes = { + END_OF_MSGTYPES_LIST, + }, +}; + +static void __init nfnl_queue_init(void) +{ + nl_cache_mngt_register(&nfnl_queue_ops); +} + +static void __exit nfnl_queue_exit(void) +{ + nl_cache_mngt_unregister(&nfnl_queue_ops); +} + +/** @} */ diff --git a/libnl/lib/netfilter/queue_msg.c b/libnl/lib/netfilter/queue_msg.c new file mode 100644 index 0000000..6870221 --- /dev/null +++ b/libnl/lib/netfilter/queue_msg.c @@ -0,0 +1,305 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2007, 2008 Patrick McHardy + * Copyright (c) 2010 Karl Hiramoto + */ + +/** + * @ingroup nfnl + * @defgroup queue Queue + * @brief + * @{ + */ + +#define _GNU_SOURCE + +#include +#include + +#include +#include +#include +#include +#include + +static struct nl_cache_ops nfnl_queue_msg_ops; + +static struct nla_policy queue_policy[NFQA_MAX+1] = { + [NFQA_PACKET_HDR] = { + .minlen = sizeof(struct nfqnl_msg_packet_hdr), + }, + [NFQA_VERDICT_HDR] = { + .minlen = sizeof(struct nfqnl_msg_verdict_hdr), + }, + [NFQA_MARK] = { .type = NLA_U32 }, + [NFQA_TIMESTAMP] = { + .minlen = sizeof(struct nfqnl_msg_packet_timestamp), + }, + [NFQA_IFINDEX_INDEV] = { .type = NLA_U32 }, + [NFQA_IFINDEX_OUTDEV] = { .type = NLA_U32 }, + [NFQA_IFINDEX_PHYSINDEV] = { .type = NLA_U32 }, + [NFQA_IFINDEX_PHYSOUTDEV] = { .type = NLA_U32 }, + [NFQA_HWADDR] = { + .minlen = sizeof(struct nfqnl_msg_packet_hw), + }, +}; + +int nfnlmsg_queue_msg_parse(struct nlmsghdr *nlh, + struct nfnl_queue_msg **result) +{ + struct nfnl_queue_msg *msg; + struct nlattr *tb[NFQA_MAX+1]; + struct nlattr *attr; + int err; + + msg = nfnl_queue_msg_alloc(); + if (!msg) + return -NLE_NOMEM; + + msg->ce_msgtype = nlh->nlmsg_type; + + err = nlmsg_parse(nlh, sizeof(struct nfgenmsg), tb, NFQA_MAX, + queue_policy); + if (err < 0) + goto errout; + + nfnl_queue_msg_set_group(msg, nfnlmsg_res_id(nlh)); + nfnl_queue_msg_set_family(msg, nfnlmsg_family(nlh)); + + attr = tb[NFQA_PACKET_HDR]; + if (attr) { + struct nfqnl_msg_packet_hdr *hdr = nla_data(attr); + + nfnl_queue_msg_set_packetid(msg, ntohl(hdr->packet_id)); + if (hdr->hw_protocol) + nfnl_queue_msg_set_hwproto(msg, hdr->hw_protocol); + nfnl_queue_msg_set_hook(msg, hdr->hook); + } + + attr = tb[NFQA_MARK]; + if (attr) + nfnl_queue_msg_set_mark(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFQA_TIMESTAMP]; + if (attr) { + struct nfqnl_msg_packet_timestamp *timestamp = nla_data(attr); + struct timeval tv; + + tv.tv_sec = ntohll(timestamp->sec); + tv.tv_usec = ntohll(timestamp->usec); + nfnl_queue_msg_set_timestamp(msg, &tv); + } + + attr = tb[NFQA_IFINDEX_INDEV]; + if (attr) + nfnl_queue_msg_set_indev(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFQA_IFINDEX_OUTDEV]; + if (attr) + nfnl_queue_msg_set_outdev(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFQA_IFINDEX_PHYSINDEV]; + if (attr) + nfnl_queue_msg_set_physindev(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFQA_IFINDEX_PHYSOUTDEV]; + if (attr) + nfnl_queue_msg_set_physoutdev(msg, ntohl(nla_get_u32(attr))); + + attr = tb[NFQA_HWADDR]; + if (attr) { + struct nfqnl_msg_packet_hw *hw = nla_data(attr); + + nfnl_queue_msg_set_hwaddr(msg, hw->hw_addr, + ntohs(hw->hw_addrlen)); + } + + attr = tb[NFQA_PAYLOAD]; + if (attr) { + err = nfnl_queue_msg_set_payload(msg, nla_data(attr), + nla_len(attr)); + if (err < 0) + goto errout; + } + + *result = msg; + return 0; + +errout: + nfnl_queue_msg_put(msg); + return err; +} + +static int queue_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, struct nl_parser_param *pp) +{ + struct nfnl_queue_msg *msg; + int err; + + if ((err = nfnlmsg_queue_msg_parse(nlh, &msg)) < 0) + return err; + + err = pp->pp_cb((struct nl_object *) msg, pp); + nfnl_queue_msg_put(msg); + return err; +} + +/** @} */ + +static struct nl_msg * +__nfnl_queue_msg_build_verdict(const struct nfnl_queue_msg *msg, + uint8_t type) +{ + struct nl_msg *nlmsg; + struct nfqnl_msg_verdict_hdr verdict; + + nlmsg = nfnlmsg_alloc_simple(NFNL_SUBSYS_QUEUE, type, 0, + nfnl_queue_msg_get_family(msg), + nfnl_queue_msg_get_group(msg)); + if (nlmsg == NULL) + return NULL; + + verdict.id = htonl(nfnl_queue_msg_get_packetid(msg)); + verdict.verdict = htonl(nfnl_queue_msg_get_verdict(msg)); + if (nla_put(nlmsg, NFQA_VERDICT_HDR, sizeof(verdict), &verdict) < 0) + goto nla_put_failure; + + if (nfnl_queue_msg_test_mark(msg) && + nla_put_u32(nlmsg, NFQA_MARK, + ntohl(nfnl_queue_msg_get_mark(msg))) < 0) + goto nla_put_failure; + + return nlmsg; + +nla_put_failure: + nlmsg_free(nlmsg); + return NULL; +} + +struct nl_msg * +nfnl_queue_msg_build_verdict(const struct nfnl_queue_msg *msg) +{ + return __nfnl_queue_msg_build_verdict(msg, NFQNL_MSG_VERDICT); +} + +struct nl_msg * +nfnl_queue_msg_build_verdict_batch(const struct nfnl_queue_msg *msg) +{ + return __nfnl_queue_msg_build_verdict(msg, NFQNL_MSG_VERDICT_BATCH); +} + +/** +* Send a message verdict/mark +* @arg nlh netlink messsage header +* @arg msg queue msg +* @return 0 on OK or error code +*/ +int nfnl_queue_msg_send_verdict(struct nl_sock *nlh, + const struct nfnl_queue_msg *msg) +{ + struct nl_msg *nlmsg; + int err; + + nlmsg = nfnl_queue_msg_build_verdict(msg); + if (nlmsg == NULL) + return -NLE_NOMEM; + + err = nl_send_auto_complete(nlh, nlmsg); + nlmsg_free(nlmsg); + if (err < 0) + return err; + return wait_for_ack(nlh); +} + +/** +* Send a message batched verdict/mark +* @arg nlh netlink messsage header +* @arg msg queue msg +* @return 0 on OK or error code +*/ +int nfnl_queue_msg_send_verdict_batch(struct nl_sock *nlh, + const struct nfnl_queue_msg *msg) +{ + struct nl_msg *nlmsg; + int err; + + nlmsg = nfnl_queue_msg_build_verdict_batch(msg); + if (nlmsg == NULL) + return -NLE_NOMEM; + + err = nl_send_auto_complete(nlh, nlmsg); + nlmsg_free(nlmsg); + if (err < 0) + return err; + return wait_for_ack(nlh); +} + +/** +* Send a message verdict including the payload +* @arg nlh netlink messsage header +* @arg msg queue msg +* @arg payload_data packet payload data +* @arg payload_len payload length +* @return 0 on OK or error code +*/ +int nfnl_queue_msg_send_verdict_payload(struct nl_sock *nlh, + const struct nfnl_queue_msg *msg, + const void *payload_data, unsigned payload_len) +{ + struct nl_msg *nlmsg; + int err; + struct iovec iov[3]; + struct nlattr nla; + + nlmsg = nfnl_queue_msg_build_verdict(msg); + if (nlmsg == NULL) + return -NLE_NOMEM; + + memset(iov, 0, sizeof(iov)); + + iov[0].iov_base = (void *) nlmsg_hdr(nlmsg); + iov[0].iov_len = nlmsg_hdr(nlmsg)->nlmsg_len; + + nla.nla_type = NFQA_PAYLOAD; + nla.nla_len = payload_len + sizeof(nla); + nlmsg_hdr(nlmsg)->nlmsg_len += nla.nla_len; + + iov[1].iov_base = (void *) &nla; + iov[1].iov_len = sizeof(nla); + + iov[2].iov_base = (void *) payload_data; + iov[2].iov_len = NLA_ALIGN(payload_len); + + nl_complete_msg(nlh, nlmsg); + err = nl_send_iovec(nlh, nlmsg, iov, 3); + + nlmsg_free(nlmsg); + if (err < 0) + return err; + return wait_for_ack(nlh); +} + +#define NFNLMSG_QUEUE_TYPE(type) NFNLMSG_TYPE(NFNL_SUBSYS_QUEUE, (type)) +static struct nl_cache_ops nfnl_queue_msg_ops = { + .co_name = "netfilter/queue_msg", + .co_hdrsize = NFNL_HDRLEN, + .co_msgtypes = { + { NFNLMSG_QUEUE_TYPE(NFQNL_MSG_PACKET), NL_ACT_NEW, "new" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_NETFILTER, + .co_msg_parser = queue_msg_parser, + .co_obj_ops = &queue_msg_obj_ops, +}; + +static void __init nfnl_msg_queue_init(void) +{ + nl_cache_mngt_register(&nfnl_queue_msg_ops); +} + +static void __exit nfnl_queue_msg_exit(void) +{ + nl_cache_mngt_unregister(&nfnl_queue_msg_ops); +} + +/** @} */ diff --git a/libnl/lib/netfilter/queue_msg_obj.c b/libnl/lib/netfilter/queue_msg_obj.c new file mode 100644 index 0000000..5d7105a --- /dev/null +++ b/libnl/lib/netfilter/queue_msg_obj.c @@ -0,0 +1,494 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2007, 2008 Patrick McHardy + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define QUEUE_MSG_ATTR_GROUP (1UL << 0) +#define QUEUE_MSG_ATTR_FAMILY (1UL << 1) +#define QUEUE_MSG_ATTR_PACKETID (1UL << 2) +#define QUEUE_MSG_ATTR_HWPROTO (1UL << 3) +#define QUEUE_MSG_ATTR_HOOK (1UL << 4) +#define QUEUE_MSG_ATTR_MARK (1UL << 5) +#define QUEUE_MSG_ATTR_TIMESTAMP (1UL << 6) +#define QUEUE_MSG_ATTR_INDEV (1UL << 7) +#define QUEUE_MSG_ATTR_OUTDEV (1UL << 8) +#define QUEUE_MSG_ATTR_PHYSINDEV (1UL << 9) +#define QUEUE_MSG_ATTR_PHYSOUTDEV (1UL << 10) +#define QUEUE_MSG_ATTR_HWADDR (1UL << 11) +#define QUEUE_MSG_ATTR_PAYLOAD (1UL << 12) +#define QUEUE_MSG_ATTR_VERDICT (1UL << 13) +/** @endcond */ + +static void nfnl_queue_msg_free_data(struct nl_object *c) +{ + struct nfnl_queue_msg *msg = (struct nfnl_queue_msg *) c; + + if (msg == NULL) + return; + + free(msg->queue_msg_payload); +} + +static int nfnl_queue_msg_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct nfnl_queue_msg *dst = (struct nfnl_queue_msg *) _dst; + struct nfnl_queue_msg *src = (struct nfnl_queue_msg *) _src; + int err; + + if (src->queue_msg_payload) { + err = nfnl_queue_msg_set_payload(dst, src->queue_msg_payload, + src->queue_msg_payload_len); + if (err < 0) + goto errout; + } + + return 0; +errout: + return err; +} + +static void nfnl_queue_msg_dump(struct nl_object *a, struct nl_dump_params *p) +{ + struct nfnl_queue_msg *msg = (struct nfnl_queue_msg *) a; + struct nl_cache *link_cache; + char buf[64]; + + link_cache = nl_cache_mngt_require_safe("route/link"); + + nl_new_line(p); + + if (msg->ce_mask & QUEUE_MSG_ATTR_GROUP) + nl_dump(p, "GROUP=%u ", msg->queue_msg_group); + + if (msg->ce_mask & QUEUE_MSG_ATTR_INDEV) { + if (link_cache) + nl_dump(p, "IN=%s ", + rtnl_link_i2name(link_cache, + msg->queue_msg_indev, + buf, sizeof(buf))); + else + nl_dump(p, "IN=%d ", msg->queue_msg_indev); + } + + if (msg->ce_mask & QUEUE_MSG_ATTR_PHYSINDEV) { + if (link_cache) + nl_dump(p, "PHYSIN=%s ", + rtnl_link_i2name(link_cache, + msg->queue_msg_physindev, + buf, sizeof(buf))); + else + nl_dump(p, "IN=%d ", msg->queue_msg_physindev); + } + + if (msg->ce_mask & QUEUE_MSG_ATTR_OUTDEV) { + if (link_cache) + nl_dump(p, "OUT=%s ", + rtnl_link_i2name(link_cache, + msg->queue_msg_outdev, + buf, sizeof(buf))); + else + nl_dump(p, "OUT=%d ", msg->queue_msg_outdev); + } + + if (msg->ce_mask & QUEUE_MSG_ATTR_PHYSOUTDEV) { + if (link_cache) + nl_dump(p, "PHYSOUT=%s ", + rtnl_link_i2name(link_cache, + msg->queue_msg_physoutdev, + buf, sizeof(buf))); + else + nl_dump(p, "PHYSOUT=%d ", msg->queue_msg_physoutdev); + } + + if (msg->ce_mask & QUEUE_MSG_ATTR_HWADDR) { + int i; + + nl_dump(p, "MAC"); + for (i = 0; i < msg->queue_msg_hwaddr_len; i++) + nl_dump(p, "%c%02x", i?':':'=', + msg->queue_msg_hwaddr[i]); + nl_dump(p, " "); + } + + if (msg->ce_mask & QUEUE_MSG_ATTR_FAMILY) + nl_dump(p, "FAMILY=%s ", + nl_af2str(msg->queue_msg_family, buf, sizeof(buf))); + + if (msg->ce_mask & QUEUE_MSG_ATTR_HWPROTO) + nl_dump(p, "HWPROTO=%s ", + nl_ether_proto2str(ntohs(msg->queue_msg_hwproto), + buf, sizeof(buf))); + + if (msg->ce_mask & QUEUE_MSG_ATTR_HOOK) + nl_dump(p, "HOOK=%s ", + nfnl_inet_hook2str(msg->queue_msg_hook, + buf, sizeof(buf))); + + if (msg->ce_mask & QUEUE_MSG_ATTR_MARK) + nl_dump(p, "MARK=%d ", msg->queue_msg_mark); + + if (msg->ce_mask & QUEUE_MSG_ATTR_PAYLOAD) + nl_dump(p, "PAYLOADLEN=%d ", msg->queue_msg_payload_len); + + if (msg->ce_mask & QUEUE_MSG_ATTR_PACKETID) + nl_dump(p, "PACKETID=%u ", msg->queue_msg_packetid); + + if (msg->ce_mask & QUEUE_MSG_ATTR_VERDICT) + nl_dump(p, "VERDICT=%s ", + nfnl_verdict2str(msg->queue_msg_verdict, + buf, sizeof(buf))); + + nl_dump(p, "\n"); + + if (link_cache) + nl_cache_put(link_cache); +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct nfnl_queue_msg *nfnl_queue_msg_alloc(void) +{ + return (struct nfnl_queue_msg *) nl_object_alloc(&queue_msg_obj_ops); +} + +void nfnl_queue_msg_get(struct nfnl_queue_msg *msg) +{ + nl_object_get((struct nl_object *) msg); +} + +void nfnl_queue_msg_put(struct nfnl_queue_msg *msg) +{ + nl_object_put((struct nl_object *) msg); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +void nfnl_queue_msg_set_group(struct nfnl_queue_msg *msg, uint16_t group) +{ + msg->queue_msg_group = group; + msg->ce_mask |= QUEUE_MSG_ATTR_GROUP; +} + +int nfnl_queue_msg_test_group(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_GROUP); +} + +uint16_t nfnl_queue_msg_get_group(const struct nfnl_queue_msg *msg) +{ + return msg->queue_msg_group; +} + +/** +* Set the protocol family +* @arg msg NF queue message +* @arg family AF_XXX address family example: AF_INET, AF_UNIX, etc +*/ +void nfnl_queue_msg_set_family(struct nfnl_queue_msg *msg, uint8_t family) +{ + msg->queue_msg_family = family; + msg->ce_mask |= QUEUE_MSG_ATTR_FAMILY; +} + +int nfnl_queue_msg_test_family(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_FAMILY); +} + +uint8_t nfnl_queue_msg_get_family(const struct nfnl_queue_msg *msg) +{ + if (msg->ce_mask & QUEUE_MSG_ATTR_FAMILY) + return msg->queue_msg_family; + else + return AF_UNSPEC; +} + +void nfnl_queue_msg_set_packetid(struct nfnl_queue_msg *msg, uint32_t packetid) +{ + msg->queue_msg_packetid = packetid; + msg->ce_mask |= QUEUE_MSG_ATTR_PACKETID; +} + +int nfnl_queue_msg_test_packetid(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_PACKETID); +} + +uint32_t nfnl_queue_msg_get_packetid(const struct nfnl_queue_msg *msg) +{ + return msg->queue_msg_packetid; +} + +void nfnl_queue_msg_set_hwproto(struct nfnl_queue_msg *msg, uint16_t hwproto) +{ + msg->queue_msg_hwproto = hwproto; + msg->ce_mask |= QUEUE_MSG_ATTR_HWPROTO; +} + +int nfnl_queue_msg_test_hwproto(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_HWPROTO); +} + +uint16_t nfnl_queue_msg_get_hwproto(const struct nfnl_queue_msg *msg) +{ + return msg->queue_msg_hwproto; +} + +void nfnl_queue_msg_set_hook(struct nfnl_queue_msg *msg, uint8_t hook) +{ + msg->queue_msg_hook = hook; + msg->ce_mask |= QUEUE_MSG_ATTR_HOOK; +} + +int nfnl_queue_msg_test_hook(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_HOOK); +} + +uint8_t nfnl_queue_msg_get_hook(const struct nfnl_queue_msg *msg) +{ + return msg->queue_msg_hook; +} + +void nfnl_queue_msg_set_mark(struct nfnl_queue_msg *msg, uint32_t mark) +{ + msg->queue_msg_mark = mark; + msg->ce_mask |= QUEUE_MSG_ATTR_MARK; +} + +int nfnl_queue_msg_test_mark(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_MARK); +} + +uint32_t nfnl_queue_msg_get_mark(const struct nfnl_queue_msg *msg) +{ + return msg->queue_msg_mark; +} + +void nfnl_queue_msg_set_timestamp(struct nfnl_queue_msg *msg, + struct timeval *tv) +{ + msg->queue_msg_timestamp.tv_sec = tv->tv_sec; + msg->queue_msg_timestamp.tv_usec = tv->tv_usec; + msg->ce_mask |= QUEUE_MSG_ATTR_TIMESTAMP; +} + +int nfnl_queue_msg_test_timestamp(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_TIMESTAMP); +} + +const struct timeval *nfnl_queue_msg_get_timestamp(const struct nfnl_queue_msg *msg) +{ + if (!(msg->ce_mask & QUEUE_MSG_ATTR_TIMESTAMP)) + return NULL; + return &msg->queue_msg_timestamp; +} + +void nfnl_queue_msg_set_indev(struct nfnl_queue_msg *msg, uint32_t indev) +{ + msg->queue_msg_indev = indev; + msg->ce_mask |= QUEUE_MSG_ATTR_INDEV; +} + +int nfnl_queue_msg_test_indev(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_INDEV); +} + +uint32_t nfnl_queue_msg_get_indev(const struct nfnl_queue_msg *msg) +{ + return msg->queue_msg_indev; +} + +void nfnl_queue_msg_set_outdev(struct nfnl_queue_msg *msg, uint32_t outdev) +{ + msg->queue_msg_outdev = outdev; + msg->ce_mask |= QUEUE_MSG_ATTR_OUTDEV; +} + +int nfnl_queue_msg_test_outdev(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_OUTDEV); +} + +uint32_t nfnl_queue_msg_get_outdev(const struct nfnl_queue_msg *msg) +{ + return msg->queue_msg_outdev; +} + +void nfnl_queue_msg_set_physindev(struct nfnl_queue_msg *msg, + uint32_t physindev) +{ + msg->queue_msg_physindev = physindev; + msg->ce_mask |= QUEUE_MSG_ATTR_PHYSINDEV; +} + +int nfnl_queue_msg_test_physindev(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_PHYSINDEV); +} + +uint32_t nfnl_queue_msg_get_physindev(const struct nfnl_queue_msg *msg) +{ + return msg->queue_msg_physindev; +} + +void nfnl_queue_msg_set_physoutdev(struct nfnl_queue_msg *msg, + uint32_t physoutdev) +{ + msg->queue_msg_physoutdev = physoutdev; + msg->ce_mask |= QUEUE_MSG_ATTR_PHYSOUTDEV; +} + +int nfnl_queue_msg_test_physoutdev(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_PHYSOUTDEV); +} + +uint32_t nfnl_queue_msg_get_physoutdev(const struct nfnl_queue_msg *msg) +{ + return msg->queue_msg_physoutdev; +} + +void nfnl_queue_msg_set_hwaddr(struct nfnl_queue_msg *msg, uint8_t *hwaddr, + int len) +{ + if (len > sizeof(msg->queue_msg_hwaddr)) + len = sizeof(msg->queue_msg_hwaddr); + + msg->queue_msg_hwaddr_len = len; + memcpy(msg->queue_msg_hwaddr, hwaddr, len); + msg->ce_mask |= QUEUE_MSG_ATTR_HWADDR; +} + +int nfnl_queue_msg_test_hwaddr(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_HWADDR); +} + +const uint8_t *nfnl_queue_msg_get_hwaddr(const struct nfnl_queue_msg *msg, + int *len) +{ + if (!(msg->ce_mask & QUEUE_MSG_ATTR_HWADDR)) { + *len = 0; + return NULL; + } + + *len = msg->queue_msg_hwaddr_len; + return msg->queue_msg_hwaddr; +} + +int nfnl_queue_msg_set_payload(struct nfnl_queue_msg *msg, uint8_t *payload, + int len) +{ + void *new_payload = malloc(len); + + if (new_payload == NULL) + return -NLE_NOMEM; + memcpy(new_payload, payload, len); + + free(msg->queue_msg_payload); + + msg->queue_msg_payload = new_payload; + msg->queue_msg_payload_len = len; + msg->ce_mask |= QUEUE_MSG_ATTR_PAYLOAD; + return 0; +} + +int nfnl_queue_msg_test_payload(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_PAYLOAD); +} + +const void *nfnl_queue_msg_get_payload(const struct nfnl_queue_msg *msg, int *len) +{ + if (!(msg->ce_mask & QUEUE_MSG_ATTR_PAYLOAD)) { + *len = 0; + return NULL; + } + + *len = msg->queue_msg_payload_len; + return msg->queue_msg_payload; +} + +/** +* Return the number of items matching a filter in the cache +* @arg msg queue msg +* @arg verdict NF_DROP, NF_ACCEPT, NF_REPEAT, etc +*/ +void nfnl_queue_msg_set_verdict(struct nfnl_queue_msg *msg, + unsigned int verdict) +{ + msg->queue_msg_verdict = verdict; + msg->ce_mask |= QUEUE_MSG_ATTR_VERDICT; +} + +int nfnl_queue_msg_test_verdict(const struct nfnl_queue_msg *msg) +{ + return !!(msg->ce_mask & QUEUE_MSG_ATTR_VERDICT); +} + +unsigned int nfnl_queue_msg_get_verdict(const struct nfnl_queue_msg *msg) +{ + return msg->queue_msg_verdict; +} + +static const struct trans_tbl nfnl_queue_msg_attrs[] = { + __ADD(QUEUE_MSG_ATTR_GROUP, group), + __ADD(QUEUE_MSG_ATTR_FAMILY, family), + __ADD(QUEUE_MSG_ATTR_PACKETID, packetid), + __ADD(QUEUE_MSG_ATTR_HWPROTO, hwproto), + __ADD(QUEUE_MSG_ATTR_HOOK, hook), + __ADD(QUEUE_MSG_ATTR_MARK, mark), + __ADD(QUEUE_MSG_ATTR_TIMESTAMP, timestamp), + __ADD(QUEUE_MSG_ATTR_INDEV, indev), + __ADD(QUEUE_MSG_ATTR_OUTDEV, outdev), + __ADD(QUEUE_MSG_ATTR_PHYSINDEV, physindev), + __ADD(QUEUE_MSG_ATTR_PHYSOUTDEV, physoutdev), + __ADD(QUEUE_MSG_ATTR_HWADDR, hwaddr), + __ADD(QUEUE_MSG_ATTR_PAYLOAD, payload), + __ADD(QUEUE_MSG_ATTR_VERDICT, verdict), +}; + +static char *nfnl_queue_msg_attrs2str(int attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, nfnl_queue_msg_attrs, + ARRAY_SIZE(nfnl_queue_msg_attrs)); +} + +/** @} */ + +struct nl_object_ops queue_msg_obj_ops = { + .oo_name = "netfilter/queuemsg", + .oo_size = sizeof(struct nfnl_queue_msg), + .oo_free_data = nfnl_queue_msg_free_data, + .oo_clone = nfnl_queue_msg_clone, + .oo_dump = { + [NL_DUMP_LINE] = nfnl_queue_msg_dump, + [NL_DUMP_DETAILS] = nfnl_queue_msg_dump, + [NL_DUMP_STATS] = nfnl_queue_msg_dump, + }, + .oo_attrs2str = nfnl_queue_msg_attrs2str, +}; + +/** @} */ diff --git a/libnl/lib/netfilter/queue_obj.c b/libnl/lib/netfilter/queue_obj.c new file mode 100644 index 0000000..097ac45 --- /dev/null +++ b/libnl/lib/netfilter/queue_obj.c @@ -0,0 +1,211 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2007, 2008 Patrick McHardy + */ + +/** + * @ingroup nfnl + * @defgroup queue Queue + * @brief + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include + +/** @cond SKIP */ +#define QUEUE_ATTR_GROUP (1UL << 0) +#define QUEUE_ATTR_MAXLEN (1UL << 1) +#define QUEUE_ATTR_COPY_MODE (1UL << 2) +#define QUEUE_ATTR_COPY_RANGE (1UL << 3) +/** @endcond */ + + +static void nfnl_queue_dump(struct nl_object *a, struct nl_dump_params *p) +{ + struct nfnl_queue *queue = (struct nfnl_queue *) a; + char buf[64]; + + nl_new_line(p); + + if (queue->ce_mask & QUEUE_ATTR_GROUP) + nl_dump(p, "group=%u ", queue->queue_group); + + if (queue->ce_mask & QUEUE_ATTR_MAXLEN) + nl_dump(p, "maxlen=%u ", queue->queue_maxlen); + + if (queue->ce_mask & QUEUE_ATTR_COPY_MODE) + nl_dump(p, "copy_mode=%s ", + nfnl_queue_copy_mode2str(queue->queue_copy_mode, + buf, sizeof(buf))); + + if (queue->ce_mask & QUEUE_ATTR_COPY_RANGE) + nl_dump(p, "copy_range=%u ", queue->queue_copy_range); + + nl_dump(p, "\n"); +} + +static const struct trans_tbl copy_modes[] = { + __ADD(NFNL_QUEUE_COPY_NONE, none), + __ADD(NFNL_QUEUE_COPY_META, meta), + __ADD(NFNL_QUEUE_COPY_PACKET, packet), +}; + +char *nfnl_queue_copy_mode2str(enum nfnl_queue_copy_mode copy_mode, char *buf, + size_t len) +{ + return __type2str(copy_mode, buf, len, copy_modes, + ARRAY_SIZE(copy_modes)); +} + +int nfnl_queue_str2copy_mode(const char *name) +{ + return __str2type(name, copy_modes, ARRAY_SIZE(copy_modes)); +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct nfnl_queue *nfnl_queue_alloc(void) +{ + return (struct nfnl_queue *) nl_object_alloc(&queue_obj_ops); +} + +void nfnl_queue_get(struct nfnl_queue *queue) +{ + nl_object_get((struct nl_object *) queue); +} + +void nfnl_queue_put(struct nfnl_queue *queue) +{ + nl_object_put((struct nl_object *) queue); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +void nfnl_queue_set_group(struct nfnl_queue *queue, uint16_t group) +{ + queue->queue_group = group; + queue->ce_mask |= QUEUE_ATTR_GROUP; +} + +int nfnl_queue_test_group(const struct nfnl_queue *queue) +{ + return !!(queue->ce_mask & QUEUE_ATTR_GROUP); +} + +uint16_t nfnl_queue_get_group(const struct nfnl_queue *queue) +{ + return queue->queue_group; +} + +void nfnl_queue_set_maxlen(struct nfnl_queue *queue, uint32_t maxlen) +{ + queue->queue_maxlen = maxlen; + queue->ce_mask |= QUEUE_ATTR_MAXLEN; +} + +int nfnl_queue_test_maxlen(const struct nfnl_queue *queue) +{ + return !!(queue->ce_mask & QUEUE_ATTR_MAXLEN); +} + +uint32_t nfnl_queue_get_maxlen(const struct nfnl_queue *queue) +{ + return queue->queue_maxlen; +} + +void nfnl_queue_set_copy_mode(struct nfnl_queue *queue, enum nfnl_queue_copy_mode mode) +{ + queue->queue_copy_mode = mode; + queue->ce_mask |= QUEUE_ATTR_COPY_MODE; +} + +int nfnl_queue_test_copy_mode(const struct nfnl_queue *queue) +{ + return !!(queue->ce_mask & QUEUE_ATTR_COPY_MODE); +} + +enum nfnl_queue_copy_mode nfnl_queue_get_copy_mode(const struct nfnl_queue *queue) +{ + return queue->queue_copy_mode; +} + +void nfnl_queue_set_copy_range(struct nfnl_queue *queue, uint32_t copy_range) +{ + queue->queue_copy_range = copy_range; + queue->ce_mask |= QUEUE_ATTR_COPY_RANGE; +} + +int nfnl_queue_test_copy_range(const struct nfnl_queue *queue) +{ + return !!(queue->ce_mask & QUEUE_ATTR_COPY_RANGE); +} + +uint32_t nfnl_queue_get_copy_range(const struct nfnl_queue *queue) +{ + return queue->queue_copy_range; +} + +static uint64_t nfnl_queue_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + struct nfnl_queue *a = (struct nfnl_queue *) _a; + struct nfnl_queue *b = (struct nfnl_queue *) _b; + uint64_t diff = 0; + +#define NFNL_QUEUE_DIFF(ATTR, EXPR) \ + ATTR_DIFF(attrs, QUEUE_ATTR_##ATTR, a, b, EXPR) +#define NFNL_QUEUE_DIFF_VAL(ATTR, FIELD) \ + NFNL_QUEUE_DIFF(ATTR, a->FIELD != b->FIELD) + + diff |= NFNL_QUEUE_DIFF_VAL(GROUP, queue_group); + diff |= NFNL_QUEUE_DIFF_VAL(MAXLEN, queue_maxlen); + diff |= NFNL_QUEUE_DIFF_VAL(COPY_MODE, queue_copy_mode); + diff |= NFNL_QUEUE_DIFF_VAL(COPY_RANGE, queue_copy_range); + +#undef NFNL_QUEUE_DIFF +#undef NFNL_QUEUE_DIFF_VAL + + return diff; +} + +static const struct trans_tbl nfnl_queue_attrs[] = { + __ADD(QUEUE_ATTR_GROUP, group), + __ADD(QUEUE_ATTR_MAXLEN, maxlen), + __ADD(QUEUE_ATTR_COPY_MODE, copy_mode), + __ADD(QUEUE_ATTR_COPY_RANGE, copy_range), +}; + +static char *nfnl_queue_attrs2str(int attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, nfnl_queue_attrs, + ARRAY_SIZE(nfnl_queue_attrs)); +} + +/** @} */ + +struct nl_object_ops queue_obj_ops = { + .oo_name = "netfilter/queue", + .oo_size = sizeof(struct nfnl_queue), + .oo_dump = { + [NL_DUMP_LINE] = nfnl_queue_dump, + [NL_DUMP_DETAILS] = nfnl_queue_dump, + [NL_DUMP_STATS] = nfnl_queue_dump, + }, + .oo_compare = nfnl_queue_compare, + .oo_attrs2str = nfnl_queue_attrs2str, + .oo_id_attrs = QUEUE_ATTR_GROUP, +}; + +/** @} */ diff --git a/libnl/lib/nl.c b/libnl/lib/nl.c new file mode 100644 index 0000000..5c0dfa0 --- /dev/null +++ b/libnl/lib/nl.c @@ -0,0 +1,1253 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +/** + * @defgroup core Core Library (libnl) + * + * Socket handling, connection management, sending and receiving of data, + * message construction and parsing, object caching system, ... + * + * This is the API reference of the core library. It is not meant as a guide + * but as a reference. Please refer to the core library guide for detailed + * documentation on the library architecture and examples: + * + * * @ref_asciidoc{core,_,Netlink Core Library Development Guide} + * + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @defgroup core_types Data Types + * + * Core library data types + * @{ + * @} + * + * @defgroup send_recv Send & Receive Data + * + * Connection management, sending & receiving of data + * + * Related sections in the development guide: + * - @core_doc{core_send_recv, Sending & Receiving} + * - @core_doc{core_sockets, Sockets} + * + * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ + */ + +/** + * @name Connection Management + * @{ + */ + +/** + * Create file descriptor and bind socket. + * @arg sk Netlink socket (required) + * @arg protocol Netlink protocol to use (required) + * + * Creates a new Netlink socket using `socket()` and binds the socket to the + * protocol and local port specified in the `sk` socket object. Fails if + * the socket is already connected. + * + * @note If available, the `close-on-exec` (`SOCK_CLOEXEC`) feature is enabled + * automatically on the new file descriptor. This causes the socket to + * be closed automatically if any of the `exec` family functions succeed. + * This is essential for multi threaded programs. + * + * @note The local port (`nl_socket_get_local_port()`) is unspecified after + * creating a new socket. It only gets determined when accessing the + * port the first time or during `nl_connect()`. When nl_connect() + * fails during `bind()` due to `ADDRINUSE`, it will retry with + * different ports if the port is unspecified. Unless you want to enforce + * the use of a specific local port, don't access the local port (or + * reset it to `unspecified` by calling `nl_socket_set_local_port(sk, 0)`). + * This capability is indicated by + * `%NL_CAPABILITY_NL_CONNECT_RETRY_GENERATE_PORT_ON_ADDRINUSE`. + * + * @note nl_connect() creates and sets the file descriptor. You can setup the file + * descriptor yourself by creating and binding it, and then calling + * nl_socket_set_fd(). The result will be the same. + * + * @see nl_socket_alloc() + * @see nl_close() + * @see nl_socket_set_fd() + * + * @return 0 on success or a negative error code. + * + * @retval -NLE_BAD_SOCK Socket is already connected + */ +int nl_connect(struct nl_sock *sk, int protocol) +{ + int err, flags = 0; + int errsv; + socklen_t addrlen; + struct sockaddr_nl local = { 0 }; + int try_bind = 1; + +#ifdef SOCK_CLOEXEC + flags |= SOCK_CLOEXEC; +#endif + + if (sk->s_fd != -1) + return -NLE_BAD_SOCK; + + sk->s_fd = socket(AF_NETLINK, SOCK_RAW | flags, protocol); + if (sk->s_fd < 0) { + errsv = errno; + NL_DBG(4, "nl_connect(%p): socket() failed with %d (%s)\n", sk, errsv, + nl_strerror_l(errsv)); + err = -nl_syserr2nlerr(errsv); + goto errout; + } + + err = nl_socket_set_buffer_size(sk, 0, 0); + if (err < 0) + goto errout; + + if (_nl_socket_is_local_port_unspecified (sk)) { + uint32_t port; + uint32_t used_ports[32] = { 0 }; + int ntries = 0; + + while (1) { + if (ntries++ > 5) { + /* try only a few times. We hit this only if many ports are already in + * use but allocated *outside* libnl/generate_local_port(). */ + _nl_socket_set_local_port_no_release (sk, 0); + break; + } + + port = _nl_socket_set_local_port_no_release(sk, 1); + if (port == 0) + break; + + err = bind(sk->s_fd, (struct sockaddr*) &sk->s_local, + sizeof(sk->s_local)); + if (err == 0) { + try_bind = 0; + break; + } + + errsv = errno; + if (errsv == EADDRINUSE) { + NL_DBG(4, "nl_connect(%p): local port %u already in use. Retry.\n", sk, (unsigned) port); + _nl_socket_used_ports_set(used_ports, port); + } else { + NL_DBG(4, "nl_connect(%p): bind() for port %u failed with %d (%s)\n", + sk, (unsigned) port, errsv, nl_strerror_l(errsv)); + _nl_socket_used_ports_release_all(used_ports); + err = -nl_syserr2nlerr(errsv); + goto errout; + } + } + _nl_socket_used_ports_release_all(used_ports); + } + if (try_bind) { + err = bind(sk->s_fd, (struct sockaddr*) &sk->s_local, + sizeof(sk->s_local)); + if (err != 0) { + errsv = errno; + NL_DBG(4, "nl_connect(%p): bind() failed with %d (%s)\n", + sk, errsv, nl_strerror_l(errsv)); + err = -nl_syserr2nlerr(errsv); + goto errout; + } + } + + addrlen = sizeof(local); + err = getsockname(sk->s_fd, (struct sockaddr *) &local, + &addrlen); + if (err < 0) { + NL_DBG(4, "nl_connect(%p): getsockname() failed with %d (%s)\n", + sk, errno, nl_strerror_l(errno)); + err = -nl_syserr2nlerr(errno); + goto errout; + } + + if (addrlen != sizeof(local)) { + err = -NLE_NOADDR; + goto errout; + } + + if (local.nl_family != AF_NETLINK) { + err = -NLE_AF_NOSUPPORT; + goto errout; + } + + if (sk->s_local.nl_pid != local.nl_pid) { + /* The port id is different. That can happen if the port id was zero + * and kernel assigned a local port. */ + nl_socket_set_local_port (sk, local.nl_pid); + } + sk->s_local = local; + sk->s_proto = protocol; + + return 0; +errout: + if (sk->s_fd != -1) { + close(sk->s_fd); + sk->s_fd = -1; + } + + return err; +} + +/** + * Close Netlink socket + * @arg sk Netlink socket (required) + * + * Closes the Netlink socket using `close()`. + * + * @note The socket is closed automatically if a `struct nl_sock` object is + * freed using `nl_socket_free()`. + * + * @see nl_connect() + */ +void nl_close(struct nl_sock *sk) +{ + if (sk->s_fd >= 0) { + close(sk->s_fd); + sk->s_fd = -1; + } + + sk->s_proto = 0; +} + +/** @} */ + +/** + * @name Send + * @{ + */ + +/** + * Transmit raw data over Netlink socket. + * @arg sk Netlink socket (required) + * @arg buf Buffer carrying data to send (required) + * @arg size Size of buffer (required) + * + * Transmits "raw" data over the specified Netlink socket. Unlike the other + * transmit functions it does not modify the data in any way. It directly + * passes the buffer \c buf of \c size to sendto(). + * + * The message is addressed to the peer as specified in the socket by either + * the nl_socket_set_peer_port() or nl_socket_set_peer_groups() function. + * + * @note Because there is no indication on the message boundaries of the data + * being sent, the \c NL_CB_MSG_OUT callback handler will not be invoked + * for data that is being sent using this function. + * + * @see nl_socket_set_peer_port() + * @see nl_socket_set_peer_groups() + * @see nl_sendmsg() + * + * @return Number of bytes sent or a negative error code. + */ +int nl_sendto(struct nl_sock *sk, void *buf, size_t size) +{ + int ret; + + if (!buf) + return -NLE_INVAL; + + if (sk->s_fd < 0) + return -NLE_BAD_SOCK; + + ret = sendto(sk->s_fd, buf, size, 0, (struct sockaddr *) + &sk->s_peer, sizeof(sk->s_peer)); + if (ret < 0) { + NL_DBG(4, "nl_sendto(%p): sendto() failed with %d (%s)\n", + sk, errno, nl_strerror_l(errno)); + return -nl_syserr2nlerr(errno); + } + + return ret; +} + +/** + * Transmit Netlink message using sendmsg() + * @arg sk Netlink socket (required) + * @arg msg Netlink message to be sent (required) + * @arg hdr sendmsg() message header (required) + * + * Transmits the message specified in \c hdr over the Netlink socket using the + * sendmsg() system call. + * + * @attention + * The `msg` argument will *not* be used to derive the message payload that + * is being sent out. The `msg` argument is *only* passed on to the + * `NL_CB_MSG_OUT` callback. The caller is responsible to initialize the + * `hdr` struct properly and have it point to the message payload and + * socket address. + * + * @note + * This function uses `nlmsg_set_src()` to modify the `msg` argument prior to + * invoking the `NL_CB_MSG_OUT` callback to provide the local port number. + * + * @callback This function triggers the `NL_CB_MSG_OUT` callback. + * + * @attention + * Think twice before using this function. It provides a low level access to + * the Netlink socket. Among other limitations, it does not add credentials + * even if enabled or respect the destination address specified in the `msg` + * object. + * + * @see nl_socket_set_local_port() + * @see nl_send_auto() + * @see nl_send_iovec() + * + * @return Number of bytes sent on success or a negative error code. + * + * @lowlevel + */ +int nl_sendmsg(struct nl_sock *sk, struct nl_msg *msg, struct msghdr *hdr) +{ + struct nl_cb *cb; + int ret; + + if (sk->s_fd < 0) + return -NLE_BAD_SOCK; + + nlmsg_set_src(msg, &sk->s_local); + + cb = sk->s_cb; + if (cb->cb_set[NL_CB_MSG_OUT]) + if ((ret = nl_cb_call(cb, NL_CB_MSG_OUT, msg)) != NL_OK) + return ret; + + ret = sendmsg(sk->s_fd, hdr, 0); + if (ret < 0) { + NL_DBG(4, "nl_sendmsg(%p): sendmsg() failed with %d (%s)\n", + sk, errno, nl_strerror_l(errno)); + return -nl_syserr2nlerr(errno); + } + + NL_DBG(4, "sent %d bytes\n", ret); + return ret; +} + + +/** + * Transmit Netlink message (taking IO vector) + * @arg sk Netlink socket (required) + * @arg msg Netlink message to be sent (required) + * @arg iov IO vector to be sent (required) + * @arg iovlen Number of struct iovec to be sent (required) + * + * This function is identical to nl_send() except that instead of taking a + * `struct nl_msg` object it takes an IO vector. Please see the description + * of `nl_send()`. + * + * @callback This function triggers the `NL_CB_MSG_OUT` callback. + * + * @see nl_send() + * + * @return Number of bytes sent on success or a negative error code. + * + * @lowlevel + */ +int nl_send_iovec(struct nl_sock *sk, struct nl_msg *msg, struct iovec *iov, unsigned iovlen) +{ + struct sockaddr_nl *dst; + struct ucred *creds; + struct msghdr hdr = { + .msg_name = (void *) &sk->s_peer, + .msg_namelen = sizeof(struct sockaddr_nl), + .msg_iov = iov, + .msg_iovlen = iovlen, + }; + char buf[CMSG_SPACE(sizeof(struct ucred))]; + + /* Overwrite destination if specified in the message itself, defaults + * to the peer address of the socket. + */ + dst = nlmsg_get_dst(msg); + if (dst->nl_family == AF_NETLINK) + hdr.msg_name = dst; + + /* Add credentials if present. */ + creds = nlmsg_get_creds(msg); + if (creds != NULL) { + struct cmsghdr *cmsg; + + hdr.msg_control = buf; + hdr.msg_controllen = sizeof(buf); + + cmsg = CMSG_FIRSTHDR(&hdr); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_CREDENTIALS; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); + memcpy(CMSG_DATA(cmsg), creds, sizeof(struct ucred)); + } + + return nl_sendmsg(sk, msg, &hdr); +} + +/** + * Transmit Netlink message + * @arg sk Netlink socket (required) + * @arg msg Netlink message (required) + * + * Transmits the Netlink message `msg` over the Netlink socket using the + * `sendmsg()` system call. This function is based on `nl_send_iovec()` but + * takes care of initializing a `struct iovec` based on the `msg` object. + * + * The message is addressed to the peer as specified in the socket by either + * the nl_socket_set_peer_port() or nl_socket_set_peer_groups() function. + * The peer address can be overwritten by specifying an address in the `msg` + * object using nlmsg_set_dst(). + * + * If present in the `msg`, credentials set by the nlmsg_set_creds() function + * are added to the control buffer of the message. + * + * @par Overwriting Capability: + * Calls to this function can be overwritten by providing an alternative using + * the nl_cb_overwrite_send() function. + * + * @callback This function triggers the `NL_CB_MSG_OUT` callback. + * + * @attention + * Unlike `nl_send_auto()`, this function does *not* finalize the message in + * terms of automatically adding needed flags or filling out port numbers. + * + * @see nl_send_auto() + * @see nl_send_iovec() + * @see nl_socket_set_peer_port() + * @see nl_socket_set_peer_groups() + * @see nlmsg_set_dst() + * @see nlmsg_set_creds() + * @see nl_cb_overwrite_send() + * + * @return Number of bytes sent on success or a negative error code. +*/ +int nl_send(struct nl_sock *sk, struct nl_msg *msg) +{ + struct nl_cb *cb = sk->s_cb; + + if (cb->cb_send_ow) + return cb->cb_send_ow(sk, msg); + else { + struct iovec iov = { + .iov_base = (void *) nlmsg_hdr(msg), + .iov_len = nlmsg_hdr(msg)->nlmsg_len, + }; + + return nl_send_iovec(sk, msg, &iov, 1); + } +} + +/** + * Finalize Netlink message + * @arg sk Netlink socket (required) + * @arg msg Netlink message (required) + * + * This function finalizes a Netlink message by completing the message with + * desirable flags and values depending on the socket configuration. + * + * - If not yet filled out, the source address of the message (`nlmsg_pid`) + * will be set to the local port number of the socket. + * - If not yet specified, the next available sequence number is assigned + * to the message (`nlmsg_seq`). + * - If not yet specified, the protocol field of the message will be set to + * the protocol field of the socket. + * - The `NLM_F_REQUEST` Netlink message flag will be set. + * - The `NLM_F_ACK` flag will be set if Auto-ACK mode is enabled on the + * socket. + */ +void nl_complete_msg(struct nl_sock *sk, struct nl_msg *msg) +{ + struct nlmsghdr *nlh; + + nlh = nlmsg_hdr(msg); + if (nlh->nlmsg_pid == NL_AUTO_PORT) + nlh->nlmsg_pid = nl_socket_get_local_port(sk); + + if (nlh->nlmsg_seq == NL_AUTO_SEQ) + nlh->nlmsg_seq = sk->s_seq_next++; + + if (msg->nm_protocol == -1) + msg->nm_protocol = sk->s_proto; + + nlh->nlmsg_flags |= NLM_F_REQUEST; + + if (!(sk->s_flags & NL_NO_AUTO_ACK)) + nlh->nlmsg_flags |= NLM_F_ACK; +} + +/** + * Finalize and transmit Netlink message + * @arg sk Netlink socket (required) + * @arg msg Netlink message (required) + * + * Finalizes the message by passing it to `nl_complete_msg()` and transmits it + * by passing it to `nl_send()`. + * + * @callback This function triggers the `NL_CB_MSG_OUT` callback. + * + * @see nl_complete_msg() + * @see nl_send() + * + * @return Number of bytes sent or a negative error code. + */ +int nl_send_auto(struct nl_sock *sk, struct nl_msg *msg) +{ + nl_complete_msg(sk, msg); + + return nl_send(sk, msg); +} + +/** + * Finalize and transmit Netlink message and wait for ACK or error message + * @arg sk Netlink socket (required) + * @arg msg Netlink message (required) + * + * Passes the `msg` to `nl_send_auto()` to finalize and transmit it. Frees the + * message and waits (sleeps) for the ACK or error message to be received. + * + * @attention + * Disabling Auto-ACK (nl_socket_disable_auto_ack()) will cause this function + * to return immediately after transmitting the message. However, the peer may + * still be returning an error message in response to the request. It is the + * responsibility of the caller to handle such messages. + * + * @callback This function triggers the `NL_CB_MSG_OUT` callback. + * + * @attention + * This function frees the `msg` object after transmitting it by calling + * `nlmsg_free()`. + * + * @see nl_send_auto(). + * @see nl_wait_for_ack() + * + * @return 0 on success or a negative error code. + */ +int nl_send_sync(struct nl_sock *sk, struct nl_msg *msg) +{ + int err; + + err = nl_send_auto(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +/** + * Construct and transmit a Netlink message + * @arg sk Netlink socket (required) + * @arg type Netlink message type (required) + * @arg flags Netlink message flags (optional) + * @arg buf Data buffer (optional) + * @arg size Size of data buffer (optional) + * + * Allocates a new Netlink message based on `type` and `flags`. If `buf` + * points to payload of length `size` that payload will be appended to the + * message. + * + * Sends out the message using `nl_send_auto()` and frees the message + * afterwards. + * + * @see nl_send_auto() + * + * @return Number of characters sent on success or a negative error code. + * @retval -NLE_NOMEM Unable to allocate Netlink message + */ +int nl_send_simple(struct nl_sock *sk, int type, int flags, void *buf, + size_t size) +{ + int err; + struct nl_msg *msg; + + msg = nlmsg_alloc_simple(type, flags); + if (!msg) + return -NLE_NOMEM; + + if (buf && size) { + err = nlmsg_append(msg, buf, size, NLMSG_ALIGNTO); + if (err < 0) + goto errout; + } + + err = nl_send_auto(sk, msg); +errout: + nlmsg_free(msg); + + return err; +} + +/** @} */ + +/** + * @name Receive + * @{ + */ + +/** + * Receive data from netlink socket + * @arg sk Netlink socket (required) + * @arg nla Netlink socket structure to hold address of peer (required) + * @arg buf Destination pointer for message content (required) + * @arg creds Destination pointer for credentials (optional) + * + * Receives data from a connected netlink socket using recvmsg() and returns + * the number of bytes read. The read data is stored in a newly allocated + * buffer that is assigned to \c *buf. The peer's netlink address will be + * stored in \c *nla. + * + * This function blocks until data is available to be read unless the socket + * has been put into non-blocking mode using nl_socket_set_nonblocking() in + * which case this function will return immediately with a return value of + * -NLA_AGAIN (versions before 3.2.22 returned instead 0, in which case you + * should check first clear errno and then check for errno EAGAIN). + * + * The buffer size used when reading from the netlink socket and thus limiting + * the maximum size of a netlink message that can be read defaults to the size + * of a memory page (getpagesize()). The buffer size can be modified on a per + * socket level using the function nl_socket_set_msg_buf_size(). + * + * If message peeking is enabled using nl_socket_enable_msg_peek() the size of + * the message to be read will be determined using the MSG_PEEK flag prior to + * performing the actual read. This leads to an additional recvmsg() call for + * every read operation which has performance implications and is not + * recommended for high throughput protocols. + * + * An eventual interruption of the recvmsg() system call is automatically + * handled by retrying the operation. + * + * If receiving of credentials has been enabled using the function + * nl_socket_set_passcred(), this function will allocate a new struct ucred + * filled with the received credentials and assign it to \c *creds. The caller + * is responsible for freeing the buffer. + * + * @note The caller is responsible to free the returned data buffer and if + * enabled, the credentials buffer. + * + * @see nl_socket_set_nonblocking() + * @see nl_socket_set_msg_buf_size() + * @see nl_socket_enable_msg_peek() + * @see nl_socket_set_passcred() + * + * @return Number of bytes read, 0 on EOF, 0 on no data event (non-blocking + * mode), or a negative error code. + */ +int nl_recv(struct nl_sock *sk, struct sockaddr_nl *nla, + unsigned char **buf, struct ucred **creds) +{ + ssize_t n; + int flags = 0; + static int page_size = 0; + struct iovec iov; + struct msghdr msg = { + .msg_name = (void *) nla, + .msg_namelen = sizeof(struct sockaddr_nl), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + struct ucred* tmpcreds = NULL; + int retval = 0; + + if (!buf || !nla) + return -NLE_INVAL; + + if ( (sk->s_flags & NL_MSG_PEEK) + || (!(sk->s_flags & NL_MSG_PEEK_EXPLICIT) && sk->s_bufsize == 0)) + flags |= MSG_PEEK | MSG_TRUNC; + + if (page_size == 0) + page_size = getpagesize() * 4; + + iov.iov_len = sk->s_bufsize ? sk->s_bufsize : page_size; + iov.iov_base = malloc(iov.iov_len); + + if (!iov.iov_base) { + retval = -NLE_NOMEM; + goto abort; + } + + if (creds && (sk->s_flags & NL_SOCK_PASSCRED)) { + msg.msg_controllen = CMSG_SPACE(sizeof(struct ucred)); + msg.msg_control = malloc(msg.msg_controllen); + if (!msg.msg_control) { + retval = -NLE_NOMEM; + goto abort; + } + } +retry: + + n = recvmsg(sk->s_fd, &msg, flags); + if (!n) { + retval = 0; + goto abort; + } + if (n < 0) { + if (errno == EINTR) { + NL_DBG(3, "recvmsg() returned EINTR, retrying\n"); + goto retry; + } + + NL_DBG(4, "recvmsg(%p): nl_recv() failed with %d (%s)\n", + sk, errno, nl_strerror_l(errno)); + retval = -nl_syserr2nlerr(errno); + goto abort; + } + + if (msg.msg_flags & MSG_CTRUNC) { + void *tmp; + + if (msg.msg_controllen == 0) { + retval = -NLE_MSG_TRUNC; + NL_DBG(4, "recvmsg(%p): Received unexpected control data", sk); + goto abort; + } + + msg.msg_controllen *= 2; + tmp = realloc(msg.msg_control, msg.msg_controllen); + if (!tmp) { + retval = -NLE_NOMEM; + goto abort; + } + msg.msg_control = tmp; + goto retry; + } + + if (iov.iov_len < n || (msg.msg_flags & MSG_TRUNC)) { + void *tmp; + + /* respond with error to an incomplete message */ + if (flags == 0) { + retval = -NLE_MSG_TRUNC; + goto abort; + } + + /* Provided buffer is not long enough, enlarge it + * to size of n (which should be total length of the message) + * and try again. */ + iov.iov_len = n; + tmp = realloc(iov.iov_base, iov.iov_len); + if (!tmp) { + retval = -NLE_NOMEM; + goto abort; + } + iov.iov_base = tmp; + flags = 0; + goto retry; + } + + if (flags != 0) { + /* Buffer is big enough, do the actual reading */ + flags = 0; + goto retry; + } + + if (msg.msg_namelen != sizeof(struct sockaddr_nl)) { + retval = -NLE_NOADDR; + goto abort; + } + + if (creds && (sk->s_flags & NL_SOCK_PASSCRED)) { + struct cmsghdr *cmsg; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level != SOL_SOCKET) + continue; + if (cmsg->cmsg_type != SCM_CREDENTIALS) + continue; + tmpcreds = malloc(sizeof(*tmpcreds)); + if (!tmpcreds) { + retval = -NLE_NOMEM; + goto abort; + } + memcpy(tmpcreds, CMSG_DATA(cmsg), sizeof(*tmpcreds)); + break; + } + } + + retval = n; +abort: + free(msg.msg_control); + + if (retval <= 0) { + free(iov.iov_base); + iov.iov_base = NULL; + free(tmpcreds); + tmpcreds = NULL; + } else + *buf = iov.iov_base; + + if (creds) + *creds = tmpcreds; + + return retval; +} + +/** @cond SKIP */ +#define NL_CB_CALL(cb, type, msg) \ +do { \ + err = nl_cb_call(cb, type, msg); \ + switch (err) { \ + case NL_OK: \ + err = 0; \ + break; \ + case NL_SKIP: \ + goto skip; \ + case NL_STOP: \ + goto stop; \ + default: \ + goto out; \ + } \ +} while (0) +/** @endcond */ + +static int recvmsgs(struct nl_sock *sk, struct nl_cb *cb) +{ + int n, err = 0, multipart = 0, interrupted = 0, nrecv = 0; + unsigned char *buf = NULL; + struct nlmsghdr *hdr; + + /* + nla is passed on to not only to nl_recv() but may also be passed + to a function pointer provided by the caller which may or may not + initialize the variable. Thomas Graf. + */ + struct sockaddr_nl nla = {0}; + struct nl_msg *msg = NULL; + struct ucred *creds = NULL; + +continue_reading: + NL_DBG(3, "Attempting to read from %p\n", sk); + if (cb->cb_recv_ow) + n = cb->cb_recv_ow(sk, &nla, &buf, &creds); + else + n = nl_recv(sk, &nla, &buf, &creds); + + if (n <= 0) + return n; + + NL_DBG(3, "recvmsgs(%p): Read %d bytes\n", sk, n); + + hdr = (struct nlmsghdr *) buf; + while (nlmsg_ok(hdr, n)) { + NL_DBG(3, "recvmsgs(%p): Processing valid message...\n", sk); + + nlmsg_free(msg); + msg = nlmsg_convert(hdr); + if (!msg) { + err = -NLE_NOMEM; + goto out; + } + + nlmsg_set_proto(msg, sk->s_proto); + nlmsg_set_src(msg, &nla); + if (creds) + nlmsg_set_creds(msg, creds); + + nrecv++; + + /* Raw callback is the first, it gives the most control + * to the user and he can do his very own parsing. */ + if (cb->cb_set[NL_CB_MSG_IN]) + NL_CB_CALL(cb, NL_CB_MSG_IN, msg); + + /* Sequence number checking. The check may be done by + * the user, otherwise a very simple check is applied + * enforcing strict ordering */ + if (cb->cb_set[NL_CB_SEQ_CHECK]) { + NL_CB_CALL(cb, NL_CB_SEQ_CHECK, msg); + + /* Only do sequence checking if auto-ack mode is enabled */ + } else if (!(sk->s_flags & NL_NO_AUTO_ACK)) { + if (hdr->nlmsg_seq != sk->s_seq_expect) { + if (cb->cb_set[NL_CB_INVALID]) + NL_CB_CALL(cb, NL_CB_INVALID, msg); + else { + err = -NLE_SEQ_MISMATCH; + goto out; + } + } + } + + if (hdr->nlmsg_type == NLMSG_DONE || + hdr->nlmsg_type == NLMSG_ERROR || + hdr->nlmsg_type == NLMSG_NOOP || + hdr->nlmsg_type == NLMSG_OVERRUN) { + /* We can't check for !NLM_F_MULTI since some netlink + * users in the kernel are broken. */ + sk->s_seq_expect++; + NL_DBG(3, "recvmsgs(%p): Increased expected " \ + "sequence number to %d\n", + sk, sk->s_seq_expect); + } + + if (hdr->nlmsg_flags & NLM_F_MULTI) + multipart = 1; + + if (hdr->nlmsg_flags & NLM_F_DUMP_INTR) { + if (cb->cb_set[NL_CB_DUMP_INTR]) + NL_CB_CALL(cb, NL_CB_DUMP_INTR, msg); + else { + /* + * We have to continue reading to clear + * all messages until a NLMSG_DONE is + * received and report the inconsistency. + */ + interrupted = 1; + } + } + + /* Other side wishes to see an ack for this message */ + if (hdr->nlmsg_flags & NLM_F_ACK) { + if (cb->cb_set[NL_CB_SEND_ACK]) + NL_CB_CALL(cb, NL_CB_SEND_ACK, msg); + else { + /* FIXME: implement */ + } + } + + /* messages terminates a multipart message, this is + * usually the end of a message and therefore we slip + * out of the loop by default. the user may overrule + * this action by skipping this packet. */ + if (hdr->nlmsg_type == NLMSG_DONE) { + multipart = 0; + if (cb->cb_set[NL_CB_FINISH]) + NL_CB_CALL(cb, NL_CB_FINISH, msg); + } + + /* Message to be ignored, the default action is to + * skip this message if no callback is specified. The + * user may overrule this action by returning + * NL_PROCEED. */ + else if (hdr->nlmsg_type == NLMSG_NOOP) { + if (cb->cb_set[NL_CB_SKIPPED]) + NL_CB_CALL(cb, NL_CB_SKIPPED, msg); + else + goto skip; + } + + /* Data got lost, report back to user. The default action is to + * quit parsing. The user may overrule this action by retuning + * NL_SKIP or NL_PROCEED (dangerous) */ + else if (hdr->nlmsg_type == NLMSG_OVERRUN) { + if (cb->cb_set[NL_CB_OVERRUN]) + NL_CB_CALL(cb, NL_CB_OVERRUN, msg); + else { + err = -NLE_MSG_OVERFLOW; + goto out; + } + } + + /* Message carries a nlmsgerr */ + else if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *e = nlmsg_data(hdr); + + if (hdr->nlmsg_len < nlmsg_size(sizeof(*e))) { + /* Truncated error message, the default action + * is to stop parsing. The user may overrule + * this action by returning NL_SKIP or + * NL_PROCEED (dangerous) */ + if (cb->cb_set[NL_CB_INVALID]) + NL_CB_CALL(cb, NL_CB_INVALID, msg); + else { + err = -NLE_MSG_TRUNC; + goto out; + } + } else if (e->error) { + NL_DBG(4, "recvmsgs(%p): RTNETLINK responded with %d (%s)\n", + sk, -e->error, nl_strerror_l(-e->error)); + + /* Error message reported back from kernel. */ + if (cb->cb_err) { + err = cb->cb_err(&nla, e, + cb->cb_err_arg); + if (err < 0) + goto out; + else if (err == NL_SKIP) + goto skip; + else if (err == NL_STOP) { + err = -nl_syserr2nlerr(e->error); + goto out; + } + } else { + err = -nl_syserr2nlerr(e->error); + goto out; + } + } else if (cb->cb_set[NL_CB_ACK]) + NL_CB_CALL(cb, NL_CB_ACK, msg); + } else { + /* Valid message (not checking for MULTIPART bit to + * get along with broken kernels. NL_SKIP has no + * effect on this. */ + if (cb->cb_set[NL_CB_VALID]) + NL_CB_CALL(cb, NL_CB_VALID, msg); + } +skip: + err = 0; + hdr = nlmsg_next(hdr, &n); + } + + nlmsg_free(msg); + free(buf); + free(creds); + buf = NULL; + msg = NULL; + creds = NULL; + + if (multipart) { + /* Multipart message not yet complete, continue reading */ + goto continue_reading; + } +stop: + err = 0; +out: + nlmsg_free(msg); + free(buf); + free(creds); + + if (interrupted) + err = -NLE_DUMP_INTR; + + if (!err) + err = nrecv; + + return err; +} + +/** + * Receive a set of messages from a netlink socket and report parsed messages + * @arg sk Netlink socket. + * @arg cb set of callbacks to control behaviour. + * + * This function is identical to nl_recvmsgs() to the point that it will + * return the number of parsed messages instead of 0 on success. + * + * @see nl_recvmsgs() + * + * @return Number of received messages or a negative error code from nl_recv(). + */ +int nl_recvmsgs_report(struct nl_sock *sk, struct nl_cb *cb) +{ + if (cb->cb_recvmsgs_ow) + return cb->cb_recvmsgs_ow(sk, cb); + else + return recvmsgs(sk, cb); +} + +/** + * Receive a set of messages from a netlink socket. + * @arg sk Netlink socket. + * @arg cb set of callbacks to control behaviour. + * + * Repeatedly calls nl_recv() or the respective replacement if provided + * by the application (see nl_cb_overwrite_recv()) and parses the + * received data as netlink messages. Stops reading if one of the + * callbacks returns NL_STOP or nl_recv returns either 0 or a negative error code. + * + * A non-blocking sockets causes the function to return immediately if + * no data is available. + * + * @see nl_recvmsgs_report() + * + * @return 0 on success or a negative error code from nl_recv(). + */ +int nl_recvmsgs(struct nl_sock *sk, struct nl_cb *cb) +{ + int err; + + if ((err = nl_recvmsgs_report(sk, cb)) > 0) + err = 0; + + return err; +} + +/** + * Receive a set of message from a netlink socket using handlers in nl_sock. + * @arg sk Netlink socket. + * + * Calls nl_recvmsgs() with the handlers configured in the netlink socket. + */ +int nl_recvmsgs_default(struct nl_sock *sk) +{ + return nl_recvmsgs(sk, sk->s_cb); + +} + +static int ack_wait_handler(struct nl_msg *msg, void *arg) +{ + return NL_STOP; +} + +/** + * Wait for ACK. + * @arg sk Netlink socket. + * @pre The netlink socket must be in blocking state. + * + * Waits until an ACK is received for the latest not yet acknowledged + * netlink message. + */ +int nl_wait_for_ack(struct nl_sock *sk) +{ + int err; + struct nl_cb *cb; + + cb = nl_cb_clone(sk->s_cb); + if (cb == NULL) + return -NLE_NOMEM; + + nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_wait_handler, NULL); + err = nl_recvmsgs(sk, cb); + nl_cb_put(cb); + + return err; +} + +/** @cond SKIP */ +struct pickup_param +{ + int (*parser)(struct nl_cache_ops *, struct sockaddr_nl *, + struct nlmsghdr *, struct nl_parser_param *); + struct nl_object *result; + int *syserror; +}; + +static int __store_answer(struct nl_object *obj, struct nl_parser_param *p) +{ + struct pickup_param *pp = p->pp_arg; + /* + * the parser will put() the object at the end, expecting the cache + * to take the reference. + */ + nl_object_get(obj); + pp->result = obj; + + return 0; +} + +static int __pickup_answer(struct nl_msg *msg, void *arg) +{ + struct pickup_param *pp = arg; + struct nl_parser_param parse_arg = { + .pp_cb = __store_answer, + .pp_arg = pp, + }; + + return pp->parser(NULL, &msg->nm_src, msg->nm_nlh, &parse_arg); +} + +static int __pickup_answer_syserr(struct sockaddr_nl *nla, struct nlmsgerr *nlerr, void *arg) +{ + *(((struct pickup_param *) arg)->syserror) = nlerr->error; + + return -nl_syserr2nlerr(nlerr->error); +} + +/** @endcond */ + +/** + * Pickup netlink answer, parse is and return object + * @arg sk Netlink socket + * @arg parser Parser function to parse answer + * @arg result Result pointer to return parsed object + * + * @return 0 on success or a negative error code. + */ +int nl_pickup(struct nl_sock *sk, + int (*parser)(struct nl_cache_ops *, struct sockaddr_nl *, + struct nlmsghdr *, struct nl_parser_param *), + struct nl_object **result) +{ + return nl_pickup_keep_syserr(sk, parser, result, NULL); +} + +/** + * Pickup netlink answer, parse is and return object with preserving system error + * @arg sk Netlink socket + * @arg parser Parser function to parse answer + * @arg result Result pointer to return parsed object + * @arg syserr Result pointer for the system error in case of failure + * + * @return 0 on success or a negative error code. + */ +int nl_pickup_keep_syserr(struct nl_sock *sk, + int (*parser)(struct nl_cache_ops *, struct sockaddr_nl *, + struct nlmsghdr *, struct nl_parser_param *), + struct nl_object **result, + int *syserror) +{ + struct nl_cb *cb; + int err; + struct pickup_param pp = { + .parser = parser, + }; + + cb = nl_cb_clone(sk->s_cb); + if (cb == NULL) + return -NLE_NOMEM; + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, __pickup_answer, &pp); + if (syserror) { + *syserror = 0; + pp.syserror = syserror; + nl_cb_err(cb, NL_CB_CUSTOM, __pickup_answer_syserr, &pp); + } + + err = nl_recvmsgs(sk, cb); + if (err < 0) + goto errout; + + *result = pp.result; +errout: + nl_cb_put(cb); + + return err; +} + +/** @} */ + +/** + * @name Deprecated + * @{ + */ + +/** + * @deprecated Please use nl_complete_msg() + */ +void nl_auto_complete(struct nl_sock *sk, struct nl_msg *msg) +{ + nl_complete_msg(sk, msg); +} + +/** + * @deprecated Please use nl_send_auto() + */ +int nl_send_auto_complete(struct nl_sock *sk, struct nl_msg *msg) +{ + return nl_send_auto(sk, msg); +} + + +/** @} */ + +/** @} */ + +/** @} */ diff --git a/libnl/lib/object.c b/libnl/lib/object.c new file mode 100644 index 0000000..b8abd6a --- /dev/null +++ b/libnl/lib/object.c @@ -0,0 +1,569 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +/** + * @ingroup core_types + * @defgroup object Object (Cacheable) + * + * Generic object data type, for inheritance purposes to implement cacheable + * data types. + * + * Related sections in the development guide: + * + * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include + +static inline struct nl_object_ops *obj_ops(struct nl_object *obj) +{ + if (!obj->ce_ops) + BUG(); + + return obj->ce_ops; +} + +/** + * @name Object Creation/Deletion + * @{ + */ + +/** + * Allocate a new object of kind specified by the operations handle + * @arg ops cache operations handle + * @return The new object or NULL + */ +struct nl_object *nl_object_alloc(struct nl_object_ops *ops) +{ + struct nl_object *new; + + if (ops->oo_size < sizeof(*new)) + BUG(); + + new = calloc(1, ops->oo_size); + if (!new) + return NULL; + + new->ce_refcnt = 1; + nl_init_list_head(&new->ce_list); + + new->ce_ops = ops; + if (ops->oo_constructor) + ops->oo_constructor(new); + + NL_DBG(4, "Allocated new object %p\n", new); + + return new; +} + +/** + * Allocate new object of kind specified by the name + * @arg kind name of object type + * @arg result Result pointer + * + * @return 0 on success or a negative error code. + */ +int nl_object_alloc_name(const char *kind, struct nl_object **result) +{ + struct nl_cache_ops *ops; + + ops = nl_cache_ops_lookup_safe(kind); + if (!ops) + return -NLE_OPNOTSUPP; + + *result = nl_object_alloc(ops->co_obj_ops); + nl_cache_ops_put(ops); + if (!*result) + return -NLE_NOMEM; + + return 0; +} + +struct nl_derived_object { + NLHDR_COMMON + char data; +}; + +/** + * Allocate a new object and copy all data from an existing object + * @arg obj object to inherite data from + * @return The new object or NULL. + */ +struct nl_object *nl_object_clone(struct nl_object *obj) +{ + struct nl_object *new; + struct nl_object_ops *ops; + int doff = offsetof(struct nl_derived_object, data); + int size; + + if (!obj) + return NULL; + + ops = obj_ops(obj); + new = nl_object_alloc(ops); + if (!new) + return NULL; + + size = ops->oo_size - doff; + if (size < 0) + BUG(); + + new->ce_ops = obj->ce_ops; + new->ce_msgtype = obj->ce_msgtype; + new->ce_mask = obj->ce_mask; + + if (size) + memcpy((char *)new + doff, (char *)obj + doff, size); + + if (ops->oo_clone) { + if (ops->oo_clone(new, obj) < 0) { + nl_object_free(new); + return NULL; + } + } else if (size && ops->oo_free_data) + BUG(); + + return new; +} + +/** + * Merge a cacheable object + * @arg dst object to be merged into + * @arg src new object to be merged into dst + * + * @return 0 or a negative error code. + */ +int nl_object_update(struct nl_object *dst, struct nl_object *src) +{ + struct nl_object_ops *ops = obj_ops(dst); + + if (ops->oo_update) + return ops->oo_update(dst, src); + + return -NLE_OPNOTSUPP; +} + +/** + * Free a cacheable object + * @arg obj object to free + * + * @return 0 or a negative error code. + */ +void nl_object_free(struct nl_object *obj) +{ + struct nl_object_ops *ops; + + if (!obj) + return; + + ops = obj_ops(obj); + + if (obj->ce_refcnt > 0) + NL_DBG(1, "Warning: Freeing object in use...\n"); + + if (obj->ce_cache) + nl_cache_remove(obj); + + if (ops->oo_free_data) + ops->oo_free_data(obj); + + NL_DBG(4, "Freed object %p\n", obj); + + free(obj); +} + +/** @} */ + +/** + * @name Reference Management + * @{ + */ + +/** + * Acquire a reference on a object + * @arg obj object to acquire reference from + */ +void nl_object_get(struct nl_object *obj) +{ + obj->ce_refcnt++; + NL_DBG(4, "New reference to object %p, total %d\n", + obj, obj->ce_refcnt); +} + +/** + * Release a reference from an object + * @arg obj object to release reference from + */ +void nl_object_put(struct nl_object *obj) +{ + if (!obj) + return; + + obj->ce_refcnt--; + NL_DBG(4, "Returned object reference %p, %d remaining\n", + obj, obj->ce_refcnt); + + if (obj->ce_refcnt < 0) + BUG(); + + if (obj->ce_refcnt <= 0) + nl_object_free(obj); +} + +/** + * Check whether this object is used by multiple users + * @arg obj object to check + * @return true or false + */ +int nl_object_shared(struct nl_object *obj) +{ + return obj->ce_refcnt > 1; +} + +/** @} */ + +/** + * @name Marks + * @{ + */ + +/** + * Add mark to object + * @arg obj Object to mark + */ +void nl_object_mark(struct nl_object *obj) +{ + obj->ce_flags |= NL_OBJ_MARK; +} + +/** + * Remove mark from object + * @arg obj Object to unmark + */ +void nl_object_unmark(struct nl_object *obj) +{ + obj->ce_flags &= ~NL_OBJ_MARK; +} + +/** + * Return true if object is marked + * @arg obj Object to check + * @return true if object is marked, otherwise false + */ +int nl_object_is_marked(struct nl_object *obj) +{ + return (obj->ce_flags & NL_OBJ_MARK); +} + +/** @} */ + +/** + * @name Utillities + * @{ + */ + +/** + * Dump this object according to the specified parameters + * @arg obj object to dump + * @arg params dumping parameters + */ +void nl_object_dump(struct nl_object *obj, struct nl_dump_params *params) +{ + if (params->dp_buf) + memset(params->dp_buf, 0, params->dp_buflen); + + dump_from_ops(obj, params); +} + +void nl_object_dump_buf(struct nl_object *obj, char *buf, size_t len) +{ + struct nl_dump_params dp = { + .dp_buf = buf, + .dp_buflen = len, + }; + + nl_object_dump(obj, &dp); +} + +/** + * Check if the identifiers of two objects are identical + * @arg a an object + * @arg b another object of same type + * + * @return true if both objects have equal identifiers, otherwise false. + */ +int nl_object_identical(struct nl_object *a, struct nl_object *b) +{ + struct nl_object_ops *ops; + uint64_t req_attrs_a; + uint64_t req_attrs_b; + + if (a == b) + return 1; + + /* Both objects must be of same type */ + ops = obj_ops(a); + if (ops != obj_ops(b)) + return 0; + + /* Can't judge unless we can compare */ + if (ops->oo_compare == NULL) + return 0; + + if (ops->oo_id_attrs_get) { + req_attrs_a = ops->oo_id_attrs_get(a); + req_attrs_b = ops->oo_id_attrs_get(b); + } else if (ops->oo_id_attrs) { + req_attrs_a = ops->oo_id_attrs; + req_attrs_b = req_attrs_a; + } else { + req_attrs_a = UINT64_MAX; + req_attrs_b = req_attrs_a; + } + + req_attrs_a &= a->ce_mask; + req_attrs_b &= b->ce_mask; + + /* Both objects must provide all required attributes to uniquely + * identify an object */ + if (req_attrs_a != req_attrs_b) + return 0; + + return !(ops->oo_compare(a, b, req_attrs_a, ID_COMPARISON)); +} + +/** + * Compute bitmask representing difference in attribute values + * @arg a an object + * @arg b another object of same type + * + * The bitmask returned is specific to an object type, each bit set represents + * an attribute which mismatches in either of the two objects. Unavailability + * of an attribute in one object and presence in the other is regarded a + * mismatch as well. + * + * @return Bitmask describing differences or 0 if they are completely identical. + */ +uint64_t nl_object_diff64(struct nl_object *a, struct nl_object *b) +{ + struct nl_object_ops *ops = obj_ops(a); + + if (ops != obj_ops(b) || ops->oo_compare == NULL) + return UINT64_MAX; + + return ops->oo_compare(a, b, UINT64_MAX, 0); +} + +/** + * Compute 32-bit bitmask representing difference in attribute values + * @arg a an object + * @arg b another object of same type + * + * The bitmask returned is specific to an object type, each bit set represents + * an attribute which mismatches in either of the two objects. Unavailability + * of an attribute in one object and presence in the other is regarded a + * mismatch as well. + * + * @return Bitmask describing differences or 0 if they are completely identical. + * 32nd bit indicates if higher bits from the 64-bit compare were + * different. + */ +uint32_t nl_object_diff(struct nl_object *a, struct nl_object *b) +{ + uint64_t diff; + + diff = nl_object_diff64(a, b); + + return (diff & ~((uint64_t) 0xFFFFFFFF)) + ? (uint32_t) diff | (1 << 31) + : (uint32_t) diff; +} + +/** + * Match a filter against an object + * @arg obj object to check + * @arg filter object of same type acting as filter + * + * @return 1 if the object matches the filter or 0 + * if no filter procedure is available or if the + * filter does not match. + */ +int nl_object_match_filter(struct nl_object *obj, struct nl_object *filter) +{ + struct nl_object_ops *ops = obj_ops(obj); + + if (ops != obj_ops(filter) || ops->oo_compare == NULL) + return 0; + + return !(ops->oo_compare(obj, filter, filter->ce_mask, + LOOSE_COMPARISON)); +} + +/** + * Convert bitmask of attributes to a character string + * @arg obj object of same type as attribute bitmask + * @arg attrs bitmask of attribute types + * @arg buf destination buffer + * @arg len length of destination buffer + * + * Converts the bitmask of attribute types into a list of attribute + * names separated by comas. + * + * @return destination buffer. + */ +char *nl_object_attrs2str(struct nl_object *obj, uint32_t attrs, + char *buf, size_t len) +{ + struct nl_object_ops *ops = obj_ops(obj); + + if (ops->oo_attrs2str != NULL) + return ops->oo_attrs2str(attrs, buf, len); + else { + memset(buf, 0, len); + return buf; + } +} + +/** + * Return list of attributes present in an object + * @arg obj an object + * @arg buf destination buffer + * @arg len length of destination buffer + * + * @return destination buffer. + */ +char *nl_object_attr_list(struct nl_object *obj, char *buf, size_t len) +{ + return nl_object_attrs2str(obj, obj->ce_mask, buf, len); +} + +/** + * Generate object hash key + * @arg obj the object + * @arg hashkey destination buffer to be used for key stream + * @arg hashtbl_sz hash table size + * + * @return hash key in destination buffer + */ +void nl_object_keygen(struct nl_object *obj, uint32_t *hashkey, + uint32_t hashtbl_sz) +{ + struct nl_object_ops *ops = obj_ops(obj); + + if (ops->oo_keygen) + ops->oo_keygen(obj, hashkey, hashtbl_sz); + else + *hashkey = 0; + + return; +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +/** + * Return number of references held + * @arg obj object + * + * @return The number of references held to this object + */ +int nl_object_get_refcnt(struct nl_object *obj) +{ + return obj->ce_refcnt; +} + +/** + * Return cache the object is associated with + * @arg obj object + * + * @note The returned pointer is not protected with a reference counter, + * it is your responsibility. + * + * @return Pointer to cache or NULL if not associated with a cache. + */ +struct nl_cache *nl_object_get_cache(struct nl_object *obj) +{ + return obj->ce_cache; +} + +/** + * Return the object's type + * @arg obj object + * + * FIXME: link to list of object types + * + * @return Name of the object type + */ +const char *nl_object_get_type(const struct nl_object *obj) +{ + if (!obj->ce_ops) + BUG(); + + return obj->ce_ops->oo_name; +} + +/** + * Return the netlink message type the object was derived from + * @arg obj object + * + * @return Netlink message type or 0. + */ +int nl_object_get_msgtype(const struct nl_object *obj) +{ + return obj->ce_msgtype; +} + +/** + * Return object operations structure + * @arg obj object + * + * @return Pointer to the object operations structure + */ +struct nl_object_ops *nl_object_get_ops(const struct nl_object *obj) +{ + return obj->ce_ops; +} + +/** + * Return object id attribute mask + * @arg obj object + * + * @return object id attribute mask + */ +uint32_t nl_object_get_id_attrs(struct nl_object *obj) +{ + struct nl_object_ops *ops = obj_ops(obj); + uint32_t id_attrs; + + if (!ops) + return 0; + + if (ops->oo_id_attrs_get) + id_attrs = ops->oo_id_attrs_get(obj); + else + id_attrs = ops->oo_id_attrs; + + return id_attrs; +} + +/** @} */ + +/** @} */ diff --git a/libnl/lib/route/act.c b/libnl/lib/route/act.c new file mode 100644 index 0000000..ca8de3a --- /dev/null +++ b/libnl/lib/route/act.c @@ -0,0 +1,592 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Cong Wang + */ + +/** + * @ingroup tc + * @defgroup act Action + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + + +static struct nl_object_ops act_obj_ops; +static struct nl_cache_ops rtnl_act_ops; + +struct rtnl_act * rtnl_act_next(struct rtnl_act *act) +{ + if (act == NULL) { + return NULL; + } + + return act->a_next; +} + +int rtnl_act_append(struct rtnl_act **head, struct rtnl_act *new) +{ + struct rtnl_act *p_act; + int count = 1; + + if (*head == NULL) { + *head = new; + return 0; + } + + p_act = *head; + while (p_act->a_next) { + ++count; + p_act = p_act->a_next; + } + + if (count > TCA_ACT_MAX_PRIO) + return -NLE_RANGE; + + p_act->a_next = new; + return 0; +} + +int rtnl_act_remove(struct rtnl_act **head, struct rtnl_act *act) +{ + struct rtnl_act *a, **ap; + + for (ap = head; (a = *ap) != NULL; ap = &a->a_next) + if (a == act) + break; + if (a) { + *ap = a->a_next; + a->a_next = NULL; + return 0; + } + + return -NLE_OBJ_NOTFOUND; +} + +static int rtnl_act_fill_one(struct nl_msg *msg, struct rtnl_act *act, int order) +{ + struct rtnl_tc *tc = TC_CAST(act); + struct rtnl_tc_ops *ops; + struct nlattr *nest; + int err = -NLE_NOMEM; + + nest = nla_nest_start(msg, order); + if (!nest) + goto nla_put_failure; + + if (tc->ce_mask & TCA_ATTR_KIND) + NLA_PUT_STRING(msg, TCA_ACT_KIND, tc->tc_kind); + + ops = rtnl_tc_get_ops(tc); + if (ops && (ops->to_msg_fill || ops->to_msg_fill_raw)) { + struct nlattr *opts; + void *data = rtnl_tc_data(tc); + + if (ops->to_msg_fill) { + if (!(opts = nla_nest_start(msg, TCA_ACT_OPTIONS))) + goto nla_put_failure; + + if ((err = ops->to_msg_fill(tc, data, msg)) < 0) + goto nla_put_failure; + + nla_nest_end(msg, opts); + } else if ((err = ops->to_msg_fill_raw(tc, data, msg)) < 0) + goto nla_put_failure; + } + nla_nest_end(msg, nest); + return 0; + +nla_put_failure: + return err; +} + +int rtnl_act_fill(struct nl_msg *msg, int attrtype, struct rtnl_act *act) +{ + struct rtnl_act *p_act = act; + struct nlattr *nest; + int err, order = 0; + + nest = nla_nest_start(msg, attrtype); + if (!nest) + return -NLE_MSGSIZE; + + while (p_act) { + err = rtnl_act_fill_one(msg, p_act, ++order); + if (err < 0) + return err; + p_act = p_act->a_next; + } + + nla_nest_end(msg, nest); + return 0; +} + +static int rtnl_act_msg_build(struct rtnl_act *act, int type, int flags, + struct nl_msg **result) +{ + struct nl_msg *msg; + struct tcamsg tcahdr = { + .tca_family = AF_UNSPEC, + }; + int err = -NLE_MSGSIZE; + + msg = nlmsg_alloc_simple(type, flags); + if (!msg) + return -NLE_NOMEM; + + if (nlmsg_append(msg, &tcahdr, sizeof(tcahdr), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + err = rtnl_act_fill(msg, TCA_ACT_TAB, act); + if (err < 0) + goto nla_put_failure; + + *result = msg; + return 0; + +nla_put_failure: + nlmsg_free(msg); + return err; +} + +static int act_build(struct rtnl_act *act, int type, int flags, + struct nl_msg **result) +{ + int err; + + err = rtnl_act_msg_build(act, type, flags, result); + if (err < 0) + return err; + return 0; +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct rtnl_act *rtnl_act_alloc(void) +{ + struct rtnl_tc *tc; + + tc = TC_CAST(nl_object_alloc(&act_obj_ops)); + if (tc) + tc->tc_type = RTNL_TC_TYPE_ACT; + + return (struct rtnl_act *) tc; +} + +void rtnl_act_get(struct rtnl_act *act) +{ + nl_object_get(OBJ_CAST(act)); +} + +void rtnl_act_put(struct rtnl_act *act) +{ + nl_object_put((struct nl_object *) act); +} + +/** @} */ + +/** + * @name Addition/Modification/Deletion + * @{ + */ + +/** + * Build a netlink message requesting the addition of an action + * @arg act Action to add + * @arg flags Additional netlink message flags + * @arg result Pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_act_add() with + * the exception that it will not send the message but return it int the + * provided return pointer instead. + * + * @see rtnl_act_add() + * + * @return 0 on success or a negative error code. + */ +int rtnl_act_build_add_request(struct rtnl_act *act, int flags, + struct nl_msg **result) +{ + return act_build(act, RTM_NEWACTION, flags, result); +} + +/** + * Add/Update action + * @arg sk Netlink socket + * @arg act Action to add/update + * @arg flags Additional netlink message flags + * + * Builds a \c RTM_NEWACTION netlink message requesting the addition + * of a new action and sends the message to the kernel. The + * configuration of the action is derived from the attributes of + * the specified traffic class. + * + * The following flags may be specified: + * - \c NLM_F_CREATE: Create action if it does not exist, + * otherwise -NLE_OBJ_NOTFOUND is returned. + * - \c NLM_F_EXCL: Return -NLE_EXISTS if an action with + * matching handle exists already. + * + * Existing actions with matching handles will be updated, unless + * the flag \c NLM_F_EXCL is specified. If no matching action + * exists, it will be created if the flag \c NLM_F_CREATE is set, + * otherwise the error -NLE_OBJ_NOTFOUND is returned. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause + * this function to return immediately after sending. In this case, + * it is the responsibility of the caller to handle any error + * messages returned. + * + * @return 0 on success or a negative error code. + */ +int rtnl_act_add(struct nl_sock *sk, struct rtnl_act *act, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_act_build_add_request(act, flags, &msg)) < 0) + return err; + + return nl_send_sync(sk, msg); +} + +/** + * Build a netlink message to change action attributes + * @arg act Action to change + * @arg flags additional netlink message flags + * @arg result Pointer to store resulting message. + * + * Builds a new netlink message requesting a change of a neigh + * attributes. The netlink message header isn't fully equipped with + * all relevant fields and must thus be sent out via nl_send_auto_complete() + * or supplemented as needed. + * + * @return 0 on success or a negative error code. + */ +int rtnl_act_build_change_request(struct rtnl_act *act, int flags, + struct nl_msg **result) +{ + return act_build(act, RTM_NEWACTION, NLM_F_REPLACE | flags, result); +} + +/** + * Change an action + * @arg sk Netlink socket. + * @arg act action to change + * @arg flags additional netlink message flags + * + * Builds a netlink message by calling rtnl_act_build_change_request(), + * sends the request to the kernel and waits for the next ACK to be + * received and thus blocks until the request has been processed. + * + * @return 0 on sucess or a negative error if an error occured. + */ +int rtnl_act_change(struct nl_sock *sk, struct rtnl_act *act, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_act_build_change_request(act, flags, &msg)) < 0) + return err; + + return nl_send_sync(sk, msg); +} + +/** + * Build netlink message requesting the deletion of an action + * @arg act Action to delete + * @arg flags Additional netlink message flags + * @arg result Pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_act_delete() with + * the exception that it will not send the message but return it in the + * provided return pointer instead. + * + * @see rtnl_act_delete() + * + * @return 0 on success or a negative error code. + */ +int rtnl_act_build_delete_request(struct rtnl_act *act, int flags, + struct nl_msg **result) +{ + return act_build(act, RTM_DELACTION, flags, result); +} + +/** + * Delete action + * @arg sk Netlink socket + * @arg act Action to delete + * @arg flags Additional netlink message flags + * + * Builds a \c RTM_DELACTION netlink message requesting the deletion + * of an action and sends the message to the kernel. + * + * The message is constructed out of the following attributes: + * - \c ifindex (required) + * - \c prio (required) + * - \c protocol (required) + * - \c handle (required) + * - \c parent (optional, if not specified parent equals root-qdisc) + * - \c kind (optional, must match if provided) + * + * All other action attributes including all class type specific + * attributes are ignored. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause + * this function to return immediately after sending. In this case, + * it is the responsibility of the caller to handle any error + * messages returned. + * + * @return 0 on success or a negative error code. + */ +int rtnl_act_delete(struct nl_sock *sk, struct rtnl_act *act, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_act_build_delete_request(act, flags, &msg)) < 0) + return err; + + return nl_send_sync(sk, msg); +} + +/** @} */ + +static void act_dump_line(struct rtnl_tc *tc, struct nl_dump_params *p) +{ +} + +void rtnl_act_put_all(struct rtnl_act **head) +{ + struct rtnl_act *curr, *next; + + curr = *head; + while (curr) { + next = curr->a_next; + rtnl_act_put(curr); + curr = next; + } + *head = NULL; +} + +int rtnl_act_parse(struct rtnl_act **head, struct nlattr *tb) +{ + struct rtnl_act *act; + struct rtnl_tc_ops *ops; + struct nlattr *tb2[TCA_ACT_MAX + 1]; + struct nlattr *nla[TCA_ACT_MAX_PRIO + 1]; + char kind[TCKINDSIZ]; + int err, i; + + err = nla_parse(nla, TCA_ACT_MAX_PRIO, nla_data(tb), + NLMSG_ALIGN(nla_len(tb)), NULL); + if (err < 0) + return err; + + for (i = 0; i < TCA_ACT_MAX_PRIO; i++) { + struct rtnl_tc *tc; + + if (nla[i] == NULL) + continue; + + act = rtnl_act_alloc(); + if (!act) { + err = -NLE_NOMEM; + goto err_free; + } + tc = TC_CAST(act); + err = nla_parse(tb2, TCA_ACT_MAX, nla_data(nla[i]), + nla_len(nla[i]), NULL); + if (err < 0) + goto err_free; + + if (tb2[TCA_ACT_KIND] == NULL) { + err = -NLE_MISSING_ATTR; + goto err_free; + } + + nla_strlcpy(kind, tb2[TCA_ACT_KIND], sizeof(kind)); + rtnl_tc_set_kind(tc, kind); + + if (tb2[TCA_ACT_OPTIONS]) { + tc->tc_opts = nl_data_alloc_attr(tb2[TCA_ACT_OPTIONS]); + if (!tc->tc_opts) { + err = -NLE_NOMEM; + goto err_free; + } + tc->ce_mask |= TCA_ATTR_OPTS; + } + + ops = rtnl_tc_get_ops(tc); + if (ops && ops->to_msg_parser) { + void *data = rtnl_tc_data(tc); + + if (!data) { + err = -NLE_NOMEM; + goto err_free; + } + + err = ops->to_msg_parser(tc, data); + if (err < 0) + goto err_free; + } + err = rtnl_act_append(head, act); + if (err < 0) + goto err_free; + } + return 0; + +err_free: + rtnl_act_put (act); + rtnl_act_put_all(head); + + return err; +} + +static int rtnl_act_msg_parse(struct nlmsghdr *n, struct rtnl_act **act) +{ + struct rtnl_tc *tc = TC_CAST(*act); + struct nl_cache *link_cache; + struct nlattr *tb[TCAA_MAX + 1]; + struct tcamsg *tm; + int err; + + tc->ce_msgtype = n->nlmsg_type; + + err = nlmsg_parse(n, sizeof(*tm), tb, TCAA_MAX, NULL); + if (err < 0) + return err; + + tm = nlmsg_data(n); + tc->tc_family = tm->tca_family; + + if (tb[TCA_ACT_TAB] == NULL) + return -NLE_MISSING_ATTR; + + err = rtnl_act_parse(act, tb[TCA_ACT_TAB]); + if (err < 0) + return err; + + if ((link_cache = __nl_cache_mngt_require("route/link"))) { + struct rtnl_link *link; + + if ((link = rtnl_link_get(link_cache, tc->tc_ifindex))) { + rtnl_tc_set_link(tc, link); + + /* rtnl_tc_set_link incs refcnt */ + rtnl_link_put(link); + } + } + + return 0; +} +static int act_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, struct nl_parser_param *pp) +{ + struct rtnl_act *act, *p_act; + int err; + + if (!(act = rtnl_act_alloc())) + return -NLE_NOMEM; + + if ((err = rtnl_act_msg_parse(nlh, &act)) < 0) + goto errout; + + p_act = act; + while(p_act) { + err = pp->pp_cb(OBJ_CAST(act), pp); + if (err) { + if (err > 0) { + _nl_assert_not_reached(); + err = -NLE_FAILURE; + } + break; + } + p_act = p_act->a_next; + } +errout: + rtnl_act_put(act); + + return err; +} + +static int act_request_update(struct nl_cache *cache, struct nl_sock *sk) +{ + struct tcamsg tcahdr = { + .tca_family = AF_UNSPEC, + }; + + return nl_send_simple(sk, RTM_GETACTION, NLM_F_DUMP, &tcahdr, + sizeof(tcahdr)); +} + +static struct rtnl_tc_type_ops act_ops = { + .tt_type = RTNL_TC_TYPE_ACT, + .tt_dump_prefix = "act", + .tt_dump = { + [NL_DUMP_LINE] = act_dump_line, + }, +}; + +static struct nl_cache_ops rtnl_act_ops = { + .co_name = "route/act", + .co_hdrsize = sizeof(struct tcmsg), + .co_msgtypes = { + { RTM_NEWACTION, NL_ACT_NEW, "new" }, + { RTM_DELACTION, NL_ACT_DEL, "del" }, + { RTM_GETACTION, NL_ACT_GET, "get" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_ROUTE, + .co_request_update = act_request_update, + .co_msg_parser = act_msg_parser, + .co_obj_ops = &act_obj_ops, +}; + +static struct nl_object_ops act_obj_ops = { + .oo_name = "route/act", + .oo_size = sizeof(struct rtnl_act), + .oo_free_data = rtnl_tc_free_data, + .oo_clone = rtnl_tc_clone, + .oo_dump = { + [NL_DUMP_LINE] = rtnl_tc_dump_line, + [NL_DUMP_DETAILS] = rtnl_tc_dump_details, + [NL_DUMP_STATS] = rtnl_tc_dump_stats, + }, + .oo_compare = rtnl_tc_compare, + .oo_id_attrs = (TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE), +}; + +static void __init act_init(void) +{ + rtnl_tc_type_register(&act_ops); + nl_cache_mngt_register(&rtnl_act_ops); +} + +static void __exit act_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_act_ops); + rtnl_tc_type_unregister(&act_ops); +} + +/** @} */ diff --git a/libnl/lib/route/act/gact.c b/libnl/lib/route/act/gact.c new file mode 100644 index 0000000..645ca97 --- /dev/null +++ b/libnl/lib/route/act/gact.c @@ -0,0 +1,179 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2016 Sushma Sitaram + */ + +/** + * @ingroup act + * @defgroup act_gact GACT Editing + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +static struct nla_policy gact_policy[TCA_GACT_MAX + 1] = { + [TCA_GACT_PARMS] = { .minlen = sizeof(struct tc_gact) }, +}; + +static int gact_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct rtnl_gact *u = data; + struct nlattr *tb[TCA_GACT_MAX + 1]; + int err; + + err = tca_parse(tb, TCA_GACT_MAX, tc, gact_policy); + if (err < 0) + return err; + + if (!tb[TCA_GACT_PARMS]) + return -NLE_MISSING_ATTR; + + nla_memcpy(&u->g_parm, tb[TCA_GACT_PARMS], sizeof(u->g_parm)); + + return 0; +} + +static void gact_free_data(struct rtnl_tc *tc, void *data) +{ +} + +static int gact_clone(void *_dst, void *_src) +{ + struct rtnl_gact *dst = _dst, *src = _src; + + memcpy(&dst->g_parm, &src->g_parm, sizeof(src->g_parm)); + return 0; +} + +static void gact_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_gact *u = data; + + if (!u) + return; + + switch(u->g_parm.action){ + case TC_ACT_UNSPEC: + nl_dump(p, " continue"); + break; + case TC_ACT_SHOT: + nl_dump(p, " drop"); + break; + case TC_ACT_RECLASSIFY: + nl_dump(p, " reclassify"); + break; + case TC_ACT_OK: + nl_dump(p, " pass"); + break; + } + +} + +static void gact_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ +} + +static void gact_dump_stats(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_gact *u = data; + + if (!u) + return; + /* TODO */ +} + + +static int gact_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + struct rtnl_gact *u = data; + + if (!u) + return 0; + + NLA_PUT(msg, TCA_GACT_PARMS, sizeof(u->g_parm), &u->g_parm); + + return 0; + +nla_put_failure: + return -NLE_NOMEM; +} + +/** + * @name Attribute Modifications + * @{ + */ + +int rtnl_gact_set_action(struct rtnl_act *act, int action) +{ + struct rtnl_gact *u; + + if (!(u = (struct rtnl_gact *) rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + + if (action > TC_ACT_SHOT || action < TC_ACT_UNSPEC) + return -NLE_INVAL; + + switch (action) { + case TC_ACT_UNSPEC: + case TC_ACT_SHOT: + u->g_parm.action = action; + break; + case TC_ACT_OK: + case TC_ACT_RECLASSIFY: + default: + return NLE_OPNOTSUPP; + } + + return 0; +} + +int rtnl_gact_get_action(struct rtnl_act *act) +{ + struct rtnl_gact *u; + + if (!(u = (struct rtnl_gact *) rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + return u->g_parm.action; +} + + +/** @} */ + +static struct rtnl_tc_ops gact_ops = { + .to_kind = "gact", + .to_type = RTNL_TC_TYPE_ACT, + .to_size = sizeof(struct rtnl_gact), + .to_msg_parser = gact_msg_parser, + .to_free_data = gact_free_data, + .to_clone = gact_clone, + .to_msg_fill = gact_msg_fill, + .to_dump = { + [NL_DUMP_LINE] = gact_dump_line, + [NL_DUMP_DETAILS] = gact_dump_details, + [NL_DUMP_STATS] = gact_dump_stats, + }, +}; + +static void __init gact_init(void) +{ + rtnl_tc_register(&gact_ops); +} + +static void __exit gact_exit(void) +{ + rtnl_tc_unregister(&gact_ops); +} + +/** @} */ diff --git a/libnl/lib/route/act/mirred.c b/libnl/lib/route/act/mirred.c new file mode 100644 index 0000000..45c18d1 --- /dev/null +++ b/libnl/lib/route/act/mirred.c @@ -0,0 +1,238 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Cong Wang + */ + +/** + * @ingroup act + * @defgroup act_mirred Mirror and Redirect + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +static struct nla_policy mirred_policy[TCA_MIRRED_MAX + 1] = { + [TCA_MIRRED_PARMS] = { .minlen = sizeof(struct tc_mirred) }, +}; + +static int mirred_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct rtnl_mirred *u = data; + struct nlattr *tb[TCA_MIRRED_MAX + 1]; + int err; + + err = tca_parse(tb, TCA_MIRRED_MAX, tc, mirred_policy); + if (err < 0) + return err; + + if (!tb[TCA_MIRRED_PARMS]) + return -NLE_MISSING_ATTR; + + nla_memcpy(&u->m_parm, tb[TCA_MIRRED_PARMS], sizeof(u->m_parm)); + return 0; +} + +static void mirred_free_data(struct rtnl_tc *tc, void *data) +{ +} + +static int mirred_clone(void *_dst, void *_src) +{ + struct rtnl_mirred *dst = _dst, *src = _src; + + memcpy(&dst->m_parm, &src->m_parm, sizeof(src->m_parm)); + return 0; +} + +static void mirred_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_mirred *u = data; + if (!u) + return; + + nl_dump(p, " index %u", u->m_parm.ifindex); + + if (u->m_parm.eaction == TCA_EGRESS_MIRROR) + nl_dump(p, " egress mirror"); + else if (u->m_parm.eaction == TCA_EGRESS_REDIR) + nl_dump(p, " egress redirect"); + + switch(u->m_parm.action) { + case TC_ACT_UNSPEC: + nl_dump(p, " unspecified"); + break; + case TC_ACT_PIPE: + nl_dump(p, " pipe"); + break; + case TC_ACT_STOLEN: + nl_dump(p, " stolen"); + break; + case TC_ACT_SHOT: + nl_dump(p, " shot"); + break; + case TC_ACT_QUEUED: + nl_dump(p, " queued"); + break; + case TC_ACT_REPEAT: + nl_dump(p, " repeat"); + break; + } +} + +static void mirred_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ +} + +static void mirred_dump_stats(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_mirred *u = data; + + if (!u) + return; + /* TODO */ +} + + +static int mirred_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + struct rtnl_mirred *u = data; + + if (!u) + return 0; + + NLA_PUT(msg, TCA_MIRRED_PARMS, sizeof(u->m_parm), &u->m_parm); + return 0; + +nla_put_failure: + return -NLE_NOMEM; +} + +/** + * @name Attribute Modifications + * @{ + */ + +int rtnl_mirred_set_action(struct rtnl_act *act, int action) +{ + struct rtnl_mirred *u; + + if (!(u = (struct rtnl_mirred *) rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + + if (action > TCA_INGRESS_MIRROR || action < TCA_EGRESS_REDIR) + return -NLE_INVAL; + + switch (action) { + case TCA_EGRESS_MIRROR: + case TCA_EGRESS_REDIR: + u->m_parm.eaction = action; + break; + case TCA_INGRESS_REDIR: + case TCA_INGRESS_MIRROR: + default: + return NLE_OPNOTSUPP; + } + return 0; +} + +int rtnl_mirred_get_action(struct rtnl_act *act) +{ + struct rtnl_mirred *u; + + if (!(u = (struct rtnl_mirred *) rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + return u->m_parm.eaction; +} + +int rtnl_mirred_set_ifindex(struct rtnl_act *act, uint32_t ifindex) +{ + struct rtnl_mirred *u; + + if (!(u = (struct rtnl_mirred *) rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + + u->m_parm.ifindex = ifindex; + return 0; +} + +uint32_t rtnl_mirred_get_ifindex(struct rtnl_act *act) +{ + struct rtnl_mirred *u; + + if ((u = (struct rtnl_mirred *) rtnl_tc_data(TC_CAST(act)))) + return u->m_parm.ifindex; + return 0; +} + +int rtnl_mirred_set_policy(struct rtnl_act *act, int policy) +{ + struct rtnl_mirred *u; + + if (!(u = (struct rtnl_mirred *) rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + + if (policy > TC_ACT_REPEAT || policy < TC_ACT_OK) + return -NLE_INVAL; + + switch (u->m_parm.eaction) { + case TCA_EGRESS_MIRROR: + case TCA_EGRESS_REDIR: + u->m_parm.action = policy; + break; + case TCA_INGRESS_REDIR: + case TCA_INGRESS_MIRROR: + default: + return NLE_OPNOTSUPP; + } + return 0; +} + +int rtnl_mirred_get_policy(struct rtnl_act *act) +{ + struct rtnl_mirred *u; + + if (!(u = (struct rtnl_mirred *) rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + return u->m_parm.action; +} + +/** @} */ + +static struct rtnl_tc_ops mirred_ops = { + .to_kind = "mirred", + .to_type = RTNL_TC_TYPE_ACT, + .to_size = sizeof(struct rtnl_mirred), + .to_msg_parser = mirred_msg_parser, + .to_free_data = mirred_free_data, + .to_clone = mirred_clone, + .to_msg_fill = mirred_msg_fill, + .to_dump = { + [NL_DUMP_LINE] = mirred_dump_line, + [NL_DUMP_DETAILS] = mirred_dump_details, + [NL_DUMP_STATS] = mirred_dump_stats, + }, +}; + +static void __init mirred_init(void) +{ + rtnl_tc_register(&mirred_ops); +} + +static void __exit mirred_exit(void) +{ + rtnl_tc_unregister(&mirred_ops); +} + +/** @} */ diff --git a/libnl/lib/route/act/skbedit.c b/libnl/lib/route/act/skbedit.c new file mode 100644 index 0000000..eea190c --- /dev/null +++ b/libnl/lib/route/act/skbedit.c @@ -0,0 +1,286 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2015 Cong Wang + */ + +/** + * @ingroup act + * @defgroup act_skbedit SKB Editing + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +static struct nla_policy skbedit_policy[TCA_SKBEDIT_MAX + 1] = { + [TCA_SKBEDIT_PARMS] = { .minlen = sizeof(struct tc_skbedit) }, + [TCA_SKBEDIT_PRIORITY] = { .type = NLA_U32 }, + [TCA_SKBEDIT_QUEUE_MAPPING] = { .type = NLA_U16 }, + [TCA_SKBEDIT_MARK] = { .type = NLA_U32 }, +}; + +static int skbedit_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct rtnl_skbedit *u = data; + struct nlattr *tb[TCA_SKBEDIT_MAX + 1]; + int err; + + err = tca_parse(tb, TCA_SKBEDIT_MAX, tc, skbedit_policy); + if (err < 0) + return err; + + if (!tb[TCA_SKBEDIT_PARMS]) + return -NLE_MISSING_ATTR; + + u->s_flags = 0; + if (tb[TCA_SKBEDIT_PRIORITY] != NULL) { + u->s_flags |= SKBEDIT_F_PRIORITY; + u->s_prio = nla_get_u32(tb[TCA_SKBEDIT_PRIORITY]); + } + + if (tb[TCA_SKBEDIT_QUEUE_MAPPING] != NULL) { + u->s_flags |= SKBEDIT_F_QUEUE_MAPPING; + u->s_queue_mapping = nla_get_u16(tb[TCA_SKBEDIT_QUEUE_MAPPING]); + } + + if (tb[TCA_SKBEDIT_MARK] != NULL) { + u->s_flags |= SKBEDIT_F_MARK; + u->s_mark = nla_get_u32(tb[TCA_SKBEDIT_MARK]); + } + + return 0; +} + +static void skbedit_free_data(struct rtnl_tc *tc, void *data) +{ +} + +static int skbedit_clone(void *_dst, void *_src) +{ + struct rtnl_skbedit *dst = _dst, *src = _src; + + memcpy(dst, src, sizeof(*src)); + return 0; +} + +static void skbedit_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_skbedit *u = data; + + if (!u) + return; + + if (u->s_flags & SKBEDIT_F_PRIORITY) + nl_dump(p, " priority %u", u->s_prio); + + if (u->s_flags & SKBEDIT_F_MARK) + nl_dump(p, " mark %u", u->s_mark); + + if (u->s_flags & SKBEDIT_F_QUEUE_MAPPING) + nl_dump(p, " queue_mapping %u", u->s_queue_mapping); + + switch(u->s_parm.action){ + case TC_ACT_UNSPEC: + nl_dump(p, " unspecified"); + break; + case TC_ACT_PIPE: + nl_dump(p, " pipe"); + break; + case TC_ACT_STOLEN: + nl_dump(p, " stolen"); + break; + case TC_ACT_SHOT: + nl_dump(p, " shot"); + break; + case TC_ACT_QUEUED: + nl_dump(p, " queued"); + break; + case TC_ACT_REPEAT: + nl_dump(p, " repeat"); + break; + } +} + +static void skbedit_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ +} + +static void skbedit_dump_stats(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_skbedit *u = data; + + if (!u) + return; + /* TODO */ +} + + +static int skbedit_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + struct rtnl_skbedit *u = data; + + if (!u) + return 0; + + NLA_PUT(msg, TCA_SKBEDIT_PARMS, sizeof(u->s_parm), &u->s_parm); + + if (u->s_flags & SKBEDIT_F_MARK) + NLA_PUT_U32(msg, TCA_SKBEDIT_MARK, u->s_mark); + + if (u->s_flags & SKBEDIT_F_PRIORITY) + NLA_PUT_U32(msg, TCA_SKBEDIT_PRIORITY, u->s_prio); + + if (u->s_flags & SKBEDIT_F_QUEUE_MAPPING) + NLA_PUT_U32(msg, TCA_SKBEDIT_QUEUE_MAPPING, u->s_queue_mapping); + + return 0; + +nla_put_failure: + return -NLE_NOMEM; +} + +/** + * @name Attribute Modifications + * @{ + */ + +int rtnl_skbedit_set_action(struct rtnl_act *act, int action) +{ + struct rtnl_skbedit *u; + + if (!(u = (struct rtnl_skbedit *) rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + + if (action > TC_ACT_REPEAT || action < TC_ACT_UNSPEC) + return -NLE_INVAL; + + u->s_parm.action = action; + return 0; +} + +int rtnl_skbedit_get_action(struct rtnl_act *act) +{ + struct rtnl_skbedit *u; + + if (!(u = (struct rtnl_skbedit *) rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + return u->s_parm.action; +} + +int rtnl_skbedit_set_queue_mapping(struct rtnl_act *act, uint16_t index) +{ + struct rtnl_skbedit *u; + + if (!(u = (struct rtnl_skbedit *) rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + + u->s_queue_mapping = index; + u->s_flags |= SKBEDIT_F_QUEUE_MAPPING; + return 0; +} + +int rtnl_skbedit_get_queue_mapping(struct rtnl_act *act, uint16_t *index) +{ + struct rtnl_skbedit *u; + + u = (struct rtnl_skbedit *) rtnl_tc_data(TC_CAST(act)); + if (!u) + return -NLE_NOMEM; + if (!(u->s_flags & SKBEDIT_F_QUEUE_MAPPING)) + return -NLE_NOATTR; + + *index = u->s_queue_mapping; + return 0; +} + +int rtnl_skbedit_set_mark(struct rtnl_act *act, uint32_t mark) +{ + struct rtnl_skbedit *u; + + if (!(u = (struct rtnl_skbedit *) rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + + u->s_mark = mark; + u->s_flags |= SKBEDIT_F_MARK; + return 0; +} + +int rtnl_skbedit_get_mark(struct rtnl_act *act, uint32_t *mark) +{ + struct rtnl_skbedit *u; + + u = (struct rtnl_skbedit *) rtnl_tc_data(TC_CAST(act)); + if (!u) + return -NLE_NOMEM; + if (!(u->s_flags & SKBEDIT_F_MARK)) + return -NLE_NOATTR; + + *mark = u->s_mark; + return 0; +} + +int rtnl_skbedit_set_priority(struct rtnl_act *act, uint32_t prio) +{ + struct rtnl_skbedit *u; + + if (!(u = (struct rtnl_skbedit *) rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + + u->s_prio = prio; + u->s_flags |= SKBEDIT_F_PRIORITY; + return 0; +} + +int rtnl_skbedit_get_priority(struct rtnl_act *act, uint32_t *prio) +{ + struct rtnl_skbedit *u; + + u = (struct rtnl_skbedit *) rtnl_tc_data(TC_CAST(act)); + if (!u) + return -NLE_NOMEM; + if (!(u->s_flags & SKBEDIT_F_PRIORITY)) + return -NLE_NOATTR; + + *prio = u->s_prio; + return 0; +} + +/** @} */ + +static struct rtnl_tc_ops skbedit_ops = { + .to_kind = "skbedit", + .to_type = RTNL_TC_TYPE_ACT, + .to_size = sizeof(struct rtnl_skbedit), + .to_msg_parser = skbedit_msg_parser, + .to_free_data = skbedit_free_data, + .to_clone = skbedit_clone, + .to_msg_fill = skbedit_msg_fill, + .to_dump = { + [NL_DUMP_LINE] = skbedit_dump_line, + [NL_DUMP_DETAILS] = skbedit_dump_details, + [NL_DUMP_STATS] = skbedit_dump_stats, + }, +}; + +static void __init skbedit_init(void) +{ + rtnl_tc_register(&skbedit_ops); +} + +static void __exit skbedit_exit(void) +{ + rtnl_tc_unregister(&skbedit_ops); +} + +/** @} */ diff --git a/libnl/lib/route/act/vlan.c b/libnl/lib/route/act/vlan.c new file mode 100644 index 0000000..b27c7ea --- /dev/null +++ b/libnl/lib/route/act/vlan.c @@ -0,0 +1,422 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2018 Volodymyr Bendiuga + */ + +/** + * @ingroup act + * @defgroup act_vlan VLAN Manipulation + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + + +#define VLAN_F_VID (1 << 0) +#define VLAN_F_PROTO (1 << 1) +#define VLAN_F_PRIO (1 << 2) +#define VLAN_F_ACT (1 << 3) +#define VLAN_F_MODE (1 << 4) + +static struct nla_policy vlan_policy[TCA_VLAN_MAX + 1] = { + [TCA_VLAN_PARMS] = { .minlen = sizeof(struct tc_vlan) }, + [TCA_VLAN_PUSH_VLAN_ID] = { .type = NLA_U16 }, + [TCA_VLAN_PUSH_VLAN_PROTOCOL] = { .type = NLA_U16 }, + [TCA_VLAN_PUSH_VLAN_PRIORITY] = { .type = NLA_U8 }, +}; + +static int vlan_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct rtnl_vlan *v = data; + struct nlattr *tb[TCA_VLAN_MAX + 1]; + int err; + + err = tca_parse(tb, TCA_VLAN_MAX, tc, vlan_policy); + if (err < 0) + return err; + + v->v_flags = 0; + if (!tb[TCA_VLAN_PARMS]) + return -NLE_MISSING_ATTR; + else { + nla_memcpy(&v->v_parm, tb[TCA_VLAN_PARMS], sizeof(v->v_parm)); + v->v_flags |= VLAN_F_ACT; + v->v_flags |= VLAN_F_MODE; + } + + if (tb[TCA_VLAN_PUSH_VLAN_ID]) { + v->v_vid = nla_get_u16(tb[TCA_VLAN_PUSH_VLAN_ID]); + v->v_flags |= VLAN_F_VID; + } + + if (tb[TCA_VLAN_PUSH_VLAN_PROTOCOL]) { + v->v_proto = nla_get_u16(tb[TCA_VLAN_PUSH_VLAN_PROTOCOL]); + v->v_flags |= VLAN_F_PROTO; + } + + if (tb[TCA_VLAN_PUSH_VLAN_PRIORITY]) { + v->v_prio = nla_get_u8(tb[TCA_VLAN_PUSH_VLAN_PRIORITY]); + v->v_flags |= VLAN_F_PRIO; + } + + return 0; +} + +static int vlan_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + struct rtnl_vlan *v = data; + + if (!v) + return 0; + if (!(v->v_flags & VLAN_F_MODE)) + return -NLE_MISSING_ATTR; + + NLA_PUT(msg, TCA_VLAN_PARMS, sizeof(v->v_parm), &v->v_parm); + + /* vid is required for PUSH & MODIFY modes */ + if ((v->v_parm.v_action != TCA_VLAN_ACT_POP) && !(v->v_flags & VLAN_F_VID)) + return -NLE_MISSING_ATTR; + + if (v->v_flags & VLAN_F_VID) + NLA_PUT_U16(msg, TCA_VLAN_PUSH_VLAN_ID, v->v_vid); + + if (v->v_flags & VLAN_F_PROTO) + NLA_PUT_U16(msg, TCA_VLAN_PUSH_VLAN_PROTOCOL, v->v_proto); + + if (v->v_flags & VLAN_F_PRIO) + NLA_PUT_U8(msg, TCA_VLAN_PUSH_VLAN_PRIORITY, v->v_prio); + + return 0; + +nla_put_failure: + return -NLE_NOMEM; +} + +static void vlan_free_data(struct rtnl_tc *tc, void *data) +{ +} + +static int vlan_clone(void *_dst, void *_src) +{ + struct rtnl_vlan *dst = _dst, *src = _src; + + memcpy(&dst->v_parm, &src->v_parm, sizeof(src->v_parm)); + return 0; +} + +static void vlan_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_vlan *v = data; + + if (!v) + return; + + if (!(v->v_flags & VLAN_F_ACT)) + return; + + if (TC_ACT_EXT_CMP(v->v_parm.action, TC_ACT_GOTO_CHAIN)) + nl_dump(p, " goto chain %u", v->v_parm.action & TC_ACT_EXT_VAL_MASK); + + if (TC_ACT_EXT_CMP(v->v_parm.action, TC_ACT_JUMP)) + nl_dump(p, " jump %u", v->v_parm.action & TC_ACT_EXT_VAL_MASK); + + switch(v->v_parm.action){ + case TC_ACT_UNSPEC: + nl_dump(p, " unspecified"); + break; + case TC_ACT_PIPE: + nl_dump(p, " pipe"); + break; + case TC_ACT_STOLEN: + nl_dump(p, " stolen"); + break; + case TC_ACT_SHOT: + nl_dump(p, " shot"); + break; + case TC_ACT_QUEUED: + nl_dump(p, " queued"); + break; + case TC_ACT_REPEAT: + nl_dump(p, " repeat"); + break; + } +} + +static void vlan_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_vlan *v = data; + + if (!v) + return; + + if (v->v_flags & VLAN_F_MODE) { + switch (v->v_parm.v_action) { + case TCA_VLAN_ACT_POP: + nl_dump(p, " mode POP"); + break; + case TCA_VLAN_ACT_PUSH: + nl_dump(p, " mode PUSH"); + break; + case TCA_VLAN_ACT_MODIFY: + nl_dump(p, " mode MODIFY"); + break; + } + } + + if (v->v_flags & VLAN_F_VID) + nl_dump(p, " vlan id %u", v->v_vid); + + if (v->v_flags & VLAN_F_PRIO) + nl_dump(p, " priority %u", v->v_prio); + + if (v->v_flags & VLAN_F_PROTO) + nl_dump(p, " protocol %u", v->v_proto); +} + +/** + * @name Attribute Modifications + * @{ + */ + +/** + * Set vlan mode + * @arg act vlan action + * @arg mode one of (TCA_VLAN_ACT_*: POP, PUSH, MODIFY) + * @return 0 on success or a negative error code. + */ +int rtnl_vlan_set_mode(struct rtnl_act *act, int mode) +{ + struct rtnl_vlan *v; + + if (!(v = (struct rtnl_vlan *) rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + + if (mode > TCA_VLAN_ACT_MODIFY) + return -NLE_RANGE; + + v->v_parm.v_action = mode; + v->v_flags |= VLAN_F_MODE; + + return 0; +} + +/** + * Get vlan mode + * @arg act vlan action + * @arg out_mode vlan mode output paramter + * @return 0 on success if the vlan mode was returned or a negative error code. +*/ +int rtnl_vlan_get_mode(struct rtnl_act *act, int *out_mode) +{ + struct rtnl_vlan *v; + + if (!(v = (struct rtnl_vlan *) rtnl_tc_data_peek(TC_CAST(act)))) + return -NLE_INVAL; + + if (!(v->v_flags & VLAN_F_MODE)) + return -NLE_MISSING_ATTR; + + *out_mode = v->v_parm.v_action; + return 0; +} + +/** + * Set general action + * @arg act vlan action + * @arg action one of (TCA_ACT_*: PIPE, SHOT, GOTO_CHAIN, etc) + * @return 0 on success or a negative error code. + */ +int rtnl_vlan_set_action(struct rtnl_act *act, int action) +{ + struct rtnl_vlan *v; + + if (!(v = (struct rtnl_vlan *) rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + + v->v_parm.action = action; + v->v_flags |= VLAN_F_ACT; + + return 0; +} + +/** + * Get general action + * @arg act vlan action + * @arg out_action output parameter + * @return general 0 if out_action was set or a negative error code. +*/ +int rtnl_vlan_get_action(struct rtnl_act *act, int *out_action) +{ + struct rtnl_vlan *v; + + if (!(v = (struct rtnl_vlan *) rtnl_tc_data_peek(TC_CAST(act)))) + return -NLE_INVAL; + + if (!(v->v_flags & VLAN_F_ACT)) + return -NLE_MISSING_ATTR; + + *out_action = v->v_parm.action; + return 0; +} + +/** + * Set protocol + * @arg act vlan action + * @arg protocol one of (ETH_P_8021Q || ETH_P_8021AD) + * @return 0 on success or a negative error code. + */ +int rtnl_vlan_set_protocol(struct rtnl_act *act, uint16_t protocol) +{ + struct rtnl_vlan *v; + + if (!(v = (struct rtnl_vlan *) rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + + v->v_proto = protocol; + v->v_flags |= VLAN_F_PROTO; + + return 0; +} + +/** + * Get protocol + * @arg act vlan action + * @arg out_protocol protocol output argument + * @return 0 if the protocol was returned or a negative error code. +*/ +int rtnl_vlan_get_protocol(struct rtnl_act *act, uint16_t *out_protocol) +{ + struct rtnl_vlan *v; + + if (!(v = (struct rtnl_vlan *) rtnl_tc_data_peek(TC_CAST(act)))) + return -NLE_INVAL; + + if (!(v->v_flags & VLAN_F_PROTO)) + return -NLE_MISSING_ATTR; + + *out_protocol = v->v_proto; + return 0; +} + +/** + * Set vlan id + * @arg act vlan action + * @arg vid vlan id + * @return 0 on success or a negative error code. + */ +int rtnl_vlan_set_vlan_id(struct rtnl_act *act, uint16_t vid) +{ + struct rtnl_vlan *v; + + if (!(v = (struct rtnl_vlan *) rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + + if (vid > 4095) + return -NLE_RANGE; + + v->v_vid = vid; + v->v_flags |= VLAN_F_VID; + + return 0; +} + +/** + * Get vlan id + * @arg act vlan action + * @arg out_vid output vlan id + * @return 0 if the vlan id was returned or a negative error code. +*/ +int rtnl_vlan_get_vlan_id(struct rtnl_act *act, uint16_t *out_vid) +{ + struct rtnl_vlan *v; + + if (!(v = (struct rtnl_vlan *) rtnl_tc_data_peek(TC_CAST(act)))) + return -NLE_INVAL; + + if (!(v->v_flags & VLAN_F_VID)) + return -NLE_MISSING_ATTR; + + *out_vid = v->v_vid; + return 0; +} + +/** + * Set vlan prio + * @arg act vlan action + * @arg prio vlan priority (0 - 7) + * @return 0 on success or a negative error code. + */ +int rtnl_vlan_set_vlan_prio(struct rtnl_act *act, uint8_t prio) +{ + struct rtnl_vlan *v; + + if (!(v = (struct rtnl_vlan *) rtnl_tc_data(TC_CAST(act)))) + return -NLE_NOMEM; + + if (prio > 7) + return -NLE_RANGE; + + v->v_prio = prio; + v->v_flags |= VLAN_F_PRIO; + + return 0; +} + +/** + * Get vlan prio + * @arg act vlan action + * @arg out_prio the output vlan prio + * @return 0 if the vlan prio was returned or a negative error code. +*/ +int rtnl_vlan_get_vlan_prio(struct rtnl_act *act, uint8_t *out_prio) +{ + struct rtnl_vlan *v; + + if (!(v = (struct rtnl_vlan *) rtnl_tc_data_peek(TC_CAST(act)))) + return -NLE_INVAL; + + if (!(v->v_flags & VLAN_F_PRIO)) + return -NLE_MISSING_ATTR; + + *out_prio = v->v_prio; + return 0; +} + +/** @} */ + +static struct rtnl_tc_ops vlan_ops = { + .to_kind = "vlan", + .to_type = RTNL_TC_TYPE_ACT, + .to_size = sizeof(struct rtnl_vlan), + .to_msg_parser = vlan_msg_parser, + .to_free_data = vlan_free_data, + .to_clone = vlan_clone, + .to_msg_fill = vlan_msg_fill, + .to_dump = { + [NL_DUMP_LINE] = vlan_dump_line, + [NL_DUMP_DETAILS] = vlan_dump_details, + }, +}; + +static void __init vlan_init(void) +{ + rtnl_tc_register(&vlan_ops); +} + +static void __exit vlan_exit(void) +{ + rtnl_tc_unregister(&vlan_ops); +} + +/** @} */ diff --git a/libnl/lib/route/addr.c b/libnl/lib/route/addr.c new file mode 100644 index 0000000..af3e461 --- /dev/null +++ b/libnl/lib/route/addr.c @@ -0,0 +1,1206 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + * Copyright (c) 2003-2006 Baruch Even + * Copyright (c) 2003-2006 Mediatrix Telecom, inc. + */ + +/** + * @ingroup rtnl + * @defgroup rtaddr Addresses + * @brief + * + * @note The maximum size of an address label is IFNAMSIZ. + * + * @note The address may not contain a prefix length if the peer address + * has been specified already. + * + * @par 1) Address Addition + * @code + * // Allocate an empty address object to be filled out with the attributes + * // of the new address. + * struct rtnl_addr *addr = rtnl_addr_alloc(); + * + * // Fill out the mandatory attributes of the new address. Setting the + * // local address will automatically set the address family and the + * // prefix length to the correct values. + * rtnl_addr_set_ifindex(addr, ifindex); + * rtnl_addr_set_local(addr, local_addr); + * + * // The label of the address can be specified, currently only supported + * // by IPv4 and DECnet. + * rtnl_addr_set_label(addr, "mylabel"); + * + * // The peer address can be specified if necessary, in either case a peer + * // address will be sent to the kernel in order to fullfil the interface + * // requirements. If none is set, it will equal the local address. + * // Note: Real peer addresses are only supported by IPv4 for now. + * rtnl_addr_set_peer(addr, peer_addr); + * + * // In case you want to have the address have a scope other than global + * // it may be overwritten using rtnl_addr_set_scope(). The scope currently + * // cannot be set for IPv6 addresses. + * rtnl_addr_set_scope(addr, rtnl_str2scope("site")); + * + * // Broadcast address may be specified using the relevant + * // functions, the address family will be verified if one of the other + * // addresses has been set already. Currently only works for IPv4. + * rtnl_addr_set_broadcast(addr, broadcast_addr); + * + * // Build the netlink message and send it to the kernel, the operation will + * // block until the operation has been completed. Alternatively the required + * // netlink message can be built using rtnl_addr_build_add_request() to be + * // sent out using nl_send_auto_complete(). + * rtnl_addr_add(sk, addr, 0); + * + * // Free the memory + * rtnl_addr_put(addr); + * @endcode + * + * @par 2) Address Deletion + * @code + * // Allocate an empty address object to be filled out with the attributes + * // matching the address to be deleted. Alternatively a fully equipped + * // address object out of a cache can be used instead. + * struct rtnl_addr *addr = rtnl_addr_alloc(); + * + * // The only mandatory parameter besides the address family is the interface + * // index the address is on, i.e. leaving out all other parameters will + * // result in all addresses of the specified address family interface tuple + * // to be deleted. + * rtnl_addr_set_ifindex(addr, ifindex); + * + * // Specyfing the address family manually is only required if neither the + * // local nor peer address have been specified. + * rtnl_addr_set_family(addr, AF_INET); + * + * // Specyfing the local address is optional but the best choice to delete + * // specific addresses. + * rtnl_addr_set_local(addr, local_addr); + * + * // The label of the address can be specified, currently only supported + * // by IPv4 and DECnet. + * rtnl_addr_set_label(addr, "mylabel"); + * + * // The peer address can be specified if necessary, in either case a peer + * // address will be sent to the kernel in order to fullfil the interface + * // requirements. If none is set, it will equal the local address. + * // Note: Real peer addresses are only supported by IPv4 for now. + * rtnl_addr_set_peer(addr, peer_addr); + * + * // Build the netlink message and send it to the kernel, the operation will + * // block until the operation has been completed. Alternatively the required + * // netlink message can be built using rtnl_addr_build_delete_request() + * // to be sent out using nl_send_auto_complete(). + * rtnl_addr_delete(sk, addr, 0); + * + * // Free the memory + * rtnl_addr_put(addr); + * @endcode + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define ADDR_ATTR_FAMILY 0x0001 +#define ADDR_ATTR_PREFIXLEN 0x0002 +#define ADDR_ATTR_FLAGS 0x0004 +#define ADDR_ATTR_SCOPE 0x0008 +#define ADDR_ATTR_IFINDEX 0x0010 +#define ADDR_ATTR_LABEL 0x0020 +#define ADDR_ATTR_CACHEINFO 0x0040 +#define ADDR_ATTR_PEER 0x0080 +#define ADDR_ATTR_LOCAL 0x0100 +#define ADDR_ATTR_BROADCAST 0x0200 +#define ADDR_ATTR_MULTICAST 0x0400 +#define ADDR_ATTR_ANYCAST 0x0800 + +static struct nl_cache_ops rtnl_addr_ops; +static struct nl_object_ops addr_obj_ops; +/** @endcond */ + +static void addr_constructor(struct nl_object *obj) +{ + struct rtnl_addr *addr = nl_object_priv(obj); + + addr->a_scope = RT_SCOPE_NOWHERE; +} + +static void addr_free_data(struct nl_object *obj) +{ + struct rtnl_addr *addr = nl_object_priv(obj); + + if (!addr) + return; + + nl_addr_put(addr->a_peer); + nl_addr_put(addr->a_local); + nl_addr_put(addr->a_bcast); + nl_addr_put(addr->a_multicast); + nl_addr_put(addr->a_anycast); + rtnl_link_put(addr->a_link); +} + +static int addr_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct rtnl_addr *dst = nl_object_priv(_dst); + struct rtnl_addr *src = nl_object_priv(_src); + + if (src->a_link) { + nl_object_get(OBJ_CAST(src->a_link)); + dst->a_link = src->a_link; + } + + if (src->a_peer) + if (!(dst->a_peer = nl_addr_clone(src->a_peer))) + return -NLE_NOMEM; + + if (src->a_local) + if (!(dst->a_local = nl_addr_clone(src->a_local))) + return -NLE_NOMEM; + + if (src->a_bcast) + if (!(dst->a_bcast = nl_addr_clone(src->a_bcast))) + return -NLE_NOMEM; + + if (src->a_multicast) + if (!(dst->a_multicast = nl_addr_clone(src->a_multicast))) + return -NLE_NOMEM; + + if (src->a_anycast) + if (!(dst->a_anycast = nl_addr_clone(src->a_anycast))) + return -NLE_NOMEM; + + return 0; +} + +static struct nla_policy addr_policy[IFA_MAX+1] = { + [IFA_LABEL] = { .type = NLA_STRING, + .maxlen = IFNAMSIZ }, + [IFA_CACHEINFO] = { .minlen = sizeof(struct ifa_cacheinfo) }, +}; + +static int addr_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, struct nl_parser_param *pp) +{ + struct rtnl_addr *addr; + struct ifaddrmsg *ifa; + struct nlattr *tb[IFA_MAX+1]; + int err, family; + struct nl_cache *link_cache; + struct nl_addr *plen_addr = NULL; + + addr = rtnl_addr_alloc(); + if (!addr) + return -NLE_NOMEM; + + addr->ce_msgtype = nlh->nlmsg_type; + + err = nlmsg_parse(nlh, sizeof(*ifa), tb, IFA_MAX, addr_policy); + if (err < 0) + goto errout; + + ifa = nlmsg_data(nlh); + addr->a_family = family = ifa->ifa_family; + addr->a_prefixlen = ifa->ifa_prefixlen; + addr->a_scope = ifa->ifa_scope; + addr->a_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) : + ifa->ifa_flags; + addr->a_ifindex = ifa->ifa_index; + + addr->ce_mask = (ADDR_ATTR_FAMILY | ADDR_ATTR_PREFIXLEN | + ADDR_ATTR_FLAGS | ADDR_ATTR_SCOPE | ADDR_ATTR_IFINDEX); + + if (tb[IFA_LABEL]) { + nla_strlcpy(addr->a_label, tb[IFA_LABEL], IFNAMSIZ); + addr->ce_mask |= ADDR_ATTR_LABEL; + } + + /* IPv6 only */ + if (tb[IFA_CACHEINFO]) { + struct ifa_cacheinfo *ca; + + ca = nla_data(tb[IFA_CACHEINFO]); + addr->a_cacheinfo.aci_prefered = ca->ifa_prefered; + addr->a_cacheinfo.aci_valid = ca->ifa_valid; + addr->a_cacheinfo.aci_cstamp = ca->cstamp; + addr->a_cacheinfo.aci_tstamp = ca->tstamp; + addr->ce_mask |= ADDR_ATTR_CACHEINFO; + } + + if (family == AF_INET) { + uint32_t null = 0; + + /* for IPv4/AF_INET, kernel always sets IFA_LOCAL and IFA_ADDRESS, unless it + * is effectively 0.0.0.0. */ + if (tb[IFA_LOCAL]) + addr->a_local = nl_addr_alloc_attr(tb[IFA_LOCAL], family); + else + addr->a_local = nl_addr_build(family, &null, sizeof (null)); + if (!addr->a_local) + goto errout_nomem; + addr->ce_mask |= ADDR_ATTR_LOCAL; + + if (tb[IFA_ADDRESS]) + addr->a_peer = nl_addr_alloc_attr(tb[IFA_ADDRESS], family); + else + addr->a_peer = nl_addr_build(family, &null, sizeof (null)); + if (!addr->a_peer) + goto errout_nomem; + + if (!nl_addr_cmp (addr->a_local, addr->a_peer)) { + /* having IFA_ADDRESS equal to IFA_LOCAL does not really mean + * there is no peer. It means the peer is equal to the local address, + * which is the case for "normal" addresses. + * + * Still, clear the peer and pretend it is unset for backward + * compatibility. */ + nl_addr_put(addr->a_peer); + addr->a_peer = NULL; + } else + addr->ce_mask |= ADDR_ATTR_PEER; + + plen_addr = addr->a_local; + } else { + if (tb[IFA_LOCAL]) { + addr->a_local = nl_addr_alloc_attr(tb[IFA_LOCAL], family); + if (!addr->a_local) + goto errout_nomem; + addr->ce_mask |= ADDR_ATTR_LOCAL; + plen_addr = addr->a_local; + } + + if (tb[IFA_ADDRESS]) { + struct nl_addr *a; + + a = nl_addr_alloc_attr(tb[IFA_ADDRESS], family); + if (!a) + goto errout_nomem; + + /* IPv6 sends the local address as IFA_ADDRESS with + * no IFA_LOCAL, IPv4 sends both IFA_LOCAL and IFA_ADDRESS + * with IFA_ADDRESS being the peer address if they differ */ + if (!tb[IFA_LOCAL] || !nl_addr_cmp(a, addr->a_local)) { + nl_addr_put(addr->a_local); + addr->a_local = a; + addr->ce_mask |= ADDR_ATTR_LOCAL; + } else { + addr->a_peer = a; + addr->ce_mask |= ADDR_ATTR_PEER; + } + + plen_addr = a; + } + } + + if (plen_addr) + nl_addr_set_prefixlen(plen_addr, addr->a_prefixlen); + + /* IPv4 only */ + if (tb[IFA_BROADCAST]) { + addr->a_bcast = nl_addr_alloc_attr(tb[IFA_BROADCAST], family); + if (!addr->a_bcast) + goto errout_nomem; + + addr->ce_mask |= ADDR_ATTR_BROADCAST; + } + + /* IPv6 only */ + if (tb[IFA_MULTICAST]) { + addr->a_multicast = nl_addr_alloc_attr(tb[IFA_MULTICAST], + family); + if (!addr->a_multicast) + goto errout_nomem; + + addr->ce_mask |= ADDR_ATTR_MULTICAST; + } + + /* IPv6 only */ + if (tb[IFA_ANYCAST]) { + addr->a_anycast = nl_addr_alloc_attr(tb[IFA_ANYCAST], + family); + if (!addr->a_anycast) + goto errout_nomem; + + addr->ce_mask |= ADDR_ATTR_ANYCAST; + } + + if ((link_cache = __nl_cache_mngt_require("route/link"))) { + struct rtnl_link *link; + + if ((link = rtnl_link_get(link_cache, addr->a_ifindex))) { + rtnl_addr_set_link(addr, link); + + /* rtnl_addr_set_link incs refcnt */ + rtnl_link_put(link); + } + } + + err = pp->pp_cb((struct nl_object *) addr, pp); +errout: + rtnl_addr_put(addr); + + return err; + +errout_nomem: + err = -NLE_NOMEM; + goto errout; +} + +static int addr_request_update(struct nl_cache *cache, struct nl_sock *sk) +{ + return nl_rtgen_request(sk, RTM_GETADDR, AF_UNSPEC, NLM_F_DUMP); +} + +static void addr_dump_line(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_addr *addr = (struct rtnl_addr *) obj; + struct nl_cache *link_cache; + char buf[128]; + + link_cache = nl_cache_mngt_require_safe("route/link"); + + if (addr->ce_mask & ADDR_ATTR_LOCAL) + nl_dump_line(p, "%s", + nl_addr2str(addr->a_local, buf, sizeof(buf))); + else + nl_dump_line(p, "none"); + + if (addr->ce_mask & ADDR_ATTR_PEER) + nl_dump(p, " peer %s", + nl_addr2str(addr->a_peer, buf, sizeof(buf))); + + nl_dump(p, " %s ", nl_af2str(addr->a_family, buf, sizeof(buf))); + + if (link_cache) + nl_dump(p, "dev %s ", + rtnl_link_i2name(link_cache, addr->a_ifindex, + buf, sizeof(buf))); + else + nl_dump(p, "dev %d ", addr->a_ifindex); + + nl_dump(p, "scope %s", + rtnl_scope2str(addr->a_scope, buf, sizeof(buf))); + + rtnl_addr_flags2str(addr->a_flags, buf, sizeof(buf)); + if (buf[0]) + nl_dump(p, " <%s>", buf); + + nl_dump(p, "\n"); + + if (link_cache) + nl_cache_put(link_cache); +} + +static void addr_dump_details(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_addr *addr = (struct rtnl_addr *) obj; + char buf[128]; + + addr_dump_line(obj, p); + + if (addr->ce_mask & (ADDR_ATTR_LABEL | ADDR_ATTR_BROADCAST | + ADDR_ATTR_MULTICAST)) { + nl_dump_line(p, " "); + + if (addr->ce_mask & ADDR_ATTR_LABEL) + nl_dump(p, " label %s", addr->a_label); + + if (addr->ce_mask & ADDR_ATTR_BROADCAST) + nl_dump(p, " broadcast %s", + nl_addr2str(addr->a_bcast, buf, sizeof(buf))); + + if (addr->ce_mask & ADDR_ATTR_MULTICAST) + nl_dump(p, " multicast %s", + nl_addr2str(addr->a_multicast, buf, + sizeof(buf))); + + if (addr->ce_mask & ADDR_ATTR_ANYCAST) + nl_dump(p, " anycast %s", + nl_addr2str(addr->a_anycast, buf, + sizeof(buf))); + + nl_dump(p, "\n"); + } + + if (addr->ce_mask & ADDR_ATTR_CACHEINFO) { + struct rtnl_addr_cacheinfo *ci = &addr->a_cacheinfo; + + nl_dump_line(p, " valid-lifetime %s", + ci->aci_valid == 0xFFFFFFFFU ? "forever" : + nl_msec2str(ci->aci_valid * 1000, + buf, sizeof(buf))); + + nl_dump(p, " preferred-lifetime %s\n", + ci->aci_prefered == 0xFFFFFFFFU ? "forever" : + nl_msec2str(ci->aci_prefered * 1000, + buf, sizeof(buf))); + + nl_dump_line(p, " created boot-time+%s ", + nl_msec2str(addr->a_cacheinfo.aci_cstamp * 10, + buf, sizeof(buf))); + + nl_dump(p, "last-updated boot-time+%s\n", + nl_msec2str(addr->a_cacheinfo.aci_tstamp * 10, + buf, sizeof(buf))); + } +} + +static void addr_dump_stats(struct nl_object *obj, struct nl_dump_params *p) +{ + addr_dump_details(obj, p); +} + +static uint32_t addr_id_attrs_get(struct nl_object *obj) +{ + struct rtnl_addr *addr = (struct rtnl_addr *)obj; + uint32_t rv; + + switch (addr->a_family) { + case AF_INET: + rv = (ADDR_ATTR_FAMILY | ADDR_ATTR_IFINDEX | + ADDR_ATTR_LOCAL | ADDR_ATTR_PREFIXLEN); + if (addr->a_peer) + rv |= ADDR_ATTR_PEER; + return rv; + case AF_INET6: + return (ADDR_ATTR_FAMILY | ADDR_ATTR_IFINDEX | + ADDR_ATTR_LOCAL); + default: + return (ADDR_ATTR_FAMILY | ADDR_ATTR_IFINDEX | + ADDR_ATTR_LOCAL | ADDR_ATTR_PREFIXLEN); + } +} + +static uint64_t addr_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + struct rtnl_addr *a = (struct rtnl_addr *) _a; + struct rtnl_addr *b = (struct rtnl_addr *) _b; + uint64_t diff = 0; + +#define ADDR_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ADDR_ATTR_##ATTR, a, b, EXPR) + + diff |= ADDR_DIFF(IFINDEX, a->a_ifindex != b->a_ifindex); + diff |= ADDR_DIFF(FAMILY, a->a_family != b->a_family); + diff |= ADDR_DIFF(SCOPE, a->a_scope != b->a_scope); + diff |= ADDR_DIFF(LABEL, strcmp(a->a_label, b->a_label)); + if (attrs & ADDR_ATTR_PEER) { + if ( (flags & ID_COMPARISON) + && a->a_family == AF_INET + && b->a_family == AF_INET + && a->a_peer + && b->a_peer + && a->a_prefixlen == b->a_prefixlen) { + /* when comparing two IPv4 addresses for id-equality, the network part + * of the PEER address shall be compared. + */ + diff |= ADDR_DIFF(PEER, nl_addr_cmp_prefix(a->a_peer, b->a_peer)); + } else + diff |= ADDR_DIFF(PEER, nl_addr_cmp(a->a_peer, b->a_peer)); + } + diff |= ADDR_DIFF(LOCAL, nl_addr_cmp(a->a_local, b->a_local)); + diff |= ADDR_DIFF(MULTICAST, nl_addr_cmp(a->a_multicast, + b->a_multicast)); + diff |= ADDR_DIFF(BROADCAST, nl_addr_cmp(a->a_bcast, b->a_bcast)); + diff |= ADDR_DIFF(ANYCAST, nl_addr_cmp(a->a_anycast, b->a_anycast)); + diff |= ADDR_DIFF(CACHEINFO, memcmp(&a->a_cacheinfo, &b->a_cacheinfo, + sizeof (a->a_cacheinfo))); + + if (flags & LOOSE_COMPARISON) + diff |= ADDR_DIFF(FLAGS, + (a->a_flags ^ b->a_flags) & b->a_flag_mask); + else + diff |= ADDR_DIFF(FLAGS, a->a_flags != b->a_flags); + +#undef ADDR_DIFF + + return diff; +} + +static const struct trans_tbl addr_attrs[] = { + __ADD(ADDR_ATTR_FAMILY, family), + __ADD(ADDR_ATTR_PREFIXLEN, prefixlen), + __ADD(ADDR_ATTR_FLAGS, flags), + __ADD(ADDR_ATTR_SCOPE, scope), + __ADD(ADDR_ATTR_IFINDEX, ifindex), + __ADD(ADDR_ATTR_LABEL, label), + __ADD(ADDR_ATTR_CACHEINFO, cacheinfo), + __ADD(ADDR_ATTR_PEER, peer), + __ADD(ADDR_ATTR_LOCAL, local), + __ADD(ADDR_ATTR_BROADCAST, broadcast), + __ADD(ADDR_ATTR_MULTICAST, multicast), +}; + +static char *addr_attrs2str(int attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, addr_attrs, + ARRAY_SIZE(addr_attrs)); +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct rtnl_addr *rtnl_addr_alloc(void) +{ + return (struct rtnl_addr *) nl_object_alloc(&addr_obj_ops); +} + +void rtnl_addr_put(struct rtnl_addr *addr) +{ + nl_object_put((struct nl_object *) addr); +} + +/** @} */ + +/** + * @name Cache Management + * @{ + */ + +int rtnl_addr_alloc_cache(struct nl_sock *sk, struct nl_cache **result) +{ + return nl_cache_alloc_and_fill(&rtnl_addr_ops, sk, result); +} + +/** + * Search address in cache + * @arg cache Address cache + * @arg ifindex Interface index of address + * @arg addr Local address part + * + * Searches address cache previously allocated with rtnl_addr_alloc_cache() + * for an address with a matching local address. + * + * The reference counter is incremented before returning the address, therefore + * the reference must be given back with rtnl_addr_put() after usage. + * + * @return Address object or NULL if no match was found. + */ +struct rtnl_addr *rtnl_addr_get(struct nl_cache *cache, int ifindex, + struct nl_addr *addr) +{ + struct rtnl_addr *a; + + if (cache->c_ops != &rtnl_addr_ops) + return NULL; + + nl_list_for_each_entry(a, &cache->c_items, ce_list) { + if (ifindex && a->a_ifindex != ifindex) + continue; + + if (a->ce_mask & ADDR_ATTR_LOCAL && + !nl_addr_cmp(a->a_local, addr)) { + nl_object_get((struct nl_object *) a); + return a; + } + } + + return NULL; +} + +/** @} */ + +static int build_addr_msg(struct rtnl_addr *tmpl, int cmd, int flags, + struct nl_msg **result) +{ + struct nl_msg *msg; + struct ifaddrmsg am = { + .ifa_family = tmpl->a_family, + .ifa_index = tmpl->a_ifindex, + .ifa_prefixlen = tmpl->a_prefixlen, + .ifa_flags = tmpl->a_flags, + }; + + if (tmpl->ce_mask & ADDR_ATTR_SCOPE) + am.ifa_scope = tmpl->a_scope; + else { + /* compatibility hack */ + if (tmpl->a_family == AF_INET && + tmpl->ce_mask & ADDR_ATTR_LOCAL && + *((char *) nl_addr_get_binary_addr(tmpl->a_local)) == 127) + am.ifa_scope = RT_SCOPE_HOST; + else + am.ifa_scope = RT_SCOPE_UNIVERSE; + } + + msg = nlmsg_alloc_simple(cmd, flags); + if (!msg) + return -NLE_NOMEM; + + if (nlmsg_append(msg, &am, sizeof(am), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + if (tmpl->ce_mask & ADDR_ATTR_LOCAL) + NLA_PUT_ADDR(msg, IFA_LOCAL, tmpl->a_local); + + if (tmpl->ce_mask & ADDR_ATTR_PEER) + NLA_PUT_ADDR(msg, IFA_ADDRESS, tmpl->a_peer); + else if (tmpl->ce_mask & ADDR_ATTR_LOCAL) + NLA_PUT_ADDR(msg, IFA_ADDRESS, tmpl->a_local); + + if (tmpl->ce_mask & ADDR_ATTR_LABEL) + NLA_PUT_STRING(msg, IFA_LABEL, tmpl->a_label); + + if (tmpl->ce_mask & ADDR_ATTR_BROADCAST) + NLA_PUT_ADDR(msg, IFA_BROADCAST, tmpl->a_bcast); + + if (tmpl->ce_mask & ADDR_ATTR_CACHEINFO) { + struct ifa_cacheinfo ca = { + .ifa_valid = tmpl->a_cacheinfo.aci_valid, + .ifa_prefered = tmpl->a_cacheinfo.aci_prefered, + }; + + NLA_PUT(msg, IFA_CACHEINFO, sizeof(ca), &ca); + } + + if (tmpl->a_flags & ~0xFF) { + /* only set the IFA_FLAGS attribute, if they actually contain additional + * flags that are not already set to am.ifa_flags. + * + * Older kernels refuse RTM_NEWADDR and RTM_NEWROUTE messages with EINVAL + * if they contain unknown netlink attributes. See net/core/rtnetlink.c, which + * was fixed by kernel commit 661d2967b3f1b34eeaa7e212e7b9bbe8ee072b59. + * + * With this workaround, libnl will function correctly with older kernels, + * unless there is a new libnl user that wants to set these flags. In this + * case it's up to the user to workaround this issue. */ + NLA_PUT_U32(msg, IFA_FLAGS, tmpl->a_flags); + } + + *result = msg; + return 0; + +nla_put_failure: + nlmsg_free(msg); + return -NLE_MSGSIZE; +} + +/** + * @name Addition + * @{ + */ + +/** + * Build netlink request message to request addition of new address + * @arg addr Address object representing the new address. + * @arg flags Additional netlink message flags. + * @arg result Pointer to store resulting message. + * + * Builds a new netlink message requesting the addition of a new + * address. The netlink message header isn't fully equipped with + * all relevant fields and must thus be sent out via nl_send_auto_complete() + * or supplemented as needed. + * + * Minimal required attributes: + * - interface index (rtnl_addr_set_ifindex()) + * - local address (rtnl_addr_set_local()) + * + * The scope will default to universe except for loopback addresses in + * which case a host scope is used if not specified otherwise. + * + * @note Free the memory after usage using nlmsg_free(). + * + * @return 0 on success or a negative error code. + */ +int rtnl_addr_build_add_request(struct rtnl_addr *addr, int flags, + struct nl_msg **result) +{ + uint32_t required = ADDR_ATTR_IFINDEX | ADDR_ATTR_FAMILY | + ADDR_ATTR_PREFIXLEN | ADDR_ATTR_LOCAL; + + if ((addr->ce_mask & required) != required) + return -NLE_MISSING_ATTR; + + return build_addr_msg(addr, RTM_NEWADDR, NLM_F_CREATE | flags, result); +} + +/** + * Request addition of new address + * @arg sk Netlink socket. + * @arg addr Address object representing the new address. + * @arg flags Additional netlink message flags. + * + * Builds a netlink message by calling rtnl_addr_build_add_request(), + * sends the request to the kernel and waits for the next ACK to be + * received and thus blocks until the request has been fullfilled. + * + * @see rtnl_addr_build_add_request() + * + * @return 0 on sucess or a negative error if an error occured. + */ +int rtnl_addr_add(struct nl_sock *sk, struct rtnl_addr *addr, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_addr_build_add_request(addr, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +/** @} */ + +/** + * @name Deletion + * @{ + */ + +/** + * Build a netlink request message to request deletion of an address + * @arg addr Address object to be deleteted. + * @arg flags Additional netlink message flags. + * @arg result Pointer to store resulting message. + * + * Builds a new netlink message requesting a deletion of an address. + * The netlink message header isn't fully equipped with all relevant + * fields and must thus be sent out via nl_send_auto_complete() + * or supplemented as needed. + * + * Minimal required attributes: + * - interface index (rtnl_addr_set_ifindex()) + * - address family (rtnl_addr_set_family()) + * + * Optional attributes: + * - local address (rtnl_addr_set_local()) + * - label (rtnl_addr_set_label(), IPv4/DECnet only) + * - peer address (rtnl_addr_set_peer(), IPv4 only) + * + * @note Free the memory after usage using nlmsg_free(). + * + * @return 0 on success or a negative error code. + */ +int rtnl_addr_build_delete_request(struct rtnl_addr *addr, int flags, + struct nl_msg **result) +{ + uint32_t required = ADDR_ATTR_IFINDEX | ADDR_ATTR_FAMILY; + + if ((addr->ce_mask & required) != required) + return -NLE_MISSING_ATTR; + + return build_addr_msg(addr, RTM_DELADDR, flags, result); +} + +/** + * Request deletion of an address + * @arg sk Netlink socket. + * @arg addr Address object to be deleted. + * @arg flags Additional netlink message flags. + * + * Builds a netlink message by calling rtnl_addr_build_delete_request(), + * sends the request to the kernel and waits for the next ACK to be + * received and thus blocks until the request has been fullfilled. + * + * @see rtnl_addr_build_delete_request(); + * + * @return 0 on sucess or a negative error if an error occured. + */ +int rtnl_addr_delete(struct nl_sock *sk, struct rtnl_addr *addr, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_addr_build_delete_request(addr, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +int rtnl_addr_set_label(struct rtnl_addr *addr, const char *label) +{ + if (strlen(label) > sizeof(addr->a_label) - 1) + return -NLE_RANGE; + + strcpy(addr->a_label, label); + addr->ce_mask |= ADDR_ATTR_LABEL; + + return 0; +} + +char *rtnl_addr_get_label(struct rtnl_addr *addr) +{ + if (addr->ce_mask & ADDR_ATTR_LABEL) + return addr->a_label; + else + return NULL; +} + +void rtnl_addr_set_ifindex(struct rtnl_addr *addr, int ifindex) +{ + addr->a_ifindex = ifindex; + addr->ce_mask |= ADDR_ATTR_IFINDEX; +} + +int rtnl_addr_get_ifindex(struct rtnl_addr *addr) +{ + return addr->a_ifindex; +} + +void rtnl_addr_set_link(struct rtnl_addr *addr, struct rtnl_link *link) +{ + rtnl_link_put(addr->a_link); + + if (!link) + return; + + nl_object_get(OBJ_CAST(link)); + addr->a_link = link; + addr->a_ifindex = link->l_index; + addr->ce_mask |= ADDR_ATTR_IFINDEX; +} + +struct rtnl_link *rtnl_addr_get_link(struct rtnl_addr *addr) +{ + if (addr->a_link) { + nl_object_get(OBJ_CAST(addr->a_link)); + return addr->a_link; + } + + return NULL; +} + +void rtnl_addr_set_family(struct rtnl_addr *addr, int family) +{ + addr->a_family = family; + addr->ce_mask |= ADDR_ATTR_FAMILY; +} + +int rtnl_addr_get_family(struct rtnl_addr *addr) +{ + return addr->a_family; +} + +/** + * Set the prefix length / netmask + * @arg addr Address + * @arg prefixlen Length of prefix (netmask) + * + * Modifies the length of the prefix. If the address object contains a peer + * address the prefix length will apply to it, otherwise the prefix length + * will apply to the local address of the address. + * + * If the address object contains a peer or local address the corresponding + * `struct nl_addr` will be updated with the new prefix length. + * + * @note Specifying a length of 0 will remove the prefix length alltogether. + * + * @see rtnl_addr_get_prefixlen() + */ +void rtnl_addr_set_prefixlen(struct rtnl_addr *addr, int prefixlen) +{ + addr->a_prefixlen = prefixlen; + + if (prefixlen) + addr->ce_mask |= ADDR_ATTR_PREFIXLEN; + else + addr->ce_mask &= ~ADDR_ATTR_PREFIXLEN; + + /* + * The prefix length always applies to the peer address if + * a peer address is present. + */ + if (addr->a_peer) + nl_addr_set_prefixlen(addr->a_peer, prefixlen); + else if (addr->a_local) + nl_addr_set_prefixlen(addr->a_local, prefixlen); +} + +int rtnl_addr_get_prefixlen(struct rtnl_addr *addr) +{ + return addr->a_prefixlen; +} + +void rtnl_addr_set_scope(struct rtnl_addr *addr, int scope) +{ + addr->a_scope = scope; + addr->ce_mask |= ADDR_ATTR_SCOPE; +} + +int rtnl_addr_get_scope(struct rtnl_addr *addr) +{ + return addr->a_scope; +} + +void rtnl_addr_set_flags(struct rtnl_addr *addr, unsigned int flags) +{ + addr->a_flag_mask |= flags; + addr->a_flags |= flags; + addr->ce_mask |= ADDR_ATTR_FLAGS; +} + +void rtnl_addr_unset_flags(struct rtnl_addr *addr, unsigned int flags) +{ + addr->a_flag_mask |= flags; + addr->a_flags &= ~flags; + addr->ce_mask |= ADDR_ATTR_FLAGS; +} + +unsigned int rtnl_addr_get_flags(struct rtnl_addr *addr) +{ + return addr->a_flags; +} + +static inline int __assign_addr(struct rtnl_addr *addr, struct nl_addr **pos, + struct nl_addr *new, int flag) +{ + if (new) { + if (addr->ce_mask & ADDR_ATTR_FAMILY) { + if (new->a_family != addr->a_family) + return -NLE_AF_MISMATCH; + } else + addr->a_family = new->a_family; + + if (*pos) + nl_addr_put(*pos); + + *pos = nl_addr_get(new); + addr->ce_mask |= (flag | ADDR_ATTR_FAMILY); + } else { + if (*pos) + nl_addr_put(*pos); + + *pos = NULL; + addr->ce_mask &= ~flag; + } + + return 0; +} + +int rtnl_addr_set_local(struct rtnl_addr *addr, struct nl_addr *local) +{ + int err; + + /* Prohibit local address with prefix length if peer address is present */ + if ((addr->ce_mask & ADDR_ATTR_PEER) && local && + nl_addr_get_prefixlen(local)) + return -NLE_INVAL; + + err = __assign_addr(addr, &addr->a_local, local, ADDR_ATTR_LOCAL); + if (err < 0) + return err; + + /* Never overwrite the prefix length if a peer address is present */ + if (!(addr->ce_mask & ADDR_ATTR_PEER)) + rtnl_addr_set_prefixlen(addr, local ? nl_addr_get_prefixlen(local) : 0); + + return 0; +} + +struct nl_addr *rtnl_addr_get_local(struct rtnl_addr *addr) +{ + return addr->a_local; +} + +int rtnl_addr_set_peer(struct rtnl_addr *addr, struct nl_addr *peer) +{ + int err; + + if (peer && peer->a_family != AF_INET) + return -NLE_AF_NOSUPPORT; + + err = __assign_addr(addr, &addr->a_peer, peer, ADDR_ATTR_PEER); + if (err < 0) + return err; + + rtnl_addr_set_prefixlen(addr, peer ? nl_addr_get_prefixlen(peer) : 0); + + return 0; +} + +struct nl_addr *rtnl_addr_get_peer(struct rtnl_addr *addr) +{ + return addr->a_peer; +} + +int rtnl_addr_set_broadcast(struct rtnl_addr *addr, struct nl_addr *bcast) +{ + if (bcast && bcast->a_family != AF_INET) + return -NLE_AF_NOSUPPORT; + + return __assign_addr(addr, &addr->a_bcast, bcast, ADDR_ATTR_BROADCAST); +} + +struct nl_addr *rtnl_addr_get_broadcast(struct rtnl_addr *addr) +{ + return addr->a_bcast; +} + +int rtnl_addr_set_multicast(struct rtnl_addr *addr, struct nl_addr *multicast) +{ + if (multicast && multicast->a_family != AF_INET6) + return -NLE_AF_NOSUPPORT; + + return __assign_addr(addr, &addr->a_multicast, multicast, + ADDR_ATTR_MULTICAST); +} + +struct nl_addr *rtnl_addr_get_multicast(struct rtnl_addr *addr) +{ + return addr->a_multicast; +} + +int rtnl_addr_set_anycast(struct rtnl_addr *addr, struct nl_addr *anycast) +{ + if (anycast && anycast->a_family != AF_INET6) + return -NLE_AF_NOSUPPORT; + + return __assign_addr(addr, &addr->a_anycast, anycast, + ADDR_ATTR_ANYCAST); +} + +struct nl_addr *rtnl_addr_get_anycast(struct rtnl_addr *addr) +{ + return addr->a_anycast; +} + +uint32_t rtnl_addr_get_valid_lifetime(struct rtnl_addr *addr) +{ + if (addr->ce_mask & ADDR_ATTR_CACHEINFO) + return addr->a_cacheinfo.aci_valid; + else + return 0xFFFFFFFFU; +} + +void rtnl_addr_set_valid_lifetime(struct rtnl_addr *addr, uint32_t lifetime) +{ + addr->a_cacheinfo.aci_valid = lifetime; + addr->ce_mask |= ADDR_ATTR_CACHEINFO; +} + +uint32_t rtnl_addr_get_preferred_lifetime(struct rtnl_addr *addr) +{ + if (addr->ce_mask & ADDR_ATTR_CACHEINFO) + return addr->a_cacheinfo.aci_prefered; + else + return 0xFFFFFFFFU; +} + +void rtnl_addr_set_preferred_lifetime(struct rtnl_addr *addr, uint32_t lifetime) +{ + addr->a_cacheinfo.aci_prefered = lifetime; + addr->ce_mask |= ADDR_ATTR_CACHEINFO; +} + +uint32_t rtnl_addr_get_create_time(struct rtnl_addr *addr) +{ + return addr->a_cacheinfo.aci_cstamp; +} + +uint32_t rtnl_addr_get_last_update_time(struct rtnl_addr *addr) +{ + return addr->a_cacheinfo.aci_tstamp; +} + +/** @} */ + +/** + * @name Flags Translations + * @{ + */ + +static const struct trans_tbl addr_flags[] = { + __ADD(IFA_F_SECONDARY, secondary), + __ADD(IFA_F_NODAD, nodad), + __ADD(IFA_F_OPTIMISTIC, optimistic), + __ADD(IFA_F_DADFAILED, dadfailed), + __ADD(IFA_F_HOMEADDRESS, homeaddress), + __ADD(IFA_F_DEPRECATED, deprecated), + __ADD(IFA_F_TENTATIVE, tentative), + __ADD(IFA_F_PERMANENT, permanent), + __ADD(IFA_F_MANAGETEMPADDR, mngtmpaddr), + __ADD(IFA_F_NOPREFIXROUTE, noprefixroute), +}; + +char *rtnl_addr_flags2str(int flags, char *buf, size_t size) +{ + return __flags2str(flags, buf, size, addr_flags, + ARRAY_SIZE(addr_flags)); +} + +int rtnl_addr_str2flags(const char *name) +{ + return __str2flags(name, addr_flags, ARRAY_SIZE(addr_flags)); +} + +/** @} */ + +static struct nl_object_ops addr_obj_ops = { + .oo_name = "route/addr", + .oo_size = sizeof(struct rtnl_addr), + .oo_constructor = addr_constructor, + .oo_free_data = addr_free_data, + .oo_clone = addr_clone, + .oo_dump = { + [NL_DUMP_LINE] = addr_dump_line, + [NL_DUMP_DETAILS] = addr_dump_details, + [NL_DUMP_STATS] = addr_dump_stats, + }, + .oo_compare = addr_compare, + .oo_attrs2str = addr_attrs2str, + .oo_id_attrs_get = addr_id_attrs_get, + .oo_id_attrs = (ADDR_ATTR_FAMILY | ADDR_ATTR_IFINDEX | + ADDR_ATTR_LOCAL | ADDR_ATTR_PREFIXLEN), +}; + +static struct nl_af_group addr_groups[] = { + { AF_INET, RTNLGRP_IPV4_IFADDR }, + { AF_INET6, RTNLGRP_IPV6_IFADDR }, + { END_OF_GROUP_LIST }, +}; + +static struct nl_cache_ops rtnl_addr_ops = { + .co_name = "route/addr", + .co_hdrsize = sizeof(struct ifaddrmsg), + .co_msgtypes = { + { RTM_NEWADDR, NL_ACT_NEW, "new" }, + { RTM_DELADDR, NL_ACT_DEL, "del" }, + { RTM_GETADDR, NL_ACT_GET, "get" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_ROUTE, + .co_groups = addr_groups, + .co_request_update = addr_request_update, + .co_msg_parser = addr_msg_parser, + .co_obj_ops = &addr_obj_ops, +}; + +static void __init addr_init(void) +{ + nl_cache_mngt_register(&rtnl_addr_ops); +} + +static void __exit addr_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_addr_ops); +} + +/** @} */ diff --git a/libnl/lib/route/class.c b/libnl/lib/route/class.c new file mode 100644 index 0000000..e3b6603 --- /dev/null +++ b/libnl/lib/route/class.c @@ -0,0 +1,502 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + */ + +/** + * @ingroup tc + * @defgroup class Traffic Classes + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +static struct nl_cache_ops rtnl_class_ops; +static struct nl_object_ops class_obj_ops; + +static void class_dump_details(struct rtnl_tc *tc, struct nl_dump_params *p) +{ + struct rtnl_class *class = (struct rtnl_class *) tc; + char buf[32]; + + if (class->c_info) + nl_dump(p, "child-qdisc %s ", + rtnl_tc_handle2str(class->c_info, buf, sizeof(buf))); +} + + +static int class_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, struct nl_parser_param *pp) +{ + struct rtnl_class *class; + int err; + + if (!(class = rtnl_class_alloc())) + return -NLE_NOMEM; + + if ((err = rtnl_tc_msg_parse(nlh, TC_CAST(class))) < 0) + goto errout; + + err = pp->pp_cb(OBJ_CAST(class), pp); +errout: + rtnl_class_put(class); + + return err; +} + +static int class_request_update(struct nl_cache *cache, struct nl_sock *sk) +{ + struct tcmsg tchdr = { + .tcm_family = AF_UNSPEC, + .tcm_ifindex = cache->c_iarg1, + }; + + return nl_send_simple(sk, RTM_GETTCLASS, NLM_F_DUMP, &tchdr, + sizeof(tchdr)); +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct rtnl_class *rtnl_class_alloc(void) +{ + struct rtnl_tc *tc; + + tc = TC_CAST(nl_object_alloc(&class_obj_ops)); + if (tc) + tc->tc_type = RTNL_TC_TYPE_CLASS; + + return (struct rtnl_class *) tc; +} + +void rtnl_class_put(struct rtnl_class *class) +{ + nl_object_put((struct nl_object *) class); +} + +/** @} */ + + +/** + * @name Addition/Modification/Deletion + * @{ + */ + +static int class_build(struct rtnl_class *class, int type, int flags, + struct nl_msg **result) +{ + uint32_t needed = TCA_ATTR_PARENT | TCA_ATTR_HANDLE; + + if ((class->ce_mask & needed) == needed && + TC_H_MAJ(class->c_parent) && TC_H_MAJ(class->c_handle) && + TC_H_MAJ(class->c_parent) != TC_H_MAJ(class->c_handle)) { + APPBUG("TC_H_MAJ(parent) must match TC_H_MAJ(handle)"); + return -NLE_INVAL; + } + + return rtnl_tc_msg_build(TC_CAST(class), type, flags, result); +} + +/** + * Build a netlink message requesting the addition of a traffic class + * @arg class Traffic class to add + * @arg flags Additional netlink message flags + * @arg result Pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_class_add() with + * the exception that it will not send the message but return it int the + * provided return pointer instead. + * + * @see rtnl_class_add() + * + * @return 0 on success or a negative error code. + */ +int rtnl_class_build_add_request(struct rtnl_class *class, int flags, + struct nl_msg **result) +{ + return class_build(class, RTM_NEWTCLASS, flags, result); +} + +/** + * Add/Update traffic class + * @arg sk Netlink socket + * @arg class Traffic class to add + * @arg flags Additional netlink message flags + * + * Builds a \c RTM_NEWTCLASS netlink message requesting the addition + * of a new traffic class and sends the message to the kernel. The + * configuration of the traffic class is derived from the attributes + * of the specified traffic class. + * + * The following flags may be specified: + * - \c NLM_F_CREATE: Create traffic class if it does not exist, + * otherwise -NLE_OBJ_NOTFOUND is returned. + * - \c NLM_F_EXCL: Return -NLE_EXISTS if a traffic class with + * matching handle exists already. + * + * Existing traffic classes with matching handles will be updated, + * unless the flag \c NLM_F_EXCL is specified. If no matching traffic + * class exists, it will be created if the flag \c NLM_F_CREATE is set, + * otherwise the error -NLE_OBJ_NOTFOUND is returned. + * + * If the parent qdisc does not support classes, the error + * \c NLE_OPNOTSUPP is returned. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause + * this function to return immediately after sending. In this case, + * it is the responsibility of the caller to handle any error + * messages returned. + * + * @return 0 on success or a negative error code. + */ +int rtnl_class_add(struct nl_sock *sk, struct rtnl_class *class, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_class_build_add_request(class, flags, &msg)) < 0) + return err; + + return nl_send_sync(sk, msg); +} + +/** + * Build netlink message requesting the deletion of a traffic class + * @arg class Traffic class to delete + * @arg result Pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_class_delete() with + * the exception that it will not send the message but return it in the + * provided return pointer instead. + * + * @see rtnl_class_delete() + * + * @return 0 on success or a negative error code. + */ +int rtnl_class_build_delete_request(struct rtnl_class *class, struct nl_msg **result) +{ + struct nl_msg *msg; + struct tcmsg tchdr; + uint32_t required = TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE; + + if ((class->ce_mask & required) != required) { + APPBUG("ifindex and handle must be specified"); + return -NLE_MISSING_ATTR; + } + + if (!(msg = nlmsg_alloc_simple(RTM_DELTCLASS, 0))) + return -NLE_NOMEM; + + memset(&tchdr, 0, sizeof(tchdr)); + tchdr.tcm_family = AF_UNSPEC; + tchdr.tcm_ifindex = class->c_ifindex; + tchdr.tcm_handle = class->c_handle; + + if (class->ce_mask & TCA_ATTR_PARENT) + tchdr.tcm_parent = class->c_parent; + + if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) { + nlmsg_free(msg); + return -NLE_MSGSIZE; + } + + *result = msg; + return 0; +} + +/** + * Delete traffic class + * @arg sk Netlink socket + * @arg class Traffic class to delete + * + * Builds a \c RTM_DELTCLASS netlink message requesting the deletion + * of a traffic class and sends the message to the kernel. + * + * The message is constructed out of the following attributes: + * - \c ifindex and \c handle (required) + * - \c parent (optional, must match if provided) + * + * All other class attributes including all class type specific + * attributes are ignored. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause + * this function to return immediately after sending. In this case, + * it is the responsibility of the caller to handle any error + * messages returned. + * + * @return 0 on success or a negative error code. + */ +int rtnl_class_delete(struct nl_sock *sk, struct rtnl_class *class) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_class_build_delete_request(class, &msg)) < 0) + return err; + + return nl_send_sync(sk, msg); +} + +/** @} */ + +/** + * @name Leaf Qdisc + * @{ + */ + +/** + * Lookup the leaf qdisc of a traffic class + * @arg class the parent traffic class + * @arg cache a qdisc cache allocated using rtnl_qdisc_alloc_cache() + * + * @return Matching Qdisc or NULL if the traffic class has no leaf qdisc + */ +struct rtnl_qdisc *rtnl_class_leaf_qdisc(struct rtnl_class *class, + struct nl_cache *cache) +{ + struct rtnl_qdisc *leaf; + + if (!class->c_info) + return NULL; + + leaf = rtnl_qdisc_get_by_parent(cache, class->c_ifindex, + class->c_handle); + if (!leaf || leaf->q_handle != class->c_info) + return NULL; + + return leaf; +} + +/** @} */ + +/** + * @name Cache Related Functions + * @{ + */ + +/** + * Allocate a cache and fill it with all configured traffic classes + * @arg sk Netlink socket + * @arg ifindex Interface index of the network device + * @arg result Pointer to store the created cache + * + * Allocates a new traffic class cache and fills it with a list of all + * configured traffic classes on a specific network device. Release the + * cache with nl_cache_free(). + * + * @return 0 on success or a negative error code. + */ +int rtnl_class_alloc_cache(struct nl_sock *sk, int ifindex, + struct nl_cache **result) +{ + struct nl_cache * cache; + int err; + + if (!ifindex) { + APPBUG("ifindex must be specified"); + return -NLE_INVAL; + } + + if (!(cache = nl_cache_alloc(&rtnl_class_ops))) + return -NLE_NOMEM; + + cache->c_iarg1 = ifindex; + + if (sk && (err = nl_cache_refill(sk, cache)) < 0) { + nl_cache_free(cache); + return err; + } + + *result = cache; + return 0; +} + +/** + * Search traffic class by interface index and handle + * @arg cache Traffic class cache + * @arg ifindex Interface index + * @arg handle ID of traffic class + * + * Searches a traffic class cache previously allocated with + * rtnl_class_alloc_cache() and searches for a traffi class matching + * the interface index and handle. + * + * The reference counter is incremented before returning the traffic + * class, therefore the reference must be given back with rtnl_class_put() + * after usage. + * + * @return Traffic class or NULL if no match was found. + */ +struct rtnl_class *rtnl_class_get(struct nl_cache *cache, int ifindex, + uint32_t handle) +{ + struct rtnl_class *class; + + if (cache->c_ops != &rtnl_class_ops) + return NULL; + + nl_list_for_each_entry(class, &cache->c_items, ce_list) { + if (class->c_handle == handle && class->c_ifindex == ifindex) { + nl_object_get((struct nl_object *) class); + return class; + } + } + return NULL; +} + +/** + * Search class by interface index and parent + * @arg cache Traffic class cache + * @arg ifindex Interface index + * @arg parent Handle of parent qdisc + * + * Searches a class cache previously allocated with rtnl_class_alloc_cache() + * and searches for a class matching the interface index and parent qdisc. + * + * The reference counter is incremented before returning the class, therefore + * the reference must be given back with rtnl_class_put() after usage. + * + * @return pointer to class inside the cache or NULL if no match was found. + */ +struct rtnl_class *rtnl_class_get_by_parent(struct nl_cache *cache, int ifindex, + uint32_t parent) +{ + struct rtnl_class *class; + + if (cache->c_ops != &rtnl_class_ops) + return NULL; + + nl_list_for_each_entry(class, &cache->c_items, ce_list) { + if (class->c_parent == parent && class->c_ifindex == ifindex) { + nl_object_get((struct nl_object *) class); + return class; + } + } + + return NULL; +} + +/** @} */ + +/** + * @name Deprecated Functions + * @{ + */ + +/** + * Call a callback for each child of a class + * + * @deprecated Use of this function is deprecated, it does not allow + * to handle the out of memory situation that can occur. + */ +void rtnl_class_foreach_child(struct rtnl_class *class, struct nl_cache *cache, + void (*cb)(struct nl_object *, void *), void *arg) +{ + struct rtnl_class *filter; + + filter = rtnl_class_alloc(); + if (!filter) + return; + + rtnl_tc_set_parent(TC_CAST(filter), class->c_handle); + rtnl_tc_set_ifindex(TC_CAST(filter), class->c_ifindex); + rtnl_tc_set_kind(TC_CAST(filter), class->c_kind); + + nl_cache_foreach_filter(cache, OBJ_CAST(filter), cb, arg); + rtnl_class_put(filter); +} + +/** + * Call a callback for each classifier attached to the class + * + * @deprecated Use of this function is deprecated, it does not allow + * to handle the out of memory situation that can occur. + */ +void rtnl_class_foreach_cls(struct rtnl_class *class, struct nl_cache *cache, + void (*cb)(struct nl_object *, void *), void *arg) +{ + struct rtnl_cls *filter; + + filter = rtnl_cls_alloc(); + if (!filter) + return; + + rtnl_tc_set_ifindex((struct rtnl_tc *) filter, class->c_ifindex); + rtnl_tc_set_parent((struct rtnl_tc *) filter, class->c_parent); + + nl_cache_foreach_filter(cache, (struct nl_object *) filter, cb, arg); + rtnl_cls_put(filter); +} + +/** @} */ + +static struct rtnl_tc_type_ops class_ops = { + .tt_type = RTNL_TC_TYPE_CLASS, + .tt_dump_prefix = "class", + .tt_dump = { + [NL_DUMP_DETAILS] = class_dump_details, + }, +}; + +static struct nl_object_ops class_obj_ops = { + .oo_name = "route/class", + .oo_size = sizeof(struct rtnl_class), + .oo_free_data = rtnl_tc_free_data, + .oo_clone = rtnl_tc_clone, + .oo_dump = { + [NL_DUMP_LINE] = rtnl_tc_dump_line, + [NL_DUMP_DETAILS] = rtnl_tc_dump_details, + [NL_DUMP_STATS] = rtnl_tc_dump_stats, + }, + .oo_compare = rtnl_tc_compare, + .oo_id_attrs = (TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE), +}; + +static struct nl_cache_ops rtnl_class_ops = { + .co_name = "route/class", + .co_hdrsize = sizeof(struct tcmsg), + .co_msgtypes = { + { RTM_NEWTCLASS, NL_ACT_NEW, "new" }, + { RTM_DELTCLASS, NL_ACT_DEL, "del" }, + { RTM_GETTCLASS, NL_ACT_GET, "get" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_ROUTE, + .co_groups = tc_groups, + .co_request_update = &class_request_update, + .co_msg_parser = &class_msg_parser, + .co_obj_ops = &class_obj_ops, +}; + +static void __init class_init(void) +{ + rtnl_tc_type_register(&class_ops); + nl_cache_mngt_register(&rtnl_class_ops); +} + +static void __exit class_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_class_ops); + rtnl_tc_type_unregister(&class_ops); +} + +/** @} */ diff --git a/libnl/lib/route/classid.c b/libnl/lib/route/classid.c new file mode 100644 index 0000000..7149083 --- /dev/null +++ b/libnl/lib/route/classid.c @@ -0,0 +1,453 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010-2013 Thomas Graf + */ + +/** + * @ingroup tc + * @defgroup classid ClassID Management + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include + +struct classid_map +{ + uint32_t classid; + char * name; + struct nl_list_head name_list; +}; + +#define CLASSID_NAME_HT_SIZ 256 + +static struct nl_list_head tbl_name[CLASSID_NAME_HT_SIZ]; + +static void *id_root = NULL; + +static int compare_id(const void *pa, const void *pb) +{ + const struct classid_map *ma = pa; + const struct classid_map *mb = pb; + + if (ma->classid < mb->classid) + return -1; + + if (ma->classid > mb->classid) + return 1; + + return 0; +} + +/* djb2 */ +static unsigned int classid_tbl_hash(const char *str) +{ + unsigned long hash = 5381; + int c; + + while ((c = *str++)) + hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ + + return hash % CLASSID_NAME_HT_SIZ; +} + +static int classid_lookup(const char *name, uint32_t *result) +{ + struct classid_map *map; + int n = classid_tbl_hash(name); + + nl_list_for_each_entry(map, &tbl_name[n], name_list) { + if (!strcasecmp(map->name, name)) { + *result = map->classid; + return 0; + } + } + + return -NLE_OBJ_NOTFOUND; +} + +static char *name_lookup(const uint32_t classid) +{ + void *res; + struct classid_map cm = { + .classid = classid, + .name = "search entry", + }; + + if ((res = tfind(&cm, &id_root, &compare_id))) + return (*(struct classid_map **) res)->name; + + return NULL; +} + +/** + * @name Traffic Control Handle Translations + * @{ + */ + +/** + * Convert a traffic control handle to a character string (Reentrant). + * @arg handle traffic control handle + * @arg buf destination buffer + * @arg len buffer length + * + * Converts a tarffic control handle to a character string in the + * form of \c MAJ:MIN and stores it in the specified destination buffer. + * + * @return The destination buffer or the type encoded in hexidecimal + * form if no match was found. + */ +char *rtnl_tc_handle2str(uint32_t handle, char *buf, size_t len) +{ + if (TC_H_ROOT == handle) + snprintf(buf, len, "root"); + else if (TC_H_UNSPEC == handle) + snprintf(buf, len, "none"); + else if (TC_H_INGRESS == handle) + snprintf(buf, len, "ingress"); + else { + char *name; + + if ((name = name_lookup(handle))) + snprintf(buf, len, "%s", name); + else if (0 == TC_H_MAJ(handle)) + snprintf(buf, len, ":%x", TC_H_MIN(handle)); + else if (0 == TC_H_MIN(handle)) + snprintf(buf, len, "%x:", TC_H_MAJ(handle) >> 16); + else + snprintf(buf, len, "%x:%x", + TC_H_MAJ(handle) >> 16, TC_H_MIN(handle)); + } + + return buf; +} + +/** + * Convert a charactering strint to a traffic control handle + * @arg str traffic control handle as character string + * @arg res destination buffer + * + * Converts the provided character string specifying a traffic + * control handle to the corresponding numeric value. + * + * The handle must be provided in one of the following formats: + * - NAME + * - root + * - none + * - MAJ: + * - :MIN + * - NAME:MIN + * - MAJ:MIN + * - MAJMIN + * + * @return 0 on success or a negative error code + */ +int rtnl_tc_str2handle(const char *str, uint32_t *res) +{ + char *colon, *end; + uint32_t h; + int err; + + if (!strcasecmp(str, "root")) { + *res = TC_H_ROOT; + return 0; + } + + if (!strcasecmp(str, "none")) { + *res = TC_H_UNSPEC; + return 0; + } + + if (!strcasecmp(str, "ingress")) { + *res = TC_H_INGRESS; + return 0; + } + + h = strtoul(str, &colon, 16); + + /* MAJ is not a number */ + if (colon == str) { +not_a_number: + if (*colon == ':') { + /* :YYYY */ + h = 0; + } else { + size_t len; + char name[64] = { 0 }; + + if (!(colon = strpbrk(str, ":"))) { + /* NAME */ + return classid_lookup(str, res); + } else { + /* NAME:YYYY */ + len = colon - str; + if (len >= sizeof(name)) + return -NLE_INVAL; + + memcpy(name, str, len); + + if ((err = classid_lookup(name, &h)) < 0) + return err; + + /* Name must point to a qdisc alias */ + if (TC_H_MIN(h)) + return -NLE_INVAL; + + /* NAME: is not allowed */ + if (colon[1] == '\0') + return -NLE_INVAL; + + goto update; + } + } + } + + if (':' == *colon) { + /* check if we would lose bits */ + if (TC_H_MAJ(h)) + return -NLE_RANGE; + h <<= 16; + + if ('\0' == colon[1]) { + /* XXXX: */ + *res = h; + } else { + /* XXXX:YYYY */ + uint32_t l; + +update: + l = strtoul(colon+1, &end, 16); + + /* check if we overlap with major part */ + if (TC_H_MAJ(l)) + return -NLE_RANGE; + + if ('\0' != *end) + return -NLE_INVAL; + + *res = (h | l); + } + } else if ('\0' == *colon) { + /* XXXXYYYY */ + *res = h; + } else + goto not_a_number; + + return 0; +} + +static void free_nothing(void *arg) +{ +} + +static void classid_map_free(struct classid_map *map) +{ + if (!map) + return; + + free(map->name); + free(map); +} + +static void clear_hashtable(void) +{ + int i; + + for (i = 0; i < CLASSID_NAME_HT_SIZ; i++) { + struct classid_map *map, *n; + + nl_list_for_each_entry_safe(map, n, &tbl_name[i], name_list) + classid_map_free(map); + + nl_init_list_head(&tbl_name[i]); + + } + + if (id_root) { + tdestroy(&id_root, &free_nothing); + id_root = NULL; + } +} + +static int classid_map_add(uint32_t classid, const char *name) +{ + struct classid_map *map; + int n; + + if (!(map = calloc(1, sizeof(*map)))) + return -NLE_NOMEM; + + map->classid = classid; + map->name = strdup(name); + + n = classid_tbl_hash(map->name); + nl_list_add_tail(&map->name_list, &tbl_name[n]); + + if (!tsearch((void *) map, &id_root, &compare_id)) { + classid_map_free(map); + return -NLE_NOMEM; + } + + return 0; +} + +/** + * (Re-)read classid file + * + * Rereads the contents of the classid file (typically found at the location + * /etc/libnl/classid) and refreshes the classid maps. + * + * @return 0 on success or a negative error code. + */ +int rtnl_tc_read_classid_file(void) +{ + static time_t last_read; + struct stat st; + char buf[256], *path; + FILE *fd; + int err; + + if (build_sysconf_path(&path, "classid") < 0) + return -NLE_NOMEM; + + /* if stat fails, just (re-)read the file */ + if (stat(path, &st) == 0) { + /* Don't re-read file if file is unchanged */ + if (last_read == st.st_mtime) { + err = 0; + goto errout; + } + } + + if (!(fd = fopen(path, "re"))) { + err = -nl_syserr2nlerr(errno); + goto errout; + } + + clear_hashtable(); + + while (fgets(buf, sizeof(buf), fd)) { + uint32_t classid; + char *ptr, *tok; + + /* ignore comments and empty lines */ + if (*buf == '#' || *buf == '\n' || *buf == '\r') + continue; + + /* token 1 */ + if (!(tok = strtok_r(buf, " \t", &ptr))) { + err = -NLE_INVAL; + goto errout_close; + } + + if ((err = rtnl_tc_str2handle(tok, &classid)) < 0) + goto errout_close; + + if (!(tok = strtok_r(NULL, " \t\n\r#", &ptr))) { + err = -NLE_INVAL; + goto errout_close; + } + + if ((err = classid_map_add(classid, tok)) < 0) + goto errout_close; + } + + err = 0; + last_read = st.st_mtime; + +errout_close: + fclose(fd); +errout: + free(path); + + return err; + +} + +int rtnl_classid_generate(const char *name, uint32_t *result, uint32_t parent) +{ + static uint32_t base = 0x4000 << 16; + uint32_t classid; + char *path; + FILE *fd; + int err = 0; + + if (parent == TC_H_ROOT || parent == TC_H_INGRESS) { + do { + base += (1 << 16); + if (base == TC_H_MAJ(TC_H_ROOT)) + base = 0x4000 << 16; + } while (name_lookup(base)); + + classid = base; + } else { + classid = TC_H_MAJ(parent); + do { + if (TC_H_MIN(++classid) == TC_H_MIN(TC_H_ROOT)) + return -NLE_RANGE; + } while (name_lookup(classid)); + } + + NL_DBG(2, "Generated new classid %#x\n", classid); + + if (build_sysconf_path(&path, "classid") < 0) + return -NLE_NOMEM; + + if (!(fd = fopen(path, "ae"))) { + err = -nl_syserr2nlerr(errno); + goto errout; + } + + fprintf(fd, "%x:", TC_H_MAJ(classid) >> 16); + if (TC_H_MIN(classid)) + fprintf(fd, "%x", TC_H_MIN(classid)); + fprintf(fd, "\t\t\t%s\n", name); + + fclose(fd); + + if ((err = classid_map_add(classid, name)) < 0) { + /* + * Error adding classid map, re-read classid file is best + * option here. It is likely to fail as well but better + * than nothing, entry was added to the file already anyway. + */ + rtnl_tc_read_classid_file(); + } + + *result = classid; + err = 0; +errout: + free(path); + + return err; +} + +/** @} */ + +static void __init classid_init(void) +{ + int err, i; + + for (i = 0; i < CLASSID_NAME_HT_SIZ; i++) + nl_init_list_head(&tbl_name[i]); + + if ((err = rtnl_tc_read_classid_file()) < 0) + NL_DBG(1, "Failed to read classid file: %s\n", nl_geterror(err)); +} + +static void free_map(void *map) +{ + free(((struct classid_map *)map)->name); + free(map); +} + +static void __exit classid_exit(void) +{ + tdestroy(id_root, free_map); +} +/** @} */ diff --git a/libnl/lib/route/cls.c b/libnl/lib/route/cls.c new file mode 100644 index 0000000..2609d12 --- /dev/null +++ b/libnl/lib/route/cls.c @@ -0,0 +1,461 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + */ + +/** + * @ingroup tc + * @defgroup cls Classifiers + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define CLS_ATTR_PRIO (TCA_ATTR_MAX << 1) +#define CLS_ATTR_PROTOCOL (TCA_ATTR_MAX << 2) +/** @endcond */ + +static struct nl_object_ops cls_obj_ops; +static struct nl_cache_ops rtnl_cls_ops; + + +static int cls_build(struct rtnl_cls *cls, int type, int flags, + struct nl_msg **result) +{ + int err, prio, proto; + struct tcmsg *tchdr; + uint32_t required = TCA_ATTR_IFINDEX; + + if ((cls->ce_mask & required) != required) { + APPBUG("ifindex must be specified"); + return -NLE_MISSING_ATTR; + } + + err = rtnl_tc_msg_build(TC_CAST(cls), type, flags, result); + if (err < 0) + return err; + + tchdr = nlmsg_data(nlmsg_hdr(*result)); + prio = rtnl_cls_get_prio(cls); + proto = rtnl_cls_get_protocol(cls); + tchdr->tcm_info = TC_H_MAKE(prio << 16, htons(proto)); + + return 0; +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct rtnl_cls *rtnl_cls_alloc(void) +{ + struct rtnl_tc *tc; + + tc = TC_CAST(nl_object_alloc(&cls_obj_ops)); + if (tc) + tc->tc_type = RTNL_TC_TYPE_CLS; + + return (struct rtnl_cls *) tc; +} + +void rtnl_cls_put(struct rtnl_cls *cls) +{ + nl_object_put((struct nl_object *) cls); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +void rtnl_cls_set_prio(struct rtnl_cls *cls, uint16_t prio) +{ + cls->c_prio = prio; + cls->ce_mask |= CLS_ATTR_PRIO; +} + +uint16_t rtnl_cls_get_prio(struct rtnl_cls *cls) +{ + if (cls->ce_mask & CLS_ATTR_PRIO) + return cls->c_prio; + else + return 0; +} + +void rtnl_cls_set_protocol(struct rtnl_cls *cls, uint16_t protocol) +{ + cls->c_protocol = protocol; + cls->ce_mask |= CLS_ATTR_PROTOCOL; +} + +uint16_t rtnl_cls_get_protocol(struct rtnl_cls *cls) +{ + if (cls->ce_mask & CLS_ATTR_PROTOCOL) + return cls->c_protocol; + else + return ETH_P_ALL; +} + +/** @} */ + + +/** + * @name Addition/Modification/Deletion + * @{ + */ + +/** + * Build a netlink message requesting the addition of a classifier + * @arg cls Classifier to add + * @arg flags Additional netlink message flags + * @arg result Pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_cls_add() with + * the exception that it will not send the message but return it int the + * provided return pointer instead. + * + * @see rtnl_cls_add() + * + * @return 0 on success or a negative error code. + */ +int rtnl_cls_build_add_request(struct rtnl_cls *cls, int flags, + struct nl_msg **result) +{ + if (!(flags & NLM_F_CREATE) && !(cls->ce_mask & CLS_ATTR_PRIO)) { + APPBUG("prio must be specified if not a new classifier"); + return -NLE_MISSING_ATTR; + } + + return cls_build(cls, RTM_NEWTFILTER, flags, result); +} + +/** + * Add/Update classifier + * @arg sk Netlink socket + * @arg cls Classifier to add/update + * @arg flags Additional netlink message flags + * + * Builds a \c RTM_NEWTFILTER netlink message requesting the addition + * of a new classifier and sends the message to the kernel. The + * configuration of the classifier is derived from the attributes of + * the specified traffic class. + * + * The following flags may be specified: + * - \c NLM_F_CREATE: Create classifier if it does not exist, + * otherwise -NLE_OBJ_NOTFOUND is returned. + * - \c NLM_F_EXCL: Return -NLE_EXISTS if a classifier with + * matching handle exists already. + * + * Existing classifiers with matching handles will be updated, unless + * the flag \c NLM_F_EXCL is specified. If no matching classifier + * exists, it will be created if the flag \c NLM_F_CREATE is set, + * otherwise the error -NLE_OBJ_NOTFOUND is returned. + * + * If the parent qdisc does not support classes, the error + * \c NLE_OPNOTSUPP is returned. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause + * this function to return immediately after sending. In this case, + * it is the responsibility of the caller to handle any error + * messages returned. + * + * @return 0 on success or a negative error code. + */ +int rtnl_cls_add(struct nl_sock *sk, struct rtnl_cls *cls, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_cls_build_add_request(cls, flags, &msg)) < 0) + return err; + + return nl_send_sync(sk, msg); +} + +/** + * Build a netlink message to change classifier attributes + * @arg cls classifier to change + * @arg flags additional netlink message flags + * @arg result Pointer to store resulting message. + * + * Builds a new netlink message requesting a change of a neigh + * attributes. The netlink message header isn't fully equipped with + * all relevant fields and must thus be sent out via nl_send_auto_complete() + * or supplemented as needed. + * + * @return 0 on success or a negative error code. + */ +int rtnl_cls_build_change_request(struct rtnl_cls *cls, int flags, + struct nl_msg **result) +{ + return cls_build(cls, RTM_NEWTFILTER, NLM_F_REPLACE | flags, result); +} + +/** + * Change a classifier + * @arg sk Netlink socket. + * @arg cls classifier to change + * @arg flags additional netlink message flags + * + * Builds a netlink message by calling rtnl_cls_build_change_request(), + * sends the request to the kernel and waits for the next ACK to be + * received and thus blocks until the request has been processed. + * + * @return 0 on sucess or a negative error if an error occured. + */ +int rtnl_cls_change(struct nl_sock *sk, struct rtnl_cls *cls, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_cls_build_change_request(cls, flags, &msg)) < 0) + return err; + + return nl_send_sync(sk, msg); +} + +/** + * Build netlink message requesting the deletion of a classifier + * @arg cls Classifier to delete + * @arg flags Additional netlink message flags + * @arg result Pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_cls_delete() with + * the exception that it will not send the message but return it in the + * provided return pointer instead. + * + * @see rtnl_cls_delete() + * + * @return 0 on success or a negative error code. + */ +int rtnl_cls_build_delete_request(struct rtnl_cls *cls, int flags, + struct nl_msg **result) +{ + uint32_t required = CLS_ATTR_PRIO; + + if ((cls->ce_mask & required) != required) { + APPBUG("prio must be specified"); + return -NLE_MISSING_ATTR; + } + + return cls_build(cls, RTM_DELTFILTER, flags, result); +} + +/** + * Delete classifier + * @arg sk Netlink socket + * @arg cls Classifier to delete + * @arg flags Additional netlink message flags + * + * Builds a \c RTM_DELTFILTER netlink message requesting the deletion + * of a classifier and sends the message to the kernel. + * + * The message is constructed out of the following attributes: + * - \c ifindex (required) + * - \c prio (required) + * - \c protocol (required) + * - \c handle (required) + * - \c parent (optional, if not specified parent equals root-qdisc) + * - \c kind (optional, must match if provided) + * + * All other classifier attributes including all class type specific + * attributes are ignored. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause + * this function to return immediately after sending. In this case, + * it is the responsibility of the caller to handle any error + * messages returned. + * + * @return 0 on success or a negative error code. + */ +int rtnl_cls_delete(struct nl_sock *sk, struct rtnl_cls *cls, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_cls_build_delete_request(cls, flags, &msg)) < 0) + return err; + + return nl_send_sync(sk, msg); +} + +/** @} */ + +/** + * @name Cache Related Functions + * @{ + */ + +/** + * Allocate a cache and fill it with all configured classifiers + * @arg sk Netlink socket + * @arg ifindex Interface index of the network device + * @arg parent Parent qdisc/traffic class class + * @arg result Pointer to store the created cache + * + * Allocates a new classifier cache and fills it with a list of all + * configured classifier attached to the specified parent qdisc/traffic + * class on the specified network device. Release the cache with + * nl_cache_free(). + * + * @return 0 on success or a negative error code. + */ +int rtnl_cls_alloc_cache(struct nl_sock *sk, int ifindex, uint32_t parent, + struct nl_cache **result) +{ + struct nl_cache * cache; + int err; + + if (!(cache = nl_cache_alloc(&rtnl_cls_ops))) + return -NLE_NOMEM; + + cache->c_iarg1 = ifindex; + cache->c_iarg2 = parent; + + if (sk && (err = nl_cache_refill(sk, cache)) < 0) { + nl_cache_free(cache); + return err; + } + + *result = cache; + return 0; +} + +/** + * Set interface index and parent handle for classifier cache. + * @arg cache Pointer to cache + * @arg parent Parent qdisc/traffic class class + * + * Set the interface index and parent handle of a classifier cache. + * This is useful for reusing some existed classifier cache to reduce + * the overhead introduced by memory allocation. + * + * @return void. + */ +void rtnl_cls_cache_set_tc_params(struct nl_cache *cache, + int ifindex, uint32_t parent) +{ + cache->c_iarg1 = ifindex; + cache->c_iarg2 = parent; +} + +/** @} */ + +static void cls_dump_line(struct rtnl_tc *tc, struct nl_dump_params *p) +{ + struct rtnl_cls *cls = (struct rtnl_cls *) tc; + char buf[32]; + + nl_dump(p, " prio %u protocol %s", cls->c_prio, + nl_ether_proto2str(cls->c_protocol, buf, sizeof(buf))); +} + +static int cls_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, struct nl_parser_param *pp) +{ + struct rtnl_cls *cls; + int err; + + if (!(cls = rtnl_cls_alloc())) + return -NLE_NOMEM; + + if ((err = rtnl_tc_msg_parse(nlh, TC_CAST(cls))) < 0) + goto errout; + + cls->c_prio = TC_H_MAJ(cls->c_info) >> 16; + if (cls->c_prio) + cls->ce_mask |= CLS_ATTR_PRIO; + cls->c_protocol = ntohs(TC_H_MIN(cls->c_info)); + if (cls->c_protocol) + cls->ce_mask |= CLS_ATTR_PROTOCOL; + + err = pp->pp_cb(OBJ_CAST(cls), pp); +errout: + rtnl_cls_put(cls); + + return err; +} + +static int cls_request_update(struct nl_cache *cache, struct nl_sock *sk) +{ + struct tcmsg tchdr = { + .tcm_family = AF_UNSPEC, + .tcm_ifindex = cache->c_iarg1, + .tcm_parent = cache->c_iarg2, + }; + + return nl_send_simple(sk, RTM_GETTFILTER, NLM_F_DUMP, &tchdr, + sizeof(tchdr)); +} + +static struct rtnl_tc_type_ops cls_ops = { + .tt_type = RTNL_TC_TYPE_CLS, + .tt_dump_prefix = "cls", + .tt_dump = { + [NL_DUMP_LINE] = cls_dump_line, + }, +}; + +static struct nl_cache_ops rtnl_cls_ops = { + .co_name = "route/cls", + .co_hdrsize = sizeof(struct tcmsg), + .co_msgtypes = { + { RTM_NEWTFILTER, NL_ACT_NEW, "new" }, + { RTM_DELTFILTER, NL_ACT_DEL, "del" }, + { RTM_GETTFILTER, NL_ACT_GET, "get" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_ROUTE, + .co_groups = tc_groups, + .co_request_update = cls_request_update, + .co_msg_parser = cls_msg_parser, + .co_obj_ops = &cls_obj_ops, +}; + +static struct nl_object_ops cls_obj_ops = { + .oo_name = "route/cls", + .oo_size = sizeof(struct rtnl_cls), + .oo_free_data = rtnl_tc_free_data, + .oo_clone = rtnl_tc_clone, + .oo_dump = { + [NL_DUMP_LINE] = rtnl_tc_dump_line, + [NL_DUMP_DETAILS] = rtnl_tc_dump_details, + [NL_DUMP_STATS] = rtnl_tc_dump_stats, + }, + .oo_compare = rtnl_tc_compare, + .oo_id_attrs = (TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE), +}; + +static void __init cls_init(void) +{ + rtnl_tc_type_register(&cls_ops); + nl_cache_mngt_register(&rtnl_cls_ops); +} + +static void __exit cls_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_cls_ops); + rtnl_tc_type_unregister(&cls_ops); +} + +/** @} */ diff --git a/libnl/lib/route/cls/basic.c b/libnl/lib/route/cls/basic.c new file mode 100644 index 0000000..ff32415 --- /dev/null +++ b/libnl/lib/route/cls/basic.c @@ -0,0 +1,297 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008-2013 Thomas Graf + */ + +/** + * @ingroup cls + * @defgroup cls_basic Basic Classifier + * + * @par Introduction + * The basic classifier is the simplest form of a classifier. It does + * not have any special classification capabilities, instead it can be + * used to classify exclusively based on extended matches or to + * create a "catch-all" filter. + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +struct rtnl_basic +{ + uint32_t b_target; + struct rtnl_ematch_tree * b_ematch; + int b_mask; + struct rtnl_act * b_act; +}; + +/** @cond SKIP */ +#define BASIC_ATTR_TARGET 0x001 +#define BASIC_ATTR_EMATCH 0x002 +#define BASIC_ATTR_ACTION 0x004 +/** @endcond */ + +static struct nla_policy basic_policy[TCA_BASIC_MAX+1] = { + [TCA_BASIC_CLASSID] = { .type = NLA_U32 }, + [TCA_BASIC_EMATCHES] = { .type = NLA_NESTED }, +}; + +static int basic_clone(void *_dst, void *_src) +{ + return -NLE_OPNOTSUPP; +} + +static void basic_free_data(struct rtnl_tc *tc, void *data) +{ + struct rtnl_basic *b = data; + + if (!b) + return; + + if (b->b_act) + rtnl_act_put_all(&b->b_act); + rtnl_ematch_tree_free(b->b_ematch); +} + +static int basic_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct nlattr *tb[TCA_BASIC_MAX + 1]; + struct rtnl_basic *b = data; + int err; + + err = tca_parse(tb, TCA_BASIC_MAX, tc, basic_policy); + if (err < 0) + return err; + + if (tb[TCA_BASIC_CLASSID]) { + b->b_target = nla_get_u32(tb[TCA_BASIC_CLASSID]); + b->b_mask |= BASIC_ATTR_TARGET; + } + + if (tb[TCA_BASIC_EMATCHES]) { + if ((err = rtnl_ematch_parse_attr(tb[TCA_BASIC_EMATCHES], + &b->b_ematch)) < 0) + return err; + + if (b->b_ematch) + b->b_mask |= BASIC_ATTR_EMATCH; + } + if (tb[TCA_BASIC_ACT]) { + b->b_mask |= BASIC_ATTR_ACTION; + err = rtnl_act_parse(&b->b_act, tb[TCA_BASIC_ACT]); + if (err < 0) + return err; + } + + return 0; +} + +static void basic_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_basic *b = data; + char buf[32]; + + if (!b) + return; + + if (b->b_mask & BASIC_ATTR_EMATCH) + nl_dump(p, " ematch"); + else + nl_dump(p, " match-all"); + + if (b->b_mask & BASIC_ATTR_TARGET) + nl_dump(p, " target %s", + rtnl_tc_handle2str(b->b_target, buf, sizeof(buf))); +} + +static void basic_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_basic *b = data; + + if (!b) + return; + + if (b->b_mask & BASIC_ATTR_EMATCH) { + nl_dump_line(p, " ematch "); + rtnl_ematch_tree_dump(b->b_ematch, p); + } else + nl_dump(p, "no options.\n"); +} + +static int basic_msg_fill(struct rtnl_tc *tc, void *data, + struct nl_msg *msg) +{ + struct rtnl_basic *b = data; + + if (!b) + return 0; + + if (b->b_mask & BASIC_ATTR_TARGET) + NLA_PUT_U32(msg, TCA_BASIC_CLASSID, b->b_target); + + if (b->b_mask & BASIC_ATTR_EMATCH && + rtnl_ematch_fill_attr(msg, TCA_BASIC_EMATCHES, b->b_ematch) < 0) + goto nla_put_failure; + + if (b->b_mask & BASIC_ATTR_ACTION) { + int err; + + err = rtnl_act_fill(msg, TCA_BASIC_ACT, b->b_act); + if (err) + return err; + } + + return 0; + +nla_put_failure: + return -NLE_NOMEM; +} + +/** + * @name Attribute Modifications + * @{ + */ + +void rtnl_basic_set_target(struct rtnl_cls *cls, uint32_t target) +{ + struct rtnl_basic *b; + + if (!(b = rtnl_tc_data(TC_CAST(cls)))) + return; + + b->b_target = target; + b->b_mask |= BASIC_ATTR_TARGET; +} + +uint32_t rtnl_basic_get_target(struct rtnl_cls *cls) +{ + struct rtnl_basic *b; + + if (!(b = rtnl_tc_data(TC_CAST(cls)))) + return 0; + + return b->b_target; +} + +void rtnl_basic_set_ematch(struct rtnl_cls *cls, struct rtnl_ematch_tree *tree) +{ + struct rtnl_basic *b; + + if (!(b = rtnl_tc_data(TC_CAST(cls)))) + return; + + if (b->b_ematch) { + rtnl_ematch_tree_free(b->b_ematch); + b->b_mask &= ~BASIC_ATTR_EMATCH; + } + + b->b_ematch = tree; + + if (tree) + b->b_mask |= BASIC_ATTR_EMATCH; +} + +struct rtnl_ematch_tree *rtnl_basic_get_ematch(struct rtnl_cls *cls) +{ + struct rtnl_basic *b; + + if (!(b = rtnl_tc_data(TC_CAST(cls)))) + return NULL; + + return b->b_ematch; +} + +int rtnl_basic_add_action(struct rtnl_cls *cls, struct rtnl_act *act) +{ + struct rtnl_basic *b; + int err; + + if (!act) + return 0; + + if (!(b = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + b->b_mask |= BASIC_ATTR_ACTION; + if ((err = rtnl_act_append(&b->b_act, act))) + return err; + + /* In case user frees it */ + rtnl_act_get(act); + return 0; +} + +struct rtnl_act* rtnl_basic_get_action(struct rtnl_cls *cls) +{ + struct rtnl_basic *b; + + if (!(b = rtnl_tc_data_peek(TC_CAST(cls)))) + return NULL; + + if (!(b->b_mask & BASIC_ATTR_ACTION)) + return NULL; + + return b->b_act; +} + +int rtnl_basic_del_action(struct rtnl_cls *cls, struct rtnl_act *act) +{ + struct rtnl_basic *b; + int ret; + + if (!act) + return 0; + + if (!(b = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + if (!(b->b_mask & BASIC_ATTR_ACTION)) + return -NLE_INVAL; + ret = rtnl_act_remove(&b->b_act, act); + if (ret) + return ret; + + if (!b->b_act) + b->b_mask &= ~BASIC_ATTR_ACTION; + rtnl_act_put(act); + return 0; +} +/** @} */ + +static struct rtnl_tc_ops basic_ops = { + .to_kind = "basic", + .to_type = RTNL_TC_TYPE_CLS, + .to_size = sizeof(struct rtnl_basic), + .to_msg_parser = basic_msg_parser, + .to_clone = basic_clone, + .to_free_data = basic_free_data, + .to_msg_fill = basic_msg_fill, + .to_dump = { + [NL_DUMP_LINE] = basic_dump_line, + [NL_DUMP_DETAILS] = basic_dump_details, + }, +}; + +static void __init basic_init(void) +{ + rtnl_tc_register(&basic_ops); +} + +static void __exit basic_exit(void) +{ + rtnl_tc_unregister(&basic_ops); +} + +/** @} */ diff --git a/libnl/lib/route/cls/cgroup.c b/libnl/lib/route/cls/cgroup.c new file mode 100644 index 0000000..e53a452 --- /dev/null +++ b/libnl/lib/route/cls/cgroup.c @@ -0,0 +1,193 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2009-2013 Thomas Graf + */ + +/** + * @ingroup cls + * @defgroup cls_cgroup Control Groups Classifier + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define CGROUP_ATTR_EMATCH 0x001 +/** @endcond */ + +static struct nla_policy cgroup_policy[TCA_CGROUP_MAX+1] = { + [TCA_CGROUP_EMATCHES] = { .type = NLA_NESTED }, +}; + +static int cgroup_clone(void *_dst, void *_src) +{ + struct rtnl_cgroup *dst = _dst, *src = _src; + + if (src->cg_ematch) { + dst->cg_ematch = rtnl_ematch_tree_clone(src->cg_ematch); + if (!dst->cg_ematch) + return -NLE_NOMEM; + } + + return 0; +} + +static void cgroup_free_data(struct rtnl_tc *tc, void *data) +{ + struct rtnl_cgroup *c = data; + + if (!c) + return; + + rtnl_ematch_tree_free(c->cg_ematch); +} + +static int cgroup_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct nlattr *tb[TCA_CGROUP_MAX + 1]; + struct rtnl_cgroup *c = data; + int err; + + err = tca_parse(tb, TCA_CGROUP_MAX, tc, cgroup_policy); + if (err < 0) + return err; + + if (tb[TCA_CGROUP_EMATCHES]) { + if ((err = rtnl_ematch_parse_attr(tb[TCA_CGROUP_EMATCHES], + &c->cg_ematch)) < 0) + return err; + c->cg_mask |= CGROUP_ATTR_EMATCH; + } + +#if 0 + TODO: + TCA_CGROUP_ACT, + TCA_CGROUP_POLICE, +#endif + + return 0; +} + +static void cgroup_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_cgroup *c = data; + + if (!c) + return; + + if (c->cg_mask & CGROUP_ATTR_EMATCH) + nl_dump(p, " ematch"); + else + nl_dump(p, " match-all"); +} + +static void cgroup_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_cgroup *c = data; + + if (!c) + return; + + if (c->cg_mask & CGROUP_ATTR_EMATCH) { + nl_dump_line(p, " ematch "); + + if (c->cg_ematch) + rtnl_ematch_tree_dump(c->cg_ematch, p); + else + nl_dump(p, ""); + } else + nl_dump(p, "no options"); +} + +static int cgroup_fill_msg(struct rtnl_tc *tc, void *data, + struct nl_msg *msg) +{ + struct rtnl_cgroup *c = data; + + if (!c) + BUG(); + + if (!(tc->ce_mask & TCA_ATTR_HANDLE)) + return -NLE_MISSING_ATTR; + + if (c->cg_mask & CGROUP_ATTR_EMATCH) + return rtnl_ematch_fill_attr(msg, TCA_CGROUP_EMATCHES, + c->cg_ematch); + + return 0; +} + + +/** + * @name Attribute Modifications + * @{ + */ + +void rtnl_cgroup_set_ematch(struct rtnl_cls *cls, struct rtnl_ematch_tree *tree) +{ + struct rtnl_cgroup *c; + + if (!(c = rtnl_tc_data(TC_CAST(cls)))) + BUG(); + + if (c->cg_ematch) { + rtnl_ematch_tree_free(c->cg_ematch); + c->cg_mask &= ~CGROUP_ATTR_EMATCH; + } + + c->cg_ematch = tree; + + if (tree) + c->cg_mask |= CGROUP_ATTR_EMATCH; +} + +struct rtnl_ematch_tree *rtnl_cgroup_get_ematch(struct rtnl_cls *cls) +{ + struct rtnl_cgroup *c; + + if (!(c = rtnl_tc_data(TC_CAST(cls)))) + BUG(); + + return c->cg_ematch; +} + +/** @} */ + +static struct rtnl_tc_ops cgroup_ops = { + .to_kind = "cgroup", + .to_type = RTNL_TC_TYPE_CLS, + .to_size = sizeof(struct rtnl_cgroup), + .to_clone = cgroup_clone, + .to_msg_parser = cgroup_msg_parser, + .to_free_data = cgroup_free_data, + .to_msg_fill = cgroup_fill_msg, + .to_dump = { + [NL_DUMP_LINE] = cgroup_dump_line, + [NL_DUMP_DETAILS] = cgroup_dump_details, + }, +}; + +static void __init cgroup_init(void) +{ + rtnl_tc_register(&cgroup_ops); +} + +static void __exit cgroup_exit(void) +{ + rtnl_tc_unregister(&cgroup_ops); +} + +/** @} */ diff --git a/libnl/lib/route/cls/ematch.c b/libnl/lib/route/cls/ematch.c new file mode 100644 index 0000000..7f35376 --- /dev/null +++ b/libnl/lib/route/cls/ematch.c @@ -0,0 +1,754 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008-2013 Thomas Graf + */ + +/** + * @ingroup cls + * @defgroup ematch Extended Match + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +#include "ematch_syntax.h" +#include "ematch_grammar.h" + +/** + * @name Module API + * @{ + */ + +static NL_LIST_HEAD(ematch_ops_list); + +/** + * Register ematch module + * @arg ops Module operations. + * + * This function must be called by each ematch module at initialization + * time. It registers the calling module as available module. + * + * @return 0 on success or a negative error code. + */ +int rtnl_ematch_register(struct rtnl_ematch_ops *ops) +{ + if (rtnl_ematch_lookup_ops(ops->eo_kind)) + return -NLE_EXIST; + + NL_DBG(1, "ematch module \"%s\" registered\n", ops->eo_name); + + nl_list_add_tail(&ops->eo_list, &ematch_ops_list); + + return 0; +} + +/** + * Lookup ematch module by identification number. + * @arg kind Module kind. + * + * Searches the list of registered ematch modules for match and returns it. + * + * @return Module operations or NULL if not found. + */ +struct rtnl_ematch_ops *rtnl_ematch_lookup_ops(int kind) +{ + struct rtnl_ematch_ops *ops; + + nl_list_for_each_entry(ops, &ematch_ops_list, eo_list) + if (ops->eo_kind == kind) + return ops; + + return NULL; +} + +/** + * Lookup ematch module by name + * @arg name Name of ematch module. + * + * Searches the list of registered ematch modules for a match and returns it. + * + * @return Module operations or NULL if not fuond. + */ +struct rtnl_ematch_ops *rtnl_ematch_lookup_ops_by_name(const char *name) +{ + struct rtnl_ematch_ops *ops; + + nl_list_for_each_entry(ops, &ematch_ops_list, eo_list) + if (!strcasecmp(ops->eo_name, name)) + return ops; + + return NULL; +} + +/** @} */ + +/** + * @name Match + */ + +/** + * Allocate ematch object. + * + * Allocates and initializes an ematch object. + * + * @return New ematch object or NULL. + */ +struct rtnl_ematch *rtnl_ematch_alloc(void) +{ + struct rtnl_ematch *e; + + if (!(e = calloc(1, sizeof(*e)))) + return NULL; + + NL_DBG(2, "allocated ematch %p\n", e); + + NL_INIT_LIST_HEAD(&e->e_list); + NL_INIT_LIST_HEAD(&e->e_childs); + + return e; +} + +/** + * Add ematch to the end of the parent's list of children. + * @arg parent parent ematch object + * @arg child ematch object to be added to parent + * + * The parent must be a container ematch. + */ +int rtnl_ematch_add_child(struct rtnl_ematch *parent, + struct rtnl_ematch *child) +{ + if (parent->e_kind != TCF_EM_CONTAINER) + return -NLE_OPNOTSUPP; + + NL_DBG(2, "added ematch %p \"%s\" to container %p\n", + child, child->e_ops->eo_name, parent); + + nl_list_add_tail(&child->e_list, &parent->e_childs); + + return 0; +} + +/** + * Remove ematch from the list of ematches it is linked to. + * @arg ematch ematch object + */ +void rtnl_ematch_unlink(struct rtnl_ematch *ematch) +{ + NL_DBG(2, "unlinked ematch %p from any lists\n", ematch); + + if (!nl_list_empty(&ematch->e_childs)) + NL_DBG(1, "warning: ematch %p with childs was unlinked\n", + ematch); + + nl_list_del(&ematch->e_list); + nl_init_list_head(&ematch->e_list); +} + +void rtnl_ematch_free(struct rtnl_ematch *ematch) +{ + NL_DBG(2, "freed ematch %p\n", ematch); + rtnl_ematch_unlink(ematch); + free(ematch->e_data); + free(ematch); +} + +int rtnl_ematch_set_ops(struct rtnl_ematch *ematch, struct rtnl_ematch_ops *ops) +{ + if (ematch->e_ops) + return -NLE_EXIST; + + ematch->e_ops = ops; + ematch->e_kind = ops->eo_kind; + + if (ops->eo_datalen) { + ematch->e_data = calloc(1, ops->eo_datalen); + if (!ematch->e_data) + return -NLE_NOMEM; + + ematch->e_datalen = ops->eo_datalen; + } + + return 0; +} + +int rtnl_ematch_set_kind(struct rtnl_ematch *ematch, uint16_t kind) +{ + struct rtnl_ematch_ops *ops; + + if (ematch->e_kind) + return -NLE_EXIST; + + ematch->e_kind = kind; + + if ((ops = rtnl_ematch_lookup_ops(kind))) + rtnl_ematch_set_ops(ematch, ops); + + return 0; +} + +int rtnl_ematch_set_name(struct rtnl_ematch *ematch, const char *name) +{ + struct rtnl_ematch_ops *ops; + + if (ematch->e_kind) + return -NLE_EXIST; + + if (!(ops = rtnl_ematch_lookup_ops_by_name(name))) + return -NLE_OPNOTSUPP; + + rtnl_ematch_set_ops(ematch, ops); + + return 0; +} + +void rtnl_ematch_set_flags(struct rtnl_ematch *ematch, uint16_t flags) +{ + ematch->e_flags |= flags; +} + +void rtnl_ematch_unset_flags(struct rtnl_ematch *ematch, uint16_t flags) +{ + ematch->e_flags &= ~flags; +} + +uint16_t rtnl_ematch_get_flags(struct rtnl_ematch *ematch) +{ + return ematch->e_flags; +} + +void *rtnl_ematch_data(struct rtnl_ematch *ematch) +{ + return ematch->e_data; +} + +/** @} */ + +/** + * @name Tree + */ + +/** + * Allocate ematch tree object + * @arg progid program id + */ +struct rtnl_ematch_tree *rtnl_ematch_tree_alloc(uint16_t progid) +{ + struct rtnl_ematch_tree *tree; + + if (!(tree = calloc(1, sizeof(*tree)))) + return NULL; + + NL_INIT_LIST_HEAD(&tree->et_list); + tree->et_progid = progid; + + NL_DBG(2, "allocated new ematch tree %p, progid=%u\n", tree, progid); + + return tree; +} + +static void free_ematch_list(struct nl_list_head *head) +{ + struct rtnl_ematch *pos, *next; + + nl_list_for_each_entry_safe(pos, next, head, e_list) { + if (!nl_list_empty(&pos->e_childs)) + free_ematch_list(&pos->e_childs); + rtnl_ematch_free(pos); + } +} + +/** + * Free ematch tree object + * @arg tree ematch tree object + * + * This function frees the ematch tree and all ematches attached to it. + */ +void rtnl_ematch_tree_free(struct rtnl_ematch_tree *tree) +{ + if (!tree) + return; + + free_ematch_list(&tree->et_list); + + NL_DBG(2, "Freed ematch tree %p\n", tree); + + free(tree); +} + +static int clone_ematch_list(struct nl_list_head *dst, struct nl_list_head *src) +{ + struct rtnl_ematch *new = NULL, *pos = NULL; + + nl_list_for_each_entry(pos, src, e_list) { + new = rtnl_ematch_alloc(); + if (!new) + goto nomem; + + new->e_id = pos->e_id; + new->e_kind = pos->e_kind; + new->e_flags = pos->e_flags; + new->e_index = pos->e_index; + new->e_datalen = pos->e_datalen; + + if (pos->e_ops) { + if (rtnl_ematch_set_ops(new, pos->e_ops)) + goto nomem; + } + + if (!nl_list_empty(&pos->e_childs)) { + if (clone_ematch_list(&new->e_childs, &pos->e_childs) < 0) + goto nomem; + } + nl_list_add_tail(&new->e_list, dst); + } + + return 0; + +nomem: + if (new) + free(new); + free_ematch_list(dst); + return -NLE_NOMEM; +} + +/** + * Clone ematch tree object + * @arg src ematch tree object + * + * This function clones the ematch tree and all ematches attached to it. + */ +struct rtnl_ematch_tree *rtnl_ematch_tree_clone(struct rtnl_ematch_tree *src) +{ + struct rtnl_ematch_tree *dst = NULL; + + if (!src) + return NULL; + + if (!(dst = rtnl_ematch_tree_alloc(src->et_progid))) + return NULL; + + clone_ematch_list(&dst->et_list, &src->et_list); + + return dst; +} + +/** + * Add ematch object to the end of the ematch tree + * @arg tree ematch tree object + * @arg ematch ematch object to add + */ +void rtnl_ematch_tree_add(struct rtnl_ematch_tree *tree, + struct rtnl_ematch *ematch) +{ + nl_list_add_tail(&ematch->e_list, &tree->et_list); +} + +static inline uint32_t container_ref(struct rtnl_ematch *ematch) +{ + return *((uint32_t *) rtnl_ematch_data(ematch)); +} + +static int link_tree(struct rtnl_ematch *index[], int nmatches, int pos, + struct nl_list_head *root) +{ + struct rtnl_ematch *ematch; + int i; + + for (i = pos; i < nmatches; i++) { + ematch = index[i]; + + nl_list_add_tail(&ematch->e_list, root); + + if (ematch->e_kind == TCF_EM_CONTAINER) + link_tree(index, nmatches, container_ref(ematch), + &ematch->e_childs); + + if (!(ematch->e_flags & TCF_EM_REL_MASK)) + return 0; + } + + /* Last entry in chain can't possibly have no relation */ + return -NLE_INVAL; +} + +static struct nla_policy tree_policy[TCA_EMATCH_TREE_MAX+1] = { + [TCA_EMATCH_TREE_HDR] = { .minlen=sizeof(struct tcf_ematch_tree_hdr) }, + [TCA_EMATCH_TREE_LIST] = { .type = NLA_NESTED }, +}; + +/** + * Parse ematch netlink attributes + * + * @return 0 on success or a negative error code. + */ +int rtnl_ematch_parse_attr(struct nlattr *attr, struct rtnl_ematch_tree **result) +{ + struct nlattr *a, *tb[TCA_EMATCH_TREE_MAX+1]; + struct tcf_ematch_tree_hdr *thdr; + struct rtnl_ematch_tree *tree; + struct rtnl_ematch **index; + int nmatches = 0, err, remaining; + + NL_DBG(2, "Parsing attribute %p as ematch tree\n", attr); + + err = nla_parse_nested(tb, TCA_EMATCH_TREE_MAX, attr, tree_policy); + if (err < 0) + return err; + + if (!tb[TCA_EMATCH_TREE_HDR]) + return -NLE_MISSING_ATTR; + + thdr = nla_data(tb[TCA_EMATCH_TREE_HDR]); + + /* Ignore empty trees */ + if (thdr->nmatches == 0) { + NL_DBG(2, "Ignoring empty ematch configuration\n"); + return 0; + } + + if (!tb[TCA_EMATCH_TREE_LIST]) + return -NLE_MISSING_ATTR; + + NL_DBG(2, "ematch tree found with nmatches=%u, progid=%u\n", + thdr->nmatches, thdr->progid); + + /* + * Do some basic sanity checking since we will allocate + * index[thdr->nmatches]. Calculate how many ematch headers fit into + * the provided data and make sure nmatches does not exceed it. + */ + if (thdr->nmatches > (nla_len(tb[TCA_EMATCH_TREE_LIST]) / + nla_total_size(sizeof(struct tcf_ematch_hdr)))) + return -NLE_INVAL; + + if (!(index = calloc(thdr->nmatches, sizeof(struct rtnl_ematch *)))) + return -NLE_NOMEM; + + if (!(tree = rtnl_ematch_tree_alloc(thdr->progid))) { + err = -NLE_NOMEM; + goto errout; + } + + nla_for_each_nested(a, tb[TCA_EMATCH_TREE_LIST], remaining) { + struct rtnl_ematch_ops *ops; + struct tcf_ematch_hdr *hdr; + struct rtnl_ematch *ematch; + void *data; + size_t len; + + NL_DBG(3, "parsing ematch attribute %d, len=%u\n", + nmatches+1, nla_len(a)); + + if (nla_len(a) < sizeof(*hdr)) { + err = -NLE_INVAL; + goto errout; + } + + /* Quit as soon as we've parsed more matches than expected */ + if (nmatches >= thdr->nmatches) { + err = -NLE_RANGE; + goto errout; + } + + hdr = nla_data(a); + data = (char *) nla_data(a) + NLA_ALIGN(sizeof(*hdr)); + len = nla_len(a) - NLA_ALIGN(sizeof(*hdr)); + + NL_DBG(3, "ematch attribute matchid=%u, kind=%u, flags=%u\n", + hdr->matchid, hdr->kind, hdr->flags); + + /* + * Container matches contain a reference to another sequence + * of matches. Ensure that the reference is within boundries. + */ + if (hdr->kind == TCF_EM_CONTAINER && + *((uint32_t *) data) >= thdr->nmatches) { + err = -NLE_INVAL; + goto errout; + } + + if (!(ematch = rtnl_ematch_alloc())) { + err = -NLE_NOMEM; + goto errout; + } + + ematch->e_id = hdr->matchid; + ematch->e_kind = hdr->kind; + ematch->e_flags = hdr->flags; + + if ((ops = rtnl_ematch_lookup_ops(hdr->kind))) { + if (ops->eo_minlen && len < ops->eo_minlen) { + rtnl_ematch_free(ematch); + err = -NLE_INVAL; + goto errout; + } + + rtnl_ematch_set_ops(ematch, ops); + + if (ops->eo_parse && + (err = ops->eo_parse(ematch, data, len)) < 0) { + rtnl_ematch_free(ematch); + goto errout; + } + } + + NL_DBG(3, "index[%d] = %p\n", nmatches, ematch); + index[nmatches++] = ematch; + } + + if (nmatches != thdr->nmatches) { + err = -NLE_INVAL; + goto errout; + } + + err = link_tree(index, nmatches, 0, &tree->et_list); + if (err < 0) + goto errout; + + free(index); + *result = tree; + + return 0; + +errout: + rtnl_ematch_tree_free(tree); + free(index); + return err; +} + +static void dump_ematch_sequence(struct nl_list_head *head, + struct nl_dump_params *p) +{ + struct rtnl_ematch *match; + + nl_list_for_each_entry(match, head, e_list) { + if (match->e_flags & TCF_EM_INVERT) + nl_dump(p, "!"); + + if (match->e_kind == TCF_EM_CONTAINER) { + nl_dump(p, "("); + dump_ematch_sequence(&match->e_childs, p); + nl_dump(p, ")"); + } else if (!match->e_ops) { + nl_dump(p, "[unknown ematch %d]", match->e_kind); + } else { + if (match->e_ops->eo_dump) + match->e_ops->eo_dump(match, p); + else + nl_dump(p, "[data]"); + } + + switch (match->e_flags & TCF_EM_REL_MASK) { + case TCF_EM_REL_AND: + nl_dump(p, " AND "); + break; + case TCF_EM_REL_OR: + nl_dump(p, " OR "); + break; + default: + /* end of first level ematch sequence */ + return; + } + } +} + +void rtnl_ematch_tree_dump(struct rtnl_ematch_tree *tree, + struct nl_dump_params *p) +{ + if (!tree) + BUG(); + + dump_ematch_sequence(&tree->et_list, p); + nl_dump(p, "\n"); +} + +static int update_container_index(struct nl_list_head *list, int *index) +{ + struct rtnl_ematch *e; + + nl_list_for_each_entry(e, list, e_list) + e->e_index = (*index)++; + + nl_list_for_each_entry(e, list, e_list) { + if (e->e_kind == TCF_EM_CONTAINER) { + int err; + + if (nl_list_empty(&e->e_childs)) + return -NLE_OBJ_NOTFOUND; + + *((uint32_t *) e->e_data) = *index; + + err = update_container_index(&e->e_childs, index); + if (err < 0) + return err; + } + } + + return 0; +} + +static int fill_ematch_sequence(struct nl_msg *msg, struct nl_list_head *list) +{ + struct rtnl_ematch *e; + + nl_list_for_each_entry(e, list, e_list) { + struct tcf_ematch_hdr match = { + .matchid = e->e_id, + .kind = e->e_kind, + .flags = e->e_flags, + }; + struct nlattr *attr; + int err = 0; + + if (!(attr = nla_nest_start(msg, e->e_index + 1))) + return -NLE_NOMEM; + + if (nlmsg_append(msg, &match, sizeof(match), 0) < 0) + return -NLE_NOMEM; + + if (e->e_ops->eo_fill) + err = e->e_ops->eo_fill(e, msg); + else if (e->e_flags & TCF_EM_SIMPLE) + err = nlmsg_append(msg, e->e_data, 4, 0); + else if (e->e_datalen > 0) + err = nlmsg_append(msg, e->e_data, e->e_datalen, 0); + + NL_DBG(3, "msg %p: added ematch [%d] id=%d kind=%d flags=%d\n", + msg, e->e_index, match.matchid, match.kind, match.flags); + + if (err < 0) + return -NLE_NOMEM; + + nla_nest_end(msg, attr); + } + + nl_list_for_each_entry(e, list, e_list) { + if (e->e_kind == TCF_EM_CONTAINER && + fill_ematch_sequence(msg, &e->e_childs) < 0) + return -NLE_NOMEM; + } + + return 0; +} + +int rtnl_ematch_fill_attr(struct nl_msg *msg, int attrid, + struct rtnl_ematch_tree *tree) +{ + struct tcf_ematch_tree_hdr thdr = { + .progid = tree->et_progid, + }; + struct nlattr *list, *topattr; + int err, index = 0; + + /* Assign index number to each ematch to allow for references + * to be made while constructing the sequence of matches. */ + err = update_container_index(&tree->et_list, &index); + if (err < 0) + return err; + + if (!(topattr = nla_nest_start(msg, attrid))) + goto nla_put_failure; + + thdr.nmatches = index; + NLA_PUT(msg, TCA_EMATCH_TREE_HDR, sizeof(thdr), &thdr); + + if (!(list = nla_nest_start(msg, TCA_EMATCH_TREE_LIST))) + goto nla_put_failure; + + if (fill_ematch_sequence(msg, &tree->et_list) < 0) + goto nla_put_failure; + + nla_nest_end(msg, list); + + nla_nest_end(msg, topattr); + + return 0; + +nla_put_failure: + return -NLE_NOMEM; +} + +/** @} */ + +extern int ematch_parse(void *, char **, struct nl_list_head *); + +int rtnl_ematch_parse_expr(const char *expr, char **errp, + struct rtnl_ematch_tree **result) +{ + struct rtnl_ematch_tree *tree; + YY_BUFFER_STATE buf = NULL; + yyscan_t scanner = NULL; + int err; + + NL_DBG(2, "Parsing ematch expression \"%s\"\n", expr); + + if (!(tree = rtnl_ematch_tree_alloc(RTNL_EMATCH_PROGID))) + return -NLE_FAILURE; + + if ((err = ematch_lex_init(&scanner)) < 0) { + err = -NLE_FAILURE; + goto errout; + } + + buf = ematch__scan_string(expr, scanner); + + if ((err = ematch_parse(scanner, errp, &tree->et_list)) != 0) { + ematch__delete_buffer(buf, scanner); + err = -NLE_PARSE_ERR; + goto errout; + } + + ematch_lex_destroy(scanner); + *result = tree; + + return 0; + +errout: + if (scanner) + ematch_lex_destroy(scanner); + + rtnl_ematch_tree_free(tree); + + return err; +} + +static const char *layer_txt[] = { + [TCF_LAYER_LINK] = "eth", + [TCF_LAYER_NETWORK] = "ip", + [TCF_LAYER_TRANSPORT] = "tcp", +}; + +char *rtnl_ematch_offset2txt(uint8_t layer, uint16_t offset, char *buf, size_t len) +{ + snprintf(buf, len, "%s+%u", + (layer <= TCF_LAYER_MAX) ? layer_txt[layer] : "?", + offset); + + return buf; +} + +static const char *operand_txt[] = { + [TCF_EM_OPND_EQ] = "=", + [TCF_EM_OPND_LT] = "<", + [TCF_EM_OPND_GT] = ">", +}; + +char *rtnl_ematch_opnd2txt(uint8_t opnd, char *buf, size_t len) +{ + snprintf(buf, len, "%s", + opnd < ARRAY_SIZE(operand_txt) ? operand_txt[opnd] : "?"); + + return buf; +} + +/** @} */ diff --git a/libnl/lib/route/cls/ematch/cmp.c b/libnl/lib/route/cls/ematch/cmp.c new file mode 100644 index 0000000..3d7c072 --- /dev/null +++ b/libnl/lib/route/cls/ematch/cmp.c @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008-2013 Thomas Graf + */ + +/** + * @ingroup ematch + * @defgroup em_cmp Simple packet data comparison + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + +void rtnl_ematch_cmp_set(struct rtnl_ematch *e, struct tcf_em_cmp *cfg) +{ + memcpy(rtnl_ematch_data(e), cfg, sizeof(*cfg)); +} + +struct tcf_em_cmp *rtnl_ematch_cmp_get(struct rtnl_ematch *e) +{ + return rtnl_ematch_data(e); +} + +static int cmp_parse(struct rtnl_ematch *e, void *data, size_t len) +{ + memcpy(rtnl_ematch_data(e), data, len); + + return 0; +} + +static const char *align_txt[] = { + [TCF_EM_ALIGN_U8] = "u8", + [TCF_EM_ALIGN_U16] = "u16", + [TCF_EM_ALIGN_U32] = "u32" +}; + +static const char *layer_txt[] = { + [TCF_LAYER_LINK] = "eth", + [TCF_LAYER_NETWORK] = "ip", + [TCF_LAYER_TRANSPORT] = "tcp" +}; + +static const char *operand_txt[] = { + [TCF_EM_OPND_EQ] = "=", + [TCF_EM_OPND_LT] = "<", + [TCF_EM_OPND_GT] = ">", +}; + +static void cmp_dump(struct rtnl_ematch *e, struct nl_dump_params *p) +{ + struct tcf_em_cmp *cmp = rtnl_ematch_data(e); + + if (cmp->flags & TCF_EM_CMP_TRANS) + nl_dump(p, "ntoh%c(", (cmp->align == TCF_EM_ALIGN_U32) ? 'l' : 's'); + + nl_dump(p, "%s at %s+%u", + align_txt[cmp->align], layer_txt[cmp->layer], cmp->off); + + if (cmp->mask) + nl_dump(p, " & 0x%x", cmp->mask); + + if (cmp->flags & TCF_EM_CMP_TRANS) + nl_dump(p, ")"); + + nl_dump(p, " %s %u", operand_txt[cmp->opnd], cmp->val); +} + +static struct rtnl_ematch_ops cmp_ops = { + .eo_kind = TCF_EM_CMP, + .eo_name = "cmp", + .eo_minlen = sizeof(struct tcf_em_cmp), + .eo_datalen = sizeof(struct tcf_em_cmp), + .eo_parse = cmp_parse, + .eo_dump = cmp_dump, +}; + +static void __init cmp_init(void) +{ + rtnl_ematch_register(&cmp_ops); +} + +/** @} */ diff --git a/libnl/lib/route/cls/ematch/container.c b/libnl/lib/route/cls/ematch/container.c new file mode 100644 index 0000000..c5fa0c8 --- /dev/null +++ b/libnl/lib/route/cls/ematch/container.c @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008-2013 Thomas Graf + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include + +static int container_parse(struct rtnl_ematch *e, void *data, size_t len __attribute__((unused))) +{ + /* + The kernel may provide more than 4 bytes of data in the future and we want + older libnl versions to be ok with that. We want interfaces to be growable + so we only ever enforce a minimum data length and copy as much as we are + aware of. Thomas Graf. + */ + memcpy(e->e_data, data, sizeof(uint32_t)); + + return 0; +} + +static int container_fill(struct rtnl_ematch *e, struct nl_msg *msg) +{ + return nlmsg_append(msg, e->e_data, sizeof(uint32_t), 0); +} + +static struct rtnl_ematch_ops container_ops = { + .eo_kind = TCF_EM_CONTAINER, + .eo_name = "container", + .eo_minlen = sizeof(uint32_t), + .eo_datalen = sizeof(uint32_t), + .eo_parse = container_parse, + .eo_fill = container_fill, +}; + +static void __init container_init(void) +{ + rtnl_ematch_register(&container_ops); +} diff --git a/libnl/lib/route/cls/ematch/meta.c b/libnl/lib/route/cls/ematch/meta.c new file mode 100644 index 0000000..e769eb4 --- /dev/null +++ b/libnl/lib/route/cls/ematch/meta.c @@ -0,0 +1,332 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010-2013 Thomas Graf + */ + +/** + * @ingroup ematch + * @defgroup em_meta Metadata Match + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + +struct rtnl_meta_value +{ + uint8_t mv_type; + uint8_t mv_shift; + uint16_t mv_id; + size_t mv_len; +}; + +struct meta_data +{ + struct rtnl_meta_value * left; + struct rtnl_meta_value * right; + uint8_t opnd; +}; + +static struct rtnl_meta_value *meta_alloc(uint8_t type, uint16_t id, + uint8_t shift, void *data, + size_t len) +{ + struct rtnl_meta_value *value; + + if (!(value = calloc(1, sizeof(*value) + len))) + return NULL; + + value->mv_type = type; + value->mv_id = id; + value->mv_shift = shift; + value->mv_len = len; + + if (len) + memcpy(value + 1, data, len); + + return value; +} + +struct rtnl_meta_value *rtnl_meta_value_alloc_int(uint64_t value) +{ + return meta_alloc(TCF_META_TYPE_INT, TCF_META_ID_VALUE, 0, &value, 8); +} + +struct rtnl_meta_value *rtnl_meta_value_alloc_var(void *data, size_t len) +{ + return meta_alloc(TCF_META_TYPE_VAR, TCF_META_ID_VALUE, 0, data, len); +} + +struct rtnl_meta_value *rtnl_meta_value_alloc_id(uint8_t type, uint16_t id, + uint8_t shift, uint64_t mask) +{ + size_t masklen = 0; + + if (id > TCF_META_ID_MAX) + return NULL; + + if (mask) { + if (type == TCF_META_TYPE_VAR) + return NULL; + + masklen = 8; + } + + return meta_alloc(type, id, shift, &mask, masklen); +} + +void rtnl_meta_value_put(struct rtnl_meta_value *mv) +{ + free(mv); +} + +void rtnl_ematch_meta_set_lvalue(struct rtnl_ematch *e, struct rtnl_meta_value *v) +{ + struct meta_data *m = rtnl_ematch_data(e); + m->left = v; +} + +void rtnl_ematch_meta_set_rvalue(struct rtnl_ematch *e, struct rtnl_meta_value *v) +{ + struct meta_data *m = rtnl_ematch_data(e); + m->right = v; +} + +void rtnl_ematch_meta_set_operand(struct rtnl_ematch *e, uint8_t opnd) +{ + struct meta_data *m = rtnl_ematch_data(e); + m->opnd = opnd; +} + +static struct nla_policy meta_policy[TCA_EM_META_MAX+1] = { + [TCA_EM_META_HDR] = { .minlen = sizeof(struct tcf_meta_hdr) }, + [TCA_EM_META_LVALUE] = { .minlen = 1, }, + [TCA_EM_META_RVALUE] = { .minlen = 1, }, +}; + +static int meta_parse(struct rtnl_ematch *e, void *data, size_t len) +{ + struct meta_data *m = rtnl_ematch_data(e); + struct nlattr *tb[TCA_EM_META_MAX+1]; + struct rtnl_meta_value *v; + struct tcf_meta_hdr *hdr; + void *vdata = NULL; + size_t vlen = 0; + int err; + + if ((err = nla_parse(tb, TCA_EM_META_MAX, data, len, meta_policy)) < 0) + return err; + + if (!tb[TCA_EM_META_HDR]) + return -NLE_MISSING_ATTR; + + hdr = nla_data(tb[TCA_EM_META_HDR]); + + if (tb[TCA_EM_META_LVALUE]) { + vdata = nla_data(tb[TCA_EM_META_LVALUE]); + vlen = nla_len(tb[TCA_EM_META_LVALUE]); + } + + v = meta_alloc(TCF_META_TYPE(hdr->left.kind), + TCF_META_ID(hdr->left.kind), + hdr->left.shift, vdata, vlen); + if (!v) + return -NLE_NOMEM; + + m->left = v; + + vlen = 0; + if (tb[TCA_EM_META_RVALUE]) { + vdata = nla_data(tb[TCA_EM_META_RVALUE]); + vlen = nla_len(tb[TCA_EM_META_RVALUE]); + } + + v = meta_alloc(TCF_META_TYPE(hdr->right.kind), + TCF_META_ID(hdr->right.kind), + hdr->right.shift, vdata, vlen); + if (!v) { + rtnl_meta_value_put(m->left); + return -NLE_NOMEM; + } + + m->right = v; + m->opnd = hdr->left.op; + + return 0; +} + +static const struct trans_tbl meta_int[] = { + __ADD(TCF_META_ID_RANDOM, random), + __ADD(TCF_META_ID_LOADAVG_0, loadavg_0), + __ADD(TCF_META_ID_LOADAVG_1, loadavg_1), + __ADD(TCF_META_ID_LOADAVG_2, loadavg_2), + __ADD(TCF_META_ID_DEV, dev), + __ADD(TCF_META_ID_PRIORITY, prio), + __ADD(TCF_META_ID_PROTOCOL, proto), + __ADD(TCF_META_ID_PKTTYPE, pkttype), + __ADD(TCF_META_ID_PKTLEN, pktlen), + __ADD(TCF_META_ID_DATALEN, datalen), + __ADD(TCF_META_ID_MACLEN, maclen), + __ADD(TCF_META_ID_NFMARK, mark), + __ADD(TCF_META_ID_TCINDEX, tcindex), + __ADD(TCF_META_ID_RTCLASSID, rtclassid), + __ADD(TCF_META_ID_RTIIF, rtiif), + __ADD(TCF_META_ID_SK_FAMILY, sk_family), + __ADD(TCF_META_ID_SK_STATE, sk_state), + __ADD(TCF_META_ID_SK_REUSE, sk_reuse), + __ADD(TCF_META_ID_SK_REFCNT, sk_refcnt), + __ADD(TCF_META_ID_SK_RCVBUF, sk_rcvbuf), + __ADD(TCF_META_ID_SK_SNDBUF, sk_sndbuf), + __ADD(TCF_META_ID_SK_SHUTDOWN, sk_sutdown), + __ADD(TCF_META_ID_SK_PROTO, sk_proto), + __ADD(TCF_META_ID_SK_TYPE, sk_type), + __ADD(TCF_META_ID_SK_RMEM_ALLOC, sk_rmem_alloc), + __ADD(TCF_META_ID_SK_WMEM_ALLOC, sk_wmem_alloc), + __ADD(TCF_META_ID_SK_WMEM_QUEUED, sk_wmem_queued), + __ADD(TCF_META_ID_SK_RCV_QLEN, sk_rcv_qlen), + __ADD(TCF_META_ID_SK_SND_QLEN, sk_snd_qlen), + __ADD(TCF_META_ID_SK_ERR_QLEN, sk_err_qlen), + __ADD(TCF_META_ID_SK_FORWARD_ALLOCS, sk_forward_allocs), + __ADD(TCF_META_ID_SK_ALLOCS, sk_allocs), + __ADD(__TCF_META_ID_SK_ROUTE_CAPS, sk_route_caps), + __ADD(TCF_META_ID_SK_HASH, sk_hash), + __ADD(TCF_META_ID_SK_LINGERTIME, sk_lingertime), + __ADD(TCF_META_ID_SK_ACK_BACKLOG, sk_ack_backlog), + __ADD(TCF_META_ID_SK_MAX_ACK_BACKLOG, sk_max_ack_backlog), + __ADD(TCF_META_ID_SK_PRIO, sk_prio), + __ADD(TCF_META_ID_SK_RCVLOWAT, sk_rcvlowat), + __ADD(TCF_META_ID_SK_RCVTIMEO, sk_rcvtimeo), + __ADD(TCF_META_ID_SK_SNDTIMEO, sk_sndtimeo), + __ADD(TCF_META_ID_SK_SENDMSG_OFF, sk_sendmsg_off), + __ADD(TCF_META_ID_SK_WRITE_PENDING, sk_write_pending), + __ADD(TCF_META_ID_VLAN_TAG, vlan), + __ADD(TCF_META_ID_RXHASH, rxhash), +}; + +static char *int_id2str(int id, char *buf, size_t size) +{ + return __type2str(id, buf, size, meta_int, ARRAY_SIZE(meta_int)); +} + +static const struct trans_tbl meta_var[] = { + __ADD(TCF_META_ID_DEV,devname), + __ADD(TCF_META_ID_SK_BOUND_IF,sk_bound_if), +}; + +static char *var_id2str(int id, char *buf, size_t size) +{ + return __type2str(id, buf, size, meta_var, ARRAY_SIZE(meta_var)); +} + +static void dump_value(struct rtnl_meta_value *v, struct nl_dump_params *p) +{ + char buf[32]; + + switch (v->mv_type) { + case TCF_META_TYPE_INT: + if (v->mv_id == TCF_META_ID_VALUE) { + nl_dump(p, "%u", + *(uint32_t *) (v + 1)); + } else { + nl_dump(p, "%s", + int_id2str(v->mv_id, buf, sizeof(buf))); + + if (v->mv_shift) + nl_dump(p, " >> %u", v->mv_shift); + + if (v->mv_len == 4) + nl_dump(p, " & %#x", *(uint32_t *) (v + 1)); + else if (v->mv_len == 8) + nl_dump(p, " & %#x", *(uint64_t *) (v + 1)); + } + break; + + case TCF_META_TYPE_VAR: + if (v->mv_id == TCF_META_ID_VALUE) { + nl_dump(p, "%s", (char *) (v + 1)); + } else { + nl_dump(p, "%s", + var_id2str(v->mv_id, buf, sizeof(buf))); + + if (v->mv_shift) + nl_dump(p, " >> %u", v->mv_shift); + } + break; + } +} + +static void meta_dump(struct rtnl_ematch *e, struct nl_dump_params *p) +{ + struct meta_data *m = rtnl_ematch_data(e); + char buf[32]; + + nl_dump(p, "meta("); + dump_value(m->left, p); + + nl_dump(p, " %s ", rtnl_ematch_opnd2txt(m->opnd, buf, sizeof(buf))); + + dump_value(m->right, p); + nl_dump(p, ")"); +} + +static int meta_fill(struct rtnl_ematch *e, struct nl_msg *msg) +{ + struct meta_data *m = rtnl_ematch_data(e); + struct tcf_meta_hdr hdr; + + if (!(m->left && m->right)) + return -NLE_MISSING_ATTR; + + memset(&hdr, 0, sizeof(hdr)); + hdr.left.kind = (m->left->mv_type << 12) & TCF_META_TYPE_MASK; + hdr.left.kind |= m->left->mv_id & TCF_META_ID_MASK; + hdr.left.shift = m->left->mv_shift; + hdr.left.op = m->opnd; + hdr.right.kind = (m->right->mv_type << 12) & TCF_META_TYPE_MASK; + hdr.right.kind |= m->right->mv_id & TCF_META_ID_MASK; + + NLA_PUT(msg, TCA_EM_META_HDR, sizeof(hdr), &hdr); + + if (m->left->mv_len) + NLA_PUT(msg, TCA_EM_META_LVALUE, m->left->mv_len, (m->left + 1)); + + if (m->right->mv_len) + NLA_PUT(msg, TCA_EM_META_RVALUE, m->right->mv_len, (m->right + 1)); + + return 0; + +nla_put_failure: + return -NLE_NOMEM; +} + +static void meta_free(struct rtnl_ematch *e) +{ + struct meta_data *m = rtnl_ematch_data(e); + free(m->left); + free(m->right); +} + +static struct rtnl_ematch_ops meta_ops = { + .eo_kind = TCF_EM_META, + .eo_name = "meta", + .eo_minlen = sizeof(struct tcf_meta_hdr), + .eo_datalen = sizeof(struct meta_data), + .eo_parse = meta_parse, + .eo_dump = meta_dump, + .eo_fill = meta_fill, + .eo_free = meta_free, +}; + +static void __init meta_init(void) +{ + rtnl_ematch_register(&meta_ops); +} + +/** @} */ diff --git a/libnl/lib/route/cls/ematch/nbyte.c b/libnl/lib/route/cls/ematch/nbyte.c new file mode 100644 index 0000000..07705e8 --- /dev/null +++ b/libnl/lib/route/cls/ematch/nbyte.c @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010-2013 Thomas Graf + */ + +/** + * @ingroup ematch + * @defgroup em_nbyte N-Byte Comparison + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + +struct nbyte_data +{ + struct tcf_em_nbyte cfg; + uint8_t * pattern; +}; + +void rtnl_ematch_nbyte_set_offset(struct rtnl_ematch *e, uint8_t layer, + uint16_t offset) +{ + struct nbyte_data *n = rtnl_ematch_data(e); + n->cfg.off = offset; + n->cfg.layer = layer; +} + +uint16_t rtnl_ematch_nbyte_get_offset(struct rtnl_ematch *e) +{ + return ((struct nbyte_data *) rtnl_ematch_data(e))->cfg.off; +} + +uint8_t rtnl_ematch_nbyte_get_layer(struct rtnl_ematch *e) +{ + return ((struct nbyte_data *) rtnl_ematch_data(e))->cfg.layer; +} + +void rtnl_ematch_nbyte_set_pattern(struct rtnl_ematch *e, + uint8_t *pattern, size_t len) +{ + struct nbyte_data *n = rtnl_ematch_data(e); + + if (n->pattern) + free(n->pattern); + + n->pattern = pattern; + n->cfg.len = len; +} + +uint8_t *rtnl_ematch_nbyte_get_pattern(struct rtnl_ematch *e) +{ + return ((struct nbyte_data *) rtnl_ematch_data(e))->pattern; +} + +size_t rtnl_ematch_nbyte_get_len(struct rtnl_ematch *e) +{ + return ((struct nbyte_data *) rtnl_ematch_data(e))->cfg.len; +} + +static const char *layer_txt(struct tcf_em_nbyte *nbyte) +{ + switch (nbyte->layer) { + case TCF_LAYER_LINK: + return "link"; + case TCF_LAYER_NETWORK: + return "net"; + case TCF_LAYER_TRANSPORT: + return "trans"; + default: + return "?"; + } +} + +static int nbyte_parse(struct rtnl_ematch *e, void *data, size_t len) +{ + struct nbyte_data *n = rtnl_ematch_data(e); + size_t hdrlen = sizeof(struct tcf_em_nbyte); + size_t plen = len - hdrlen; + + memcpy(&n->cfg, data, hdrlen); + if (plen > 0) { + if (!(n->pattern = calloc(1, plen))) + return -NLE_NOMEM; + + memcpy(n->pattern, (char *) data + hdrlen, plen); + } + + return 0; +} + +static void nbyte_dump(struct rtnl_ematch *e, struct nl_dump_params *p) +{ + struct nbyte_data *n = rtnl_ematch_data(e); + int i; + + nl_dump(p, "pattern(%u:[", n->cfg.len); + + for (i = 0; i < n->cfg.len; i++) { + nl_dump(p, "%02x", n->pattern[i]); + if (i+1 < n->cfg.len) + nl_dump(p, " "); + } + + nl_dump(p, "] at %s+%u)", layer_txt(&n->cfg), n->cfg.off); +} + +static void nbyte_free(struct rtnl_ematch *e) +{ + struct nbyte_data *n = rtnl_ematch_data(e); + free(n->pattern); +} + +static struct rtnl_ematch_ops nbyte_ops = { + .eo_kind = TCF_EM_NBYTE, + .eo_name = "nbyte", + .eo_minlen = sizeof(struct tcf_em_nbyte), + .eo_datalen = sizeof(struct nbyte_data), + .eo_parse = nbyte_parse, + .eo_dump = nbyte_dump, + .eo_free = nbyte_free, +}; + +static void __init nbyte_init(void) +{ + rtnl_ematch_register(&nbyte_ops); +} + +/** @} */ diff --git a/libnl/lib/route/cls/ematch/text.c b/libnl/lib/route/cls/ematch/text.c new file mode 100644 index 0000000..8788032 --- /dev/null +++ b/libnl/lib/route/cls/ematch/text.c @@ -0,0 +1,181 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010-2013 Thomas Graf + */ + +/** + * @ingroup ematch + * @defgroup em_text Text Search + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +struct text_data +{ + struct tcf_em_text cfg; + char * pattern; +}; + +void rtnl_ematch_text_set_from(struct rtnl_ematch *e, uint8_t layer, + uint16_t offset) +{ + struct text_data *t = rtnl_ematch_data(e); + t->cfg.from_offset = offset; + t->cfg.from_layer = layer; +} + +uint16_t rtnl_ematch_text_get_from_offset(struct rtnl_ematch *e) +{ + return ((struct text_data *) rtnl_ematch_data(e))->cfg.from_offset; +} + +uint8_t rtnl_ematch_text_get_from_layer(struct rtnl_ematch *e) +{ + return ((struct text_data *) rtnl_ematch_data(e))->cfg.from_layer; +} + +void rtnl_ematch_text_set_to(struct rtnl_ematch *e, uint8_t layer, + uint16_t offset) +{ + struct text_data *t = rtnl_ematch_data(e); + t->cfg.to_offset = offset; + t->cfg.to_layer = layer; +} + +uint16_t rtnl_ematch_text_get_to_offset(struct rtnl_ematch *e) +{ + return ((struct text_data *) rtnl_ematch_data(e))->cfg.to_offset; +} + +uint8_t rtnl_ematch_text_get_to_layer(struct rtnl_ematch *e) +{ + return ((struct text_data *) rtnl_ematch_data(e))->cfg.to_layer; +} + +void rtnl_ematch_text_set_pattern(struct rtnl_ematch *e, + char *pattern, size_t len) +{ + struct text_data *t = rtnl_ematch_data(e); + + if (t->pattern) + free(t->pattern); + + t->pattern = pattern; + t->cfg.pattern_len = len; +} + +char *rtnl_ematch_text_get_pattern(struct rtnl_ematch *e) +{ + return ((struct text_data *) rtnl_ematch_data(e))->pattern; +} + +size_t rtnl_ematch_text_get_len(struct rtnl_ematch *e) +{ + return ((struct text_data *) rtnl_ematch_data(e))->cfg.pattern_len; +} + +void rtnl_ematch_text_set_algo(struct rtnl_ematch *e, const char *algo) +{ + struct text_data *t = rtnl_ematch_data(e); + + _nl_strncpy_trunc(t->cfg.algo, algo, sizeof(t->cfg.algo)); +} + +char *rtnl_ematch_text_get_algo(struct rtnl_ematch *e) +{ + struct text_data *t = rtnl_ematch_data(e); + + return t->cfg.algo[0] ? t->cfg.algo : NULL; +} + +static int text_parse(struct rtnl_ematch *e, void *data, size_t len) +{ + struct text_data *t = rtnl_ematch_data(e); + size_t hdrlen = sizeof(struct tcf_em_text); + size_t plen = len - hdrlen; + + memcpy(&t->cfg, data, hdrlen); + + if (t->cfg.pattern_len > plen) + return -NLE_INVAL; + + if (t->cfg.pattern_len > 0) { + if (!(t->pattern = calloc(1, t->cfg.pattern_len))) + return -NLE_NOMEM; + + memcpy(t->pattern, (char *) data + hdrlen, t->cfg.pattern_len); + } + + return 0; +} + +static void text_dump(struct rtnl_ematch *e, struct nl_dump_params *p) +{ + struct text_data *t = rtnl_ematch_data(e); + char buf[64]; + + nl_dump(p, "text(%s \"%s\"", + t->cfg.algo[0] ? t->cfg.algo : "no-algo", + t->pattern ? t->pattern : "no-pattern"); + + if (t->cfg.from_layer || t->cfg.from_offset) { + nl_dump(p, " from %s", + rtnl_ematch_offset2txt(t->cfg.from_layer, + t->cfg.from_offset, + buf, sizeof(buf))); + } + + if (t->cfg.to_layer || t->cfg.to_offset) { + nl_dump(p, " to %s", + rtnl_ematch_offset2txt(t->cfg.to_layer, + t->cfg.to_offset, + buf, sizeof(buf))); + } + + nl_dump(p, ")"); +} + +static int text_fill(struct rtnl_ematch *e, struct nl_msg *msg) +{ + struct text_data *t = rtnl_ematch_data(e); + int err; + + if ((err = nlmsg_append(msg, &t->cfg, sizeof(t->cfg), 0)) < 0) + return err; + + return nlmsg_append(msg, t->pattern, t->cfg.pattern_len, 0); +} + +static void text_free(struct rtnl_ematch *e) +{ + struct text_data *t = rtnl_ematch_data(e); + free(t->pattern); +} + +static struct rtnl_ematch_ops text_ops = { + .eo_kind = TCF_EM_TEXT, + .eo_name = "text", + .eo_minlen = sizeof(struct tcf_em_text), + .eo_datalen = sizeof(struct text_data), + .eo_parse = text_parse, + .eo_dump = text_dump, + .eo_fill = text_fill, + .eo_free = text_free, +}; + +static void __init text_init(void) +{ + rtnl_ematch_register(&text_ops); +} + +/** @} */ diff --git a/libnl/lib/route/cls/ematch_grammar.c b/libnl/lib/route/cls/ematch_grammar.c new file mode 100644 index 0000000..08b58a5 --- /dev/null +++ b/libnl/lib/route/cls/ematch_grammar.c @@ -0,0 +1,2844 @@ +#line 2 "route/cls/ematch_grammar.c" + +#line 4 "route/cls/ematch_grammar.c" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yy_create_buffer +#define ematch__create_buffer_ALREADY_DEFINED +#else +#define yy_create_buffer ematch__create_buffer +#endif + +#ifdef yy_delete_buffer +#define ematch__delete_buffer_ALREADY_DEFINED +#else +#define yy_delete_buffer ematch__delete_buffer +#endif + +#ifdef yy_scan_buffer +#define ematch__scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer ematch__scan_buffer +#endif + +#ifdef yy_scan_string +#define ematch__scan_string_ALREADY_DEFINED +#else +#define yy_scan_string ematch__scan_string +#endif + +#ifdef yy_scan_bytes +#define ematch__scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes ematch__scan_bytes +#endif + +#ifdef yy_init_buffer +#define ematch__init_buffer_ALREADY_DEFINED +#else +#define yy_init_buffer ematch__init_buffer +#endif + +#ifdef yy_flush_buffer +#define ematch__flush_buffer_ALREADY_DEFINED +#else +#define yy_flush_buffer ematch__flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define ematch__load_buffer_state_ALREADY_DEFINED +#else +#define yy_load_buffer_state ematch__load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define ematch__switch_to_buffer_ALREADY_DEFINED +#else +#define yy_switch_to_buffer ematch__switch_to_buffer +#endif + +#ifdef yypush_buffer_state +#define ematch_push_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state ematch_push_buffer_state +#endif + +#ifdef yypop_buffer_state +#define ematch_pop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state ematch_pop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define ematch_ensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack ematch_ensure_buffer_stack +#endif + +#ifdef yylex +#define ematch_lex_ALREADY_DEFINED +#else +#define yylex ematch_lex +#endif + +#ifdef yyrestart +#define ematch_restart_ALREADY_DEFINED +#else +#define yyrestart ematch_restart +#endif + +#ifdef yylex_init +#define ematch_lex_init_ALREADY_DEFINED +#else +#define yylex_init ematch_lex_init +#endif + +#ifdef yylex_init_extra +#define ematch_lex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra ematch_lex_init_extra +#endif + +#ifdef yylex_destroy +#define ematch_lex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy ematch_lex_destroy +#endif + +#ifdef yyget_debug +#define ematch_get_debug_ALREADY_DEFINED +#else +#define yyget_debug ematch_get_debug +#endif + +#ifdef yyset_debug +#define ematch_set_debug_ALREADY_DEFINED +#else +#define yyset_debug ematch_set_debug +#endif + +#ifdef yyget_extra +#define ematch_get_extra_ALREADY_DEFINED +#else +#define yyget_extra ematch_get_extra +#endif + +#ifdef yyset_extra +#define ematch_set_extra_ALREADY_DEFINED +#else +#define yyset_extra ematch_set_extra +#endif + +#ifdef yyget_in +#define ematch_get_in_ALREADY_DEFINED +#else +#define yyget_in ematch_get_in +#endif + +#ifdef yyset_in +#define ematch_set_in_ALREADY_DEFINED +#else +#define yyset_in ematch_set_in +#endif + +#ifdef yyget_out +#define ematch_get_out_ALREADY_DEFINED +#else +#define yyget_out ematch_get_out +#endif + +#ifdef yyset_out +#define ematch_set_out_ALREADY_DEFINED +#else +#define yyset_out ematch_set_out +#endif + +#ifdef yyget_leng +#define ematch_get_leng_ALREADY_DEFINED +#else +#define yyget_leng ematch_get_leng +#endif + +#ifdef yyget_text +#define ematch_get_text_ALREADY_DEFINED +#else +#define yyget_text ematch_get_text +#endif + +#ifdef yyget_lineno +#define ematch_get_lineno_ALREADY_DEFINED +#else +#define yyget_lineno ematch_get_lineno +#endif + +#ifdef yyset_lineno +#define ematch_set_lineno_ALREADY_DEFINED +#else +#define yyset_lineno ematch_set_lineno +#endif + +#ifdef yyget_column +#define ematch_get_column_ALREADY_DEFINED +#else +#define yyget_column ematch_get_column +#endif + +#ifdef yyset_column +#define ematch_set_column_ALREADY_DEFINED +#else +#define yyset_column ematch_set_column +#endif + +#ifdef yywrap +#define ematch_wrap_ALREADY_DEFINED +#else +#define yywrap ematch_wrap +#endif + +#ifdef yyget_lval +#define ematch_get_lval_ALREADY_DEFINED +#else +#define yyget_lval ematch_get_lval +#endif + +#ifdef yyset_lval +#define ematch_set_lval_ALREADY_DEFINED +#else +#define yyset_lval ematch_set_lval +#endif + +#ifdef yyalloc +#define ematch_alloc_ALREADY_DEFINED +#else +#define yyalloc ematch_alloc +#endif + +#ifdef yyrealloc +#define ematch_realloc_ALREADY_DEFINED +#else +#define yyrealloc ematch_realloc +#endif + +#ifdef yyfree +#define ematch_free_ALREADY_DEFINED +#else +#define yyfree ematch_free +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an + * integer in range [0..255] for use as an array index. + */ +#define YY_SC_TO_UI(c) ((YY_CHAR) (c)) + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN yyg->yy_start = 1 + 2 * +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START ((yyg->yy_start - 1) / 2) +#define YYSTATE YY_START +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart( yyin , yyscanner ) +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + #define YY_LINENO_REWIND_TO(ptr) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = yyg->yy_hold_char; \ + YY_RESTORE_YY_MORE_OFFSET \ + yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) +#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ + ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ + : NULL) +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] + +void yyrestart ( FILE *input_file , yyscan_t yyscanner ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); +void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +void yypop_buffer_state ( yyscan_t yyscanner ); + +static void yyensure_buffer_stack ( yyscan_t yyscanner ); +static void yy_load_buffer_state ( yyscan_t yyscanner ); +static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file , yyscan_t yyscanner ); +#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER , yyscanner) + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); + +void *yyalloc ( yy_size_t , yyscan_t yyscanner ); +void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); +void yyfree ( void * , yyscan_t yyscanner ); + +#define yy_new_buffer yy_create_buffer +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +#define ematch_wrap(yyscanner) (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP +typedef flex_uint8_t YY_CHAR; + +typedef int yy_state_type; + +#define yytext_ptr yytext_r + +static yy_state_type yy_get_previous_state ( yyscan_t yyscanner ); +static yy_state_type yy_try_NUL_trans ( yy_state_type current_state , yyscan_t yyscanner); +static int yy_get_next_buffer ( yyscan_t yyscanner ); +static void yynoreturn yy_fatal_error ( const char* msg , yyscan_t yyscanner ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + yyg->yytext_ptr = yy_bp; \ + yyleng = (int) (yy_cp - yy_bp); \ + yyg->yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yyg->yy_c_buf_p = yy_cp; +#define YY_NUM_RULES 91 +#define YY_END_OF_BUFFER 92 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static const flex_int16_t yy_accept[393] = + { 0, + 0, 0, 0, 0, 92, 90, 1, 18, 2, 26, + 23, 24, 30, 5, 5, 12, 8, 10, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 91, 3, 91, 4, 90, 1, 14, 5, 90, + 28, 90, 29, 90, 90, 90, 40, 90, 90, 90, + 90, 90, 15, 90, 90, 90, 90, 32, 90, 90, + 90, 33, 90, 90, 7, 9, 90, 11, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 16, 3, 6, + 13, 19, 37, 90, 39, 90, 90, 90, 38, 17, + + 90, 90, 42, 90, 90, 34, 35, 90, 47, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 31, 36, 25, 22, 90, 90, 21, 90, + 90, 90, 90, 90, 54, 90, 90, 48, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 86, 90, 27, 90, + 90, 90, 90, 90, 90, 90, 49, 90, 90, 57, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 53, 51, + + 90, 43, 90, 87, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 20, 90, + 52, 88, 90, 50, 90, 90, 90, 90, 90, 90, + 90, 76, 90, 90, 80, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 66, 90, 90, 55, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 65, 90, 90, 90, 90, + 90, 60, 90, 90, 90, 90, 90, 90, 90, 59, + 90, 90, 41, 44, 45, 46, 56, 90, 74, 90, + + 90, 58, 90, 90, 90, 90, 62, 90, 90, 61, + 90, 90, 90, 90, 90, 63, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 89, + 72, 90, 90, 90, 70, 81, 82, 90, 90, 90, + 64, 71, 83, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 77, 90, + 67, 75, 90, 68, 90, 90, 78, 90, 90, 84, + 69, 90, 90, 90, 90, 90, 90, 85, 73, 90, + 79, 0 + + } ; + +static const YY_CHAR yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 4, 5, 1, 1, 1, 6, 1, 7, + 8, 1, 9, 1, 1, 1, 1, 10, 11, 12, + 13, 14, 14, 15, 14, 16, 14, 1, 1, 17, + 18, 19, 1, 1, 20, 21, 22, 23, 24, 25, + 1, 26, 27, 1, 28, 29, 30, 31, 32, 33, + 1, 34, 35, 36, 37, 1, 1, 38, 1, 1, + 1, 39, 1, 1, 40, 1, 41, 42, 43, 44, + + 45, 46, 47, 48, 49, 1, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 1, 1, 65, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static const YY_CHAR yy_meta[66] = + { 0, + 1, 2, 3, 1, 3, 2, 2, 2, 2, 1, + 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 4, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2 + } ; + +static const flex_int16_t yy_base[396] = + { 0, + 0, 0, 63, 64, 537, 0, 68, 0, 538, 530, + 538, 538, 538, 62, 69, 538, 538, 516, 55, 57, + 52, 55, 60, 67, 72, 73, 64, 79, 75, 100, + 115, 65, 83, 475, 102, 117, 118, 89, 121, 131, + 482, 467, 0, 538, 538, 0, 133, 538, 167, 179, + 538, 123, 0, 111, 124, 132, 516, 153, 138, 151, + 169, 171, 0, 172, 170, 178, 171, 0, 129, 515, + 517, 0, 469, 466, 0, 0, 485, 0, 183, 466, + 41, 471, 186, 475, 482, 183, 480, 538, 0, 0, + 0, 0, 0, 184, 0, 187, 189, 207, 0, 0, + + 206, 218, 0, 208, 215, 0, 0, 479, 466, 474, + 466, 466, 194, 461, 455, 469, 461, 462, 469, 228, + 456, 455, 0, 0, 0, 0, 226, 213, 0, 217, + 456, 465, 464, 459, 0, 458, 438, 0, 447, 446, + 458, 452, 439, 204, 442, 438, 237, 453, 444, 451, + 434, 249, 236, 426, 199, 445, 0, 220, 0, 227, + 443, 435, 425, 432, 431, 428, 0, 430, 423, 0, + 432, 429, 427, 417, 419, 423, 417, 415, 419, 408, + 208, 409, 152, 424, 408, 414, 406, 421, 423, 408, + 417, 412, 415, 235, 261, 406, 413, 410, 0, 0, + + 411, 0, 397, 0, 414, 399, 399, 411, 401, 387, + 400, 400, 406, 391, 385, 257, 400, 384, 389, 381, + 395, 379, 258, 378, 391, 383, 375, 370, 0, 262, + 0, 0, 392, 0, 382, 388, 386, 384, 371, 375, + 384, 0, 379, 382, 0, 368, 365, 360, 365, 369, + 364, 371, 375, 369, 361, 368, 355, 350, 360, 363, + 0, 367, 361, 0, 266, 294, 361, 363, 345, 362, + 350, 336, 342, 341, 354, 0, 345, 349, 332, 341, + 333, 0, 350, 350, 331, 334, 336, 340, 333, 0, + 266, 344, 0, 0, 0, 0, 0, 340, 0, 333, + + 336, 0, 336, 320, 328, 332, 0, 335, 330, 0, + 323, 330, 325, 309, 325, 0, 324, 317, 307, 311, + 315, 318, 310, 322, 312, 320, 306, 299, 303, 305, + 314, 314, 300, 299, 297, 299, 304, 303, 296, 0, + 0, 305, 293, 302, 0, 0, 0, 289, 287, 287, + 0, 0, 0, 286, 279, 285, 283, 285, 287, 290, + 281, 265, 275, 277, 273, 270, 266, 261, 0, 268, + 0, 0, 264, 0, 265, 214, 0, 207, 209, 0, + 0, 205, 170, 109, 93, 75, 53, 0, 0, 57, + 0, 538, 325, 329, 333 + + } ; + +static const flex_int16_t yy_def[396] = + { 0, + 392, 1, 393, 393, 392, 394, 392, 394, 392, 392, + 392, 392, 392, 394, 394, 392, 392, 392, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 392, 395, 392, 392, 394, 392, 392, 394, 394, + 392, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 392, 395, 50, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 0, 392, 392, 392 + + } ; + +static const flex_int16_t yy_nxt[604] = + { 0, + 6, 7, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 15, 15, 15, 15, 15, 16, 17, 18, 19, + 6, 20, 6, 21, 22, 6, 23, 6, 24, 25, + 26, 27, 28, 6, 29, 30, 31, 6, 6, 6, + 19, 6, 20, 32, 33, 22, 34, 6, 23, 6, + 35, 36, 26, 27, 37, 6, 38, 39, 40, 31, + 41, 6, 6, 6, 42, 44, 44, 45, 45, 47, + 47, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 52, 54, 55, 56, 114, + 53, 59, 57, 58, 115, 60, 61, 63, 64, 50, + + 65, 44, 44, 391, 62, 73, 390, 52, 54, 74, + 55, 56, 59, 53, 57, 58, 60, 61, 55, 64, + 63, 66, 65, 67, 50, 70, 62, 71, 58, 82, + 72, 68, 389, 69, 47, 47, 59, 64, 75, 388, + 60, 55, 66, 92, 67, 91, 65, 83, 105, 93, + 58, 84, 66, 68, 67, 77, 69, 79, 64, 387, + 78, 60, 68, 94, 69, 92, 91, 80, 65, 105, + 85, 93, 97, 86, 81, 67, 49, 49, 49, 49, + 49, 49, 49, 96, 68, 94, 98, 69, 90, 90, + 90, 90, 90, 90, 90, 97, 102, 217, 90, 90, + + 90, 90, 90, 90, 99, 96, 100, 101, 104, 98, + 103, 218, 386, 123, 124, 103, 125, 97, 102, 90, + 90, 90, 90, 90, 90, 111, 126, 99, 117, 100, + 101, 121, 103, 104, 118, 123, 124, 103, 125, 112, + 97, 127, 128, 129, 136, 130, 172, 126, 159, 158, + 191, 160, 137, 194, 173, 192, 214, 385, 384, 195, + 383, 215, 382, 128, 127, 229, 129, 130, 144, 145, + 158, 159, 146, 147, 160, 148, 194, 176, 149, 150, + 186, 195, 151, 187, 152, 153, 154, 229, 188, 155, + 177, 182, 230, 183, 189, 265, 247, 257, 248, 258, + + 184, 293, 185, 294, 295, 296, 318, 249, 381, 380, + 379, 378, 377, 376, 230, 250, 259, 375, 265, 374, + 373, 319, 372, 371, 293, 43, 43, 43, 43, 46, + 370, 369, 46, 89, 89, 368, 367, 366, 365, 364, + 363, 362, 361, 360, 359, 358, 357, 356, 355, 354, + 353, 352, 351, 350, 349, 348, 347, 346, 345, 344, + 343, 342, 341, 340, 339, 338, 337, 336, 335, 334, + 333, 332, 331, 330, 329, 328, 327, 326, 325, 324, + 323, 322, 321, 320, 317, 316, 315, 314, 313, 312, + 311, 310, 309, 308, 307, 306, 305, 304, 303, 302, + + 301, 300, 299, 298, 297, 292, 291, 290, 289, 288, + 287, 286, 285, 284, 283, 282, 281, 280, 279, 278, + 277, 276, 275, 274, 273, 272, 271, 270, 269, 268, + 267, 266, 264, 263, 262, 261, 260, 256, 255, 254, + 253, 252, 251, 246, 245, 244, 243, 242, 241, 240, + 239, 238, 237, 236, 235, 234, 233, 232, 231, 228, + 227, 226, 225, 224, 223, 222, 221, 220, 219, 216, + 213, 212, 211, 210, 209, 208, 207, 206, 205, 204, + 203, 202, 201, 200, 199, 198, 197, 196, 193, 190, + 181, 180, 179, 178, 175, 174, 171, 170, 169, 168, + + 167, 166, 165, 164, 163, 162, 161, 157, 156, 143, + 142, 141, 140, 139, 138, 135, 134, 133, 132, 131, + 122, 120, 119, 116, 113, 110, 109, 108, 107, 106, + 95, 88, 87, 76, 51, 48, 392, 5, 392, 392, + 392, 392, 392, 392, 392, 392, 392, 392, 392, 392, + 392, 392, 392, 392, 392, 392, 392, 392, 392, 392, + 392, 392, 392, 392, 392, 392, 392, 392, 392, 392, + 392, 392, 392, 392, 392, 392, 392, 392, 392, 392, + 392, 392, 392, 392, 392, 392, 392, 392, 392, 392, + 392, 392, 392, 392, 392, 392, 392, 392, 392, 392, + + 392, 392, 392 + } ; + +static const flex_int16_t yy_chk[604] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 3, 4, 3, 4, 7, + 7, 14, 14, 14, 14, 14, 14, 14, 15, 15, + 15, 15, 15, 15, 15, 19, 20, 21, 22, 81, + 19, 25, 23, 24, 81, 25, 26, 27, 28, 14, + + 29, 3, 4, 390, 26, 32, 387, 19, 20, 32, + 21, 22, 25, 19, 23, 24, 25, 26, 33, 28, + 27, 30, 29, 30, 14, 31, 26, 31, 35, 38, + 31, 30, 386, 30, 47, 47, 36, 37, 33, 385, + 36, 33, 30, 54, 30, 52, 39, 38, 69, 55, + 35, 38, 40, 30, 40, 35, 30, 36, 37, 384, + 35, 36, 40, 56, 40, 54, 52, 37, 39, 69, + 39, 55, 59, 40, 37, 40, 49, 49, 49, 49, + 49, 49, 49, 58, 40, 56, 60, 40, 50, 50, + 50, 50, 50, 50, 50, 59, 65, 183, 50, 50, + + 50, 50, 50, 50, 61, 58, 62, 64, 67, 60, + 66, 183, 383, 94, 96, 86, 97, 79, 65, 50, + 50, 50, 50, 50, 50, 79, 98, 61, 83, 62, + 64, 86, 66, 67, 83, 94, 96, 86, 97, 79, + 79, 101, 102, 104, 113, 105, 144, 98, 128, 127, + 155, 130, 113, 158, 144, 155, 181, 382, 379, 160, + 378, 181, 376, 102, 101, 194, 104, 105, 120, 120, + 127, 128, 120, 120, 130, 120, 158, 147, 120, 120, + 153, 160, 120, 153, 120, 120, 120, 194, 153, 120, + 147, 152, 195, 152, 153, 230, 216, 223, 216, 223, + + 152, 265, 152, 266, 266, 266, 291, 216, 375, 373, + 370, 368, 367, 366, 195, 216, 223, 365, 230, 364, + 363, 291, 362, 361, 265, 393, 393, 393, 393, 394, + 360, 359, 394, 395, 395, 358, 357, 356, 355, 354, + 350, 349, 348, 344, 343, 342, 339, 338, 337, 336, + 335, 334, 333, 332, 331, 330, 329, 328, 327, 326, + 325, 324, 323, 322, 321, 320, 319, 318, 317, 315, + 314, 313, 312, 311, 309, 308, 306, 305, 304, 303, + 301, 300, 298, 292, 289, 288, 287, 286, 285, 284, + 283, 281, 280, 279, 278, 277, 275, 274, 273, 272, + + 271, 270, 269, 268, 267, 263, 262, 260, 259, 258, + 257, 256, 255, 254, 253, 252, 251, 250, 249, 248, + 247, 246, 244, 243, 241, 240, 239, 238, 237, 236, + 235, 233, 228, 227, 226, 225, 224, 222, 221, 220, + 219, 218, 217, 215, 214, 213, 212, 211, 210, 209, + 208, 207, 206, 205, 203, 201, 198, 197, 196, 193, + 192, 191, 190, 189, 188, 187, 186, 185, 184, 182, + 180, 179, 178, 177, 176, 175, 174, 173, 172, 171, + 169, 168, 166, 165, 164, 163, 162, 161, 156, 154, + 151, 150, 149, 148, 146, 145, 143, 142, 141, 140, + + 139, 137, 136, 134, 133, 132, 131, 122, 121, 119, + 118, 117, 116, 115, 114, 112, 111, 110, 109, 108, + 87, 85, 84, 82, 80, 77, 74, 73, 71, 70, + 57, 42, 41, 34, 18, 10, 5, 392, 392, 392, + 392, 392, 392, 392, 392, 392, 392, 392, 392, 392, + 392, 392, 392, 392, 392, 392, 392, 392, 392, 392, + 392, 392, 392, 392, 392, 392, 392, 392, 392, 392, + 392, 392, 392, 392, 392, 392, 392, 392, 392, 392, + 392, 392, 392, 392, 392, 392, 392, 392, 392, 392, + 392, 392, 392, 392, 392, 392, 392, 392, 392, 392, + + 392, 392, 392 + } ; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +#line 1 "route/cls/ematch_grammar.l" +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010-2013 Thomas Graf + */ +#line 7 "route/cls/ematch_grammar.l" + #include + #include + #include + #include + #include + #include + #include "ematch_syntax.h" + + int ematch_get_column(yyscan_t); + void ematch_set_column(int, yyscan_t); +#line 924 "route/cls/ematch_grammar.c" +#define YY_NO_INPUT 1 + +#line 927 "route/cls/ematch_grammar.c" + +#define INITIAL 0 +#define QUOTE 1 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +/* Holds the entire state of the reentrant scanner. */ +struct yyguts_t + { + + /* User-defined. Not touched by flex. */ + YY_EXTRA_TYPE yyextra_r; + + /* The rest are the same as the globals declared in the non-reentrant scanner. */ + FILE *yyin_r, *yyout_r; + size_t yy_buffer_stack_top; /**< index of top of stack. */ + size_t yy_buffer_stack_max; /**< capacity of stack. */ + YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ + char yy_hold_char; + int yy_n_chars; + int yyleng_r; + char *yy_c_buf_p; + int yy_init; + int yy_start; + int yy_did_buffer_switch_on_eof; + int yy_start_stack_ptr; + int yy_start_stack_depth; + int *yy_start_stack; + yy_state_type yy_last_accepting_state; + char* yy_last_accepting_cpos; + + int yylineno_r; + int yy_flex_debug_r; + + char *yytext_r; + int yy_more_flag; + int yy_more_len; + + YYSTYPE * yylval_r; + + }; /* end struct yyguts_t */ + +static int yy_init_globals ( yyscan_t yyscanner ); + + /* This must go here because YYSTYPE and YYLTYPE are included + * from bison output in section 1.*/ + # define yylval yyg->yylval_r + +int yylex_init (yyscan_t* scanner); + +int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy ( yyscan_t yyscanner ); + +int yyget_debug ( yyscan_t yyscanner ); + +void yyset_debug ( int debug_flag , yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); + +FILE *yyget_in ( yyscan_t yyscanner ); + +void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); + +FILE *yyget_out ( yyscan_t yyscanner ); + +void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); + + int yyget_leng ( yyscan_t yyscanner ); + +char *yyget_text ( yyscan_t yyscanner ); + +int yyget_lineno ( yyscan_t yyscanner ); + +void yyset_lineno ( int _line_number , yyscan_t yyscanner ); + +int yyget_column ( yyscan_t yyscanner ); + +void yyset_column ( int _column_no , yyscan_t yyscanner ); + +YYSTYPE * yyget_lval ( yyscan_t yyscanner ); + +void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap ( yyscan_t yyscanner ); +#else +extern int yywrap ( yyscan_t yyscanner ); +#endif +#endif + +#ifndef YY_NO_UNPUT + +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const char * , yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus +static int yyinput ( yyscan_t yyscanner ); +#else +static int input ( yyscan_t yyscanner ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + int n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex \ + (YYSTYPE * yylval_param , yyscan_t yyscanner); + +#define YY_DECL int yylex \ + (YYSTYPE * yylval_param , yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK /*LINTED*/break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + yy_state_type yy_current_state; + char *yy_cp, *yy_bp; + int yy_act; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yylval = yylval_param; + + if ( !yyg->yy_init ) + { + yyg->yy_init = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! yyg->yy_start ) + yyg->yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_load_buffer_state( yyscanner ); + } + + { +#line 30 "route/cls/ematch_grammar.l" + + +#line 1203 "route/cls/ematch_grammar.c" + + while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ + { + yy_cp = yyg->yy_c_buf_p; + + /* Support of yytext. */ + *yy_cp = yyg->yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = yyg->yy_start; +yy_match: + do + { + YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 393 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 538 ); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + if ( yy_act == 0 ) + { /* have to back up */ + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + yy_act = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yyg->yy_hold_char; + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + goto yy_find_action; + +case 1: +/* rule 1 can match eol */ +YY_RULE_SETUP +#line 32 "route/cls/ematch_grammar.l" + + YY_BREAK +case 2: +YY_RULE_SETUP +#line 34 "route/cls/ematch_grammar.l" +{ + NL_DBG(4, "Beginning of quote\n"); + yylval->q.len = 32; + if (!(yylval->q.data = calloc(1, yylval->q.len))) + return ERROR; + + yylval->q.index = 0; + BEGIN(QUOTE); + } + YY_BREAK +case 3: +YY_RULE_SETUP +#line 44 "route/cls/ematch_grammar.l" +{ + memcpy(yylval->q.data + yylval->q.index, yytext, + strlen(yytext)); + yylval->q.index += strlen(yytext); + } + YY_BREAK +case 4: +YY_RULE_SETUP +#line 50 "route/cls/ematch_grammar.l" +{ + BEGIN(0); + return QUOTED; + } + YY_BREAK +case 5: +#line 57 "route/cls/ematch_grammar.l" +case 6: +YY_RULE_SETUP +#line 57 "route/cls/ematch_grammar.l" +{ + yylval->i = strtoul(yytext, NULL, 0); + return NUMBER; + } + YY_BREAK +case 7: +#line 63 "route/cls/ematch_grammar.l" +case 8: +YY_RULE_SETUP +#line 63 "route/cls/ematch_grammar.l" +return KW_EQ; + YY_BREAK +case 9: +#line 65 "route/cls/ematch_grammar.l" +case 10: +YY_RULE_SETUP +#line 65 "route/cls/ematch_grammar.l" +return KW_GT; + YY_BREAK +case 11: +#line 67 "route/cls/ematch_grammar.l" +case 12: +YY_RULE_SETUP +#line 67 "route/cls/ematch_grammar.l" +return KW_LT; + YY_BREAK +case 13: +#line 70 "route/cls/ematch_grammar.l" +case 14: +YY_RULE_SETUP +#line 70 "route/cls/ematch_grammar.l" +{ yylval->i = TCF_EM_REL_AND; return LOGIC; } + YY_BREAK +case 15: +#line 72 "route/cls/ematch_grammar.l" +case 16: +YY_RULE_SETUP +#line 72 "route/cls/ematch_grammar.l" +{ yylval->i = TCF_EM_REL_OR; return LOGIC; } + YY_BREAK +case 17: +#line 74 "route/cls/ematch_grammar.l" +case 18: +YY_RULE_SETUP +#line 74 "route/cls/ematch_grammar.l" +return NOT; + YY_BREAK +case 19: +YY_RULE_SETUP +#line 76 "route/cls/ematch_grammar.l" +{ yylval->i = TCF_EM_CMP; return EMATCH_CMP; } + YY_BREAK +case 20: +YY_RULE_SETUP +#line 77 "route/cls/ematch_grammar.l" +{ yylval->i = TCF_EM_NBYTE; return EMATCH_NBYTE; } + YY_BREAK +case 21: +YY_RULE_SETUP +#line 78 "route/cls/ematch_grammar.l" +{ yylval->i = TCF_EM_TEXT; return EMATCH_TEXT; } + YY_BREAK +case 22: +YY_RULE_SETUP +#line 79 "route/cls/ematch_grammar.l" +{ yylval->i = TCF_EM_META; return EMATCH_META; } + YY_BREAK +case 23: +YY_RULE_SETUP +#line 81 "route/cls/ematch_grammar.l" +return KW_OPEN; + YY_BREAK +case 24: +YY_RULE_SETUP +#line 82 "route/cls/ematch_grammar.l" +return KW_CLOSE; + YY_BREAK +case 25: +#line 84 "route/cls/ematch_grammar.l" +case 26: +YY_RULE_SETUP +#line 84 "route/cls/ematch_grammar.l" +return KW_MASK; + YY_BREAK +case 27: +#line 86 "route/cls/ematch_grammar.l" +case 28: +YY_RULE_SETUP +#line 86 "route/cls/ematch_grammar.l" +return KW_SHIFT; + YY_BREAK +case 29: +YY_RULE_SETUP +#line 87 "route/cls/ematch_grammar.l" +return KW_AT; + YY_BREAK +case 30: +YY_RULE_SETUP +#line 88 "route/cls/ematch_grammar.l" +return KW_PLUS; + YY_BREAK +case 31: +YY_RULE_SETUP +#line 89 "route/cls/ematch_grammar.l" +return KW_FROM; + YY_BREAK +case 32: +YY_RULE_SETUP +#line 90 "route/cls/ematch_grammar.l" +return KW_TO; + YY_BREAK +case 33: +YY_RULE_SETUP +#line 92 "route/cls/ematch_grammar.l" +{ yylval->i = TCF_EM_ALIGN_U8; return ALIGN; } + YY_BREAK +case 34: +YY_RULE_SETUP +#line 93 "route/cls/ematch_grammar.l" +{ yylval->i = TCF_EM_ALIGN_U16; return ALIGN; } + YY_BREAK +case 35: +YY_RULE_SETUP +#line 94 "route/cls/ematch_grammar.l" +{ yylval->i = TCF_EM_ALIGN_U32; return ALIGN; } + YY_BREAK +case 36: +#line 97 "route/cls/ematch_grammar.l" +case 37: +YY_RULE_SETUP +#line 97 "route/cls/ematch_grammar.l" +{ yylval->i = TCF_LAYER_LINK; return LAYER; } + YY_BREAK +case 38: +#line 99 "route/cls/ematch_grammar.l" +case 39: +#line 100 "route/cls/ematch_grammar.l" +case 40: +YY_RULE_SETUP +#line 100 "route/cls/ematch_grammar.l" +{ yylval->i = TCF_LAYER_NETWORK; return LAYER; } + YY_BREAK +case 41: +#line 102 "route/cls/ematch_grammar.l" +case 42: +YY_RULE_SETUP +#line 102 "route/cls/ematch_grammar.l" +{ yylval->i = TCF_LAYER_TRANSPORT; return LAYER; } + YY_BREAK +case 43: +YY_RULE_SETUP +#line 104 "route/cls/ematch_grammar.l" +return META_RANDOM; + YY_BREAK +case 44: +YY_RULE_SETUP +#line 105 "route/cls/ematch_grammar.l" +return META_LOADAVG_0; + YY_BREAK +case 45: +YY_RULE_SETUP +#line 106 "route/cls/ematch_grammar.l" +return META_LOADAVG_1; + YY_BREAK +case 46: +YY_RULE_SETUP +#line 107 "route/cls/ematch_grammar.l" +return META_LOADAVG_2; + YY_BREAK +case 47: +YY_RULE_SETUP +#line 108 "route/cls/ematch_grammar.l" +return META_DEV; + YY_BREAK +case 48: +YY_RULE_SETUP +#line 109 "route/cls/ematch_grammar.l" +return META_PRIO; + YY_BREAK +case 49: +YY_RULE_SETUP +#line 110 "route/cls/ematch_grammar.l" +return META_PROTO; + YY_BREAK +case 50: +YY_RULE_SETUP +#line 111 "route/cls/ematch_grammar.l" +return META_PKTTYPE; + YY_BREAK +case 51: +YY_RULE_SETUP +#line 112 "route/cls/ematch_grammar.l" +return META_PKTLEN; + YY_BREAK +case 52: +YY_RULE_SETUP +#line 113 "route/cls/ematch_grammar.l" +return META_DATALEN; + YY_BREAK +case 53: +YY_RULE_SETUP +#line 114 "route/cls/ematch_grammar.l" +return META_MACLEN; + YY_BREAK +case 54: +YY_RULE_SETUP +#line 115 "route/cls/ematch_grammar.l" +return META_MARK; + YY_BREAK +case 55: +YY_RULE_SETUP +#line 116 "route/cls/ematch_grammar.l" +return META_TCINDEX; + YY_BREAK +case 56: +YY_RULE_SETUP +#line 117 "route/cls/ematch_grammar.l" +return META_RTCLASSID; + YY_BREAK +case 57: +YY_RULE_SETUP +#line 118 "route/cls/ematch_grammar.l" +return META_RTIIF; + YY_BREAK +case 58: +YY_RULE_SETUP +#line 119 "route/cls/ematch_grammar.l" +return META_SK_FAMILY; + YY_BREAK +case 59: +YY_RULE_SETUP +#line 120 "route/cls/ematch_grammar.l" +return META_SK_STATE; + YY_BREAK +case 60: +YY_RULE_SETUP +#line 121 "route/cls/ematch_grammar.l" +return META_SK_REUSE; + YY_BREAK +case 61: +YY_RULE_SETUP +#line 122 "route/cls/ematch_grammar.l" +return META_SK_REFCNT; + YY_BREAK +case 62: +YY_RULE_SETUP +#line 123 "route/cls/ematch_grammar.l" +return META_SK_RCVBUF; + YY_BREAK +case 63: +YY_RULE_SETUP +#line 124 "route/cls/ematch_grammar.l" +return META_SK_SNDBUF; + YY_BREAK +case 64: +YY_RULE_SETUP +#line 125 "route/cls/ematch_grammar.l" +return META_SK_SHUTDOWN; + YY_BREAK +case 65: +YY_RULE_SETUP +#line 126 "route/cls/ematch_grammar.l" +return META_SK_PROTO; + YY_BREAK +case 66: +YY_RULE_SETUP +#line 127 "route/cls/ematch_grammar.l" +return META_SK_TYPE; + YY_BREAK +case 67: +YY_RULE_SETUP +#line 128 "route/cls/ematch_grammar.l" +return META_SK_RMEM_ALLOC; + YY_BREAK +case 68: +YY_RULE_SETUP +#line 129 "route/cls/ematch_grammar.l" +return META_SK_WMEM_ALLOC; + YY_BREAK +case 69: +YY_RULE_SETUP +#line 130 "route/cls/ematch_grammar.l" +return META_SK_WMEM_QUEUED; + YY_BREAK +case 70: +YY_RULE_SETUP +#line 131 "route/cls/ematch_grammar.l" +return META_SK_RCV_QLEN; + YY_BREAK +case 71: +YY_RULE_SETUP +#line 132 "route/cls/ematch_grammar.l" +return META_SK_SND_QLEN; + YY_BREAK +case 72: +YY_RULE_SETUP +#line 133 "route/cls/ematch_grammar.l" +return META_SK_ERR_QLEN; + YY_BREAK +case 73: +YY_RULE_SETUP +#line 134 "route/cls/ematch_grammar.l" +return META_SK_FORWARD_ALLOCS; + YY_BREAK +case 74: +YY_RULE_SETUP +#line 135 "route/cls/ematch_grammar.l" +return META_SK_ALLOCS; + YY_BREAK +case 75: +YY_RULE_SETUP +#line 136 "route/cls/ematch_grammar.l" +return META_SK_ROUTE_CAPS; + YY_BREAK +case 76: +YY_RULE_SETUP +#line 137 "route/cls/ematch_grammar.l" +return META_SK_HASH; + YY_BREAK +case 77: +YY_RULE_SETUP +#line 138 "route/cls/ematch_grammar.l" +return META_SK_LINGERTIME; + YY_BREAK +case 78: +YY_RULE_SETUP +#line 139 "route/cls/ematch_grammar.l" +return META_SK_ACK_BACKLOG; + YY_BREAK +case 79: +YY_RULE_SETUP +#line 140 "route/cls/ematch_grammar.l" +return META_SK_MAX_ACK_BACKLOG; + YY_BREAK +case 80: +YY_RULE_SETUP +#line 141 "route/cls/ematch_grammar.l" +return META_SK_PRIO; + YY_BREAK +case 81: +YY_RULE_SETUP +#line 142 "route/cls/ematch_grammar.l" +return META_SK_RCVLOWAT; + YY_BREAK +case 82: +YY_RULE_SETUP +#line 143 "route/cls/ematch_grammar.l" +return META_SK_RCVTIMEO; + YY_BREAK +case 83: +YY_RULE_SETUP +#line 144 "route/cls/ematch_grammar.l" +return META_SK_SNDTIMEO; + YY_BREAK +case 84: +YY_RULE_SETUP +#line 145 "route/cls/ematch_grammar.l" +return META_SK_SENDMSG_OFF; + YY_BREAK +case 85: +YY_RULE_SETUP +#line 146 "route/cls/ematch_grammar.l" +return META_SK_WRITE_PENDING; + YY_BREAK +case 86: +YY_RULE_SETUP +#line 147 "route/cls/ematch_grammar.l" +return META_VLAN; + YY_BREAK +case 87: +YY_RULE_SETUP +#line 148 "route/cls/ematch_grammar.l" +return META_RXHASH; + YY_BREAK +case 88: +YY_RULE_SETUP +#line 150 "route/cls/ematch_grammar.l" +return META_DEVNAME; + YY_BREAK +case 89: +YY_RULE_SETUP +#line 151 "route/cls/ematch_grammar.l" +return META_SK_BOUND_IF; + YY_BREAK +case 90: +YY_RULE_SETUP +#line 154 "route/cls/ematch_grammar.l" +{ + yylval->s = strdup(yytext); + if (yylval->s == NULL) + return ERROR; + NL_DBG(4, "lex STR=%s\n", yylval->s); + return STR; + } + YY_BREAK +case 91: +YY_RULE_SETUP +#line 161 "route/cls/ematch_grammar.l" +ECHO; + YY_BREAK +#line 1701 "route/cls/ematch_grammar.c" +case YY_STATE_EOF(INITIAL): +case YY_STATE_EOF(QUOTE): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = yyg->yy_hold_char; + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); + + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++yyg->yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = yyg->yy_c_buf_p; + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_END_OF_FILE: + { + yyg->yy_did_buffer_switch_on_eof = 0; + + if ( yywrap( yyscanner ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = + yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yyg->yy_c_buf_p = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of user's declarations */ +} /* end of yylex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + char *source = yyg->yytext_ptr; + int number_to_move, i; + int ret_val; + + if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr - 1); + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; + + int yy_c_buf_p_offset = + (int) (yyg->yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yyrealloc( (void *) b->yy_ch_buf, + (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = NULL; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + yyg->yy_n_chars, num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + if ( yyg->yy_n_chars == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart( yyin , yyscanner); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if ((yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( + (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size , yyscanner ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + /* "- 2" to take care of EOB's */ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); + } + + yyg->yy_n_chars += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (yyscan_t yyscanner) +{ + yy_state_type yy_current_state; + char *yy_cp; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_current_state = yyg->yy_start; + + for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) + { + YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 393 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) +{ + int yy_is_jam; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ + char *yy_cp = yyg->yy_c_buf_p; + + YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 393 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + yy_is_jam = (yy_current_state == 392); + + (void)yyg; + return yy_is_jam ? 0 : yy_current_state; +} + +#ifndef YY_NO_UNPUT + +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (yyscan_t yyscanner) +#else + static int input (yyscan_t yyscanner) +#endif + +{ + int c; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + *yyg->yy_c_buf_p = yyg->yy_hold_char; + + if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + /* This was really a NUL. */ + *yyg->yy_c_buf_p = '\0'; + + else + { /* need more input */ + int offset = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr); + ++yyg->yy_c_buf_p; + + switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart( yyin , yyscanner); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap( yyscanner ) ) + return 0; + + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(yyscanner); +#else + return input(yyscanner); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = yyg->yytext_ptr + offset; + break; + } + } + } + + c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ + *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ + yyg->yy_hold_char = *++yyg->yy_c_buf_p; + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * @param yyscanner The scanner object. + * @note This function does not reset the start condition to @c INITIAL . + */ + void yyrestart (FILE * input_file , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_init_buffer( YY_CURRENT_BUFFER, input_file , yyscanner); + yy_load_buffer_state( yyscanner ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * @param yyscanner The scanner object. + */ + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* TODO. We should be able to replace this entire function body + * with + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + yyensure_buffer_stack (yyscanner); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + yy_load_buffer_state( yyscanner ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yyg->yy_did_buffer_switch_on_eof = 1; +} + +static void yy_load_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + yyg->yy_hold_char = *yyg->yy_c_buf_p; +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * @param yyscanner The scanner object. + * @return the allocated buffer state. + */ + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file , yyscanner); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * @param yyscanner The scanner object. + */ + void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yyfree( (void *) b->yy_ch_buf , yyscanner ); + + yyfree( (void *) b , yyscanner ); +} + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) + +{ + int oerrno = errno; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_flush_buffer( b , yyscanner); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * @param yyscanner The scanner object. + */ + void yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + yy_load_buffer_state( yyscanner ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * @param yyscanner The scanner object. + */ +void yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(yyscanner); + + /* This block is copied from yy_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + yyg->yy_buffer_stack_top++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state( yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * @param yyscanner The scanner object. + */ +void yypop_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (!YY_CURRENT_BUFFER) + return; + + yy_delete_buffer(YY_CURRENT_BUFFER , yyscanner); + YY_CURRENT_BUFFER_LVALUE = NULL; + if (yyg->yy_buffer_stack_top > 0) + --yyg->yy_buffer_stack_top; + + if (YY_CURRENT_BUFFER) { + yy_load_buffer_state( yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void yyensure_buffer_stack (yyscan_t yyscanner) +{ + yy_size_t num_to_alloc; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (!yyg->yy_buffer_stack) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + yyg->yy_buffer_stack_max = num_to_alloc; + yyg->yy_buffer_stack_top = 0; + return; + } + + if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + yy_size_t grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = yyg->yy_buffer_stack_max + grow_size; + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc + (yyg->yy_buffer_stack, + num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); + yyg->yy_buffer_stack_max = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return NULL; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = NULL; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer( b , yyscanner ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to yylex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner) +{ + + return yy_scan_bytes( yystr, (int) strlen(yystr) , yyscanner); +} + +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param yybytes the byte buffer to scan + * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = (yy_size_t) (_yybytes_len + 2); + buf = (char *) yyalloc( n , yyscanner ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer( buf, n , yyscanner); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yynoreturn yy_fatal_error (const char* msg , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + yytext[yyleng] = yyg->yy_hold_char; \ + yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ + yyg->yy_hold_char = *yyg->yy_c_buf_p; \ + *yyg->yy_c_buf_p = '\0'; \ + yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the user-defined data for this scanner. + * @param yyscanner The scanner object. + */ +YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyextra; +} + +/** Get the current line number. + * @param yyscanner The scanner object. + */ +int yyget_lineno (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yylineno; +} + +/** Get the current column number. + * @param yyscanner The scanner object. + */ +int yyget_column (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yycolumn; +} + +/** Get the input stream. + * @param yyscanner The scanner object. + */ +FILE *yyget_in (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyin; +} + +/** Get the output stream. + * @param yyscanner The scanner object. + */ +FILE *yyget_out (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyout; +} + +/** Get the length of the current token. + * @param yyscanner The scanner object. + */ +int yyget_leng (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyleng; +} + +/** Get the current token. + * @param yyscanner The scanner object. + */ + +char *yyget_text (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yytext; +} + +/** Set the user-defined data. This data is never touched by the scanner. + * @param user_defined The data to be associated with this scanner. + * @param yyscanner The scanner object. + */ +void yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyextra = user_defined ; +} + +/** Set the current line number. + * @param _line_number line number + * @param yyscanner The scanner object. + */ +void yyset_lineno (int _line_number , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* lineno is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_lineno called with no buffer" ); + + yylineno = _line_number; +} + +/** Set the current column. + * @param _column_no column number + * @param yyscanner The scanner object. + */ +void yyset_column (int _column_no , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* column is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_column called with no buffer" ); + + yycolumn = _column_no; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param _in_str A readable stream. + * @param yyscanner The scanner object. + * @see yy_switch_to_buffer + */ +void yyset_in (FILE * _in_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyin = _in_str ; +} + +void yyset_out (FILE * _out_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyout = _out_str ; +} + +int yyget_debug (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yy_flex_debug; +} + +void yyset_debug (int _bdebug , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yy_flex_debug = _bdebug ; +} + +/* Accessor methods for yylval and yylloc */ + +YYSTYPE * yyget_lval (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylval; +} + +void yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylval = yylval_param; +} + +/* User-visible API */ + +/* yylex_init is special because it creates the scanner itself, so it is + * the ONLY reentrant function that doesn't take the scanner as the last argument. + * That's why we explicitly handle the declaration, instead of using our macros. + */ +int yylex_init(yyscan_t* ptr_yy_globals) +{ + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), NULL ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + return yy_init_globals ( *ptr_yy_globals ); +} + +/* yylex_init_extra has the same functionality as yylex_init, but follows the + * convention of taking the scanner as the last argument. Note however, that + * this is a *pointer* to a scanner, as it will be allocated by this call (and + * is the reason, too, why this function also must handle its own declaration). + * The user defined value in the first argument will be available to yyalloc in + * the yyextra field. + */ +int yylex_init_extra( YY_EXTRA_TYPE yy_user_defined, yyscan_t* ptr_yy_globals ) +{ + struct yyguts_t dummy_yyguts; + + yyset_extra (yy_user_defined, &dummy_yyguts); + + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in + yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + yyset_extra (yy_user_defined, *ptr_yy_globals); + + return yy_init_globals ( *ptr_yy_globals ); +} + +static int yy_init_globals (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from yylex_destroy(), so don't allocate here. + */ + + yyg->yy_buffer_stack = NULL; + yyg->yy_buffer_stack_top = 0; + yyg->yy_buffer_stack_max = 0; + yyg->yy_c_buf_p = NULL; + yyg->yy_init = 0; + yyg->yy_start = 0; + + yyg->yy_start_stack_ptr = 0; + yyg->yy_start_stack_depth = 0; + yyg->yy_start_stack = NULL; + +/* Defined in main.c */ +#ifdef YY_STDINIT + yyin = stdin; + yyout = stdout; +#else + yyin = NULL; + yyout = NULL; +#endif + + /* For future reference: Set errno on error, since we are called by + * yylex_init() + */ + return 0; +} + +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer( YY_CURRENT_BUFFER , yyscanner ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(yyscanner); + } + + /* Destroy the stack itself. */ + yyfree(yyg->yy_buffer_stack , yyscanner); + yyg->yy_buffer_stack = NULL; + + /* Destroy the start condition stack. */ + yyfree( yyg->yy_start_stack , yyscanner ); + yyg->yy_start_stack = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * yylex() is called, initialization will occur. */ + yy_init_globals( yyscanner); + + /* Destroy the main struct (reentrant only). */ + yyfree ( yyscanner , yyscanner ); + yyscanner = NULL; + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (const char * s , yyscan_t yyscanner) +{ + int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *yyalloc (yy_size_t size , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + return malloc(size); +} + +void *yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return realloc(ptr, size); +} + +void yyfree (void * ptr , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 161 "route/cls/ematch_grammar.l" diff --git a/libnl/lib/route/cls/ematch_grammar.h b/libnl/lib/route/cls/ematch_grammar.h new file mode 100644 index 0000000..c7ebc6f --- /dev/null +++ b/libnl/lib/route/cls/ematch_grammar.h @@ -0,0 +1,718 @@ +#ifndef ematch_HEADER_H +#define ematch_HEADER_H 1 +#define ematch_IN_HEADER 1 + +#line 6 "route/cls/ematch_grammar.h" + +#line 8 "route/cls/ematch_grammar.h" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yy_create_buffer +#define ematch__create_buffer_ALREADY_DEFINED +#else +#define yy_create_buffer ematch__create_buffer +#endif + +#ifdef yy_delete_buffer +#define ematch__delete_buffer_ALREADY_DEFINED +#else +#define yy_delete_buffer ematch__delete_buffer +#endif + +#ifdef yy_scan_buffer +#define ematch__scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer ematch__scan_buffer +#endif + +#ifdef yy_scan_string +#define ematch__scan_string_ALREADY_DEFINED +#else +#define yy_scan_string ematch__scan_string +#endif + +#ifdef yy_scan_bytes +#define ematch__scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes ematch__scan_bytes +#endif + +#ifdef yy_init_buffer +#define ematch__init_buffer_ALREADY_DEFINED +#else +#define yy_init_buffer ematch__init_buffer +#endif + +#ifdef yy_flush_buffer +#define ematch__flush_buffer_ALREADY_DEFINED +#else +#define yy_flush_buffer ematch__flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define ematch__load_buffer_state_ALREADY_DEFINED +#else +#define yy_load_buffer_state ematch__load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define ematch__switch_to_buffer_ALREADY_DEFINED +#else +#define yy_switch_to_buffer ematch__switch_to_buffer +#endif + +#ifdef yypush_buffer_state +#define ematch_push_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state ematch_push_buffer_state +#endif + +#ifdef yypop_buffer_state +#define ematch_pop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state ematch_pop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define ematch_ensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack ematch_ensure_buffer_stack +#endif + +#ifdef yylex +#define ematch_lex_ALREADY_DEFINED +#else +#define yylex ematch_lex +#endif + +#ifdef yyrestart +#define ematch_restart_ALREADY_DEFINED +#else +#define yyrestart ematch_restart +#endif + +#ifdef yylex_init +#define ematch_lex_init_ALREADY_DEFINED +#else +#define yylex_init ematch_lex_init +#endif + +#ifdef yylex_init_extra +#define ematch_lex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra ematch_lex_init_extra +#endif + +#ifdef yylex_destroy +#define ematch_lex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy ematch_lex_destroy +#endif + +#ifdef yyget_debug +#define ematch_get_debug_ALREADY_DEFINED +#else +#define yyget_debug ematch_get_debug +#endif + +#ifdef yyset_debug +#define ematch_set_debug_ALREADY_DEFINED +#else +#define yyset_debug ematch_set_debug +#endif + +#ifdef yyget_extra +#define ematch_get_extra_ALREADY_DEFINED +#else +#define yyget_extra ematch_get_extra +#endif + +#ifdef yyset_extra +#define ematch_set_extra_ALREADY_DEFINED +#else +#define yyset_extra ematch_set_extra +#endif + +#ifdef yyget_in +#define ematch_get_in_ALREADY_DEFINED +#else +#define yyget_in ematch_get_in +#endif + +#ifdef yyset_in +#define ematch_set_in_ALREADY_DEFINED +#else +#define yyset_in ematch_set_in +#endif + +#ifdef yyget_out +#define ematch_get_out_ALREADY_DEFINED +#else +#define yyget_out ematch_get_out +#endif + +#ifdef yyset_out +#define ematch_set_out_ALREADY_DEFINED +#else +#define yyset_out ematch_set_out +#endif + +#ifdef yyget_leng +#define ematch_get_leng_ALREADY_DEFINED +#else +#define yyget_leng ematch_get_leng +#endif + +#ifdef yyget_text +#define ematch_get_text_ALREADY_DEFINED +#else +#define yyget_text ematch_get_text +#endif + +#ifdef yyget_lineno +#define ematch_get_lineno_ALREADY_DEFINED +#else +#define yyget_lineno ematch_get_lineno +#endif + +#ifdef yyset_lineno +#define ematch_set_lineno_ALREADY_DEFINED +#else +#define yyset_lineno ematch_set_lineno +#endif + +#ifdef yyget_column +#define ematch_get_column_ALREADY_DEFINED +#else +#define yyget_column ematch_get_column +#endif + +#ifdef yyset_column +#define ematch_set_column_ALREADY_DEFINED +#else +#define yyset_column ematch_set_column +#endif + +#ifdef yywrap +#define ematch_wrap_ALREADY_DEFINED +#else +#define yywrap ematch_wrap +#endif + +#ifdef yyget_lval +#define ematch_get_lval_ALREADY_DEFINED +#else +#define yyget_lval ematch_get_lval +#endif + +#ifdef yyset_lval +#define ematch_set_lval_ALREADY_DEFINED +#else +#define yyset_lval ematch_set_lval +#endif + +#ifdef yyalloc +#define ematch_alloc_ALREADY_DEFINED +#else +#define yyalloc ematch_alloc +#endif + +#ifdef yyrealloc +#define ematch_realloc_ALREADY_DEFINED +#else +#define yyrealloc ematch_realloc +#endif + +#ifdef yyfree +#define ematch_free_ALREADY_DEFINED +#else +#define yyfree ematch_free +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +void yyrestart ( FILE *input_file , yyscan_t yyscanner ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); +void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +void yypop_buffer_state ( yyscan_t yyscanner ); + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); + +void *yyalloc ( yy_size_t , yyscan_t yyscanner ); +void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); +void yyfree ( void * , yyscan_t yyscanner ); + +#define ematch_wrap(yyscanner) (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP + +#define yytext_ptr yytext_r + +#ifdef YY_HEADER_EXPORT_START_CONDITIONS +#define INITIAL 0 +#define QUOTE 1 + +#endif + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +int yylex_init (yyscan_t* scanner); + +int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy ( yyscan_t yyscanner ); + +int yyget_debug ( yyscan_t yyscanner ); + +void yyset_debug ( int debug_flag , yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); + +FILE *yyget_in ( yyscan_t yyscanner ); + +void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); + +FILE *yyget_out ( yyscan_t yyscanner ); + +void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); + + int yyget_leng ( yyscan_t yyscanner ); + +char *yyget_text ( yyscan_t yyscanner ); + +int yyget_lineno ( yyscan_t yyscanner ); + +void yyset_lineno ( int _line_number , yyscan_t yyscanner ); + +int yyget_column ( yyscan_t yyscanner ); + +void yyset_column ( int _column_no , yyscan_t yyscanner ); + +YYSTYPE * yyget_lval ( yyscan_t yyscanner ); + +void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap ( yyscan_t yyscanner ); +#else +extern int yywrap ( yyscan_t yyscanner ); +#endif +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const char * , yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex \ + (YYSTYPE * yylval_param , yyscan_t yyscanner); + +#define YY_DECL int yylex \ + (YYSTYPE * yylval_param , yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + +#undef YY_NEW_FILE +#undef YY_FLUSH_BUFFER +#undef yy_set_bol +#undef yy_new_buffer +#undef yy_set_interactive +#undef YY_DO_BEFORE_ACTION + +#ifdef YY_DECL_IS_OURS +#undef YY_DECL_IS_OURS +#undef YY_DECL +#endif + +#ifndef ematch__create_buffer_ALREADY_DEFINED +#undef yy_create_buffer +#endif +#ifndef ematch__delete_buffer_ALREADY_DEFINED +#undef yy_delete_buffer +#endif +#ifndef ematch__scan_buffer_ALREADY_DEFINED +#undef yy_scan_buffer +#endif +#ifndef ematch__scan_string_ALREADY_DEFINED +#undef yy_scan_string +#endif +#ifndef ematch__scan_bytes_ALREADY_DEFINED +#undef yy_scan_bytes +#endif +#ifndef ematch__init_buffer_ALREADY_DEFINED +#undef yy_init_buffer +#endif +#ifndef ematch__flush_buffer_ALREADY_DEFINED +#undef yy_flush_buffer +#endif +#ifndef ematch__load_buffer_state_ALREADY_DEFINED +#undef yy_load_buffer_state +#endif +#ifndef ematch__switch_to_buffer_ALREADY_DEFINED +#undef yy_switch_to_buffer +#endif +#ifndef ematch_push_buffer_state_ALREADY_DEFINED +#undef yypush_buffer_state +#endif +#ifndef ematch_pop_buffer_state_ALREADY_DEFINED +#undef yypop_buffer_state +#endif +#ifndef ematch_ensure_buffer_stack_ALREADY_DEFINED +#undef yyensure_buffer_stack +#endif +#ifndef ematch_lex_ALREADY_DEFINED +#undef yylex +#endif +#ifndef ematch_restart_ALREADY_DEFINED +#undef yyrestart +#endif +#ifndef ematch_lex_init_ALREADY_DEFINED +#undef yylex_init +#endif +#ifndef ematch_lex_init_extra_ALREADY_DEFINED +#undef yylex_init_extra +#endif +#ifndef ematch_lex_destroy_ALREADY_DEFINED +#undef yylex_destroy +#endif +#ifndef ematch_get_debug_ALREADY_DEFINED +#undef yyget_debug +#endif +#ifndef ematch_set_debug_ALREADY_DEFINED +#undef yyset_debug +#endif +#ifndef ematch_get_extra_ALREADY_DEFINED +#undef yyget_extra +#endif +#ifndef ematch_set_extra_ALREADY_DEFINED +#undef yyset_extra +#endif +#ifndef ematch_get_in_ALREADY_DEFINED +#undef yyget_in +#endif +#ifndef ematch_set_in_ALREADY_DEFINED +#undef yyset_in +#endif +#ifndef ematch_get_out_ALREADY_DEFINED +#undef yyget_out +#endif +#ifndef ematch_set_out_ALREADY_DEFINED +#undef yyset_out +#endif +#ifndef ematch_get_leng_ALREADY_DEFINED +#undef yyget_leng +#endif +#ifndef ematch_get_text_ALREADY_DEFINED +#undef yyget_text +#endif +#ifndef ematch_get_lineno_ALREADY_DEFINED +#undef yyget_lineno +#endif +#ifndef ematch_set_lineno_ALREADY_DEFINED +#undef yyset_lineno +#endif +#ifndef ematch_get_column_ALREADY_DEFINED +#undef yyget_column +#endif +#ifndef ematch_set_column_ALREADY_DEFINED +#undef yyset_column +#endif +#ifndef ematch_wrap_ALREADY_DEFINED +#undef yywrap +#endif +#ifndef ematch_get_lval_ALREADY_DEFINED +#undef yyget_lval +#endif +#ifndef ematch_set_lval_ALREADY_DEFINED +#undef yyset_lval +#endif +#ifndef ematch_get_lloc_ALREADY_DEFINED +#undef yyget_lloc +#endif +#ifndef ematch_set_lloc_ALREADY_DEFINED +#undef yyset_lloc +#endif +#ifndef ematch_alloc_ALREADY_DEFINED +#undef yyalloc +#endif +#ifndef ematch_realloc_ALREADY_DEFINED +#undef yyrealloc +#endif +#ifndef ematch_free_ALREADY_DEFINED +#undef yyfree +#endif +#ifndef ematch_text_ALREADY_DEFINED +#undef yytext +#endif +#ifndef ematch_leng_ALREADY_DEFINED +#undef yyleng +#endif +#ifndef ematch_in_ALREADY_DEFINED +#undef yyin +#endif +#ifndef ematch_out_ALREADY_DEFINED +#undef yyout +#endif +#ifndef ematch__flex_debug_ALREADY_DEFINED +#undef yy_flex_debug +#endif +#ifndef ematch_lineno_ALREADY_DEFINED +#undef yylineno +#endif +#ifndef ematch_tables_fload_ALREADY_DEFINED +#undef yytables_fload +#endif +#ifndef ematch_tables_destroy_ALREADY_DEFINED +#undef yytables_destroy +#endif +#ifndef ematch_TABLES_NAME_ALREADY_DEFINED +#undef yyTABLES_NAME +#endif + +#line 161 "route/cls/ematch_grammar.l" + +#line 717 "route/cls/ematch_grammar.h" +#undef ematch_IN_HEADER +#endif /* ematch_HEADER_H */ diff --git a/libnl/lib/route/cls/ematch_grammar.l b/libnl/lib/route/cls/ematch_grammar.l new file mode 100644 index 0000000..4f57951 --- /dev/null +++ b/libnl/lib/route/cls/ematch_grammar.l @@ -0,0 +1,160 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010-2013 Thomas Graf + */ + +%{ + #include + #include + #include + #include + #include + #include + #include "ematch_syntax.h" + + int ematch_get_column(yyscan_t); + void ematch_set_column(int, yyscan_t); +%} + +%option 8bit +%option reentrant +%option warn +%option noyywrap +%option noinput +%option nounput +%option bison-bridge +%option prefix="ematch_" + +%x QUOTE + +%% + +[ \t\r\n]+ + +\" { + NL_DBG(4, "Beginning of quote\n"); + yylval->q.len = 32; + if (!(yylval->q.data = calloc(1, yylval->q.len))) + return ERROR; + + yylval->q.index = 0; + BEGIN(QUOTE); + } + +[^\\\n\"]+ { + memcpy(yylval->q.data + yylval->q.index, yytext, + strlen(yytext)); + yylval->q.index += strlen(yytext); + } + +\" { + BEGIN(0); + return QUOTED; + } + + +[[:digit:]]+ | +0[xX][[:xdigit:]]+ { + yylval->i = strtoul(yytext, NULL, 0); + return NUMBER; + } + +eq | +"=" return KW_EQ; +gt | +">" return KW_GT; +lt | +"<" return KW_LT; + +[aA][nN][dD] | +"&&" { yylval->i = TCF_EM_REL_AND; return LOGIC; } +[oO][rR] | +"||" { yylval->i = TCF_EM_REL_OR; return LOGIC; } +[nN][oO][tT] | +"!" return NOT; + +[cC][mM][pP] { yylval->i = TCF_EM_CMP; return EMATCH_CMP; } +[pP][aA][tT][tT][eE][rR][nN] { yylval->i = TCF_EM_NBYTE; return EMATCH_NBYTE; } +[tT][eE][xX][tT] { yylval->i = TCF_EM_TEXT; return EMATCH_TEXT; } +[mM][eE][tT][aA] { yylval->i = TCF_EM_META; return EMATCH_META; } + +"(" return KW_OPEN; +")" return KW_CLOSE; +[mM][aA][sS][kK] | +"&" return KW_MASK; +[sS][hH][iI][fF][tT] | +">>" return KW_SHIFT; +[aA][tT] return KW_AT; +"+" return KW_PLUS; +[fF][rR][oO][mM] return KW_FROM; +[tT][oO] return KW_TO; + +[uU]8 { yylval->i = TCF_EM_ALIGN_U8; return ALIGN; } +[uU]16 { yylval->i = TCF_EM_ALIGN_U16; return ALIGN; } +[uU]32 { yylval->i = TCF_EM_ALIGN_U32; return ALIGN; } + +[lL][iI][nN][kK] | +[eE][tT][hH] { yylval->i = TCF_LAYER_LINK; return LAYER; } +[nN][eE][tT] | +[iI][pP]6 | +[iI][pP] { yylval->i = TCF_LAYER_NETWORK; return LAYER; } +[tT][rR][aA][nN][sS][pP][oO][rR][tT] | +[tT][cC][pP] { yylval->i = TCF_LAYER_TRANSPORT; return LAYER; } + +random return META_RANDOM; +loadavg_0 return META_LOADAVG_0; +loadavg_1 return META_LOADAVG_1; +loadavg_2 return META_LOADAVG_2; +dev return META_DEV; +prio return META_PRIO; +proto return META_PROTO; +pkttype return META_PKTTYPE; +pktlen return META_PKTLEN; +datalen return META_DATALEN; +maclen return META_MACLEN; +mark return META_MARK; +tcindex return META_TCINDEX; +rtclassid return META_RTCLASSID; +rtiif return META_RTIIF; +sk_family return META_SK_FAMILY; +sk_state return META_SK_STATE; +sk_reuse return META_SK_REUSE; +sk_refcnt return META_SK_REFCNT; +sk_rcvbuf return META_SK_RCVBUF; +sk_sndbuf return META_SK_SNDBUF; +sk_shutdown return META_SK_SHUTDOWN; +sk_proto return META_SK_PROTO; +sk_type return META_SK_TYPE; +sk_rmem_alloc return META_SK_RMEM_ALLOC; +sk_wmem_alloc return META_SK_WMEM_ALLOC; +sk_wmem_queued return META_SK_WMEM_QUEUED; +sk_rcv_qlen return META_SK_RCV_QLEN; +sk_snd_qlen return META_SK_SND_QLEN; +sk_err_qlen return META_SK_ERR_QLEN; +sk_forward_allocs return META_SK_FORWARD_ALLOCS; +sk_allocs return META_SK_ALLOCS; +sk_route_caps return META_SK_ROUTE_CAPS; +sk_hash return META_SK_HASH; +sk_lingertime return META_SK_LINGERTIME; +sk_ack_backlog return META_SK_ACK_BACKLOG; +sk_max_ack_backlog return META_SK_MAX_ACK_BACKLOG; +sk_prio return META_SK_PRIO; +sk_rcvlowat return META_SK_RCVLOWAT; +sk_rcvtimeo return META_SK_RCVTIMEO; +sk_sndtimeo return META_SK_SNDTIMEO; +sk_sendmsg_off return META_SK_SENDMSG_OFF; +sk_write_pending return META_SK_WRITE_PENDING; +vlan return META_VLAN; +rxhash return META_RXHASH; + +devname return META_DEVNAME; +sk_bound_if return META_SK_BOUND_IF; + + +[^ \t\r\n+()=<>&|\"]+ { + yylval->s = strdup(yytext); + if (yylval->s == NULL) + return ERROR; + NL_DBG(4, "lex STR=%s\n", yylval->s); + return STR; + } diff --git a/libnl/lib/route/cls/ematch_syntax.c b/libnl/lib/route/cls/ematch_syntax.c new file mode 100644 index 0000000..524bde6 --- /dev/null +++ b/libnl/lib/route/cls/ematch_syntax.c @@ -0,0 +1,2540 @@ +/* A Bison parser, made by GNU Bison 3.5.1. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Undocumented macros, especially those whose name start with YY_, + are private implementation details. Do not rely on them. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "3.5.1" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + +/* Substitute the variable and function names. */ +#define yyparse ematch_parse +#define yylex ematch_lex +#define yyerror ematch_error +#define yydebug ematch_debug +#define yynerrs ematch_nerrs + +/* First part of user prologue. */ +#line 6 "route/cls/ematch_syntax.y" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define META_ALLOC rtnl_meta_value_alloc_id +#define META_ID(name) TCF_META_ID_##name +#define META_INT TCF_META_TYPE_INT +#define META_VAR TCF_META_TYPE_VAR + +#line 95 "route/cls/ematch_syntax.c" + +# ifndef YY_CAST +# ifdef __cplusplus +# define YY_CAST(Type, Val) static_cast (Val) +# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast (Val) +# else +# define YY_CAST(Type, Val) ((Type) (Val)) +# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) +# endif +# endif +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 1 +#endif + +/* Use api.header.include to #include this header + instead of duplicating it here. */ +#ifndef YY_EMATCH_ROUTE_CLS_EMATCH_SYNTAX_H_INCLUDED +# define YY_EMATCH_ROUTE_CLS_EMATCH_SYNTAX_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int ematch_debug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + ERROR = 258, + LOGIC = 259, + NOT = 260, + OPERAND = 261, + NUMBER = 262, + ALIGN = 263, + LAYER = 264, + KW_OPEN = 265, + KW_CLOSE = 266, + KW_PLUS = 267, + KW_MASK = 268, + KW_SHIFT = 269, + KW_AT = 270, + EMATCH_CMP = 271, + EMATCH_NBYTE = 272, + EMATCH_TEXT = 273, + EMATCH_META = 274, + KW_EQ = 275, + KW_GT = 276, + KW_LT = 277, + KW_FROM = 278, + KW_TO = 279, + META_RANDOM = 280, + META_LOADAVG_0 = 281, + META_LOADAVG_1 = 282, + META_LOADAVG_2 = 283, + META_DEV = 284, + META_PRIO = 285, + META_PROTO = 286, + META_PKTTYPE = 287, + META_PKTLEN = 288, + META_DATALEN = 289, + META_MACLEN = 290, + META_MARK = 291, + META_TCINDEX = 292, + META_RTCLASSID = 293, + META_RTIIF = 294, + META_SK_FAMILY = 295, + META_SK_STATE = 296, + META_SK_REUSE = 297, + META_SK_REFCNT = 298, + META_SK_RCVBUF = 299, + META_SK_SNDBUF = 300, + META_SK_SHUTDOWN = 301, + META_SK_PROTO = 302, + META_SK_TYPE = 303, + META_SK_RMEM_ALLOC = 304, + META_SK_WMEM_ALLOC = 305, + META_SK_WMEM_QUEUED = 306, + META_SK_RCV_QLEN = 307, + META_SK_SND_QLEN = 308, + META_SK_ERR_QLEN = 309, + META_SK_FORWARD_ALLOCS = 310, + META_SK_ALLOCS = 311, + META_SK_ROUTE_CAPS = 312, + META_SK_HASH = 313, + META_SK_LINGERTIME = 314, + META_SK_ACK_BACKLOG = 315, + META_SK_MAX_ACK_BACKLOG = 316, + META_SK_PRIO = 317, + META_SK_RCVLOWAT = 318, + META_SK_RCVTIMEO = 319, + META_SK_SNDTIMEO = 320, + META_SK_SENDMSG_OFF = 321, + META_SK_WRITE_PENDING = 322, + META_VLAN = 323, + META_RXHASH = 324, + META_DEVNAME = 325, + META_SK_BOUND_IF = 326, + STR = 327, + QUOTED = 328 + }; +#endif +/* Tokens. */ +#define ERROR 258 +#define LOGIC 259 +#define NOT 260 +#define OPERAND 261 +#define NUMBER 262 +#define ALIGN 263 +#define LAYER 264 +#define KW_OPEN 265 +#define KW_CLOSE 266 +#define KW_PLUS 267 +#define KW_MASK 268 +#define KW_SHIFT 269 +#define KW_AT 270 +#define EMATCH_CMP 271 +#define EMATCH_NBYTE 272 +#define EMATCH_TEXT 273 +#define EMATCH_META 274 +#define KW_EQ 275 +#define KW_GT 276 +#define KW_LT 277 +#define KW_FROM 278 +#define KW_TO 279 +#define META_RANDOM 280 +#define META_LOADAVG_0 281 +#define META_LOADAVG_1 282 +#define META_LOADAVG_2 283 +#define META_DEV 284 +#define META_PRIO 285 +#define META_PROTO 286 +#define META_PKTTYPE 287 +#define META_PKTLEN 288 +#define META_DATALEN 289 +#define META_MACLEN 290 +#define META_MARK 291 +#define META_TCINDEX 292 +#define META_RTCLASSID 293 +#define META_RTIIF 294 +#define META_SK_FAMILY 295 +#define META_SK_STATE 296 +#define META_SK_REUSE 297 +#define META_SK_REFCNT 298 +#define META_SK_RCVBUF 299 +#define META_SK_SNDBUF 300 +#define META_SK_SHUTDOWN 301 +#define META_SK_PROTO 302 +#define META_SK_TYPE 303 +#define META_SK_RMEM_ALLOC 304 +#define META_SK_WMEM_ALLOC 305 +#define META_SK_WMEM_QUEUED 306 +#define META_SK_RCV_QLEN 307 +#define META_SK_SND_QLEN 308 +#define META_SK_ERR_QLEN 309 +#define META_SK_FORWARD_ALLOCS 310 +#define META_SK_ALLOCS 311 +#define META_SK_ROUTE_CAPS 312 +#define META_SK_HASH 313 +#define META_SK_LINGERTIME 314 +#define META_SK_ACK_BACKLOG 315 +#define META_SK_MAX_ACK_BACKLOG 316 +#define META_SK_PRIO 317 +#define META_SK_RCVLOWAT 318 +#define META_SK_RCVTIMEO 319 +#define META_SK_SNDTIMEO 320 +#define META_SK_SENDMSG_OFF 321 +#define META_SK_WRITE_PENDING 322 +#define META_VLAN 323 +#define META_RXHASH 324 +#define META_DEVNAME 325 +#define META_SK_BOUND_IF 326 +#define STR 327 +#define QUOTED 328 + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 35 "route/cls/ematch_syntax.y" + + struct tcf_em_cmp cmp; + struct ematch_quoted q; + struct rtnl_ematch * e; + struct rtnl_pktloc * loc; + struct rtnl_meta_value *mv; + uint32_t i; + uint64_t i64; + char * s; + +#line 304 "route/cls/ematch_syntax.c" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + + +int ematch_parse (void *scanner, char **errp, struct nl_list_head *root); + +#endif /* !YY_EMATCH_ROUTE_CLS_EMATCH_SYNTAX_H_INCLUDED */ + +/* Second part of user prologue. */ +#line 46 "route/cls/ematch_syntax.y" + +extern int ematch_lex(YYSTYPE *, void *); + +static void yyerror(void *scanner, char **errp, struct nl_list_head *root, const char *msg) +{ + if (msg) + *errp = strdup(msg); + else + *errp = NULL; +} + +#line 331 "route/cls/ematch_syntax.c" + + +#ifdef short +# undef short +#endif + +/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure + and (if available) are included + so that the code can choose integer types of a good width. */ + +#ifndef __PTRDIFF_MAX__ +# include /* INFRINGES ON USER NAME SPACE */ +# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_STDINT_H +# endif +#endif + +/* Narrow types that promote to a signed type and that can represent a + signed or unsigned integer of at least N bits. In tables they can + save space and decrease cache pressure. Promoting to a signed type + helps avoid bugs in integer arithmetic. */ + +#ifdef __INT_LEAST8_MAX__ +typedef __INT_LEAST8_TYPE__ yytype_int8; +#elif defined YY_STDINT_H +typedef int_least8_t yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef __INT_LEAST16_MAX__ +typedef __INT_LEAST16_TYPE__ yytype_int16; +#elif defined YY_STDINT_H +typedef int_least16_t yytype_int16; +#else +typedef short yytype_int16; +#endif + +#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST8_TYPE__ yytype_uint8; +#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST8_MAX <= INT_MAX) +typedef uint_least8_t yytype_uint8; +#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX +typedef unsigned char yytype_uint8; +#else +typedef short yytype_uint8; +#endif + +#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST16_TYPE__ yytype_uint16; +#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST16_MAX <= INT_MAX) +typedef uint_least16_t yytype_uint16; +#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX +typedef unsigned short yytype_uint16; +#else +typedef int yytype_uint16; +#endif + +#ifndef YYPTRDIFF_T +# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ +# define YYPTRDIFF_T __PTRDIFF_TYPE__ +# define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ +# elif defined PTRDIFF_MAX +# ifndef ptrdiff_t +# include /* INFRINGES ON USER NAME SPACE */ +# endif +# define YYPTRDIFF_T ptrdiff_t +# define YYPTRDIFF_MAXIMUM PTRDIFF_MAX +# else +# define YYPTRDIFF_T long +# define YYPTRDIFF_MAXIMUM LONG_MAX +# endif +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned +# endif +#endif + +#define YYSIZE_MAXIMUM \ + YY_CAST (YYPTRDIFF_T, \ + (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ + ? YYPTRDIFF_MAXIMUM \ + : YY_CAST (YYSIZE_T, -1))) + +#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) + +/* Stored state numbers (used for stacks). */ +typedef yytype_int8 yy_state_t; + +/* State numbers in computations. */ +typedef int yy_state_fast_t; + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + +#ifndef YY_ATTRIBUTE_PURE +# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# else +# define YY_ATTRIBUTE_PURE +# endif +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# else +# define YY_ATTRIBUTE_UNUSED +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(E) ((void) (E)) +#else +# define YYUSE(E) /* empty */ +#endif + +#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ +# define YY_IGNORE_USELESS_CAST_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") +# define YY_IGNORE_USELESS_CAST_END \ + _Pragma ("GCC diagnostic pop") +#endif +#ifndef YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_END +#endif + + +#define YY_ASSERT(E) ((void) (0 && (E))) + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yy_state_t yyss_alloc; + YYSTYPE yyvs_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYPTRDIFF_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / YYSIZEOF (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYPTRDIFF_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 26 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 138 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 74 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 18 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 84 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 118 + +#define YYUNDEFTOK 2 +#define YYMAXUTOK 328 + + +/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, with out-of-bounds checking. */ +#define YYTRANSLATE(YYX) \ + (0 <= (YYX) && (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex. */ +static const yytype_int8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73 +}; + +#if YYDEBUG + /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_int16 yyrline[] = +{ + 0, 144, 144, 146, 153, 157, 169, 174, 182, 197, + 215, 242, 261, 289, 291, 296, 317, 318, 324, 325, + 330, 332, 334, 336, 341, 342, 343, 344, 345, 346, + 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, + 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, + 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, + 377, 378, 379, 380, 381, 382, 383, 384, 385, 389, + 390, 397, 401, 430, 443, 469, 470, 472, 478, 479, + 485, 486, 491, 493, 495 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || 1 +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "ERROR", "LOGIC", "NOT", "OPERAND", + "NUMBER", "ALIGN", "LAYER", "\"(\"", "\")\"", "\"+\"", "\"mask\"", + "\">>\"", "\"at\"", "\"cmp\"", "\"pattern\"", "\"text\"", "\"meta\"", + "\"=\"", "\">\"", "\"<\"", "\"from\"", "\"to\"", "\"random\"", + "\"loadavg_0\"", "\"loadavg_1\"", "\"loadavg_2\"", "\"dev\"", "\"prio\"", + "\"proto\"", "\"pkttype\"", "\"pktlen\"", "\"datalen\"", "\"maclen\"", + "\"mark\"", "\"tcindex\"", "\"rtclassid\"", "\"rtiif\"", "\"sk_family\"", + "\"sk_state\"", "\"sk_reuse\"", "\"sk_refcnt\"", "\"sk_rcvbuf\"", + "\"sk_sndbuf\"", "\"sk_shutdown\"", "\"sk_proto\"", "\"sk_type\"", + "\"sk_rmem_alloc\"", "\"sk_wmem_alloc\"", "\"sk_wmem_queued\"", + "\"sk_rcv_qlen\"", "\"sk_snd_qlen\"", "\"sk_err_qlen\"", + "\"sk_forward_allocs\"", "\"sk_allocs\"", "\"sk_route_caps\"", + "\"sk_hash\"", "\"sk_lingertime\"", "\"sk_ack_backlog\"", + "\"sk_max_ack_backlog\"", "\"sk_prio\"", "\"sk_rcvlowat\"", + "\"sk_rcvtimeo\"", "\"sk_sndtimeo\"", "\"sk_sendmsg_off\"", + "\"sk_write_pending\"", "\"vlan\"", "\"rxhash\"", "\"devname\"", + "\"sk_bound_if\"", "STR", "QUOTED", "$accept", "input", "expr", "match", + "ematch", "cmp_match", "cmp_expr", "text_from", "text_to", "meta_value", + "meta_int_id", "meta_var_id", "pattern", "pktloc", "align", "mask", + "shift", "operand", YY_NULLPTR +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[NUM] -- (External) token number corresponding to the + (internal) symbol number NUM (which must be that of a token). */ +static const yytype_int16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, + 325, 326, 327, 328 +}; +# endif + +#define YYPACT_NINF (-63) + +#define yypact_value_is_default(Yyn) \ + ((Yyn) == YYPACT_NINF) + +#define YYTABLE_NINF (-76) + +#define yytable_value_is_error(Yyn) \ + 0 + + /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int8 yypact[] = +{ + -4, 15, -13, -8, 11, 10, 14, 25, 29, -63, + 26, -63, 37, -63, -63, -63, 16, 33, -63, -63, + -63, 32, 1, 1, -28, 65, -63, 11, -63, -63, + -63, 38, 34, -63, 36, 28, -24, -63, -63, -63, + -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, + -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, + -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, + -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, + -63, -63, -63, -63, -63, -63, 16, 39, 39, -63, + -63, 43, -63, -62, 31, 65, 44, 42, -63, 42, + -63, -63, 41, 1, 35, 45, -63, 50, -63, -63, + -63, -63, 1, 47, -63, -63, -63, -63 +}; + + /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_int8 yydefact[] = +{ + 2, 75, 0, 0, 75, 0, 0, 0, 0, 73, + 0, 3, 4, 7, 8, 14, 0, 0, 6, 77, + 76, 0, 75, 75, 0, 0, 1, 75, 82, 83, + 84, 0, 0, 12, 0, 0, 0, 21, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, + 66, 67, 68, 69, 70, 20, 0, 80, 80, 5, + 15, 0, 13, 0, 16, 0, 0, 78, 23, 78, + 72, 71, 0, 75, 18, 0, 81, 0, 22, 74, + 9, 17, 75, 0, 11, 79, 19, 10 +}; + + /* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -63, -63, 13, -63, 59, -63, 40, -63, -63, -34, + -63, -63, -63, -23, -63, -36, -22, -21 +}; + + /* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + -1, 10, 11, 12, 13, 14, 15, 104, 113, 86, + 87, 88, 102, 16, 17, 108, 97, 31 +}; + + /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_int8 yytable[] = +{ + 35, 1, 19, 2, 3, -75, 4, 20, 2, 3, + 100, 101, 5, 6, 7, 8, 1, 21, 2, 3, + 22, 4, 2, 3, 23, 4, 26, 5, 6, 7, + 8, 5, 6, 7, 8, 24, 28, 29, 30, 25, + 89, 27, 32, 33, 36, 90, 91, 92, 93, 94, + 99, 106, 110, 96, 103, 107, 114, 115, 117, 112, + 18, 105, 34, 109, 0, 95, 98, 0, 9, 0, + 0, 0, 37, 9, 0, 0, 0, 0, 0, 0, + 111, 0, 0, 9, 0, 0, 0, 9, 0, 116, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 78, 79, 80, 81, 82, 83, 84, 0, 85 +}; + +static const yytype_int8 yycheck[] = +{ + 23, 5, 15, 7, 8, 9, 10, 15, 7, 8, + 72, 73, 16, 17, 18, 19, 5, 4, 7, 8, + 10, 10, 7, 8, 10, 10, 0, 16, 17, 18, + 19, 16, 17, 18, 19, 10, 20, 21, 22, 10, + 27, 4, 9, 11, 72, 7, 12, 11, 20, 73, + 7, 7, 11, 14, 23, 13, 11, 7, 11, 24, + 1, 95, 22, 99, -1, 86, 88, -1, 72, -1, + -1, -1, 7, 72, -1, -1, -1, -1, -1, -1, + 103, -1, -1, 72, -1, -1, -1, 72, -1, 112, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, -1, 73 +}; + + /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_int8 yystos[] = +{ + 0, 5, 7, 8, 10, 16, 17, 18, 19, 72, + 75, 76, 77, 78, 79, 80, 87, 88, 78, 15, + 15, 76, 10, 10, 10, 10, 0, 4, 20, 21, + 22, 91, 9, 11, 80, 87, 72, 7, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71, 73, 83, 84, 85, 76, + 7, 12, 11, 20, 73, 91, 14, 90, 90, 7, + 72, 73, 86, 23, 81, 83, 7, 13, 89, 89, + 11, 87, 24, 82, 11, 7, 87, 11 +}; + + /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_int8 yyr1[] = +{ + 0, 74, 75, 75, 76, 76, 77, 77, 78, 78, + 78, 78, 78, 79, 79, 80, 81, 81, 82, 82, + 83, 83, 83, 83, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 85, + 85, 86, 86, 87, 87, 88, 88, 88, 89, 89, + 90, 90, 91, 91, 91 +}; + + /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ +static const yytype_int8 yyr2[] = +{ + 0, 2, 0, 1, 1, 3, 2, 1, 1, 6, + 7, 6, 3, 4, 1, 3, 0, 2, 0, 2, + 1, 1, 3, 2, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 5, 0, 2, 2, 0, 2, + 0, 2, 1, 1, 1 +}; + + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ + do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (scanner, errp, root, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ + while (0) + +/* Error token number */ +#define YYTERROR 1 +#define YYERRCODE 256 + + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + +/* This macro is provided for backward compatibility. */ +#ifndef YY_LOCATION_PRINT +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +#endif + + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value, scanner, errp, root); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*-----------------------------------. +| Print this symbol's value on YYO. | +`-----------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, void *scanner, char **errp, struct nl_list_head *root) +{ + FILE *yyoutput = yyo; + YYUSE (yyoutput); + YYUSE (scanner); + YYUSE (errp); + YYUSE (root); + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyo, yytoknum[yytype], *yyvaluep); +# endif + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yytype); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/*---------------------------. +| Print this symbol on YYO. | +`---------------------------*/ + +static void +yy_symbol_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, void *scanner, char **errp, struct nl_list_head *root) +{ + YYFPRINTF (yyo, "%s %s (", + yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); + + yy_symbol_value_print (yyo, yytype, yyvaluep, scanner, errp, root); + YYFPRINTF (yyo, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, int yyrule, void *scanner, char **errp, struct nl_list_head *root) +{ + int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + yystos[+yyssp[yyi + 1 - yynrhs]], + &yyvsp[(yyi + 1) - (yynrhs)] + , scanner, errp, root); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, Rule, scanner, errp, root); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen(S) (YY_CAST (YYPTRDIFF_T, strlen (S))) +# else +/* Return the length of YYSTR. */ +static YYPTRDIFF_T +yystrlen (const char *yystr) +{ + YYPTRDIFF_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +yystpcpy (char *yydest, const char *yysrc) +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYPTRDIFF_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYPTRDIFF_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + else + goto append; + + append: + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (yyres) + return yystpcpy (yyres, yystr) - yyres; + else + return yystrlen (yystr); +} +# endif + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYPTRDIFF_T *yymsg_alloc, char **yymsg, + yy_state_t *yyssp, int yytoken) +{ + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULLPTR; + /* Arguments of yyformat: reported tokens (one for the "unexpected", + one per "expected"). */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Actual size of YYARG. */ + int yycount = 0; + /* Cumulated lengths of YYARG. */ + YYPTRDIFF_T yysize = 0; + + /* There are many possibilities here to consider: + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[+*yyssp]; + YYPTRDIFF_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]); + yysize = yysize0; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + { + YYPTRDIFF_T yysize1 + = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]); + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return 2; + } + } + } + } + + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + default: /* Avoid compiler warnings. */ + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } + + { + /* Don't count the "%s"s in the final size, but reserve room for + the terminator. */ + YYPTRDIFF_T yysize1 = yysize + (yystrlen (yyformat) - 2 * yycount) + 1; + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return 2; + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; + } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + ++yyp; + ++yyformat; + } + } + return 0; +} +#endif /* YYERROR_VERBOSE */ + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, void *scanner, char **errp, struct nl_list_head *root) +{ + YYUSE (yyvaluep); + YYUSE (scanner); + YYUSE (errp); + YYUSE (root); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + switch (yytype) + { + case 72: /* STR */ +#line 135 "route/cls/ematch_syntax.y" + { free(((*yyvaluep).s)); NL_DBG(2, "string destructor\n"); } +#line 1342 "route/cls/ematch_syntax.c" + break; + + case 73: /* QUOTED */ +#line 137 "route/cls/ematch_syntax.y" + { free(((*yyvaluep).q).data); NL_DBG(2, "quoted destructor\n"); } +#line 1348 "route/cls/ematch_syntax.c" + break; + + case 81: /* text_from */ +#line 136 "route/cls/ematch_syntax.y" + { rtnl_pktloc_put(((*yyvaluep).loc)); NL_DBG(2, "pktloc destructor\n"); } +#line 1354 "route/cls/ematch_syntax.c" + break; + + case 82: /* text_to */ +#line 136 "route/cls/ematch_syntax.y" + { rtnl_pktloc_put(((*yyvaluep).loc)); NL_DBG(2, "pktloc destructor\n"); } +#line 1360 "route/cls/ematch_syntax.c" + break; + + case 83: /* meta_value */ +#line 138 "route/cls/ematch_syntax.y" + { rtnl_meta_value_put(((*yyvaluep).mv)); NL_DBG(2, "meta value destructor\n"); } +#line 1366 "route/cls/ematch_syntax.c" + break; + + case 86: /* pattern */ +#line 137 "route/cls/ematch_syntax.y" + { free(((*yyvaluep).q).data); NL_DBG(2, "quoted destructor\n"); } +#line 1372 "route/cls/ematch_syntax.c" + break; + + case 87: /* pktloc */ +#line 136 "route/cls/ematch_syntax.y" + { rtnl_pktloc_put(((*yyvaluep).loc)); NL_DBG(2, "pktloc destructor\n"); } +#line 1378 "route/cls/ematch_syntax.c" + break; + + default: + break; + } + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (void *scanner, char **errp, struct nl_list_head *root) +{ +/* The lookahead symbol. */ +int yychar; + + +/* The semantic value of the lookahead symbol. */ +/* Default value used for initialization, for pacifying older GCCs + or non-GCC compilers. */ +YY_INITIAL_VALUE (static YYSTYPE yyval_default;) +YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); + + /* Number of syntax errors so far. */ + int yynerrs; + + yy_state_fast_t yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + 'yyss': related to states. + 'yyvs': related to semantic values. + + Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yy_state_t yyssa[YYINITDEPTH]; + yy_state_t *yyss; + yy_state_t *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + YYPTRDIFF_T yystacksize; + + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken = 0; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYPTRDIFF_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + yyssp = yyss = yyssa; + yyvsp = yyvs = yyvsa; + yystacksize = YYINITDEPTH; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + goto yysetstate; + + +/*------------------------------------------------------------. +| yynewstate -- push a new state, which is found in yystate. | +`------------------------------------------------------------*/ +yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + +/*--------------------------------------------------------------------. +| yysetstate -- set current state (the top of the stack) to yystate. | +`--------------------------------------------------------------------*/ +yysetstate: + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + YY_ASSERT (0 <= yystate && yystate < YYNSTATES); + YY_IGNORE_USELESS_CAST_BEGIN + *yyssp = YY_CAST (yy_state_t, yystate); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) +#if !defined yyoverflow && !defined YYSTACK_RELOCATE + goto yyexhaustedlab; +#else + { + /* Get the current used size of the three stacks, in elements. */ + YYPTRDIFF_T yysize = yyssp - yyss + 1; + +# if defined yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + yy_state_t *yyss1 = yyss; + YYSTYPE *yyvs1 = yyvs; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * YYSIZEOF (*yyssp), + &yyvs1, yysize * YYSIZEOF (*yyvsp), + &yystacksize); + yyss = yyss1; + yyvs = yyvs1; + } +# else /* defined YYSTACK_RELOCATE */ + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yy_state_t *yyss1 = yyss; + union yyalloc *yyptr = + YY_CAST (union yyalloc *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + YY_IGNORE_USELESS_CAST_BEGIN + YYDPRINTF ((stderr, "Stack size increased to %ld\n", + YY_CAST (long, yystacksize))); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } +#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = yylex (&yylval, scanner); + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + /* Discard the shifted token. */ + yychar = YYEMPTY; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + '$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 3: +#line 147 "route/cls/ematch_syntax.y" + { + nl_list_add_tail(root, &(yyvsp[0].e)->e_list); + } +#line 1654 "route/cls/ematch_syntax.c" + break; + + case 4: +#line 154 "route/cls/ematch_syntax.y" + { + (yyval.e) = (yyvsp[0].e); + } +#line 1662 "route/cls/ematch_syntax.c" + break; + + case 5: +#line 158 "route/cls/ematch_syntax.y" + { + rtnl_ematch_set_flags((yyvsp[-2].e), (yyvsp[-1].i)); + + /* make ematch new head */ + nl_list_add_tail(&(yyvsp[-2].e)->e_list, &(yyvsp[0].e)->e_list); + + (yyval.e) = (yyvsp[-2].e); + } +#line 1675 "route/cls/ematch_syntax.c" + break; + + case 6: +#line 170 "route/cls/ematch_syntax.y" + { + rtnl_ematch_set_flags((yyvsp[0].e), TCF_EM_INVERT); + (yyval.e) = (yyvsp[0].e); + } +#line 1684 "route/cls/ematch_syntax.c" + break; + + case 7: +#line 175 "route/cls/ematch_syntax.y" + { + (yyval.e) = (yyvsp[0].e); + } +#line 1692 "route/cls/ematch_syntax.c" + break; + + case 8: +#line 183 "route/cls/ematch_syntax.y" + { + struct rtnl_ematch *e; + + if (!(e = rtnl_ematch_alloc())) { + *errp = strdup("Unable to allocate ematch object"); + YYABORT; + } + + if (rtnl_ematch_set_kind(e, TCF_EM_CMP) < 0) + BUG(); + + rtnl_ematch_cmp_set(e, &(yyvsp[0].cmp)); + (yyval.e) = e; + } +#line 1711 "route/cls/ematch_syntax.c" + break; + + case 9: +#line 198 "route/cls/ematch_syntax.y" + { + struct rtnl_ematch *e; + + if (!(e = rtnl_ematch_alloc())) { + *errp = strdup("Unable to allocate ematch object"); + YYABORT; + } + + if (rtnl_ematch_set_kind(e, TCF_EM_NBYTE) < 0) + BUG(); + + rtnl_ematch_nbyte_set_offset(e, (yyvsp[-3].loc)->layer, (yyvsp[-3].loc)->offset); + rtnl_pktloc_put((yyvsp[-3].loc)); + rtnl_ematch_nbyte_set_pattern(e, (uint8_t *) (yyvsp[-1].q).data, (yyvsp[-1].q).index); + + (yyval.e) = e; + } +#line 1733 "route/cls/ematch_syntax.c" + break; + + case 10: +#line 216 "route/cls/ematch_syntax.y" + { + struct rtnl_ematch *e; + + if (!(e = rtnl_ematch_alloc())) { + *errp = strdup("Unable to allocate ematch object"); + YYABORT; + } + + if (rtnl_ematch_set_kind(e, TCF_EM_TEXT) < 0) + BUG(); + + rtnl_ematch_text_set_algo(e, (yyvsp[-4].s)); + rtnl_ematch_text_set_pattern(e, (yyvsp[-3].q).data, (yyvsp[-3].q).index); + + if ((yyvsp[-2].loc)) { + rtnl_ematch_text_set_from(e, (yyvsp[-2].loc)->layer, (yyvsp[-2].loc)->offset); + rtnl_pktloc_put((yyvsp[-2].loc)); + } + + if ((yyvsp[-1].loc)) { + rtnl_ematch_text_set_to(e, (yyvsp[-1].loc)->layer, (yyvsp[-1].loc)->offset); + rtnl_pktloc_put((yyvsp[-1].loc)); + } + + (yyval.e) = e; + } +#line 1764 "route/cls/ematch_syntax.c" + break; + + case 11: +#line 243 "route/cls/ematch_syntax.y" + { + struct rtnl_ematch *e; + + if (!(e = rtnl_ematch_alloc())) { + *errp = strdup("Unable to allocate ematch object"); + YYABORT; + } + + if (rtnl_ematch_set_kind(e, TCF_EM_META) < 0) + BUG(); + + rtnl_ematch_meta_set_lvalue(e, (yyvsp[-3].mv)); + rtnl_ematch_meta_set_rvalue(e, (yyvsp[-1].mv)); + rtnl_ematch_meta_set_operand(e, (yyvsp[-2].i)); + + (yyval.e) = e; + } +#line 1786 "route/cls/ematch_syntax.c" + break; + + case 12: +#line 262 "route/cls/ematch_syntax.y" + { + struct rtnl_ematch *e; + + if (!(e = rtnl_ematch_alloc())) { + *errp = strdup("Unable to allocate ematch object"); + YYABORT; + } + + if (rtnl_ematch_set_kind(e, TCF_EM_CONTAINER) < 0) + BUG(); + + /* Make e->childs the list head of a the ematch sequence */ + nl_list_add_tail(&e->e_childs, &(yyvsp[-1].e)->e_list); + + (yyval.e) = e; + } +#line 1807 "route/cls/ematch_syntax.c" + break; + + case 13: +#line 290 "route/cls/ematch_syntax.y" + { (yyval.cmp) = (yyvsp[-1].cmp); } +#line 1813 "route/cls/ematch_syntax.c" + break; + + case 14: +#line 292 "route/cls/ematch_syntax.y" + { (yyval.cmp) = (yyvsp[0].cmp); } +#line 1819 "route/cls/ematch_syntax.c" + break; + + case 15: +#line 297 "route/cls/ematch_syntax.y" + { + if ((yyvsp[-2].loc)->align == TCF_EM_ALIGN_U16 || + (yyvsp[-2].loc)->align == TCF_EM_ALIGN_U32) + (yyval.cmp).flags = TCF_EM_CMP_TRANS; + + memset(&(yyval.cmp), 0, sizeof((yyval.cmp))); + + (yyval.cmp).mask = (yyvsp[-2].loc)->mask; + (yyval.cmp).off = (yyvsp[-2].loc)->offset; + (yyval.cmp).align = (yyvsp[-2].loc)->align; + (yyval.cmp).layer = (yyvsp[-2].loc)->layer; + (yyval.cmp).opnd = (yyvsp[-1].i); + (yyval.cmp).val = (yyvsp[0].i); + + rtnl_pktloc_put((yyvsp[-2].loc)); + } +#line 1840 "route/cls/ematch_syntax.c" + break; + + case 16: +#line 317 "route/cls/ematch_syntax.y" + { (yyval.loc) = NULL; } +#line 1846 "route/cls/ematch_syntax.c" + break; + + case 17: +#line 319 "route/cls/ematch_syntax.y" + { (yyval.loc) = (yyvsp[0].loc); } +#line 1852 "route/cls/ematch_syntax.c" + break; + + case 18: +#line 324 "route/cls/ematch_syntax.y" + { (yyval.loc) = NULL; } +#line 1858 "route/cls/ematch_syntax.c" + break; + + case 19: +#line 326 "route/cls/ematch_syntax.y" + { (yyval.loc) = (yyvsp[0].loc); } +#line 1864 "route/cls/ematch_syntax.c" + break; + + case 20: +#line 331 "route/cls/ematch_syntax.y" + { (yyval.mv) = rtnl_meta_value_alloc_var((yyvsp[0].q).data, (yyvsp[0].q).len); } +#line 1870 "route/cls/ematch_syntax.c" + break; + + case 21: +#line 333 "route/cls/ematch_syntax.y" + { (yyval.mv) = rtnl_meta_value_alloc_int((yyvsp[0].i)); } +#line 1876 "route/cls/ematch_syntax.c" + break; + + case 22: +#line 335 "route/cls/ematch_syntax.y" + { (yyval.mv) = META_ALLOC(META_INT, (yyvsp[-2].i), (yyvsp[-1].i), (yyvsp[0].i64)); } +#line 1882 "route/cls/ematch_syntax.c" + break; + + case 23: +#line 337 "route/cls/ematch_syntax.y" + { (yyval.mv) = META_ALLOC(META_VAR, (yyvsp[-1].i), (yyvsp[0].i), 0); } +#line 1888 "route/cls/ematch_syntax.c" + break; + + case 24: +#line 341 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(RANDOM); } +#line 1894 "route/cls/ematch_syntax.c" + break; + + case 25: +#line 342 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(LOADAVG_0); } +#line 1900 "route/cls/ematch_syntax.c" + break; + + case 26: +#line 343 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(LOADAVG_1); } +#line 1906 "route/cls/ematch_syntax.c" + break; + + case 27: +#line 344 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(LOADAVG_2); } +#line 1912 "route/cls/ematch_syntax.c" + break; + + case 28: +#line 345 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(DEV); } +#line 1918 "route/cls/ematch_syntax.c" + break; + + case 29: +#line 346 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(PRIORITY); } +#line 1924 "route/cls/ematch_syntax.c" + break; + + case 30: +#line 347 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(PROTOCOL); } +#line 1930 "route/cls/ematch_syntax.c" + break; + + case 31: +#line 348 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(PKTTYPE); } +#line 1936 "route/cls/ematch_syntax.c" + break; + + case 32: +#line 349 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(PKTLEN); } +#line 1942 "route/cls/ematch_syntax.c" + break; + + case 33: +#line 350 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(DATALEN); } +#line 1948 "route/cls/ematch_syntax.c" + break; + + case 34: +#line 351 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(MACLEN); } +#line 1954 "route/cls/ematch_syntax.c" + break; + + case 35: +#line 352 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(NFMARK); } +#line 1960 "route/cls/ematch_syntax.c" + break; + + case 36: +#line 353 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(TCINDEX); } +#line 1966 "route/cls/ematch_syntax.c" + break; + + case 37: +#line 354 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(RTCLASSID); } +#line 1972 "route/cls/ematch_syntax.c" + break; + + case 38: +#line 355 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(RTIIF); } +#line 1978 "route/cls/ematch_syntax.c" + break; + + case 39: +#line 356 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(SK_FAMILY); } +#line 1984 "route/cls/ematch_syntax.c" + break; + + case 40: +#line 357 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(SK_STATE); } +#line 1990 "route/cls/ematch_syntax.c" + break; + + case 41: +#line 358 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(SK_REUSE); } +#line 1996 "route/cls/ematch_syntax.c" + break; + + case 42: +#line 359 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(SK_REFCNT); } +#line 2002 "route/cls/ematch_syntax.c" + break; + + case 43: +#line 360 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(SK_RCVBUF); } +#line 2008 "route/cls/ematch_syntax.c" + break; + + case 44: +#line 361 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(SK_SNDBUF); } +#line 2014 "route/cls/ematch_syntax.c" + break; + + case 45: +#line 362 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(SK_SHUTDOWN); } +#line 2020 "route/cls/ematch_syntax.c" + break; + + case 46: +#line 363 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(SK_PROTO); } +#line 2026 "route/cls/ematch_syntax.c" + break; + + case 47: +#line 364 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(SK_TYPE); } +#line 2032 "route/cls/ematch_syntax.c" + break; + + case 48: +#line 365 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(SK_RMEM_ALLOC); } +#line 2038 "route/cls/ematch_syntax.c" + break; + + case 49: +#line 366 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(SK_WMEM_ALLOC); } +#line 2044 "route/cls/ematch_syntax.c" + break; + + case 50: +#line 367 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(SK_WMEM_QUEUED); } +#line 2050 "route/cls/ematch_syntax.c" + break; + + case 51: +#line 368 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(SK_RCV_QLEN); } +#line 2056 "route/cls/ematch_syntax.c" + break; + + case 52: +#line 369 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(SK_SND_QLEN); } +#line 2062 "route/cls/ematch_syntax.c" + break; + + case 53: +#line 370 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(SK_ERR_QLEN); } +#line 2068 "route/cls/ematch_syntax.c" + break; + + case 54: +#line 371 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(SK_FORWARD_ALLOCS); } +#line 2074 "route/cls/ematch_syntax.c" + break; + + case 55: +#line 372 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(SK_ALLOCS); } +#line 2080 "route/cls/ematch_syntax.c" + break; + + case 56: +#line 373 "route/cls/ematch_syntax.y" + { (yyval.i) = __TCF_META_ID_SK_ROUTE_CAPS; } +#line 2086 "route/cls/ematch_syntax.c" + break; + + case 57: +#line 374 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(SK_HASH); } +#line 2092 "route/cls/ematch_syntax.c" + break; + + case 58: +#line 375 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(SK_LINGERTIME); } +#line 2098 "route/cls/ematch_syntax.c" + break; + + case 59: +#line 376 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(SK_ACK_BACKLOG); } +#line 2104 "route/cls/ematch_syntax.c" + break; + + case 60: +#line 377 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(SK_MAX_ACK_BACKLOG); } +#line 2110 "route/cls/ematch_syntax.c" + break; + + case 61: +#line 378 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(SK_PRIO); } +#line 2116 "route/cls/ematch_syntax.c" + break; + + case 62: +#line 379 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(SK_RCVLOWAT); } +#line 2122 "route/cls/ematch_syntax.c" + break; + + case 63: +#line 380 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(SK_RCVTIMEO); } +#line 2128 "route/cls/ematch_syntax.c" + break; + + case 64: +#line 381 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(SK_SNDTIMEO); } +#line 2134 "route/cls/ematch_syntax.c" + break; + + case 65: +#line 382 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(SK_SENDMSG_OFF); } +#line 2140 "route/cls/ematch_syntax.c" + break; + + case 66: +#line 383 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(SK_WRITE_PENDING); } +#line 2146 "route/cls/ematch_syntax.c" + break; + + case 67: +#line 384 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(VLAN_TAG); } +#line 2152 "route/cls/ematch_syntax.c" + break; + + case 68: +#line 385 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(RXHASH); } +#line 2158 "route/cls/ematch_syntax.c" + break; + + case 69: +#line 389 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(DEV); } +#line 2164 "route/cls/ematch_syntax.c" + break; + + case 70: +#line 390 "route/cls/ematch_syntax.y" + { (yyval.i) = META_ID(SK_BOUND_IF); } +#line 2170 "route/cls/ematch_syntax.c" + break; + + case 71: +#line 398 "route/cls/ematch_syntax.y" + { + (yyval.q) = (yyvsp[0].q); + } +#line 2178 "route/cls/ematch_syntax.c" + break; + + case 72: +#line 402 "route/cls/ematch_syntax.y" + { + struct nl_addr *addr; + + if (nl_addr_parse((yyvsp[0].s), AF_UNSPEC, &addr) == 0) { + (yyval.q).len = nl_addr_get_len(addr); + + (yyval.q).index = min_t(int, (yyval.q).len, nl_addr_get_prefixlen(addr)/8); + + if (!((yyval.q).data = calloc(1, (yyval.q).len))) { + nl_addr_put(addr); + YYABORT; + } + + memcpy((yyval.q).data, nl_addr_get_binary_addr(addr), (yyval.q).len); + nl_addr_put(addr); + } else { + if (asprintf(errp, "invalid pattern \"%s\"", (yyvsp[0].s)) == -1) + *errp = NULL; + YYABORT; + } + } +#line 2204 "route/cls/ematch_syntax.c" + break; + + case 73: +#line 431 "route/cls/ematch_syntax.y" + { + struct rtnl_pktloc *loc; + + if (rtnl_pktloc_lookup((yyvsp[0].s), &loc) < 0) { + if (asprintf(errp, "Packet location \"%s\" not found", (yyvsp[0].s)) == -1) + *errp = NULL; + YYABORT; + } + + (yyval.loc) = loc; + } +#line 2220 "route/cls/ematch_syntax.c" + break; + + case 74: +#line 444 "route/cls/ematch_syntax.y" + { + struct rtnl_pktloc *loc; + + if ((yyvsp[0].i64) && (!(yyvsp[-4].i) || (yyvsp[-4].i) > TCF_EM_ALIGN_U32)) { + *errp = strdup("mask only allowed for alignments u8|u16|u32"); + YYABORT; + } + + if (!(loc = rtnl_pktloc_alloc())) { + *errp = strdup("Unable to allocate packet location object"); + YYABORT; + } + + loc->name = strdup(""); + loc->align = (yyvsp[-4].i); + loc->layer = (yyvsp[-3].i); + loc->offset = (yyvsp[-1].i); + loc->mask = (yyvsp[0].i64); + + (yyval.loc) = loc; + } +#line 2246 "route/cls/ematch_syntax.c" + break; + + case 75: +#line 469 "route/cls/ematch_syntax.y" + { (yyval.i) = 0; } +#line 2252 "route/cls/ematch_syntax.c" + break; + + case 76: +#line 471 "route/cls/ematch_syntax.y" + { (yyval.i) = (yyvsp[-1].i); } +#line 2258 "route/cls/ematch_syntax.c" + break; + + case 77: +#line 473 "route/cls/ematch_syntax.y" + { (yyval.i) = (yyvsp[-1].i); } +#line 2264 "route/cls/ematch_syntax.c" + break; + + case 78: +#line 478 "route/cls/ematch_syntax.y" + { (yyval.i64) = 0; } +#line 2270 "route/cls/ematch_syntax.c" + break; + + case 79: +#line 480 "route/cls/ematch_syntax.y" + { (yyval.i64) = (yyvsp[0].i); } +#line 2276 "route/cls/ematch_syntax.c" + break; + + case 80: +#line 485 "route/cls/ematch_syntax.y" + { (yyval.i) = 0; } +#line 2282 "route/cls/ematch_syntax.c" + break; + + case 81: +#line 487 "route/cls/ematch_syntax.y" + { (yyval.i) = (yyvsp[0].i); } +#line 2288 "route/cls/ematch_syntax.c" + break; + + case 82: +#line 492 "route/cls/ematch_syntax.y" + { (yyval.i) = TCF_EM_OPND_EQ; } +#line 2294 "route/cls/ematch_syntax.c" + break; + + case 83: +#line 494 "route/cls/ematch_syntax.y" + { (yyval.i) = TCF_EM_OPND_GT; } +#line 2300 "route/cls/ematch_syntax.c" + break; + + case 84: +#line 496 "route/cls/ematch_syntax.y" + { (yyval.i) = TCF_EM_OPND_LT; } +#line 2306 "route/cls/ematch_syntax.c" + break; + + +#line 2310 "route/cls/ematch_syntax.c" + + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + /* Now 'shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + { + const int yylhs = yyr1[yyn] - YYNTOKENS; + const int yyi = yypgoto[yylhs] + *yyssp; + yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp + ? yytable[yyi] + : yydefgoto[yylhs]); + } + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (scanner, errp, root, YY_("syntax error")); +#else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) + { + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = YY_CAST (char *, YYSTACK_ALLOC (YY_CAST (YYSIZE_T, yymsg_alloc))); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (scanner, errp, root, yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; + } +# undef YYSYNTAX_ERROR +#endif + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, scanner, errp, root); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + /* Pacify compilers when the user code never invokes YYERROR and the + label yyerrorlab therefore never appears in user code. */ + if (0) + YYERROR; + + /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + yystos[yystate], yyvsp, scanner, errp, root); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + + +#if !defined yyoverflow || YYERROR_VERBOSE +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (scanner, errp, root, YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + + +/*-----------------------------------------------------. +| yyreturn -- parsing is finished, return the result. | +`-----------------------------------------------------*/ +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, scanner, errp, root); + } + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[+*yyssp], yyvsp, scanner, errp, root); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + return yyresult; +} diff --git a/libnl/lib/route/cls/ematch_syntax.h b/libnl/lib/route/cls/ematch_syntax.h new file mode 100644 index 0000000..5d3f4c0 --- /dev/null +++ b/libnl/lib/route/cls/ematch_syntax.h @@ -0,0 +1,225 @@ +/* A Bison parser, made by GNU Bison 3.5.1. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* Undocumented macros, especially those whose name start with YY_, + are private implementation details. Do not rely on them. */ + +#ifndef YY_EMATCH_ROUTE_CLS_EMATCH_SYNTAX_H_INCLUDED +# define YY_EMATCH_ROUTE_CLS_EMATCH_SYNTAX_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int ematch_debug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + ERROR = 258, + LOGIC = 259, + NOT = 260, + OPERAND = 261, + NUMBER = 262, + ALIGN = 263, + LAYER = 264, + KW_OPEN = 265, + KW_CLOSE = 266, + KW_PLUS = 267, + KW_MASK = 268, + KW_SHIFT = 269, + KW_AT = 270, + EMATCH_CMP = 271, + EMATCH_NBYTE = 272, + EMATCH_TEXT = 273, + EMATCH_META = 274, + KW_EQ = 275, + KW_GT = 276, + KW_LT = 277, + KW_FROM = 278, + KW_TO = 279, + META_RANDOM = 280, + META_LOADAVG_0 = 281, + META_LOADAVG_1 = 282, + META_LOADAVG_2 = 283, + META_DEV = 284, + META_PRIO = 285, + META_PROTO = 286, + META_PKTTYPE = 287, + META_PKTLEN = 288, + META_DATALEN = 289, + META_MACLEN = 290, + META_MARK = 291, + META_TCINDEX = 292, + META_RTCLASSID = 293, + META_RTIIF = 294, + META_SK_FAMILY = 295, + META_SK_STATE = 296, + META_SK_REUSE = 297, + META_SK_REFCNT = 298, + META_SK_RCVBUF = 299, + META_SK_SNDBUF = 300, + META_SK_SHUTDOWN = 301, + META_SK_PROTO = 302, + META_SK_TYPE = 303, + META_SK_RMEM_ALLOC = 304, + META_SK_WMEM_ALLOC = 305, + META_SK_WMEM_QUEUED = 306, + META_SK_RCV_QLEN = 307, + META_SK_SND_QLEN = 308, + META_SK_ERR_QLEN = 309, + META_SK_FORWARD_ALLOCS = 310, + META_SK_ALLOCS = 311, + META_SK_ROUTE_CAPS = 312, + META_SK_HASH = 313, + META_SK_LINGERTIME = 314, + META_SK_ACK_BACKLOG = 315, + META_SK_MAX_ACK_BACKLOG = 316, + META_SK_PRIO = 317, + META_SK_RCVLOWAT = 318, + META_SK_RCVTIMEO = 319, + META_SK_SNDTIMEO = 320, + META_SK_SENDMSG_OFF = 321, + META_SK_WRITE_PENDING = 322, + META_VLAN = 323, + META_RXHASH = 324, + META_DEVNAME = 325, + META_SK_BOUND_IF = 326, + STR = 327, + QUOTED = 328 + }; +#endif +/* Tokens. */ +#define ERROR 258 +#define LOGIC 259 +#define NOT 260 +#define OPERAND 261 +#define NUMBER 262 +#define ALIGN 263 +#define LAYER 264 +#define KW_OPEN 265 +#define KW_CLOSE 266 +#define KW_PLUS 267 +#define KW_MASK 268 +#define KW_SHIFT 269 +#define KW_AT 270 +#define EMATCH_CMP 271 +#define EMATCH_NBYTE 272 +#define EMATCH_TEXT 273 +#define EMATCH_META 274 +#define KW_EQ 275 +#define KW_GT 276 +#define KW_LT 277 +#define KW_FROM 278 +#define KW_TO 279 +#define META_RANDOM 280 +#define META_LOADAVG_0 281 +#define META_LOADAVG_1 282 +#define META_LOADAVG_2 283 +#define META_DEV 284 +#define META_PRIO 285 +#define META_PROTO 286 +#define META_PKTTYPE 287 +#define META_PKTLEN 288 +#define META_DATALEN 289 +#define META_MACLEN 290 +#define META_MARK 291 +#define META_TCINDEX 292 +#define META_RTCLASSID 293 +#define META_RTIIF 294 +#define META_SK_FAMILY 295 +#define META_SK_STATE 296 +#define META_SK_REUSE 297 +#define META_SK_REFCNT 298 +#define META_SK_RCVBUF 299 +#define META_SK_SNDBUF 300 +#define META_SK_SHUTDOWN 301 +#define META_SK_PROTO 302 +#define META_SK_TYPE 303 +#define META_SK_RMEM_ALLOC 304 +#define META_SK_WMEM_ALLOC 305 +#define META_SK_WMEM_QUEUED 306 +#define META_SK_RCV_QLEN 307 +#define META_SK_SND_QLEN 308 +#define META_SK_ERR_QLEN 309 +#define META_SK_FORWARD_ALLOCS 310 +#define META_SK_ALLOCS 311 +#define META_SK_ROUTE_CAPS 312 +#define META_SK_HASH 313 +#define META_SK_LINGERTIME 314 +#define META_SK_ACK_BACKLOG 315 +#define META_SK_MAX_ACK_BACKLOG 316 +#define META_SK_PRIO 317 +#define META_SK_RCVLOWAT 318 +#define META_SK_RCVTIMEO 319 +#define META_SK_SNDTIMEO 320 +#define META_SK_SENDMSG_OFF 321 +#define META_SK_WRITE_PENDING 322 +#define META_VLAN 323 +#define META_RXHASH 324 +#define META_DEVNAME 325 +#define META_SK_BOUND_IF 326 +#define STR 327 +#define QUOTED 328 + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 35 "route/cls/ematch_syntax.y" + + struct tcf_em_cmp cmp; + struct ematch_quoted q; + struct rtnl_ematch * e; + struct rtnl_pktloc * loc; + struct rtnl_meta_value *mv; + uint32_t i; + uint64_t i64; + char * s; + +#line 214 "route/cls/ematch_syntax.h" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + + +int ematch_parse (void *scanner, char **errp, struct nl_list_head *root); + +#endif /* !YY_EMATCH_ROUTE_CLS_EMATCH_SYNTAX_H_INCLUDED */ diff --git a/libnl/lib/route/cls/ematch_syntax.y b/libnl/lib/route/cls/ematch_syntax.y new file mode 100644 index 0000000..68ec783 --- /dev/null +++ b/libnl/lib/route/cls/ematch_syntax.y @@ -0,0 +1,497 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010-2013 Thomas Graf + */ + +%{ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define META_ALLOC rtnl_meta_value_alloc_id +#define META_ID(name) TCF_META_ID_##name +#define META_INT TCF_META_TYPE_INT +#define META_VAR TCF_META_TYPE_VAR +%} + +%error-verbose +%define api.pure +%name-prefix "ematch_" + +%parse-param {void *scanner} +%parse-param {char **errp} +%parse-param {struct nl_list_head *root} +%lex-param {void *scanner} + +%union { + struct tcf_em_cmp cmp; + struct ematch_quoted q; + struct rtnl_ematch * e; + struct rtnl_pktloc * loc; + struct rtnl_meta_value *mv; + uint32_t i; + uint64_t i64; + char * s; +} + +%{ +extern int ematch_lex(YYSTYPE *, void *); + +static void yyerror(void *scanner, char **errp, struct nl_list_head *root, const char *msg) +{ + if (msg) + *errp = strdup(msg); + else + *errp = NULL; +} +%} + +%token ERROR LOGIC NOT OPERAND NUMBER ALIGN LAYER +%token KW_OPEN "(" +%token KW_CLOSE ")" +%token KW_PLUS "+" +%token KW_MASK "mask" +%token KW_SHIFT ">>" +%token KW_AT "at" +%token EMATCH_CMP "cmp" +%token EMATCH_NBYTE "pattern" +%token EMATCH_TEXT "text" +%token EMATCH_META "meta" +%token KW_EQ "=" +%token KW_GT ">" +%token KW_LT "<" +%token KW_FROM "from" +%token KW_TO "to" + +%token META_RANDOM "random" +%token META_LOADAVG_0 "loadavg_0" +%token META_LOADAVG_1 "loadavg_1" +%token META_LOADAVG_2 "loadavg_2" +%token META_DEV "dev" +%token META_PRIO "prio" +%token META_PROTO "proto" +%token META_PKTTYPE "pkttype" +%token META_PKTLEN "pktlen" +%token META_DATALEN "datalen" +%token META_MACLEN "maclen" +%token META_MARK "mark" +%token META_TCINDEX "tcindex" +%token META_RTCLASSID "rtclassid" +%token META_RTIIF "rtiif" +%token META_SK_FAMILY "sk_family" +%token META_SK_STATE "sk_state" +%token META_SK_REUSE "sk_reuse" +%token META_SK_REFCNT "sk_refcnt" +%token META_SK_RCVBUF "sk_rcvbuf" +%token META_SK_SNDBUF "sk_sndbuf" +%token META_SK_SHUTDOWN "sk_shutdown" +%token META_SK_PROTO "sk_proto" +%token META_SK_TYPE "sk_type" +%token META_SK_RMEM_ALLOC "sk_rmem_alloc" +%token META_SK_WMEM_ALLOC "sk_wmem_alloc" +%token META_SK_WMEM_QUEUED "sk_wmem_queued" +%token META_SK_RCV_QLEN "sk_rcv_qlen" +%token META_SK_SND_QLEN "sk_snd_qlen" +%token META_SK_ERR_QLEN "sk_err_qlen" +%token META_SK_FORWARD_ALLOCS "sk_forward_allocs" +%token META_SK_ALLOCS "sk_allocs" +%token META_SK_ROUTE_CAPS "sk_route_caps" +%token META_SK_HASH "sk_hash" +%token META_SK_LINGERTIME "sk_lingertime" +%token META_SK_ACK_BACKLOG "sk_ack_backlog" +%token META_SK_MAX_ACK_BACKLOG "sk_max_ack_backlog" +%token META_SK_PRIO "sk_prio" +%token META_SK_RCVLOWAT "sk_rcvlowat" +%token META_SK_RCVTIMEO "sk_rcvtimeo" +%token META_SK_SNDTIMEO "sk_sndtimeo" +%token META_SK_SENDMSG_OFF "sk_sendmsg_off" +%token META_SK_WRITE_PENDING "sk_write_pending" +%token META_VLAN "vlan" +%token META_RXHASH "rxhash" +%token META_DEVNAME "devname" +%token META_SK_BOUND_IF "sk_bound_if" + +%token STR + +%token QUOTED + +%type align operand shift meta_int_id meta_var_id +%type mask +%type expr match ematch +%type cmp_expr cmp_match +%type pktloc text_from text_to +%type pattern +%type meta_value + +%destructor { free($$); NL_DBG(2, "string destructor\n"); } +%destructor { rtnl_pktloc_put($$); NL_DBG(2, "pktloc destructor\n"); } +%destructor { free($$.data); NL_DBG(2, "quoted destructor\n"); } +%destructor { rtnl_meta_value_put($$); NL_DBG(2, "meta value destructor\n"); } + +%start input + +%% + +input: + /* empty */ + | expr + { + nl_list_add_tail(root, &$1->e_list); + } + ; + +expr: + match + { + $$ = $1; + } + | match LOGIC expr + { + rtnl_ematch_set_flags($1, $2); + + /* make ematch new head */ + nl_list_add_tail(&$1->e_list, &$3->e_list); + + $$ = $1; + } + ; + +match: + NOT ematch + { + rtnl_ematch_set_flags($2, TCF_EM_INVERT); + $$ = $2; + } + | ematch + { + $$ = $1; + } + ; + +ematch: + /* CMP */ + cmp_match + { + struct rtnl_ematch *e; + + if (!(e = rtnl_ematch_alloc())) { + *errp = strdup("Unable to allocate ematch object"); + YYABORT; + } + + if (rtnl_ematch_set_kind(e, TCF_EM_CMP) < 0) + BUG(); + + rtnl_ematch_cmp_set(e, &$1); + $$ = e; + } + | EMATCH_NBYTE "(" pktloc KW_EQ pattern ")" + { + struct rtnl_ematch *e; + + if (!(e = rtnl_ematch_alloc())) { + *errp = strdup("Unable to allocate ematch object"); + YYABORT; + } + + if (rtnl_ematch_set_kind(e, TCF_EM_NBYTE) < 0) + BUG(); + + rtnl_ematch_nbyte_set_offset(e, $3->layer, $3->offset); + rtnl_pktloc_put($3); + rtnl_ematch_nbyte_set_pattern(e, (uint8_t *) $5.data, $5.index); + + $$ = e; + } + | EMATCH_TEXT "(" STR QUOTED text_from text_to ")" + { + struct rtnl_ematch *e; + + if (!(e = rtnl_ematch_alloc())) { + *errp = strdup("Unable to allocate ematch object"); + YYABORT; + } + + if (rtnl_ematch_set_kind(e, TCF_EM_TEXT) < 0) + BUG(); + + rtnl_ematch_text_set_algo(e, $3); + rtnl_ematch_text_set_pattern(e, $4.data, $4.index); + + if ($5) { + rtnl_ematch_text_set_from(e, $5->layer, $5->offset); + rtnl_pktloc_put($5); + } + + if ($6) { + rtnl_ematch_text_set_to(e, $6->layer, $6->offset); + rtnl_pktloc_put($6); + } + + $$ = e; + } + | EMATCH_META "(" meta_value operand meta_value ")" + { + struct rtnl_ematch *e; + + if (!(e = rtnl_ematch_alloc())) { + *errp = strdup("Unable to allocate ematch object"); + YYABORT; + } + + if (rtnl_ematch_set_kind(e, TCF_EM_META) < 0) + BUG(); + + rtnl_ematch_meta_set_lvalue(e, $3); + rtnl_ematch_meta_set_rvalue(e, $5); + rtnl_ematch_meta_set_operand(e, $4); + + $$ = e; + } + /* CONTAINER */ + | "(" expr ")" + { + struct rtnl_ematch *e; + + if (!(e = rtnl_ematch_alloc())) { + *errp = strdup("Unable to allocate ematch object"); + YYABORT; + } + + if (rtnl_ematch_set_kind(e, TCF_EM_CONTAINER) < 0) + BUG(); + + /* Make e->childs the list head of a the ematch sequence */ + nl_list_add_tail(&e->e_childs, &$2->e_list); + + $$ = e; + } + ; + +/* + * CMP match + * + * match := cmp(expr) | expr + * expr := pktloc (=|>|<) NUMBER + * pktloc := alias | definition + * + */ +cmp_match: + EMATCH_CMP "(" cmp_expr ")" + { $$ = $3; } + | cmp_expr + { $$ = $1; } + ; + +cmp_expr: + pktloc operand NUMBER + { + if ($1->align == TCF_EM_ALIGN_U16 || + $1->align == TCF_EM_ALIGN_U32) + $$.flags = TCF_EM_CMP_TRANS; + + memset(&$$, 0, sizeof($$)); + + $$.mask = $1->mask; + $$.off = $1->offset; + $$.align = $1->align; + $$.layer = $1->layer; + $$.opnd = $2; + $$.val = $3; + + rtnl_pktloc_put($1); + } + ; + +text_from: + /* empty */ + { $$ = NULL; } + | "from" pktloc + { $$ = $2; } + ; + +text_to: + /* empty */ + { $$ = NULL; } + | "to" pktloc + { $$ = $2; } + ; + +meta_value: + QUOTED + { $$ = rtnl_meta_value_alloc_var($1.data, $1.len); } + | NUMBER + { $$ = rtnl_meta_value_alloc_int($1); } + | meta_int_id shift mask + { $$ = META_ALLOC(META_INT, $1, $2, $3); } + | meta_var_id shift + { $$ = META_ALLOC(META_VAR, $1, $2, 0); } + ; + +meta_int_id: + META_RANDOM { $$ = META_ID(RANDOM); } + |META_LOADAVG_0 { $$ = META_ID(LOADAVG_0); } + |META_LOADAVG_1 { $$ = META_ID(LOADAVG_1); } + |META_LOADAVG_2 { $$ = META_ID(LOADAVG_2); } + | META_DEV { $$ = META_ID(DEV); } + | META_PRIO { $$ = META_ID(PRIORITY); } + | META_PROTO { $$ = META_ID(PROTOCOL); } + | META_PKTTYPE { $$ = META_ID(PKTTYPE); } + | META_PKTLEN { $$ = META_ID(PKTLEN); } + | META_DATALEN { $$ = META_ID(DATALEN); } + | META_MACLEN { $$ = META_ID(MACLEN); } + | META_MARK { $$ = META_ID(NFMARK); } + | META_TCINDEX { $$ = META_ID(TCINDEX); } + | META_RTCLASSID { $$ = META_ID(RTCLASSID); } + | META_RTIIF { $$ = META_ID(RTIIF); } + | META_SK_FAMILY { $$ = META_ID(SK_FAMILY); } + | META_SK_STATE { $$ = META_ID(SK_STATE); } + | META_SK_REUSE { $$ = META_ID(SK_REUSE); } + | META_SK_REFCNT { $$ = META_ID(SK_REFCNT); } + | META_SK_RCVBUF { $$ = META_ID(SK_RCVBUF); } + | META_SK_SNDBUF { $$ = META_ID(SK_SNDBUF); } + | META_SK_SHUTDOWN { $$ = META_ID(SK_SHUTDOWN); } + | META_SK_PROTO { $$ = META_ID(SK_PROTO); } + | META_SK_TYPE { $$ = META_ID(SK_TYPE); } + | META_SK_RMEM_ALLOC { $$ = META_ID(SK_RMEM_ALLOC); } + | META_SK_WMEM_ALLOC { $$ = META_ID(SK_WMEM_ALLOC); } + | META_SK_WMEM_QUEUED { $$ = META_ID(SK_WMEM_QUEUED); } + | META_SK_RCV_QLEN { $$ = META_ID(SK_RCV_QLEN); } + | META_SK_SND_QLEN { $$ = META_ID(SK_SND_QLEN); } + | META_SK_ERR_QLEN { $$ = META_ID(SK_ERR_QLEN); } + | META_SK_FORWARD_ALLOCS { $$ = META_ID(SK_FORWARD_ALLOCS); } + | META_SK_ALLOCS { $$ = META_ID(SK_ALLOCS); } + | META_SK_ROUTE_CAPS { $$ = __TCF_META_ID_SK_ROUTE_CAPS; } + | META_SK_HASH { $$ = META_ID(SK_HASH); } + | META_SK_LINGERTIME { $$ = META_ID(SK_LINGERTIME); } + | META_SK_ACK_BACKLOG { $$ = META_ID(SK_ACK_BACKLOG); } + | META_SK_MAX_ACK_BACKLOG { $$ = META_ID(SK_MAX_ACK_BACKLOG); } + | META_SK_PRIO { $$ = META_ID(SK_PRIO); } + | META_SK_RCVLOWAT { $$ = META_ID(SK_RCVLOWAT); } + | META_SK_RCVTIMEO { $$ = META_ID(SK_RCVTIMEO); } + | META_SK_SNDTIMEO { $$ = META_ID(SK_SNDTIMEO); } + | META_SK_SENDMSG_OFF { $$ = META_ID(SK_SENDMSG_OFF); } + | META_SK_WRITE_PENDING { $$ = META_ID(SK_WRITE_PENDING); } + | META_VLAN { $$ = META_ID(VLAN_TAG); } + | META_RXHASH { $$ = META_ID(RXHASH); } + ; + +meta_var_id: + META_DEVNAME { $$ = META_ID(DEV); } + | META_SK_BOUND_IF { $$ = META_ID(SK_BOUND_IF); } + ; + +/* + * pattern + */ +pattern: + QUOTED + { + $$ = $1; + } + | STR + { + struct nl_addr *addr; + + if (nl_addr_parse($1, AF_UNSPEC, &addr) == 0) { + $$.len = nl_addr_get_len(addr); + + $$.index = min_t(int, $$.len, nl_addr_get_prefixlen(addr)/8); + + if (!($$.data = calloc(1, $$.len))) { + nl_addr_put(addr); + YYABORT; + } + + memcpy($$.data, nl_addr_get_binary_addr(addr), $$.len); + nl_addr_put(addr); + } else { + if (asprintf(errp, "invalid pattern \"%s\"", $1) == -1) + *errp = NULL; + YYABORT; + } + } + ; + +/* + * packet location + */ + +pktloc: + STR + { + struct rtnl_pktloc *loc; + + if (rtnl_pktloc_lookup($1, &loc) < 0) { + if (asprintf(errp, "Packet location \"%s\" not found", $1) == -1) + *errp = NULL; + YYABORT; + } + + $$ = loc; + } + /* [u8|u16|u32|NUM at] LAYER + OFFSET [mask MASK] */ + | align LAYER "+" NUMBER mask + { + struct rtnl_pktloc *loc; + + if ($5 && (!$1 || $1 > TCF_EM_ALIGN_U32)) { + *errp = strdup("mask only allowed for alignments u8|u16|u32"); + YYABORT; + } + + if (!(loc = rtnl_pktloc_alloc())) { + *errp = strdup("Unable to allocate packet location object"); + YYABORT; + } + + loc->name = strdup(""); + loc->align = $1; + loc->layer = $2; + loc->offset = $4; + loc->mask = $5; + + $$ = loc; + } + ; + +align: + /* empty */ + { $$ = 0; } + | ALIGN "at" + { $$ = $1; } + | NUMBER "at" + { $$ = $1; } + ; + +mask: + /* empty */ + { $$ = 0; } + | KW_MASK NUMBER + { $$ = $2; } + ; + +shift: + /* empty */ + { $$ = 0; } + | KW_SHIFT NUMBER + { $$ = $2; } + ; + +operand: + KW_EQ + { $$ = TCF_EM_OPND_EQ; } + | KW_GT + { $$ = TCF_EM_OPND_GT; } + | KW_LT + { $$ = TCF_EM_OPND_LT; } + ; diff --git a/libnl/lib/route/cls/fw.c b/libnl/lib/route/cls/fw.c new file mode 100644 index 0000000..8d21ae4 --- /dev/null +++ b/libnl/lib/route/cls/fw.c @@ -0,0 +1,215 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + * Copyright (c) 2006 Petr Gotthard + * Copyright (c) 2006 Siemens AG Oesterreich + */ + +/** + * @ingroup cls + * @defgroup cls_fw Firewall Classifier + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define FW_ATTR_CLASSID 0x001 +#define FW_ATTR_ACTION 0x002 +#define FW_ATTR_POLICE 0x004 +#define FW_ATTR_INDEV 0x008 +#define FW_ATTR_MASK 0x010 +/** @endcond */ + +static struct nla_policy fw_policy[TCA_FW_MAX+1] = { + [TCA_FW_CLASSID] = { .type = NLA_U32 }, + [TCA_FW_INDEV] = { .type = NLA_STRING, + .maxlen = IFNAMSIZ }, + [TCA_FW_MASK] = { .type = NLA_U32 }, +}; + +static int fw_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct nlattr *tb[TCA_FW_MAX + 1]; + struct rtnl_fw *f = data; + int err; + + err = tca_parse(tb, TCA_FW_MAX, tc, fw_policy); + if (err < 0) + return err; + + if (tb[TCA_FW_CLASSID]) { + f->cf_classid = nla_get_u32(tb[TCA_FW_CLASSID]); + f->cf_mask |= FW_ATTR_CLASSID; + } + + if (tb[TCA_FW_ACT]) { + f->cf_act = nl_data_alloc_attr(tb[TCA_FW_ACT]); + if (!f->cf_act) + return -NLE_NOMEM; + f->cf_mask |= FW_ATTR_ACTION; + } + + if (tb[TCA_FW_POLICE]) { + f->cf_police = nl_data_alloc_attr(tb[TCA_FW_POLICE]); + if (!f->cf_police) + return -NLE_NOMEM; + f->cf_mask |= FW_ATTR_POLICE; + } + + if (tb[TCA_FW_INDEV]) { + nla_strlcpy(f->cf_indev, tb[TCA_FW_INDEV], IFNAMSIZ); + f->cf_mask |= FW_ATTR_INDEV; + } + + if (tb[TCA_FW_MASK]) { + f->cf_fwmask = nla_get_u32(tb[TCA_FW_MASK]); + f->cf_mask |= FW_ATTR_MASK; + } + + return 0; +} + +static void fw_free_data(struct rtnl_tc *tc, void *data) +{ + struct rtnl_fw *f = data; + + nl_data_free(f->cf_act); + nl_data_free(f->cf_police); +} + +static int fw_clone(void *_dst, void *_src) +{ + struct rtnl_fw *dst = _dst, *src = _src; + + if (src->cf_act && !(dst->cf_act = nl_data_clone(src->cf_act))) + return -NLE_NOMEM; + + if (src->cf_police && !(dst->cf_police = nl_data_clone(src->cf_police))) + return -NLE_NOMEM; + + return 0; +} + +static void fw_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_fw *f = data; + + if (!f) + return; + + if (f->cf_mask & FW_ATTR_CLASSID) { + char buf[32]; + + nl_dump(p, " target %s", + rtnl_tc_handle2str(f->cf_classid, buf, sizeof(buf))); + } + + if (f->cf_mask & FW_ATTR_MASK) + nl_dump(p, " mask 0x%x", f->cf_fwmask); +} + +static void fw_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_fw *f = data; + + if (f && f->cf_mask & FW_ATTR_INDEV) + nl_dump(p, "indev %s ", f->cf_indev); +} + +static int fw_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + struct rtnl_fw *f = data; + + if (!f) + return 0; + + if (f->cf_mask & FW_ATTR_CLASSID) + NLA_PUT_U32(msg, TCA_FW_CLASSID, f->cf_classid); + + if (f->cf_mask & FW_ATTR_ACTION) + NLA_PUT_DATA(msg, TCA_FW_ACT, f->cf_act); + + if (f->cf_mask & FW_ATTR_POLICE) + NLA_PUT_DATA(msg, TCA_FW_POLICE, f->cf_police); + + if (f->cf_mask & FW_ATTR_INDEV) + NLA_PUT_STRING(msg, TCA_FW_INDEV, f->cf_indev); + + if (f->cf_mask & FW_ATTR_MASK) + NLA_PUT_U32(msg, TCA_FW_MASK, f->cf_fwmask); + + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +/** + * @name Attribute Modifications + * @{ + */ + +int rtnl_fw_set_classid(struct rtnl_cls *cls, uint32_t classid) +{ + struct rtnl_fw *f; + + if (!(f = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + f->cf_classid = classid; + f->cf_mask |= FW_ATTR_CLASSID; + + return 0; +} + +int rtnl_fw_set_mask(struct rtnl_cls *cls, uint32_t mask) +{ + struct rtnl_fw *f; + + if (!(f = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + f->cf_fwmask = mask; + f->cf_mask |= FW_ATTR_MASK; + + return 0; +} + +/** @} */ + +static struct rtnl_tc_ops fw_ops = { + .to_kind = "fw", + .to_type = RTNL_TC_TYPE_CLS, + .to_size = sizeof(struct rtnl_fw), + .to_msg_parser = fw_msg_parser, + .to_msg_fill = fw_msg_fill, + .to_free_data = fw_free_data, + .to_clone = fw_clone, + .to_dump = { + [NL_DUMP_LINE] = fw_dump_line, + [NL_DUMP_DETAILS] = fw_dump_details, + }, +}; + +static void __init fw_init(void) +{ + rtnl_tc_register(&fw_ops); +} + +static void __exit fw_exit(void) +{ + rtnl_tc_unregister(&fw_ops); +} + +/** @} */ diff --git a/libnl/lib/route/cls/mall.c b/libnl/lib/route/cls/mall.c new file mode 100644 index 0000000..444e41c --- /dev/null +++ b/libnl/lib/route/cls/mall.c @@ -0,0 +1,301 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2017 Volodymyr Bendiuga + */ + +/** + * @ingroup cls + * @defgroup cls_mall Match-all Classifier + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define MALL_ATTR_CLASSID 0x01 +#define MALL_ATTR_FLAGS 0x02 +#define MALL_ATTR_ACTION 0x03 + + +static struct nla_policy mall_policy[TCA_MATCHALL_MAX + 1] = { + [TCA_MATCHALL_CLASSID] = { .type = NLA_U32 }, + [TCA_MATCHALL_FLAGS] = { .type = NLA_U32 }, +}; + +/** + * @name Attribute Modifications + * @{ + */ + +int rtnl_mall_set_classid(struct rtnl_cls *cls, uint32_t classid) +{ + struct rtnl_mall *mall; + if (!(mall = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + mall->m_classid = classid; + mall->m_mask |= MALL_ATTR_CLASSID; + + return 0; +} + +int rtnl_mall_get_classid(struct rtnl_cls *cls, uint32_t *classid) +{ + struct rtnl_mall *mall; + + if (!(mall = rtnl_tc_data_peek(TC_CAST(cls)))) + return -NLE_INVAL; + + if (!(mall->m_mask & MALL_ATTR_CLASSID)) + return -NLE_INVAL; + + *classid = mall->m_classid; + return 0; +} + +int rtnl_mall_set_flags(struct rtnl_cls *cls, uint32_t flags) +{ + struct rtnl_mall *mall; + + if (!(mall = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + mall->m_flags = flags; + mall->m_mask |= MALL_ATTR_FLAGS; + + return 0; +} + +int rtnl_mall_get_flags(struct rtnl_cls *cls, uint32_t *flags) +{ + struct rtnl_mall *mall; + + if (!(mall = rtnl_tc_data_peek(TC_CAST(cls)))) + return -NLE_INVAL; + + if (!(mall->m_mask & MALL_ATTR_FLAGS)) + return -NLE_INVAL; + + *flags = mall->m_flags; + return 0; +} + +int rtnl_mall_append_action(struct rtnl_cls *cls, struct rtnl_act *act) +{ + struct rtnl_mall *mall; + int err; + + if (!act) + return 0; + + if (!(mall = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + mall->m_mask |= MALL_ATTR_ACTION; + err = rtnl_act_append(&mall->m_act, act); + if (err < 0) + return err; + + rtnl_act_get(act); + return 0; +} + +struct rtnl_act *rtnl_mall_get_first_action(struct rtnl_cls *cls) +{ + struct rtnl_mall *mall; + struct rtnl_act *act; + + if (!(mall = rtnl_tc_data(TC_CAST(cls)))) + return NULL; + + if (!(mall->m_mask & MALL_ATTR_ACTION)) + return NULL; + + act = mall->m_act; + rtnl_act_get(act); + + return act; +} + +int rtnl_mall_del_action(struct rtnl_cls *cls, struct rtnl_act *act) +{ + struct rtnl_mall *mall; + int ret; + + if (!act) + return 0; + + if (!(mall = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + if (!(mall->m_mask & MALL_ATTR_ACTION)) + return -NLE_INVAL; + + ret = rtnl_act_remove(&mall->m_act, act); + if (ret < 0) + return ret; + + rtnl_act_put(act); + + return 0; +} + +/** @} */ + +static void mall_free_data(struct rtnl_tc *tc, void *data) +{ + struct rtnl_mall *mall = data; + + if (mall->m_act) + rtnl_act_put_all(&mall->m_act); +} + +static int mall_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct rtnl_mall *mall = data; + struct nlattr *tb[TCA_MATCHALL_MAX + 1]; + int err; + + err = tca_parse(tb, TCA_MATCHALL_MAX, tc, mall_policy); + if (err < 0) + return err; + + if (tb[TCA_MATCHALL_CLASSID]) { + mall->m_classid = nla_get_u32(tb[TCA_MATCHALL_CLASSID]); + mall->m_mask |= MALL_ATTR_CLASSID; + } + + if (tb[TCA_MATCHALL_FLAGS]) { + mall->m_flags = nla_get_u32(tb[TCA_MATCHALL_FLAGS]); + mall->m_mask |= MALL_ATTR_FLAGS; + } + + if (tb[TCA_MATCHALL_ACT]) { + mall->m_mask |= MALL_ATTR_ACTION; + err = rtnl_act_parse(&mall->m_act, tb[TCA_MATCHALL_ACT]); + if (err < 0) + return err; + } + + return 0; +} + +static int mall_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + struct rtnl_mall *mall = data; + + if (!mall) + return 0; + + if (mall->m_mask & MALL_ATTR_CLASSID) + NLA_PUT_U32(msg, TCA_MATCHALL_CLASSID, mall->m_classid); + + if (mall->m_mask & MALL_ATTR_FLAGS) + NLA_PUT_U32(msg, TCA_MATCHALL_FLAGS, mall->m_flags); + + if (mall->m_mask & MALL_ATTR_ACTION) { + int err; + + err = rtnl_act_fill(msg, TCA_MATCHALL_ACT, mall->m_act); + if (err < 0) + return err; + } + + return 0; + +nla_put_failure: + return -NLE_NOMEM; +} + +static int mall_clone(void *_dst, void *_src) +{ + struct rtnl_mall *dst = _dst, *src = _src; + struct rtnl_act *next, *new; + int err; + + if (src->m_act) { + if (!(dst->m_act = rtnl_act_alloc())) + return -NLE_NOMEM; + + /* action nl list next and prev pointers must be updated */ + nl_init_list_head(&dst->m_act->ce_list); + + memcpy(dst->m_act, src->m_act, sizeof(struct rtnl_act)); + next = rtnl_act_next(src->m_act); + while (next) { + new = (struct rtnl_act *) nl_object_clone((struct nl_object *) next); + if (!new) + return -NLE_NOMEM; + + err = rtnl_act_append(&dst->m_act, new); + if (err < 0) + return err; + + next = rtnl_act_next(next); + } + } + + return 0; +} + +static void mall_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_mall *mall = data; + char buf[32]; + + if (!mall) + return; + + if (mall->m_mask & MALL_ATTR_CLASSID) + nl_dump(p, " target %s", + rtnl_tc_handle2str(mall->m_classid, buf, sizeof(buf))); +} + +static void mall_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_mall *mall = data; + + if (!mall) + return; + + nl_dump(p, "no details for match-all"); +} + +static struct rtnl_tc_ops mall_ops = { + .to_kind = "matchall", + .to_type = RTNL_TC_TYPE_CLS, + .to_size = sizeof(struct rtnl_mall), + .to_msg_parser = mall_msg_parser, + .to_free_data = mall_free_data, + .to_clone = mall_clone, + .to_msg_fill = mall_msg_fill, + .to_dump = { + [NL_DUMP_LINE] = mall_dump_line, + [NL_DUMP_DETAILS] = mall_dump_details, + }, +}; + +static void __init mall_init(void) +{ + rtnl_tc_register(&mall_ops); +} + +static void __exit mall_exit(void) +{ + rtnl_tc_unregister(&mall_ops); +} + +/** @} */ diff --git a/libnl/lib/route/cls/police.c b/libnl/lib/route/cls/police.c new file mode 100644 index 0000000..09f1af5 --- /dev/null +++ b/libnl/lib/route/cls/police.c @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +/** + * @name Policer Type + * @{ + */ + +static const struct trans_tbl police_types[] = { + __ADD(TC_POLICE_UNSPEC,unspec), + __ADD(TC_POLICE_OK,ok), + __ADD(TC_POLICE_RECLASSIFY,reclassify), + __ADD(TC_POLICE_SHOT,shot), +#ifdef TC_POLICE_PIPE + __ADD(TC_POLICE_PIPE,pipe), +#endif +}; + +/** + * Transform a policer type number into a character string (Reentrant). + * @arg type policer type + * @arg buf destination buffer + * @arg len buffer length + * + * Transforms a policer type number into a character string and stores + * it in the provided buffer. + * + * @return The destination buffer or the type encoded in hex if no match was found. + */ +char * nl_police2str(int type, char *buf, size_t len) +{ + return __type2str(type, buf, len, police_types, + ARRAY_SIZE(police_types)); +} + +/** + * Transform a character string into a policer type number + * @arg name policer type name + * + * Transform the provided character string specifying a policer + * type into the corresponding numeric value + * + * @return Policer type number or a negative value. + */ +int nl_str2police(const char *name) +{ + return __str2type(name, police_types, ARRAY_SIZE(police_types)); +} + +/** @} */ diff --git a/libnl/lib/route/cls/u32.c b/libnl/lib/route/cls/u32.c new file mode 100644 index 0000000..0276d74 --- /dev/null +++ b/libnl/lib/route/cls/u32.c @@ -0,0 +1,830 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + * Copyright (c) 2005-2006 Petr Gotthard + * Copyright (c) 2005-2006 Siemens AG Oesterreich + */ + +/** + * @ingroup cls + * @defgroup cls_u32 Universal 32-bit Classifier + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define U32_ATTR_DIVISOR 0x001 +#define U32_ATTR_HASH 0x002 +#define U32_ATTR_CLASSID 0x004 +#define U32_ATTR_LINK 0x008 +#define U32_ATTR_PCNT 0x010 +#define U32_ATTR_SELECTOR 0x020 +#define U32_ATTR_ACTION 0x040 +#define U32_ATTR_POLICE 0x080 +#define U32_ATTR_INDEV 0x100 +#define U32_ATTR_MARK 0x200 +/** @endcond */ + +static inline struct tc_u32_sel *u32_selector(struct rtnl_u32 *u) +{ + return (struct tc_u32_sel *) u->cu_selector->d_data; +} + +static inline struct tc_u32_sel *u32_selector_alloc(struct rtnl_u32 *u) +{ + if (!u->cu_selector) + u->cu_selector = nl_data_alloc(NULL, sizeof(struct tc_u32_sel)); + + return u32_selector(u); +} + +static inline struct tc_u32_mark *u32_mark_alloc(struct rtnl_u32 *u) +{ + if (!u->cu_mark) + u->cu_mark = nl_data_alloc(NULL, sizeof(struct tc_u32_mark)); + + return (struct tc_u32_mark *) u->cu_mark->d_data; +} + +static struct nla_policy u32_policy[TCA_U32_MAX+1] = { + [TCA_U32_DIVISOR] = { .type = NLA_U32 }, + [TCA_U32_HASH] = { .type = NLA_U32 }, + [TCA_U32_CLASSID] = { .type = NLA_U32 }, + [TCA_U32_LINK] = { .type = NLA_U32 }, + [TCA_U32_INDEV] = { .type = NLA_STRING, + .maxlen = IFNAMSIZ }, + [TCA_U32_SEL] = { .minlen = sizeof(struct tc_u32_sel) }, + [TCA_U32_PCNT] = { .minlen = sizeof(struct tc_u32_pcnt) }, + [TCA_U32_MARK] = { .minlen = sizeof(struct tc_u32_mark) } +}; + +static int u32_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct rtnl_u32 *u = data; + struct nlattr *tb[TCA_U32_MAX + 1]; + int err; + + err = tca_parse(tb, TCA_U32_MAX, tc, u32_policy); + if (err < 0) + return err; + + if (tb[TCA_U32_DIVISOR]) { + u->cu_divisor = nla_get_u32(tb[TCA_U32_DIVISOR]); + u->cu_mask |= U32_ATTR_DIVISOR; + } + + if (tb[TCA_U32_SEL]) { + u->cu_selector = nl_data_alloc_attr(tb[TCA_U32_SEL]); + if (!u->cu_selector) + goto errout_nomem; + u->cu_mask |= U32_ATTR_SELECTOR; + } + + if (tb[TCA_U32_MARK]) { + u->cu_mark = nl_data_alloc_attr(tb[TCA_U32_MARK]); + if (!u->cu_mark) + goto errout_nomem; + u->cu_mask |= U32_ATTR_MARK; + } + + if (tb[TCA_U32_HASH]) { + u->cu_hash = nla_get_u32(tb[TCA_U32_HASH]); + u->cu_mask |= U32_ATTR_HASH; + } + + if (tb[TCA_U32_CLASSID]) { + u->cu_classid = nla_get_u32(tb[TCA_U32_CLASSID]); + u->cu_mask |= U32_ATTR_CLASSID; + } + + if (tb[TCA_U32_LINK]) { + u->cu_link = nla_get_u32(tb[TCA_U32_LINK]); + u->cu_mask |= U32_ATTR_LINK; + } + + if (tb[TCA_U32_ACT]) { + u->cu_mask |= U32_ATTR_ACTION; + err = rtnl_act_parse(&u->cu_act, tb[TCA_U32_ACT]); + if (err < 0) + return err; + } + + if (tb[TCA_U32_POLICE]) { + u->cu_police = nl_data_alloc_attr(tb[TCA_U32_POLICE]); + if (!u->cu_police) + goto errout_nomem; + u->cu_mask |= U32_ATTR_POLICE; + } + + if (tb[TCA_U32_PCNT]) { + struct tc_u32_sel *sel; + size_t pcnt_size; + + if (!tb[TCA_U32_SEL]) { + err = -NLE_MISSING_ATTR; + goto errout; + } + + sel = u->cu_selector->d_data; + pcnt_size = sizeof(struct tc_u32_pcnt) + + (sel->nkeys * sizeof(uint64_t)); + if (nla_len(tb[TCA_U32_PCNT]) < pcnt_size) { + err = -NLE_INVAL; + goto errout; + } + + u->cu_pcnt = nl_data_alloc_attr(tb[TCA_U32_PCNT]); + if (!u->cu_pcnt) + goto errout_nomem; + u->cu_mask |= U32_ATTR_PCNT; + } + + if (tb[TCA_U32_INDEV]) { + nla_strlcpy(u->cu_indev, tb[TCA_U32_INDEV], IFNAMSIZ); + u->cu_mask |= U32_ATTR_INDEV; + } + + return 0; + +errout_nomem: + err = -NLE_NOMEM; +errout: + return err; +} + +static void u32_free_data(struct rtnl_tc *tc, void *data) +{ + struct rtnl_u32 *u = data; + + if (u->cu_act) + rtnl_act_put_all(&u->cu_act); + nl_data_free(u->cu_mark); + nl_data_free(u->cu_selector); + nl_data_free(u->cu_police); + nl_data_free(u->cu_pcnt); +} + +static int u32_clone(void *_dst, void *_src) +{ + struct rtnl_u32 *dst = _dst, *src = _src; + + if (src->cu_selector && + !(dst->cu_selector = nl_data_clone(src->cu_selector))) + return -NLE_NOMEM; + + if (src->cu_mark && + !(dst->cu_mark = nl_data_clone(src->cu_mark))) + return -NLE_NOMEM; + + if (src->cu_act) { + if (!(dst->cu_act = rtnl_act_alloc())) + return -NLE_NOMEM; + + memcpy(dst->cu_act, src->cu_act, sizeof(struct rtnl_act)); + } + + if (src->cu_police && !(dst->cu_police = nl_data_clone(src->cu_police))) + return -NLE_NOMEM; + + if (src->cu_pcnt && !(dst->cu_pcnt = nl_data_clone(src->cu_pcnt))) + return -NLE_NOMEM; + + return 0; +} + +static void u32_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_u32 *u = data; + char buf[32]; + + if (!u) + return; + + if (u->cu_mask & U32_ATTR_DIVISOR) + nl_dump(p, " divisor %u", u->cu_divisor); + else if (u->cu_mask & U32_ATTR_CLASSID) + nl_dump(p, " target %s", + rtnl_tc_handle2str(u->cu_classid, buf, sizeof(buf))); +} + +static void print_selector(struct nl_dump_params *p, struct tc_u32_sel *sel, + struct rtnl_u32 *u) +{ + int i; + struct tc_u32_key *key; + + if (sel->hmask || sel->hoff) { + /* I guess this will never be used since the kernel only + * exports the selector if no divisor is set but hash offset + * and hash mask make only sense in hash filters with divisor + * set */ + nl_dump(p, " hash at %u & 0x%x", sel->hoff, sel->hmask); + } + + if (sel->flags & (TC_U32_OFFSET | TC_U32_VAROFFSET)) { + nl_dump(p, " offset at %u", sel->off); + + if (sel->flags & TC_U32_VAROFFSET) + nl_dump(p, " variable (at %u & 0x%x) >> %u", + sel->offoff, ntohs(sel->offmask), sel->offshift); + } + + if (sel->flags) { + int flags = sel->flags; + nl_dump(p, " <"); + +#define PRINT_FLAG(f) if (flags & TC_U32_##f) { \ + flags &= ~TC_U32_##f; nl_dump(p, #f "%s", flags ? "," : ""); } + + PRINT_FLAG(TERMINAL); + PRINT_FLAG(OFFSET); + PRINT_FLAG(VAROFFSET); + PRINT_FLAG(EAT); +#undef PRINT_FLAG + + nl_dump(p, ">"); + } + + + for (i = 0; i < sel->nkeys; i++) { + key = &sel->keys[i]; + + nl_dump(p, "\n"); + nl_dump_line(p, " match key at %s%u ", + key->offmask ? "nexthdr+" : "", key->off); + + if (key->offmask) + nl_dump(p, "[0x%u] ", key->offmask); + + nl_dump(p, "& 0x%08x == 0x%08x", ntohl(key->mask), ntohl(key->val)); + + if (p->dp_type == NL_DUMP_STATS && + (u->cu_mask & U32_ATTR_PCNT)) { + struct tc_u32_pcnt *pcnt = u->cu_pcnt->d_data; + nl_dump(p, " successful %" PRIu64, pcnt->kcnts[i]); + } + } +} + +static void u32_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_u32 *u = data; + struct tc_u32_sel *s = NULL; + struct tc_u32_mark *m; + + if (!u) + return; + + if (!(u->cu_mask & (U32_ATTR_SELECTOR & U32_ATTR_MARK))) { + nl_dump(p, "no-selector no-mark\n"); + return; + } + + if (!(u->cu_mask & U32_ATTR_SELECTOR)) { + nl_dump(p, "no-selector"); + } else { + s = u->cu_selector->d_data; + nl_dump(p, "nkeys %u", s->nkeys); + } + + if (!(u->cu_mask & U32_ATTR_MARK)) { + nl_dump(p, " no-mark"); + } else { + m = u->cu_mark->d_data; + nl_dump(p, " mark 0x%u 0x%u", m->val, m->mask); + } + + if (u->cu_mask & U32_ATTR_HASH) + nl_dump(p, " ht key 0x%x hash 0x%u", + TC_U32_USERHTID(u->cu_hash), TC_U32_HASH(u->cu_hash)); + + if (u->cu_mask & U32_ATTR_LINK) + nl_dump(p, " link %u", u->cu_link); + + if (u->cu_mask & U32_ATTR_INDEV) + nl_dump(p, " indev %s", u->cu_indev); + + if (u->cu_mask & U32_ATTR_SELECTOR) + print_selector(p, s, u); + + nl_dump(p, "\n"); +} + +static void u32_dump_stats(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_u32 *u = data; + + if (!u) + return; + + if (u->cu_mask & U32_ATTR_PCNT) { + struct tc_u32_pcnt *pc = u->cu_pcnt->d_data; + nl_dump(p, "\n"); + nl_dump_line(p, " hit %8" PRIu64 " count %8" PRIu64 "\n", + pc->rhit, pc->rcnt); + } +} + +static int u32_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + struct rtnl_u32 *u = data; + + if (!u) + return 0; + + if (u->cu_mask & U32_ATTR_DIVISOR) + NLA_PUT_U32(msg, TCA_U32_DIVISOR, u->cu_divisor); + + if (u->cu_mask & U32_ATTR_HASH) + NLA_PUT_U32(msg, TCA_U32_HASH, u->cu_hash); + + if (u->cu_mask & U32_ATTR_CLASSID) + NLA_PUT_U32(msg, TCA_U32_CLASSID, u->cu_classid); + + if (u->cu_mask & U32_ATTR_LINK) + NLA_PUT_U32(msg, TCA_U32_LINK, u->cu_link); + + if (u->cu_mask & U32_ATTR_SELECTOR) + NLA_PUT_DATA(msg, TCA_U32_SEL, u->cu_selector); + + if (u->cu_mask & U32_ATTR_MARK) + NLA_PUT_DATA(msg, TCA_U32_MARK, u->cu_mark); + + if (u->cu_mask & U32_ATTR_ACTION) { + int err; + + err = rtnl_act_fill(msg, TCA_U32_ACT, u->cu_act); + if (err < 0) + return err; + } + + if (u->cu_mask & U32_ATTR_POLICE) + NLA_PUT_DATA(msg, TCA_U32_POLICE, u->cu_police); + + if (u->cu_mask & U32_ATTR_INDEV) + NLA_PUT_STRING(msg, TCA_U32_INDEV, u->cu_indev); + + return 0; + +nla_put_failure: + return -NLE_NOMEM; +} + +/** + * @name Attribute Modifications + * @{ + */ + +void rtnl_u32_set_handle(struct rtnl_cls *cls, int htid, int hash, + int nodeid) +{ + uint32_t handle = (htid << 20) | (hash << 12) | nodeid; + + rtnl_tc_set_handle((struct rtnl_tc *) cls, handle ); +} + +int rtnl_u32_set_classid(struct rtnl_cls *cls, uint32_t classid) +{ + struct rtnl_u32 *u; + + if (!(u = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + u->cu_classid = classid; + u->cu_mask |= U32_ATTR_CLASSID; + + return 0; +} + +int rtnl_u32_get_classid(struct rtnl_cls *cls, uint32_t *classid) +{ + struct rtnl_u32 *u; + + if (!(u = rtnl_tc_data_peek(TC_CAST(cls)))) + return -NLE_INVAL; + + if (!(u->cu_mask & U32_ATTR_CLASSID)) + return -NLE_INVAL; + + *classid = u->cu_classid; + return 0; +} + +int rtnl_u32_set_divisor(struct rtnl_cls *cls, uint32_t divisor) +{ + struct rtnl_u32 *u; + + if (!(u = (struct rtnl_u32 *) rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + u->cu_divisor = divisor; + u->cu_mask |= U32_ATTR_DIVISOR; + return 0; +} + +int rtnl_u32_set_link(struct rtnl_cls *cls, uint32_t link) +{ + struct rtnl_u32 *u; + + if (!(u = (struct rtnl_u32 *) rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + u->cu_link = link; + u->cu_mask |= U32_ATTR_LINK; + return 0; +} + +int rtnl_u32_set_hashtable(struct rtnl_cls *cls, uint32_t ht) +{ + struct rtnl_u32 *u; + + if (!(u = (struct rtnl_u32 *) rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + u->cu_hash = ht; + u->cu_mask |= U32_ATTR_HASH; + return 0; +} + +int rtnl_u32_set_hashmask(struct rtnl_cls *cls, uint32_t hashmask, uint32_t offset) +{ + struct rtnl_u32 *u; + struct tc_u32_sel *sel; + + hashmask = htonl(hashmask); + + if (!(u = (struct rtnl_u32 *) rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + sel = u32_selector_alloc(u); + if (!sel) + return -NLE_NOMEM; + + sel->hmask = hashmask; + sel->hoff = offset; + return 0; +} + +int rtnl_u32_set_selector(struct rtnl_cls *cls, int offoff, uint32_t offmask, char offshift, uint16_t off, char flags) +{ + struct rtnl_u32 *u; + struct tc_u32_sel *sel; + + offmask = ntohs(offmask); + + if (!(u = (struct rtnl_u32 *) rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + sel = u32_selector_alloc(u); + if (!sel) + return -NLE_NOMEM; + + sel->offoff = offoff; + sel->offmask = offmask; + sel->offshift = offshift; + sel->flags |= TC_U32_VAROFFSET; + sel->off = off; + sel->flags |= flags; + return 0; +} + +int rtnl_u32_set_cls_terminal(struct rtnl_cls *cls) +{ + struct rtnl_u32 *u; + struct tc_u32_sel *sel; + + if (!(u = (struct rtnl_u32 *) rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + sel = u32_selector_alloc(u); + if (!sel) + return -NLE_NOMEM; + + sel->flags |= TC_U32_TERMINAL; + return 0; +} + +int rtnl_u32_add_action(struct rtnl_cls *cls, struct rtnl_act *act) +{ + struct rtnl_u32 *u; + int err; + + if (!act) + return 0; + + if (!(u = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + u->cu_mask |= U32_ATTR_ACTION; + if ((err = rtnl_act_append(&u->cu_act, act))) + return err; + + /* In case user frees it */ + rtnl_act_get(act); + return 0; +} + +struct rtnl_act* rtnl_u32_get_action(struct rtnl_cls *cls) +{ + struct rtnl_u32 *u; + + if (!(u = rtnl_tc_data_peek(TC_CAST(cls)))) + return NULL; + + if (!(u->cu_mask & U32_ATTR_ACTION)) + return NULL; + + return u->cu_act; +} + +int rtnl_u32_del_action(struct rtnl_cls *cls, struct rtnl_act *act) +{ + struct rtnl_u32 *u; + int ret; + + if (!act) + return 0; + + if (!(u = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + if (!(u->cu_mask & U32_ATTR_ACTION)) + return -NLE_INVAL; + + ret = rtnl_act_remove(&u->cu_act, act); + if (ret) + return ret; + + if (!u->cu_act) + u->cu_mask &= ~U32_ATTR_ACTION; + rtnl_act_put(act); + return 0; +} +/** @} */ + +/** + * @name Selector Modifications + * @{ + */ + +int rtnl_u32_set_flags(struct rtnl_cls *cls, int flags) +{ + struct tc_u32_sel *sel; + struct rtnl_u32 *u; + + if (!(u = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + sel = u32_selector_alloc(u); + if (!sel) + return -NLE_NOMEM; + + sel->flags |= flags; + u->cu_mask |= U32_ATTR_SELECTOR; + + return 0; +} + +/** + * Append new 32-bit key to the selector + * + * @arg cls classifier to be modifier + * @arg val value to be matched (network byte-order) + * @arg mask mask to be applied before matching (network byte-order) + * @arg off offset, in bytes, to start matching + * @arg offmask offset mask + * + * General selectors define the pattern, mask and offset the pattern will be + * matched to the packet contents. Using the general selectors you can match + * virtually any single bit in the IP (or upper layer) header. + * +*/ +int rtnl_u32_add_key(struct rtnl_cls *cls, uint32_t val, uint32_t mask, + int off, int offmask) +{ + struct tc_u32_sel *sel; + struct rtnl_u32 *u; + int err; + + if (!(u = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + sel = u32_selector_alloc(u); + if (!sel) + return -NLE_NOMEM; + + if (sel->nkeys == UCHAR_MAX) + return -NLE_NOMEM; + + err = nl_data_append(u->cu_selector, NULL, sizeof(struct tc_u32_key)); + if (err < 0) + return err; + + /* the selector might have been moved by realloc */ + sel = u32_selector(u); + + sel->keys[sel->nkeys].mask = mask; + sel->keys[sel->nkeys].val = val & mask; + sel->keys[sel->nkeys].off = off; + sel->keys[sel->nkeys].offmask = offmask; + sel->nkeys++; + u->cu_mask |= U32_ATTR_SELECTOR; + + return 0; +} + +int rtnl_u32_add_mark(struct rtnl_cls *cls, uint32_t val, uint32_t mask) +{ + struct tc_u32_mark *mark; + struct rtnl_u32 *u; + + if (!(u = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + mark = u32_mark_alloc(u); + if (!mark) + return -NLE_NOMEM; + + mark->mask = mask; + mark->val = val; + + u->cu_mask |= U32_ATTR_MARK; + + return 0; +} + +int rtnl_u32_del_mark(struct rtnl_cls *cls) +{ + struct rtnl_u32 *u; + + if (!(u = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + if (!(u->cu_mask)) + return -NLE_INVAL; + + if (!(u->cu_mask & U32_ATTR_MARK)) + return -NLE_INVAL; + + nl_data_free(u->cu_mark); + u->cu_mark = NULL; + u->cu_mask &= ~U32_ATTR_MARK; + + return 0; +} + +/** + * Get the 32-bit key from the selector + * + * @arg cls classifier to be retrieve + * @arg index the index of the array of keys, start with 0 + * @arg val pointer to store value after masked (network byte-order) + * @arg mask pointer to store the mask (network byte-order) + * @arg off pointer to store the offset + * @arg offmask pointer to store offset mask + * +*/ +int rtnl_u32_get_key(struct rtnl_cls *cls, uint8_t index, + uint32_t *val, uint32_t *mask, int *off, int *offmask) +{ + struct tc_u32_sel *sel; + struct rtnl_u32 *u; + + if (!(u = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + if (!(u->cu_mask & U32_ATTR_SELECTOR)) + return -NLE_INVAL; + + sel = u32_selector(u); + if (index >= sel->nkeys) + return -NLE_RANGE; + + *mask = sel->keys[index].mask; + *val = sel->keys[index].val; + *off = sel->keys[index].off; + *offmask = sel->keys[index].offmask; + return 0; +} + + +int rtnl_u32_add_key_uint8(struct rtnl_cls *cls, uint8_t val, uint8_t mask, + int off, int offmask) +{ + int shift = 24 - 8 * (off & 3); + + return rtnl_u32_add_key(cls, htonl((uint32_t)val << shift), + htonl((uint32_t)mask << shift), + off & ~3, offmask); +} + +/** + * Append new selector key to match a 16-bit number + * + * @arg cls classifier to be modified + * @arg val value to be matched (host byte-order) + * @arg mask mask to be applied before matching (host byte-order) + * @arg off offset, in bytes, to start matching + * @arg offmask offset mask +*/ +int rtnl_u32_add_key_uint16(struct rtnl_cls *cls, uint16_t val, uint16_t mask, + int off, int offmask) +{ + int shift = ((off & 3) == 0 ? 16 : 0); + if (off % 2) + return -NLE_INVAL; + + return rtnl_u32_add_key(cls, htonl((uint32_t)val << shift), + htonl((uint32_t)mask << shift), + off & ~3, offmask); +} + +/** + * Append new selector key to match a 32-bit number + * + * @arg cls classifier to be modified + * @arg val value to be matched (host byte-order) + * @arg mask mask to be applied before matching (host byte-order) + * @arg off offset, in bytes, to start matching + * @arg offmask offset mask +*/ +int rtnl_u32_add_key_uint32(struct rtnl_cls *cls, uint32_t val, uint32_t mask, + int off, int offmask) +{ + return rtnl_u32_add_key(cls, htonl(val), htonl(mask), + off & ~3, offmask); +} + +int rtnl_u32_add_key_in_addr(struct rtnl_cls *cls, const struct in_addr *addr, + uint8_t bitmask, int off, int offmask) +{ + uint32_t mask = 0xFFFFFFFF << (32 - bitmask); + return rtnl_u32_add_key(cls, addr->s_addr, htonl(mask), off, offmask); +} + +int rtnl_u32_add_key_in6_addr(struct rtnl_cls *cls, const struct in6_addr *addr, + uint8_t bitmask, int off, int offmask) +{ + int i, err; + + for (i = 1; i <= 4; i++) { + if (32 * i - bitmask <= 0) { + if ((err = rtnl_u32_add_key(cls, addr->s6_addr32[i-1], + 0xFFFFFFFF, off+4*(i-1), offmask)) < 0) + return err; + } + else if (32 * i - bitmask < 32) { + uint32_t mask = 0xFFFFFFFF << (32 * i - bitmask); + if ((err = rtnl_u32_add_key(cls, addr->s6_addr32[i-1], + htonl(mask), off+4*(i-1), offmask)) < 0) + return err; + } + /* otherwise, if (32*i - bitmask >= 32) no key is generated */ + } + + return 0; +} + +/** @} */ + +static struct rtnl_tc_ops u32_ops = { + .to_kind = "u32", + .to_type = RTNL_TC_TYPE_CLS, + .to_size = sizeof(struct rtnl_u32), + .to_msg_parser = u32_msg_parser, + .to_free_data = u32_free_data, + .to_clone = u32_clone, + .to_msg_fill = u32_msg_fill, + .to_dump = { + [NL_DUMP_LINE] = u32_dump_line, + [NL_DUMP_DETAILS] = u32_dump_details, + [NL_DUMP_STATS] = u32_dump_stats, + }, +}; + +static void __init u32_init(void) +{ + rtnl_tc_register(&u32_ops); +} + +static void __exit u32_exit(void) +{ + rtnl_tc_unregister(&u32_ops); +} + +/** @} */ diff --git a/libnl/lib/route/link.c b/libnl/lib/route/link.c new file mode 100644 index 0000000..629ca03 --- /dev/null +++ b/libnl/lib/route/link.c @@ -0,0 +1,3199 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +/** + * @ingroup rtnl + * @defgroup link Links (Interfaces) + * + * @details + * @route_doc{route_link, Link Documentation} + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define LINK_ATTR_MTU (1 << 0) +#define LINK_ATTR_LINK (1 << 1) +#define LINK_ATTR_TXQLEN (1 << 2) +#define LINK_ATTR_WEIGHT (1 << 3) +#define LINK_ATTR_MASTER (1 << 4) +#define LINK_ATTR_QDISC (1 << 5) +#define LINK_ATTR_MAP (1 << 6) +#define LINK_ATTR_ADDR (1 << 7) +#define LINK_ATTR_BRD (1 << 8) +#define LINK_ATTR_FLAGS (1 << 9) +#define LINK_ATTR_IFNAME (1 << 10) +#define LINK_ATTR_IFINDEX (1 << 11) +#define LINK_ATTR_FAMILY (1 << 12) +#define LINK_ATTR_ARPTYPE (1 << 13) +#define LINK_ATTR_STATS (1 << 14) +#define LINK_ATTR_CHANGE (1 << 15) +#define LINK_ATTR_OPERSTATE (1 << 16) +#define LINK_ATTR_LINKMODE (1 << 17) +#define LINK_ATTR_LINKINFO (1 << 18) +#define LINK_ATTR_IFALIAS (1 << 19) +#define LINK_ATTR_NUM_VF (1 << 20) +#define LINK_ATTR_PROMISCUITY (1 << 21) +#define LINK_ATTR_NUM_TX_QUEUES (1 << 22) +#define LINK_ATTR_NUM_RX_QUEUES (1 << 23) +#define LINK_ATTR_GROUP (1 << 24) +#define LINK_ATTR_CARRIER (1 << 25) +#define LINK_ATTR_PROTINFO (1 << 26) +#define LINK_ATTR_AF_SPEC (1 << 27) +#define LINK_ATTR_PHYS_PORT_ID (1 << 28) +#define LINK_ATTR_NS_FD (1 << 29) +#define LINK_ATTR_NS_PID (1 << 30) +/* 31 used by 32-bit api */ +#define LINK_ATTR_LINK_NETNSID ((uint64_t) 1 << 32) +#define LINK_ATTR_VF_LIST ((uint64_t) 1 << 33) +#define LINK_ATTR_CARRIER_CHANGES ((uint64_t) 1 << 34) +#define LINK_ATTR_PHYS_PORT_NAME ((uint64_t) 1 << 35) +#define LINK_ATTR_PHYS_SWITCH_ID ((uint64_t) 1 << 36) +#define LINK_ATTR_GSO_MAX_SEGS ((uint64_t) 1 << 37) +#define LINK_ATTR_GSO_MAX_SIZE ((uint64_t) 1 << 38) +#define LINK_ATTR_LINKINFO_SLAVE_KIND ((uint64_t) 1 << 39) + +static struct nl_cache_ops rtnl_link_ops; +static struct nl_object_ops link_obj_ops; +/** @endcond */ + +struct rtnl_link *link_lookup(struct nl_cache *cache, int ifindex) +{ + if (!cache) { + cache = __nl_cache_mngt_require("route/link"); + if (!cache) + return NULL; + } + + return rtnl_link_get(cache, ifindex); +} + +static struct rtnl_link_af_ops *af_lookup_and_alloc(struct rtnl_link *link, + int family) +{ + struct rtnl_link_af_ops *af_ops; + void *data; + + af_ops = rtnl_link_af_ops_lookup(family); + if (!af_ops) + return NULL; + + if (!(data = rtnl_link_af_alloc(link, af_ops))) { + rtnl_link_af_ops_put(af_ops); + return NULL; + } + + return af_ops; +} + +static int af_free(struct rtnl_link *link, struct rtnl_link_af_ops *ops, + void *data, void *arg) +{ + if (ops->ao_free) + ops->ao_free(link, data); + + rtnl_link_af_ops_put(ops); + + return 0; +} + +static int af_request_type(int af_type, struct rtnl_link *changes) +{ + struct rtnl_link_af_ops *ops; + + ops = rtnl_link_af_ops_lookup(af_type); + if (ops && ops->ao_override_rtm(changes)) + return RTM_SETLINK; + + return RTM_NEWLINK; +} + +static int af_clone(struct rtnl_link *link, struct rtnl_link_af_ops *ops, + void *data, void *arg) +{ + struct rtnl_link *dst = arg; + + if (ops->ao_clone && + !(dst->l_af_data[ops->ao_family] = ops->ao_clone(dst, data))) + return -NLE_NOMEM; + + return 0; +} + +static int af_fill(struct rtnl_link *link, struct rtnl_link_af_ops *ops, + void *data, void *arg) +{ + struct nl_msg *msg = arg; + struct nlattr *af_attr = NULL; + int err; + + if (!ops->ao_fill_af) + return 0; + + if (!ops->ao_fill_af_no_nest) + if (!(af_attr = nla_nest_start(msg, ops->ao_family))) + return -NLE_MSGSIZE; + + if ((err = ops->ao_fill_af(link, arg, data)) < 0) + return err; + + if (!ops->ao_fill_af_no_nest) + nla_nest_end(msg, af_attr); + + return 0; +} + +static int af_fill_pi(struct rtnl_link *link, struct rtnl_link_af_ops *ops, + void *data, void *arg) +{ + struct nl_msg *msg = arg; + struct nlattr *pi_attr; + int err, pi_type = IFLA_PROTINFO; + + if (!ops->ao_fill_pi) + return 0; + + if (ops->ao_fill_pi_flags > 0) + pi_type |= ops->ao_fill_pi_flags; + + if (!(pi_attr = nla_nest_start(msg, pi_type))) + return -NLE_MSGSIZE; + + if ((err = ops->ao_fill_pi(link, arg, data)) < 0) + return err; + + nla_nest_end(msg, pi_attr); + + return 0; +} + +static int af_dump_line(struct rtnl_link *link, struct rtnl_link_af_ops *ops, + void *data, void *arg) +{ + struct nl_dump_params *p = arg; + + if (ops->ao_dump[NL_DUMP_LINE]) + ops->ao_dump[NL_DUMP_LINE](link, p, data); + + return 0; +} + +static int af_dump_details(struct rtnl_link *link, struct rtnl_link_af_ops *ops, + void *data, void *arg) +{ + struct nl_dump_params *p = arg; + + if (ops->ao_dump[NL_DUMP_DETAILS]) + ops->ao_dump[NL_DUMP_DETAILS](link, p, data); + + return 0; +} + +static int af_dump_stats(struct rtnl_link *link, struct rtnl_link_af_ops *ops, + void *data, void *arg) +{ + struct nl_dump_params *p = arg; + + if (ops->ao_dump[NL_DUMP_STATS]) + ops->ao_dump[NL_DUMP_STATS](link, p, data); + + return 0; +} + +static int do_foreach_af(struct rtnl_link *link, + int (*cb)(struct rtnl_link *, + struct rtnl_link_af_ops *, void *, void *), + void *arg) +{ + int i, err; + + for (i = 0; i < AF_MAX; i++) { + if (link->l_af_data[i]) { + _nl_auto_rtnl_link_af_ops struct rtnl_link_af_ops *ops = NULL; + + if (!(ops = rtnl_link_af_ops_lookup(i))) + BUG(); + + err = cb(link, ops, link->l_af_data[i], arg); + if (err < 0) + return err; + } + } + + return 0; +} + +static void release_link_info(struct rtnl_link *link) +{ + struct rtnl_link_info_ops *io = link->l_info_ops; + + if (io != NULL) { + if (io->io_free) + io->io_free(link); + else { + /* Catch missing io_free() implementations */ + BUG_ON(link->l_info); + } + rtnl_link_info_ops_put(io); + link->l_info_ops = NULL; + } +} + +static void link_free_data(struct nl_object *c) +{ + struct rtnl_link *link = nl_object_priv(c); + + if (link) { + release_link_info(link); + + /* proto info af reference */ + rtnl_link_af_ops_put(link->l_af_ops); + + nl_addr_put(link->l_addr); + nl_addr_put(link->l_bcast); + + free(link->l_ifalias); + free(link->l_info_kind); + free(link->l_info_slave_kind); + + do_foreach_af(link, af_free, NULL); + + nl_data_free(link->l_phys_port_id); + nl_data_free(link->l_phys_switch_id); + + if (link->ce_mask & LINK_ATTR_VF_LIST) + rtnl_link_sriov_free_data(link); + } +} + +static int link_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct rtnl_link *dst = nl_object_priv(_dst); + struct rtnl_link *src = nl_object_priv(_src); + int err; + + if (src->l_addr) + if (!(dst->l_addr = nl_addr_clone(src->l_addr))) + return -NLE_NOMEM; + + if (src->l_bcast) + if (!(dst->l_bcast = nl_addr_clone(src->l_bcast))) + return -NLE_NOMEM; + + if (src->l_ifalias) + if (!(dst->l_ifalias = strdup(src->l_ifalias))) + return -NLE_NOMEM; + + if (src->l_info_kind) + if (!(dst->l_info_kind = strdup(src->l_info_kind))) + return -NLE_NOMEM; + + if (src->l_info_slave_kind) + if (!(dst->l_info_slave_kind = strdup(src->l_info_slave_kind))) + return -NLE_NOMEM; + + if (src->l_info_ops && src->l_info_ops->io_clone) { + err = src->l_info_ops->io_clone(dst, src); + if (err < 0) + return err; + } + + if ((err = do_foreach_af(src, af_clone, dst)) < 0) + return err; + + if (src->l_phys_port_id) + if (!(dst->l_phys_port_id = nl_data_clone(src->l_phys_port_id))) + return -NLE_NOMEM; + + if (src->l_phys_switch_id) + if (!(dst->l_phys_switch_id = nl_data_clone(src->l_phys_switch_id))) + return -NLE_NOMEM; + + if (src->ce_mask & LINK_ATTR_VF_LIST) + if ((err = rtnl_link_sriov_clone(dst, src)) < 0) + return err; + + return 0; +} + +struct nla_policy rtln_link_policy[IFLA_MAX+1] = { + [IFLA_IFNAME] = { .type = NLA_STRING, + .maxlen = IFNAMSIZ }, + [IFLA_MTU] = { .type = NLA_U32 }, + [IFLA_TXQLEN] = { .type = NLA_U32 }, + [IFLA_LINK] = { .type = NLA_U32 }, + [IFLA_WEIGHT] = { .type = NLA_U32 }, + [IFLA_MASTER] = { .type = NLA_U32 }, + [IFLA_OPERSTATE] = { .type = NLA_U8 }, + [IFLA_LINKMODE] = { .type = NLA_U8 }, + [IFLA_LINKINFO] = { .type = NLA_NESTED }, + [IFLA_QDISC] = { .type = NLA_STRING, + .maxlen = IFQDISCSIZ }, + [IFLA_STATS] = { .minlen = _nl_offsetofend (struct rtnl_link_stats, tx_compressed) }, + [IFLA_STATS64] = { .minlen = _nl_offsetofend (struct rtnl_link_stats64, tx_compressed) }, + [IFLA_MAP] = { .minlen = sizeof(struct rtnl_link_ifmap) }, + [IFLA_IFALIAS] = { .type = NLA_STRING, .maxlen = IFALIASZ }, + [IFLA_NUM_VF] = { .type = NLA_U32 }, + [IFLA_VFINFO_LIST] = { .type = NLA_NESTED }, + [IFLA_AF_SPEC] = { .type = NLA_NESTED }, + [IFLA_PROMISCUITY] = { .type = NLA_U32 }, + [IFLA_NUM_TX_QUEUES] = { .type = NLA_U32 }, + [IFLA_NUM_RX_QUEUES] = { .type = NLA_U32 }, + [IFLA_GSO_MAX_SEGS] = { .type = NLA_U32 }, + [IFLA_GSO_MAX_SIZE] = { .type = NLA_U32 }, + [IFLA_GROUP] = { .type = NLA_U32 }, + [IFLA_CARRIER] = { .type = NLA_U8 }, + [IFLA_CARRIER_CHANGES] = { .type = NLA_U32 }, + [IFLA_PHYS_PORT_ID] = { .type = NLA_UNSPEC }, + [IFLA_PHYS_PORT_NAME] = { .type = NLA_STRING, .maxlen = IFNAMSIZ }, + [IFLA_PHYS_SWITCH_ID] = { .type = NLA_UNSPEC }, + [IFLA_NET_NS_PID] = { .type = NLA_U32 }, + [IFLA_NET_NS_FD] = { .type = NLA_U32 }, +}; + +static struct nla_policy link_info_policy[IFLA_INFO_MAX+1] = { + [IFLA_INFO_KIND] = { .type = NLA_STRING }, + [IFLA_INFO_DATA] = { .type = NLA_NESTED }, + [IFLA_INFO_XSTATS] = { .type = NLA_NESTED }, +}; + +int rtnl_link_info_parse(struct rtnl_link *link, struct nlattr **tb) +{ + if (tb[IFLA_IFNAME] == NULL) + return -NLE_MISSING_ATTR; + + nla_strlcpy(link->l_name, tb[IFLA_IFNAME], IFNAMSIZ); + + + if (tb[IFLA_STATS]) { + struct rtnl_link_stats *st = nla_data(tb[IFLA_STATS]); + + link->l_stats[RTNL_LINK_RX_PACKETS] = st->rx_packets; + link->l_stats[RTNL_LINK_TX_PACKETS] = st->tx_packets; + link->l_stats[RTNL_LINK_RX_BYTES] = st->rx_bytes; + link->l_stats[RTNL_LINK_TX_BYTES] = st->tx_bytes; + link->l_stats[RTNL_LINK_RX_ERRORS] = st->rx_errors; + link->l_stats[RTNL_LINK_TX_ERRORS] = st->tx_errors; + link->l_stats[RTNL_LINK_RX_DROPPED] = st->rx_dropped; + link->l_stats[RTNL_LINK_TX_DROPPED] = st->tx_dropped; + link->l_stats[RTNL_LINK_MULTICAST] = st->multicast; + link->l_stats[RTNL_LINK_COLLISIONS] = st->collisions; + + link->l_stats[RTNL_LINK_RX_LEN_ERR] = st->rx_length_errors; + link->l_stats[RTNL_LINK_RX_OVER_ERR] = st->rx_over_errors; + link->l_stats[RTNL_LINK_RX_CRC_ERR] = st->rx_crc_errors; + link->l_stats[RTNL_LINK_RX_FRAME_ERR] = st->rx_frame_errors; + link->l_stats[RTNL_LINK_RX_FIFO_ERR] = st->rx_fifo_errors; + link->l_stats[RTNL_LINK_RX_MISSED_ERR] = st->rx_missed_errors; + + link->l_stats[RTNL_LINK_TX_ABORT_ERR] = st->tx_aborted_errors; + link->l_stats[RTNL_LINK_TX_CARRIER_ERR] = st->tx_carrier_errors; + link->l_stats[RTNL_LINK_TX_FIFO_ERR] = st->tx_fifo_errors; + link->l_stats[RTNL_LINK_TX_HBEAT_ERR] = st->tx_heartbeat_errors; + link->l_stats[RTNL_LINK_TX_WIN_ERR] = st->tx_window_errors; + + link->l_stats[RTNL_LINK_RX_COMPRESSED] = st->rx_compressed; + link->l_stats[RTNL_LINK_TX_COMPRESSED] = st->tx_compressed; + + /* beware: @st might not be the full struct, only fields up to + * tx_compressed are present. See _nl_offsetofend() above. */ + + if (nla_len(tb[IFLA_STATS]) >= _nl_offsetofend (struct rtnl_link_stats, rx_nohandler)) + link->l_stats[RTNL_LINK_RX_NOHANDLER] = st->rx_nohandler; + else + link->l_stats[RTNL_LINK_RX_NOHANDLER] = 0; + + link->ce_mask |= LINK_ATTR_STATS; + } + + if (tb[IFLA_STATS64]) { + /* + * This structure contains 64bit parameters, and per the + * documentation in lib/attr.c, must not be accessed + * directly (because of alignment to 4 instead of 8). + * Therefore, copy the data to the stack and access it from + * there, where it will be aligned to 8. + */ + struct rtnl_link_stats64 st = { 0 }; + + nla_memcpy(&st, tb[IFLA_STATS64], sizeof (st)); + + link->l_stats[RTNL_LINK_RX_PACKETS] = st.rx_packets; + link->l_stats[RTNL_LINK_TX_PACKETS] = st.tx_packets; + link->l_stats[RTNL_LINK_RX_BYTES] = st.rx_bytes; + link->l_stats[RTNL_LINK_TX_BYTES] = st.tx_bytes; + link->l_stats[RTNL_LINK_RX_ERRORS] = st.rx_errors; + link->l_stats[RTNL_LINK_TX_ERRORS] = st.tx_errors; + link->l_stats[RTNL_LINK_RX_DROPPED] = st.rx_dropped; + link->l_stats[RTNL_LINK_TX_DROPPED] = st.tx_dropped; + link->l_stats[RTNL_LINK_MULTICAST] = st.multicast; + link->l_stats[RTNL_LINK_COLLISIONS] = st.collisions; + + link->l_stats[RTNL_LINK_RX_LEN_ERR] = st.rx_length_errors; + link->l_stats[RTNL_LINK_RX_OVER_ERR] = st.rx_over_errors; + link->l_stats[RTNL_LINK_RX_CRC_ERR] = st.rx_crc_errors; + link->l_stats[RTNL_LINK_RX_FRAME_ERR] = st.rx_frame_errors; + link->l_stats[RTNL_LINK_RX_FIFO_ERR] = st.rx_fifo_errors; + link->l_stats[RTNL_LINK_RX_MISSED_ERR] = st.rx_missed_errors; + + link->l_stats[RTNL_LINK_TX_ABORT_ERR] = st.tx_aborted_errors; + link->l_stats[RTNL_LINK_TX_CARRIER_ERR] = st.tx_carrier_errors; + link->l_stats[RTNL_LINK_TX_FIFO_ERR] = st.tx_fifo_errors; + link->l_stats[RTNL_LINK_TX_HBEAT_ERR] = st.tx_heartbeat_errors; + link->l_stats[RTNL_LINK_TX_WIN_ERR] = st.tx_window_errors; + + link->l_stats[RTNL_LINK_RX_COMPRESSED] = st.rx_compressed; + link->l_stats[RTNL_LINK_TX_COMPRESSED] = st.tx_compressed; + + /* beware: @st might not be the full struct, only fields up to + * tx_compressed are present. See _nl_offsetofend() above. */ + + link->l_stats[RTNL_LINK_RX_NOHANDLER] = st.rx_nohandler; + + link->ce_mask |= LINK_ATTR_STATS; + } + + if (tb[IFLA_TXQLEN]) { + link->l_txqlen = nla_get_u32(tb[IFLA_TXQLEN]); + link->ce_mask |= LINK_ATTR_TXQLEN; + } + + if (tb[IFLA_MTU]) { + link->l_mtu = nla_get_u32(tb[IFLA_MTU]); + link->ce_mask |= LINK_ATTR_MTU; + } + + if (tb[IFLA_ADDRESS]) { + link->l_addr = nl_addr_alloc_attr(tb[IFLA_ADDRESS], AF_UNSPEC); + if (link->l_addr == NULL) + return -NLE_NOMEM; + nl_addr_set_family(link->l_addr, + nl_addr_guess_family(link->l_addr)); + link->ce_mask |= LINK_ATTR_ADDR; + } + + if (tb[IFLA_BROADCAST]) { + link->l_bcast = nl_addr_alloc_attr(tb[IFLA_BROADCAST], + AF_UNSPEC); + if (link->l_bcast == NULL) + return -NLE_NOMEM; + nl_addr_set_family(link->l_bcast, + nl_addr_guess_family(link->l_bcast)); + link->ce_mask |= LINK_ATTR_BRD; + } + + if (tb[IFLA_LINK]) { + link->l_link = nla_get_u32(tb[IFLA_LINK]); + link->ce_mask |= LINK_ATTR_LINK; + } + + if (tb[IFLA_LINK_NETNSID]) { + link->l_link_netnsid = nla_get_s32(tb[IFLA_LINK_NETNSID]); + link->ce_mask |= LINK_ATTR_LINK_NETNSID; + } + + if (tb[IFLA_WEIGHT]) { + link->l_weight = nla_get_u32(tb[IFLA_WEIGHT]); + link->ce_mask |= LINK_ATTR_WEIGHT; + } + + if (tb[IFLA_QDISC]) { + nla_strlcpy(link->l_qdisc, tb[IFLA_QDISC], IFQDISCSIZ); + link->ce_mask |= LINK_ATTR_QDISC; + } + + if (tb[IFLA_MAP]) { + nla_memcpy(&link->l_map, tb[IFLA_MAP], + sizeof(struct rtnl_link_ifmap)); + link->ce_mask |= LINK_ATTR_MAP; + } + + if (tb[IFLA_MASTER]) { + link->l_master = nla_get_u32(tb[IFLA_MASTER]); + link->ce_mask |= LINK_ATTR_MASTER; + } + + if (tb[IFLA_CARRIER]) { + link->l_carrier = nla_get_u8(tb[IFLA_CARRIER]); + link->ce_mask |= LINK_ATTR_CARRIER; + } + + if (tb[IFLA_CARRIER_CHANGES]) { + link->l_carrier_changes = nla_get_u32(tb[IFLA_CARRIER_CHANGES]); + link->ce_mask |= LINK_ATTR_CARRIER_CHANGES; + } + + if (tb[IFLA_OPERSTATE]) { + link->l_operstate = nla_get_u8(tb[IFLA_OPERSTATE]); + link->ce_mask |= LINK_ATTR_OPERSTATE; + } + + if (tb[IFLA_LINKMODE]) { + link->l_linkmode = nla_get_u8(tb[IFLA_LINKMODE]); + link->ce_mask |= LINK_ATTR_LINKMODE; + } + + if (tb[IFLA_IFALIAS]) { + link->l_ifalias = nla_strdup(tb[IFLA_IFALIAS]); + if (link->l_ifalias == NULL) + return -NLE_NOMEM; + link->ce_mask |= LINK_ATTR_IFALIAS; + } + + if (tb[IFLA_NET_NS_FD]) { + link->l_ns_fd = nla_get_u32(tb[IFLA_NET_NS_FD]); + link->ce_mask |= LINK_ATTR_NS_FD; + } + + if (tb[IFLA_NET_NS_PID]) { + link->l_ns_pid = nla_get_u32(tb[IFLA_NET_NS_PID]); + link->ce_mask |= LINK_ATTR_NS_PID; + } + + return 0; +} + +static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *n, struct nl_parser_param *pp) +{ + _nl_auto_rtnl_link struct rtnl_link *link = NULL; + struct nla_policy real_link_policy[ARRAY_SIZE(rtln_link_policy)]; + struct nla_policy *link_policy = rtln_link_policy; + struct rtnl_link_af_ops *af_ops_family; + struct ifinfomsg *ifi; + struct nlattr *tb[IFLA_MAX+1]; + int err, family; + + link = rtnl_link_alloc(); + if (link == NULL) + return -NLE_NOMEM; + + link->ce_msgtype = n->nlmsg_type; + + if (!nlmsg_valid_hdr(n, sizeof(*ifi))) + return -NLE_MSG_TOOSHORT; + + ifi = nlmsg_data(n); + link->l_family = family = ifi->ifi_family; + link->l_arptype = ifi->ifi_type; + link->l_index = ifi->ifi_index; + link->l_flags = ifi->ifi_flags; + link->l_change = ifi->ifi_change; + link->ce_mask = (LINK_ATTR_IFNAME | LINK_ATTR_FAMILY | + LINK_ATTR_ARPTYPE| LINK_ATTR_IFINDEX | + LINK_ATTR_FLAGS | LINK_ATTR_CHANGE); + + if ((link->l_af_ops = af_lookup_and_alloc(link, family))) { + if (link->l_af_ops->ao_protinfo_policy) { + _NL_STATIC_ASSERT (sizeof(rtln_link_policy) == sizeof(real_link_policy)); + memcpy(&real_link_policy, rtln_link_policy, sizeof(rtln_link_policy)); + memcpy(&real_link_policy[IFLA_PROTINFO], + link->l_af_ops->ao_protinfo_policy, + sizeof(struct nla_policy)); + link_policy = real_link_policy; + } + } + + af_ops_family = link->l_af_ops; + + err = nlmsg_parse(n, sizeof(*ifi), tb, IFLA_MAX, link_policy); + if (err < 0) + return err; + + err = rtnl_link_info_parse(link, tb); + if (err < 0) + return err; + + if (tb[IFLA_NUM_VF]) { + link->l_num_vf = nla_get_u32(tb[IFLA_NUM_VF]); + link->ce_mask |= LINK_ATTR_NUM_VF; + if (link->l_num_vf && tb[IFLA_VFINFO_LIST]) { + if ((err = rtnl_link_sriov_parse_vflist(link, tb)) < 0) + return err; + link->ce_mask |= LINK_ATTR_VF_LIST; + } + } + + if (tb[IFLA_LINKINFO]) { + struct nlattr *li[IFLA_INFO_MAX+1]; + + err = nla_parse_nested(li, IFLA_INFO_MAX, tb[IFLA_LINKINFO], + link_info_policy); + if (err < 0) + return err; + + if (li[IFLA_INFO_KIND]) { + struct rtnl_link_info_ops *ops; + const char *kind = nla_get_string(li[IFLA_INFO_KIND]); + int af; + + err = rtnl_link_set_type(link, kind); + if (err < 0) + return err; + + if ( (af = nl_str2af(kind)) >= 0 + && !link->l_af_ops + && (link->l_af_ops = af_lookup_and_alloc(link, af))) { + link->l_family = af; + if (link->l_af_ops->ao_protinfo_policy) + tb[IFLA_PROTINFO] = (struct nlattr *)link->l_af_ops->ao_protinfo_policy; + } + + ops = rtnl_link_info_ops_lookup(kind); + link->l_info_ops = ops; + + if (ops) { + if (ops->io_parse && + (li[IFLA_INFO_DATA] || li[IFLA_INFO_XSTATS])) { + err = ops->io_parse(link, li[IFLA_INFO_DATA], + li[IFLA_INFO_XSTATS]); + if (err < 0) + return err; + } else { + /* XXX: Warn about unparsed info? */ + } + } + + link->ce_mask |= LINK_ATTR_LINKINFO; + } + + if (li[IFLA_INFO_SLAVE_KIND]) { + const char *kind = nla_get_string(li[IFLA_INFO_SLAVE_KIND]); + + err = rtnl_link_set_slave_type(link, kind); + if (err < 0) + return err; + + link->ce_mask |= LINK_ATTR_LINKINFO_SLAVE_KIND; + } + } + + if ( tb[IFLA_PROTINFO] + && link->l_af_ops + && link->l_af_ops->ao_parse_protinfo) { + err = link->l_af_ops->ao_parse_protinfo(link, tb[IFLA_PROTINFO], + link->l_af_data[link->l_family]); + if (err < 0) + return err; + link->ce_mask |= LINK_ATTR_PROTINFO; + } + + if (tb[IFLA_AF_SPEC]) { + /* parsing of IFLA_AF_SPEC is dependent on the family used + * in the request message. + */ + if ( af_ops_family + && af_ops_family->ao_parse_af_full) { + err = af_ops_family->ao_parse_af_full(link, + tb[IFLA_AF_SPEC], + link->l_af_data[af_ops_family->ao_family]); + if (err < 0) + return err; + link->ce_mask |= LINK_ATTR_AF_SPEC; + } else if (family == AF_UNSPEC) { + struct nlattr *af_attr; + int remaining; + + nla_for_each_nested(af_attr, tb[IFLA_AF_SPEC], remaining) { + _nl_auto_rtnl_link_af_ops struct rtnl_link_af_ops *af_ops = NULL; + + af_ops = af_lookup_and_alloc(link, nla_type(af_attr)); + if (af_ops && af_ops->ao_parse_af) { + char *af_data = link->l_af_data[nla_type(af_attr)]; + + err = af_ops->ao_parse_af(link, af_attr, af_data); + if (err < 0) + return err; + } + } + link->ce_mask |= LINK_ATTR_AF_SPEC; + } else { + NL_DBG(3, "IFLA_AF_SPEC parsing not implemented for family %d\n", + family); + } + } + + if (tb[IFLA_PROMISCUITY]) { + link->l_promiscuity = nla_get_u32(tb[IFLA_PROMISCUITY]); + link->ce_mask |= LINK_ATTR_PROMISCUITY; + } + + if (tb[IFLA_NUM_TX_QUEUES]) { + link->l_num_tx_queues = nla_get_u32(tb[IFLA_NUM_TX_QUEUES]); + link->ce_mask |= LINK_ATTR_NUM_TX_QUEUES; + } + + if (tb[IFLA_NUM_RX_QUEUES]) { + link->l_num_rx_queues = nla_get_u32(tb[IFLA_NUM_RX_QUEUES]); + link->ce_mask |= LINK_ATTR_NUM_RX_QUEUES; + } + + if (tb[IFLA_GSO_MAX_SEGS]) { + link->l_gso_max_segs = nla_get_u32(tb[IFLA_GSO_MAX_SEGS]); + link->ce_mask |= LINK_ATTR_GSO_MAX_SEGS; + } + + if (tb[IFLA_GSO_MAX_SIZE]) { + link->l_gso_max_size = nla_get_u32(tb[IFLA_GSO_MAX_SIZE]); + link->ce_mask |= LINK_ATTR_GSO_MAX_SIZE; + } + + if (tb[IFLA_GROUP]) { + link->l_group = nla_get_u32(tb[IFLA_GROUP]); + link->ce_mask |= LINK_ATTR_GROUP; + } + + if (tb[IFLA_PHYS_PORT_ID]) { + link->l_phys_port_id = nl_data_alloc_attr(tb[IFLA_PHYS_PORT_ID]); + if (link->l_phys_port_id == NULL) + return -NLE_NOMEM; + link->ce_mask |= LINK_ATTR_PHYS_PORT_ID; + } + + if (tb[IFLA_PHYS_PORT_NAME]) { + nla_strlcpy(link->l_phys_port_name, tb[IFLA_PHYS_PORT_NAME], IFNAMSIZ); + link->ce_mask |= LINK_ATTR_PHYS_PORT_NAME; + } + + if (tb[IFLA_PHYS_SWITCH_ID]) { + link->l_phys_switch_id = nl_data_alloc_attr(tb[IFLA_PHYS_SWITCH_ID]); + if (link->l_phys_switch_id == NULL) + return -NLE_NOMEM; + link->ce_mask |= LINK_ATTR_PHYS_SWITCH_ID; + } + + return pp->pp_cb((struct nl_object *) link, pp); +} + +static int link_request_update(struct nl_cache *cache, struct nl_sock *sk) +{ + _nl_auto_nl_msg struct nl_msg *msg = NULL; + int family = cache->c_iarg1; + struct ifinfomsg hdr = { .ifi_family = family }; + struct rtnl_link_af_ops *ops; + int err; + __u32 ext_filter_mask = RTEXT_FILTER_VF; + + msg = nlmsg_alloc_simple(RTM_GETLINK, NLM_F_DUMP); + if (!msg) + return -NLE_NOMEM; + + if (nlmsg_append(msg, &hdr, sizeof(hdr), NLMSG_ALIGNTO) < 0) + return -NLE_MSGSIZE; + + ops = rtnl_link_af_ops_lookup(family); + if (ops && ops->ao_get_af) { + err = ops->ao_get_af(msg, &ext_filter_mask); + if (err < 0) + return err; + } + + if (ext_filter_mask) { + err = nla_put(msg, IFLA_EXT_MASK, sizeof(ext_filter_mask), &ext_filter_mask); + if (err < 0) + return err; + } + + err = nl_send_auto(sk, msg); + if (err < 0) + return 0; + + return 0; +} + +static void link_dump_line(struct nl_object *obj, struct nl_dump_params *p) +{ + char buf[128]; + struct nl_cache *cache = obj->ce_cache; + struct rtnl_link *link = (struct rtnl_link *) obj; + int fetched_cache = 0; + + if (!cache) { + cache = nl_cache_mngt_require_safe("route/link"); + fetched_cache = 1; + } + + if (link->l_family != AF_UNSPEC) + nl_dump_line(p, "%s ", nl_af2str(link->l_family, buf, sizeof(buf))); + + nl_dump_line(p, "%s %s ", link->l_name, + nl_llproto2str(link->l_arptype, buf, sizeof(buf))); + + if (link->l_addr && !nl_addr_iszero(link->l_addr)) + nl_dump(p, "%s ", nl_addr2str(link->l_addr, buf, sizeof(buf))); + + if (link->ce_mask & LINK_ATTR_MASTER) { + if (cache) { + _nl_auto_rtnl_link struct rtnl_link *master = rtnl_link_get(cache, link->l_master); + + nl_dump(p, "master %s ", master ? master->l_name : "inv"); + } else + nl_dump(p, "master %d ", link->l_master); + } + + rtnl_link_flags2str(link->l_flags, buf, sizeof(buf)); + if (buf[0]) + nl_dump(p, "<%s> ", buf); + + if (link->ce_mask & LINK_ATTR_LINK) { + if ( cache + && !(link->ce_mask & LINK_ATTR_LINK_NETNSID)) { + _nl_auto_rtnl_link struct rtnl_link *ll = rtnl_link_get(cache, link->l_link); + + nl_dump(p, "slave-of %s ", ll ? ll->l_name : "NONE"); + } else + nl_dump(p, "slave-of %d ", link->l_link); + } + if (link->ce_mask & LINK_ATTR_LINK_NETNSID) + nl_dump(p, "link-netnsid %d ", link->l_link_netnsid); + + if (link->ce_mask & LINK_ATTR_GROUP) + nl_dump(p, "group %u ", link->l_group); + + if (link->l_info_ops && link->l_info_ops->io_dump[NL_DUMP_LINE]) + link->l_info_ops->io_dump[NL_DUMP_LINE](link, p); + + do_foreach_af(link, af_dump_line, p); + + nl_dump(p, "\n"); + + if (fetched_cache) + nl_cache_put(cache); +} + +static void link_dump_details(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_link *link = (struct rtnl_link *) obj; + char buf[64]; + + link_dump_line(obj, p); + + nl_dump_line(p, " mtu %u ", link->l_mtu); + nl_dump(p, "txqlen %u weight %u ", link->l_txqlen, link->l_weight); + + if (link->ce_mask & LINK_ATTR_QDISC) + nl_dump(p, "qdisc %s ", link->l_qdisc); + + if (link->ce_mask & LINK_ATTR_MAP && link->l_map.lm_irq) + nl_dump(p, "irq %u ", link->l_map.lm_irq); + + if (link->ce_mask & LINK_ATTR_IFINDEX) + nl_dump(p, "index %u ", link->l_index); + + if (link->ce_mask & LINK_ATTR_PROMISCUITY && link->l_promiscuity > 0) + nl_dump(p, "promisc-mode (%u users) ", link->l_promiscuity); + + nl_dump(p, "\n"); + + if (link->ce_mask & LINK_ATTR_IFALIAS) + nl_dump_line(p, " alias %s\n", link->l_ifalias); + + nl_dump_line(p, " "); + + if (link->ce_mask & LINK_ATTR_NUM_TX_QUEUES) + nl_dump(p, "txq %u ", link->l_num_tx_queues); + + if (link->ce_mask & LINK_ATTR_NUM_RX_QUEUES) + nl_dump(p, "rxq %u ", link->l_num_rx_queues); + + if (link->ce_mask & LINK_ATTR_BRD) + nl_dump(p, "brd %s ", nl_addr2str(link->l_bcast, buf, + sizeof(buf))); + + if ((link->ce_mask & LINK_ATTR_OPERSTATE) && + link->l_operstate != IF_OPER_UNKNOWN) { + rtnl_link_operstate2str(link->l_operstate, buf, sizeof(buf)); + nl_dump(p, "state %s ", buf); + } + + if (link->ce_mask & LINK_ATTR_NUM_VF) + nl_dump(p, "num-vf %u ", link->l_num_vf); + + nl_dump(p, "mode %s ", + rtnl_link_mode2str(link->l_linkmode, buf, sizeof(buf))); + + nl_dump(p, "carrier %s", + rtnl_link_carrier2str(link->l_carrier, buf, sizeof(buf))); + + if (link->ce_mask & LINK_ATTR_CARRIER_CHANGES) + nl_dump(p, " carrier-changes %u", link->l_carrier_changes); + + nl_dump(p, "\n"); + + if (link->l_info_ops && link->l_info_ops->io_dump[NL_DUMP_DETAILS]) + link->l_info_ops->io_dump[NL_DUMP_DETAILS](link, p); + + do_foreach_af(link, af_dump_details, p); + + if (link->ce_mask & LINK_ATTR_VF_LIST) + rtnl_link_sriov_dump_details(link, p); +} + +static void link_dump_stats(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_link *link = (struct rtnl_link *) obj; + char *unit, fmt[64]; + float res; + + link_dump_details(obj, p); + + nl_dump_line(p, " Stats: bytes packets errors " + " dropped fifo-err compressed\n"); + + res = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_RX_BYTES], &unit); + + strcpy(fmt, " RX %X.2f %s %10" PRIu64 " %10" PRIu64 " %10" PRIu64 " %10" PRIu64 " %10" PRIu64 "\n"); + fmt[9] = *unit == 'B' ? '9' : '7'; + + nl_dump_line(p, fmt, res, unit, + link->l_stats[RTNL_LINK_RX_PACKETS], + link->l_stats[RTNL_LINK_RX_ERRORS], + link->l_stats[RTNL_LINK_RX_DROPPED], + link->l_stats[RTNL_LINK_RX_FIFO_ERR], + link->l_stats[RTNL_LINK_RX_COMPRESSED]); + + res = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_TX_BYTES], &unit); + + strcpy(fmt, " TX %X.2f %s %10" PRIu64 " %10" PRIu64 " %10" PRIu64 " %10" PRIu64 " %10" PRIu64 "\n"); + fmt[9] = *unit == 'B' ? '9' : '7'; + + nl_dump_line(p, fmt, res, unit, + link->l_stats[RTNL_LINK_TX_PACKETS], + link->l_stats[RTNL_LINK_TX_ERRORS], + link->l_stats[RTNL_LINK_TX_DROPPED], + link->l_stats[RTNL_LINK_TX_FIFO_ERR], + link->l_stats[RTNL_LINK_TX_COMPRESSED]); + + nl_dump_line(p, " Errors: length over crc " + " frame missed multicast\n"); + + nl_dump_line(p, " RX %10" PRIu64 " %10" PRIu64 " %10" + PRIu64 " %10" PRIu64 " %10" PRIu64 " %10" + PRIu64 "\n", + link->l_stats[RTNL_LINK_RX_LEN_ERR], + link->l_stats[RTNL_LINK_RX_OVER_ERR], + link->l_stats[RTNL_LINK_RX_CRC_ERR], + link->l_stats[RTNL_LINK_RX_FRAME_ERR], + link->l_stats[RTNL_LINK_RX_MISSED_ERR], + link->l_stats[RTNL_LINK_MULTICAST]); + + nl_dump_line(p, " aborted carrier heartbeat " + " window collision\n"); + + nl_dump_line(p, " TX %10" PRIu64 " %10" PRIu64 " %10" + PRIu64 " %10" PRIu64 " %10" PRIu64 "\n", + link->l_stats[RTNL_LINK_TX_ABORT_ERR], + link->l_stats[RTNL_LINK_TX_CARRIER_ERR], + link->l_stats[RTNL_LINK_TX_HBEAT_ERR], + link->l_stats[RTNL_LINK_TX_WIN_ERR], + link->l_stats[RTNL_LINK_COLLISIONS]); + + if (link->l_info_ops && link->l_info_ops->io_dump[NL_DUMP_STATS]) + link->l_info_ops->io_dump[NL_DUMP_STATS](link, p); + + do_foreach_af(link, af_dump_stats, p); + + if (link->ce_mask & LINK_ATTR_VF_LIST) + rtnl_link_sriov_dump_stats(link, p); +} + +#if 0 +static int link_handle_event(struct nl_object *a, struct rtnl_link_event_cb *cb) +{ + struct rtnl_link *l = (struct rtnl_link *) a; + struct nl_cache *c = dp_cache(a); + int nevents = 0; + + if (l->l_change == ~0U) { + if (l->ce_msgtype == RTM_NEWLINK) + cb->le_register(l); + else + cb->le_unregister(l); + + return 1; + } + + if (l->l_change & IFF_SLAVE) { + if (l->l_flags & IFF_SLAVE) { + struct rtnl_link *m = rtnl_link_get(c, l->l_master); + cb->le_new_bonding(l, m); + if (m) + rtnl_link_put(m); + } else + cb->le_cancel_bonding(l); + } + +#if 0 + if (l->l_change & IFF_UP && l->l_change & IFF_RUNNING) + dp_dump_line(p, line++, "link %s changed state to %s.\n", + l->l_name, l->l_flags & IFF_UP ? "up" : "down"); + + if (l->l_change & IFF_PROMISC) { + dp_new_line(p, line++); + dp_dump(p, "link %s %s promiscuous mode.\n", + l->l_name, l->l_flags & IFF_PROMISC ? "entered" : "left"); + } + + if (line == 0) + dp_dump_line(p, line++, "link %s sent unknown event.\n", + l->l_name); +#endif + + return nevents; +} +#endif + + +static void link_keygen(struct nl_object *obj, uint32_t *hashkey, + uint32_t table_sz) +{ + struct rtnl_link *link = (struct rtnl_link *) obj; + unsigned int lkey_sz; + struct link_hash_key { + uint32_t l_index; + uint32_t l_family; + } __attribute__((packed)) lkey; + + lkey_sz = sizeof(lkey); + lkey.l_index = link->l_index; + lkey.l_family = link->l_family; + + *hashkey = nl_hash(&lkey, lkey_sz, 0) % table_sz; + + NL_DBG(5, "link %p key (dev %d fam %d) keysz %d, hash 0x%x\n", + link, lkey.l_index, lkey.l_family, lkey_sz, *hashkey); + + return; +} + +static uint64_t link_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + struct rtnl_link *a = (struct rtnl_link *) _a; + struct rtnl_link *b = (struct rtnl_link *) _b; + uint64_t diff = 0; + +#define LINK_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, LINK_ATTR_##ATTR, a, b, EXPR) + + diff |= LINK_DIFF(IFINDEX, a->l_index != b->l_index); + diff |= LINK_DIFF(MTU, a->l_mtu != b->l_mtu); + diff |= LINK_DIFF(LINK, a->l_link != b->l_link); + diff |= LINK_DIFF(LINK_NETNSID, a->l_link_netnsid != b->l_link_netnsid); + diff |= LINK_DIFF(TXQLEN, a->l_txqlen != b->l_txqlen); + diff |= LINK_DIFF(WEIGHT, a->l_weight != b->l_weight); + diff |= LINK_DIFF(MASTER, a->l_master != b->l_master); + diff |= LINK_DIFF(FAMILY, a->l_family != b->l_family); + diff |= LINK_DIFF(OPERSTATE, a->l_operstate != b->l_operstate); + diff |= LINK_DIFF(LINKMODE, a->l_linkmode != b->l_linkmode); + diff |= LINK_DIFF(QDISC, strcmp(a->l_qdisc, b->l_qdisc)); + diff |= LINK_DIFF(IFNAME, strcmp(a->l_name, b->l_name)); + diff |= LINK_DIFF(ADDR, nl_addr_cmp(a->l_addr, b->l_addr)); + diff |= LINK_DIFF(BRD, nl_addr_cmp(a->l_bcast, b->l_bcast)); + diff |= LINK_DIFF(IFALIAS, strcmp(a->l_ifalias, b->l_ifalias)); + diff |= LINK_DIFF(NUM_VF, a->l_num_vf != b->l_num_vf); + diff |= LINK_DIFF(PROMISCUITY, a->l_promiscuity != b->l_promiscuity); + diff |= LINK_DIFF(NUM_TX_QUEUES,a->l_num_tx_queues != b->l_num_tx_queues); + diff |= LINK_DIFF(NUM_RX_QUEUES,a->l_num_rx_queues != b->l_num_rx_queues); + diff |= LINK_DIFF(GROUP, a->l_group != b->l_group); + + if (flags & LOOSE_COMPARISON) + diff |= LINK_DIFF(FLAGS, + (a->l_flags ^ b->l_flags) & b->l_flag_mask); + else + diff |= LINK_DIFF(FLAGS, a->l_flags != b->l_flags); + + /* + * Compare LINK_ATTR_PROTINFO af_data + */ + if (a->l_family == b->l_family) { + if (rtnl_link_af_data_compare(a, b, a->l_family) != 0) + goto protinfo_mismatch; + } + + diff |= LINK_DIFF(LINKINFO, rtnl_link_info_data_compare(a, b, flags) != 0); +out: + return diff; + +protinfo_mismatch: + diff |= LINK_DIFF(PROTINFO, 1); + goto out; + +#undef LINK_DIFF +} + +static const struct trans_tbl link_attrs[] = { + __ADD(LINK_ATTR_MTU, mtu), + __ADD(LINK_ATTR_LINK, link), + __ADD(LINK_ATTR_TXQLEN, txqlen), + __ADD(LINK_ATTR_WEIGHT, weight), + __ADD(LINK_ATTR_MASTER, master), + __ADD(LINK_ATTR_QDISC, qdisc), + __ADD(LINK_ATTR_MAP, map), + __ADD(LINK_ATTR_ADDR, address), + __ADD(LINK_ATTR_BRD, broadcast), + __ADD(LINK_ATTR_FLAGS, flags), + __ADD(LINK_ATTR_IFNAME, name), + __ADD(LINK_ATTR_IFINDEX, ifindex), + __ADD(LINK_ATTR_FAMILY, family), + __ADD(LINK_ATTR_ARPTYPE, arptype), + __ADD(LINK_ATTR_STATS, stats), + __ADD(LINK_ATTR_CHANGE, change), + __ADD(LINK_ATTR_OPERSTATE, operstate), + __ADD(LINK_ATTR_LINKMODE, linkmode), + __ADD(LINK_ATTR_IFALIAS, ifalias), + __ADD(LINK_ATTR_NUM_VF, num_vf), + __ADD(LINK_ATTR_PROMISCUITY, promiscuity), + __ADD(LINK_ATTR_NUM_TX_QUEUES, num_tx_queues), + __ADD(LINK_ATTR_NUM_RX_QUEUES, num_rx_queues), + __ADD(LINK_ATTR_GSO_MAX_SEGS, gso_max_segs), + __ADD(LINK_ATTR_GSO_MAX_SIZE, gso_max_size), + __ADD(LINK_ATTR_GROUP, group), + __ADD(LINK_ATTR_CARRIER, carrier), + __ADD(LINK_ATTR_CARRIER_CHANGES, carrier_changes), + __ADD(LINK_ATTR_PHYS_PORT_ID, phys_port_id), + __ADD(LINK_ATTR_PHYS_PORT_NAME, phys_port_name), + __ADD(LINK_ATTR_PHYS_SWITCH_ID, phys_switch_id), + __ADD(LINK_ATTR_NS_FD, ns_fd), + __ADD(LINK_ATTR_NS_PID, ns_pid), + __ADD(LINK_ATTR_LINK_NETNSID, link_netnsid), +}; + +static char *link_attrs2str(int attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, link_attrs, + ARRAY_SIZE(link_attrs)); +} + +/** + * @name Get / List + * @{ + */ + + +/** + * Allocate link cache and fill in all configured links. + * @arg sk Netlink socket. + * @arg family Link address family or AF_UNSPEC + * @arg result Pointer to store resulting cache. + * @arg flags Flags to set in link cache before filling + * + * Allocates and initializes a new link cache. If \c sk is valid, a netlink + * message is sent to the kernel requesting a full dump of all configured + * links. The returned messages are parsed and filled into the cache. If + * the operation succeeds, the resulting cache will contain a link object for + * each link configured in the kernel. If \c sk is NULL, returns 0 but the + * cache is still empty. + * + * If \c family is set to an address family other than \c AF_UNSPEC the + * contents of the cache can be limited to a specific address family. + * Currently the following address families are supported: + * - AF_BRIDGE + * - AF_INET6 + * + * @route_doc{link_list, Get List of Links} + * @see rtnl_link_get() + * @see rtnl_link_get_by_name() + * @return 0 on success or a negative error code. + */ +int rtnl_link_alloc_cache_flags(struct nl_sock *sk, int family, + struct nl_cache **result, unsigned int flags) +{ + struct nl_cache * cache; + int err; + + cache = nl_cache_alloc(&rtnl_link_ops); + if (!cache) + return -NLE_NOMEM; + + cache->c_iarg1 = family; + + if (flags) + nl_cache_set_flags(cache, flags); + + if (sk && (err = nl_cache_refill(sk, cache)) < 0) { + nl_cache_free(cache); + return err; + } + + *result = cache; + return 0; +} + +/** + * Allocate link cache and fill in all configured links. + * @arg sk Netlink socket. + * @arg family Link address family or AF_UNSPEC + * @arg result Pointer to store resulting cache. + * + * Allocates and initializes a new link cache. If \c sk is valid, a netlink + * message is sent to the kernel requesting a full dump of all configured + * links. The returned messages are parsed and filled into the cache. If + * the operation succeeds, the resulting cache will contain a link object for + * each link configured in the kernel. If \c sk is NULL, returns 0 but the + * cache is still empty. + * + * If \c family is set to an address family other than \c AF_UNSPEC the + * contents of the cache can be limited to a specific address family. + * Currently the following address families are supported: + * - AF_BRIDGE + * - AF_INET6 + * + * @route_doc{link_list, Get List of Links} + * @see rtnl_link_get() + * @see rtnl_link_get_by_name() + * @return 0 on success or a negative error code. + */ +int rtnl_link_alloc_cache(struct nl_sock *sk, int family, struct nl_cache **result) +{ + return rtnl_link_alloc_cache_flags(sk, family, result, 0); +} + + +/** + * Lookup link in cache by interface index + * @arg cache Link cache + * @arg ifindex Interface index + * + * Searches through the provided cache looking for a link with matching + * interface index. + * + * @attention The reference counter of the returned link object will be + * incremented. Use rtnl_link_put() to release the reference. + * + * @route_doc{link_list, Get List of Links} + * @see rtnl_link_get_by_name() + * @return Link object or NULL if no match was found. + */ +struct rtnl_link *rtnl_link_get(struct nl_cache *cache, int ifindex) +{ + struct rtnl_link *link; + + if (cache->c_ops != &rtnl_link_ops) + return NULL; + + nl_list_for_each_entry(link, &cache->c_items, ce_list) { + if (link->l_index == ifindex) { + nl_object_get((struct nl_object *) link); + return link; + } + } + + return NULL; +} + +/** + * Lookup link in cache by link name + * @arg cache Link cache + * @arg name Name of link + * + * Searches through the provided cache looking for a link with matching + * link name + * + * @attention The reference counter of the returned link object will be + * incremented. Use rtnl_link_put() to release the reference. + * + * @route_doc{link_list, Get List of Links} + * @see rtnl_link_get() + * @return Link object or NULL if no match was found. + */ +struct rtnl_link *rtnl_link_get_by_name(struct nl_cache *cache, + const char *name) +{ + struct rtnl_link *link; + + if (cache->c_ops != &rtnl_link_ops) + return NULL; + + nl_list_for_each_entry(link, &cache->c_items, ce_list) { + if (!strcmp(name, link->l_name)) { + nl_object_get((struct nl_object *) link); + return link; + } + } + + return NULL; +} + +/** + * Construct RTM_GETLINK netlink message + * @arg ifindex Interface index + * @arg name Name of link + * @arg result Pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_link_get_kernel() + * with the exception that it will not send the message but return it in + * the provided return pointer instead. + * + * @see rtnl_link_get_kernel() + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_build_get_request(int ifindex, const char *name, + struct nl_msg **result) +{ + _nl_auto_nl_msg struct nl_msg *msg = NULL; + struct ifinfomsg ifi; + __u32 vf_mask = RTEXT_FILTER_VF; + + if (ifindex <= 0 && !name) { + APPBUG("ifindex or name must be specified"); + return -NLE_MISSING_ATTR; + } + + memset(&ifi, 0, sizeof(ifi)); + + if (!(msg = nlmsg_alloc_simple(RTM_GETLINK, 0))) + return -NLE_NOMEM; + + if (ifindex > 0) + ifi.ifi_index = ifindex; + + _NL_RETURN_ON_PUT_ERR(nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO)); + + if (name) + _NL_RETURN_ON_PUT_ERR(nla_put_string(msg, IFLA_IFNAME, name)); + + _NL_RETURN_ON_PUT_ERR(nla_put(msg, IFLA_EXT_MASK, sizeof(vf_mask), &vf_mask)); + + *result = _nl_steal_pointer(&msg); + return 0; +} + +/** + * Get a link object directly from kernel + * @arg sk Netlink socket + * @arg ifindex Interface index + * @arg name Name of link + * @arg result Pointer to store resulting link object + * + * This function builds a \c RTM_GETLINK netlink message to request + * a specific link directly from the kernel. The returned answer is + * parsed into a struct rtnl_link object and returned via the result + * pointer or -NLE_OBJ_NOTFOUND is returned if no matching link was + * found. + * + * Older kernels do not support lookup by name. In that case, libnl + * will fail with -NLE_OPNOTSUPP. Note that previous version of libnl + * failed in this case with -NLE_INVAL. You can check libnl behavior + * using NL_CAPABILITY_ROUTE_LINK_GET_KERNEL_FAIL_OPNOTSUPP capability. + * + * @route_doc{link_direct_lookup, Lookup Single Link (Direct Lookup)} + * @return 0 on success or a negative error code. + */ +int rtnl_link_get_kernel(struct nl_sock *sk, int ifindex, const char *name, + struct rtnl_link **result) +{ + _nl_auto_rtnl_link struct rtnl_link *link = NULL; + _nl_auto_nl_msg struct nl_msg *msg = NULL; + int err; + int syserr; + + if ((err = rtnl_link_build_get_request(ifindex, name, &msg)) < 0) + return err; + + err = nl_send_auto(sk, msg); + if (err < 0) + return err; + + err = nl_pickup_keep_syserr(sk, link_msg_parser, (struct nl_object **) &link, &syserr); + if (err < 0) { + if ( syserr == -EINVAL + && ifindex <= 0 + && name + && *name) { + /* Older kernels do not support lookup by ifname. This was added + * by commit kernel a3d1289126e7b14307074b76bf1677015ea5036f . + * Detect this error case and return NLE_OPNOTSUPP instead of + * NLE_INVAL. */ + return -NLE_OPNOTSUPP; + } + return err; + } + + /* If an object has been returned, we also need to wait for the ACK */ + if (err == 0 && link) + wait_for_ack(sk); + + *result = _nl_steal_pointer(&link); + return 0; +} + +/** + * Translate interface index to corresponding link name + * @arg cache Link cache + * @arg ifindex Interface index + * @arg dst String to store name + * @arg len Length of destination string + * + * Translates the specified interface index to the corresponding + * link name and stores the name in the destination string. + * + * @route_doc{link_translate_ifindex, Translating interface index to link name} + * @see rtnl_link_name2i() + * @return Name of link or NULL if no match was found. + */ +char * rtnl_link_i2name(struct nl_cache *cache, int ifindex, char *dst, + size_t len) +{ + _nl_auto_rtnl_link struct rtnl_link *link = NULL; + + link = rtnl_link_get(cache, ifindex); + if (link) { + _nl_strncpy_trunc(dst, link->l_name, len); + return dst; + } + + return NULL; +} + +/** + * Translate link name to corresponding interface index + * @arg cache Link cache + * @arg name Name of link + * + * @route_doc{link_translate_ifindex, Translating interface index to link name} + * @see rtnl_link_i2name() + * @return Interface index or 0 if no match was found. + */ +int rtnl_link_name2i(struct nl_cache *cache, const char *name) +{ + _nl_auto_rtnl_link struct rtnl_link *link = NULL; + + link = rtnl_link_get_by_name(cache, name); + if (link) + return link->l_index; + + return 0; +} + +/** @} */ + +int rtnl_link_fill_info(struct nl_msg *msg, struct rtnl_link *link) +{ + if (link->ce_mask & LINK_ATTR_ADDR) + NLA_PUT_ADDR(msg, IFLA_ADDRESS, link->l_addr); + + if (link->ce_mask & LINK_ATTR_BRD) + NLA_PUT_ADDR(msg, IFLA_BROADCAST, link->l_bcast); + + if (link->ce_mask & LINK_ATTR_MTU) + NLA_PUT_U32(msg, IFLA_MTU, link->l_mtu); + + if (link->ce_mask & LINK_ATTR_TXQLEN) + NLA_PUT_U32(msg, IFLA_TXQLEN, link->l_txqlen); + + if (link->ce_mask & LINK_ATTR_WEIGHT) + NLA_PUT_U32(msg, IFLA_WEIGHT, link->l_weight); + + if (link->ce_mask & LINK_ATTR_IFNAME) + NLA_PUT_STRING(msg, IFLA_IFNAME, link->l_name); + + if (link->ce_mask & LINK_ATTR_OPERSTATE) + NLA_PUT_U8(msg, IFLA_OPERSTATE, link->l_operstate); + + if (link->ce_mask & LINK_ATTR_CARRIER) + NLA_PUT_U8(msg, IFLA_CARRIER, link->l_carrier); + + if (link->ce_mask & LINK_ATTR_LINKMODE) + NLA_PUT_U8(msg, IFLA_LINKMODE, link->l_linkmode); + + if (link->ce_mask & LINK_ATTR_IFALIAS) + NLA_PUT_STRING(msg, IFLA_IFALIAS, link->l_ifalias); + + if (link->ce_mask & LINK_ATTR_LINK) + NLA_PUT_U32(msg, IFLA_LINK, link->l_link); + + if (link->ce_mask & LINK_ATTR_LINK_NETNSID) + NLA_PUT_S32(msg, IFLA_LINK_NETNSID, link->l_link_netnsid); + + if (link->ce_mask & LINK_ATTR_MASTER) + NLA_PUT_U32(msg, IFLA_MASTER, link->l_master); + + if (link->ce_mask & LINK_ATTR_NUM_TX_QUEUES) + NLA_PUT_U32(msg, IFLA_NUM_TX_QUEUES, link->l_num_tx_queues); + + if (link->ce_mask & LINK_ATTR_NUM_RX_QUEUES) + NLA_PUT_U32(msg, IFLA_NUM_RX_QUEUES, link->l_num_rx_queues); + + if (link->ce_mask & LINK_ATTR_NS_FD) + NLA_PUT_U32(msg, IFLA_NET_NS_FD, link->l_ns_fd); + + if (link->ce_mask & LINK_ATTR_NS_PID) + NLA_PUT_U32(msg, IFLA_NET_NS_PID, link->l_ns_pid); + + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +static int build_link_msg(int cmd, struct ifinfomsg *hdr, + struct rtnl_link *link, int flags, struct nl_msg **result) +{ + _nl_auto_nl_msg struct nl_msg *msg = NULL; + struct nlattr *af_spec; + + msg = nlmsg_alloc_simple(cmd, flags); + if (!msg) + return -NLE_NOMEM; + + if (nlmsg_append(msg, hdr, sizeof(*hdr), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + if (rtnl_link_fill_info(msg, link)) + goto nla_put_failure; + + if (link->ce_mask & LINK_ATTR_GROUP) + NLA_PUT_U32(msg, IFLA_GROUP, link->l_group); + + if (link->ce_mask & (LINK_ATTR_LINKINFO|LINK_ATTR_LINKINFO_SLAVE_KIND)) { + struct nlattr *info; + + if (!(info = nla_nest_start(msg, IFLA_LINKINFO))) + goto nla_put_failure; + + if (link->ce_mask & LINK_ATTR_LINKINFO) { + NLA_PUT_STRING(msg, IFLA_INFO_KIND, link->l_info_kind); + + if (link->l_info_ops) { + if (link->l_info_ops->io_put_attrs && + link->l_info_ops->io_put_attrs(msg, link) < 0) + goto nla_put_failure; + } + } + + if (link->ce_mask & LINK_ATTR_LINKINFO_SLAVE_KIND) { + NLA_PUT_STRING(msg, IFLA_INFO_SLAVE_KIND, link->l_info_slave_kind); + } + + nla_nest_end(msg, info); + } + + if (link->ce_mask & LINK_ATTR_VF_LIST) { + if (rtnl_link_sriov_fill_vflist(msg, link) < 0) + goto nla_put_failure; + } + + if (do_foreach_af(link, af_fill_pi, msg) < 0) + goto nla_put_failure; + + if (!(af_spec = nla_nest_start(msg, IFLA_AF_SPEC))) + goto nla_put_failure; + + if (do_foreach_af(link, af_fill, msg) < 0) + goto nla_put_failure; + + nla_nest_end(msg, af_spec); + + *result = _nl_steal_pointer(&msg); + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +/** + * @name Add / Modify + * @{ + */ + +/** + * Build a netlink message requesting the addition of new virtual link + * @arg link new link to add + * @arg flags additional netlink message flags + * @arg result pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_link_add() with + * the exception that it will not send the message but return it in the + * provided return pointer instead. + * + * @see rtnl_link_add() + * + * @note This operation is not supported on all kernel versions. + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_build_add_request(struct rtnl_link *link, int flags, + struct nl_msg **result) +{ + struct ifinfomsg ifi = { + .ifi_family = link->l_family, + .ifi_index = link->l_index, + .ifi_flags = link->l_flags, + .ifi_change = link->l_flag_mask, + }; + + return build_link_msg(RTM_NEWLINK, &ifi, link, flags, result); +} + +/** + * Add virtual link + * @arg sk netlink socket. + * @arg link new link to add + * @arg flags additional netlink message flags + * + * Builds a \c RTM_NEWLINK netlink message requesting the addition of + * a new virtual link. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @copydoc auto_ack_warning + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_add(struct nl_sock *sk, struct rtnl_link *link, int flags) +{ + struct nl_msg *msg; + int err; + + err = rtnl_link_build_add_request(link, flags, &msg); + if (err < 0) + return err; + + return nl_send_sync(sk, msg); +} + +/** + * Build a netlink message requesting the modification of link + * @arg orig original link to change + * @arg changes link containing the changes to be made + * @arg flags additional netlink message flags + * @arg result pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_link_change() with + * the exception that it will not send the message but return it in the + * provided return pointer instead. + * + * @see rtnl_link_change() + * + * @note The resulting message will have message type set to RTM_NEWLINK + * which may not work with older kernels. You may have to modify it + * to RTM_SETLINK (does not allow changing link info attributes) to + * have the change request work with older kernels. + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_build_change_request(struct rtnl_link *orig, + struct rtnl_link *changes, int flags, + struct nl_msg **result) +{ + struct ifinfomsg ifi = { + .ifi_family = orig->l_family, + .ifi_index = orig->l_index, + }; + int err, rt; + + if (changes->ce_mask & LINK_ATTR_FLAGS) { + ifi.ifi_flags = orig->l_flags & ~changes->l_flag_mask; + ifi.ifi_flags |= changes->l_flags; + ifi.ifi_change = changes->l_flag_mask; + } + + if (changes->l_family && changes->l_family != orig->l_family) { + APPBUG("link change: family is immutable"); + return -NLE_IMMUTABLE; + } + + /* Avoid unnecessary name change requests */ + if (orig->ce_mask & LINK_ATTR_IFINDEX && + orig->ce_mask & LINK_ATTR_IFNAME && + changes->ce_mask & LINK_ATTR_IFNAME && + !strcmp(orig->l_name, changes->l_name)) + changes->ce_mask &= ~LINK_ATTR_IFNAME; + + rt = af_request_type(orig->l_family, changes); + + if ((err = build_link_msg(rt, &ifi, changes, flags, result)) < 0) + return err; + + return 0; +} + +/** + * Change link + * @arg sk netlink socket. + * @arg orig original link to be changed + * @arg changes link containing the changes to be made + * @arg flags additional netlink message flags + * + * Builds a \c RTM_NEWLINK netlink message requesting the change of + * a network link. If -EOPNOTSUPP is returned by the kernel, the + * message type will be changed to \c RTM_SETLINK and the message is + * resent to work around older kernel versions. + * + * The link to be changed is looked up based on the interface index + * supplied in the \p orig link. Optionaly the link name is used but + * only if no interface index is provided, otherwise providing an + * link name will result in the link name being changed. + * + * If no matching link exists, the function will return + * -NLE_OBJ_NOTFOUND. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @copydoc auto_ack_warning + * + * @note The link name can only be changed if the link has been put + * in opertional down state. (~IF_UP) + * + * @note On versions up to 3.4.0, \c NLE_SEQ_MISMATCH would be returned if the + * kernel does not supports \c RTM_NEWLINK. It is advised to ignore the + * error code if you cannot upgrade the library. + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_change(struct nl_sock *sk, struct rtnl_link *orig, + struct rtnl_link *changes, int flags) +{ + _nl_auto_nl_msg struct nl_msg *msg = NULL; + int err; + + err = rtnl_link_build_change_request(orig, changes, flags, &msg); + if (err < 0) + return err; + + BUG_ON(msg->nm_nlh->nlmsg_seq != NL_AUTO_SEQ); +retry: + err = nl_send_auto_complete(sk, msg); + if (err < 0) + return err; + + err = wait_for_ack(sk); + if ( err == -NLE_OPNOTSUPP + && msg->nm_nlh->nlmsg_type == RTM_NEWLINK) { + msg->nm_nlh->nlmsg_type = RTM_SETLINK; + msg->nm_nlh->nlmsg_seq = NL_AUTO_SEQ; + goto retry; + } + + if (err < 0) + return err; + + return 0; +} + +/** @} */ + +/** + * @name Delete + * @{ + */ + +/** + * Build a netlink message requesting the deletion of a link + * @arg link Link to delete + * @arg result Pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_link_delete() with + * the exception that it will not send the message but return it in the + * provided return pointer instead. + * + * @see rtnl_link_delete() + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_build_delete_request(const struct rtnl_link *link, + struct nl_msg **result) +{ + _nl_auto_nl_msg struct nl_msg *msg = NULL; + struct ifinfomsg ifi = { + .ifi_index = link->l_index, + }; + + if (!(link->ce_mask & (LINK_ATTR_IFINDEX | LINK_ATTR_IFNAME))) { + APPBUG("ifindex or name must be specified"); + return -NLE_MISSING_ATTR; + } + + if (!(msg = nlmsg_alloc_simple(RTM_DELLINK, 0))) + return -NLE_NOMEM; + + _NL_RETURN_ON_PUT_ERR(nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO)); + + if (link->ce_mask & LINK_ATTR_IFNAME) + _NL_RETURN_ON_PUT_ERR(nla_put_string(msg, IFLA_IFNAME, link->l_name)); + + *result = _nl_steal_pointer(&msg); + return 0; +} + +/** + * Delete link + * @arg sk Netlink socket + * @arg link Link to delete + * + * Builds a \c RTM_DELLINK netlink message requesting the deletion of + * a network link which has been previously added to the kernel and + * sends the message to the kernel. + * + * If no matching link exists, the function will return + * -NLE_OBJ_NOTFOUND. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @copydoc auto_ack_warning + * + * @note Only virtual links such as dummy interface or vlan interfaces + * can be deleted. It is not possible to delete physical interfaces + * such as ethernet interfaces or the loopback device. + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_delete(struct nl_sock *sk, const struct rtnl_link *link) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_link_build_delete_request(link, &msg)) < 0) + return err; + + return nl_send_sync(sk, msg); +} + +/** @} */ + +/** + * @name Link Object + * @{ + */ + +/** + * Allocate link object + * + * @see rtnl_link_put() + * @return New link object or NULL if allocation failed + */ +struct rtnl_link *rtnl_link_alloc(void) +{ + return (struct rtnl_link *) nl_object_alloc(&link_obj_ops); +} + +/** + * Return a link object reference + * @arg link Link object + */ +void rtnl_link_put(struct rtnl_link *link) +{ + nl_object_put((struct nl_object *) link); +} + +/** + * Set name of link object + * @arg link Link object + * @arg name New name + * + * @note To change the name of a link in the kernel, set the interface + * index to the link you wish to change, modify the link name using + * this function and pass the link object to rtnl_link_change() or + * rtnl_link_add(). + * + * @route_doc{link_attr_name, Link Name} + * @see rtnl_link_get_name() + * @see rtnl_link_set_ifindex() + */ +void rtnl_link_set_name(struct rtnl_link *link, const char *name) +{ + _nl_strncpy_trunc(link->l_name, name, sizeof(link->l_name)); + link->ce_mask |= LINK_ATTR_IFNAME; +} + +/** + * Return name of link object + * @arg link Link object + * + * @route_doc{link_attr_name, Link Name} + * @see rtnl_link_set_name() + * @return Link name or NULL if name is not specified + */ +char *rtnl_link_get_name(struct rtnl_link *link) +{ + return link->ce_mask & LINK_ATTR_IFNAME ? link->l_name : NULL; +} + +/** + * Set the group identifier of a link object + * @arg link Link object + * @arg group Group identifier + */ +void rtnl_link_set_group(struct rtnl_link *link, uint32_t group) +{ + link->l_group = group; + link->ce_mask |= LINK_ATTR_GROUP; +} + +/** + * Return the group identifier of link object + * @arg link Link object + * + * @return Group identifier or 0 if not set. + */ +uint32_t rtnl_link_get_group(struct rtnl_link *link) +{ + return link->l_group; +} + +static inline void __assign_addr(struct rtnl_link *link, struct nl_addr **pos, + struct nl_addr *new, int flag) +{ + if (*pos) + nl_addr_put(*pos); + + nl_addr_get(new); + *pos = new; + + link->ce_mask |= flag; +} + +/** + * Set link layer address of link object + * @arg link Link object + * @arg addr New link layer address + * + * The function increments the reference counter of the address object + * and overwrites any existing link layer address previously assigned. + * + * @route_doc{link_attr_address, Link layer address} + * @see rtnl_link_get_addr() + */ +void rtnl_link_set_addr(struct rtnl_link *link, struct nl_addr *addr) +{ + __assign_addr(link, &link->l_addr, addr, LINK_ATTR_ADDR); +} + +/** + * Return link layer address of link object + * @arg link Link object + * + * @copydoc pointer_lifetime_warning + * @route_doc{link_attr_address, Link Layer Address} + * @see rtnl_link_set_addr() + * @return Link layer address or NULL if not set. + */ +struct nl_addr *rtnl_link_get_addr(struct rtnl_link *link) +{ + return link->ce_mask & LINK_ATTR_ADDR ? link->l_addr : NULL; +} + +/** + * Set link layer broadcast address of link object + * @arg link Link object + * @arg addr New broadcast address + * + * The function increments the reference counter of the address object + * and overwrites any existing link layer broadcast address previously + * assigned. + * + * @route_doc{link_attr_broadcast, Link Layer Broadcast Address} + * @see rtnl_link_get_broadcast() + */ +void rtnl_link_set_broadcast(struct rtnl_link *link, struct nl_addr *addr) +{ + __assign_addr(link, &link->l_bcast, addr, LINK_ATTR_BRD); +} + +/** + * Return link layer broadcast address of link object + * @arg link Link object + * + * @copydoc pointer_lifetime_warning + * @route_doc{link_attr_address, Link Layer Address} + * @see rtnl_link_set_broadcast() + * @return Link layer address or NULL if not set. + */ +struct nl_addr *rtnl_link_get_broadcast(struct rtnl_link *link) +{ + return link->ce_mask & LINK_ATTR_BRD ? link->l_bcast : NULL; +} + +/** + * Set flags of link object + * @arg link Link object + * @arg flags Flags + * + * @see rtnl_link_get_flags() + * @see rtnl_link_unset_flags() + */ +void rtnl_link_set_flags(struct rtnl_link *link, unsigned int flags) +{ + link->l_flag_mask |= flags; + link->l_flags |= flags; + link->ce_mask |= LINK_ATTR_FLAGS; +} + +/** + * Unset flags of link object + * @arg link Link object + * @arg flags Flags + * + * @see rtnl_link_set_flags() + * @see rtnl_link_get_flags() + */ +void rtnl_link_unset_flags(struct rtnl_link *link, unsigned int flags) +{ + link->l_flag_mask |= flags; + link->l_flags &= ~flags; + link->ce_mask |= LINK_ATTR_FLAGS; +} + +/** + * Return flags of link object + * @arg link Link object + * + * @route_doc{link_attr_flags, Link Flags} + * @see rtnl_link_set_flags() + * @see rtnl_link_unset_flags() + * @return Link flags or 0 if none have been set. + */ +unsigned int rtnl_link_get_flags(struct rtnl_link *link) +{ + return link->l_flags; +} + +/** + * Set address family of link object + * + * @see rtnl_link_get_family() + */ +void rtnl_link_set_family(struct rtnl_link *link, int family) +{ + link->l_family = family; + link->ce_mask |= LINK_ATTR_FAMILY; + + if (link->l_af_ops) { + af_free(link, link->l_af_ops, + link->l_af_data[link->l_af_ops->ao_family], NULL); + link->l_af_data[link->l_af_ops->ao_family] = NULL; + } + + link->l_af_ops = af_lookup_and_alloc(link, family); +} + +/** + * Return address family of link object + * @arg link Link object + * + * @see rtnl_link_set_family() + * @return Address family or \c AF_UNSPEC if not specified. + */ +int rtnl_link_get_family(struct rtnl_link *link) +{ + return link->ce_mask & LINK_ATTR_FAMILY ? link->l_family : AF_UNSPEC; +} + +/** + * Set hardware type of link object + * @arg link Link object + * @arg arptype New hardware type \c (ARPHRD_*) + * + * @route_doc{link_attr_arptype, Hardware Type} + * @copydoc read_only_attribute + * @see rtnl_link_get_arptype() + */ +void rtnl_link_set_arptype(struct rtnl_link *link, unsigned int arptype) +{ + link->l_arptype = arptype; + link->ce_mask |= LINK_ATTR_ARPTYPE; +} + +/** + * Get hardware type of link object + * @arg link Link object + * + * @route_doc{link_attr_arptype, Hardware Type} + * @see rtnl_link_set_arptype() + * @return Hardware type \c (ARPHRD_ETHER *) or \c ARPHRD_VOID + */ +unsigned int rtnl_link_get_arptype(struct rtnl_link *link) +{ + if (link->ce_mask & LINK_ATTR_ARPTYPE) + return link->l_arptype; + else + return ARPHRD_VOID; +} + +/** + * Set interface index of link object + * @arg link Link object + * @arg ifindex Interface index + * + * @route_doc{link_attr_ifindex, Interface Index} + * @see rtnl_link_get_ifindex() + */ +void rtnl_link_set_ifindex(struct rtnl_link *link, int ifindex) +{ + link->l_index = ifindex; + link->ce_mask |= LINK_ATTR_IFINDEX; +} + + +/** + * Return interface index of link object + * @arg link Link object + * + * @route_doc{link_attr_ifindex, Interface Index} + * @see rtnl_link_set_ifindex() + * @return Interface index or 0 if not set. + */ +int rtnl_link_get_ifindex(struct rtnl_link *link) +{ + return link->l_index; +} + +/** + * Set Maximum Transmission Unit of link object + * @arg link Link object + * @arg mtu New MTU value in number of bytes + * + * @route_doc{link_attr_mtu, Maximum Transmission Unit} + * @see rtnl_link_get_mtu() + */ +void rtnl_link_set_mtu(struct rtnl_link *link, unsigned int mtu) +{ + link->l_mtu = mtu; + link->ce_mask |= LINK_ATTR_MTU; +} + +/** + * Return maximum transmission unit of link object + * @arg link Link object + * + * @route_doc{link_attr_mtu, Maximum Transmission Unit} + * @see rtnl_link_set_mtu() + * @return MTU in bytes or 0 if not set + */ +unsigned int rtnl_link_get_mtu(struct rtnl_link *link) +{ + return link->l_mtu; +} + +/** + * Set transmission queue length + * @arg link Link object + * @arg txqlen New queue length + * + * The unit is dependant on the link type. The most common units is number + * of packets. + * + * @route_doc{link_attr_txqlen, Transmission Queue Length} + */ +void rtnl_link_set_txqlen(struct rtnl_link *link, unsigned int txqlen) +{ + link->l_txqlen = txqlen; + link->ce_mask |= LINK_ATTR_TXQLEN; +} + +/** + * Return transmission queue length + * @arg link Link object + * + * The unit is dependant on the link type. The most common units is number + * of packets. + * + * @route_doc{link_attr_txqlen, Transmission Queue Length} + * @return queue length or 0 if not specified. + */ +unsigned int rtnl_link_get_txqlen(struct rtnl_link *link) +{ + return link->ce_mask & LINK_ATTR_TXQLEN ? link->l_txqlen : 0; +} + +void rtnl_link_set_link(struct rtnl_link *link, int ifindex) +{ + link->l_link = ifindex; + link->ce_mask |= LINK_ATTR_LINK; +} + +int rtnl_link_get_link(struct rtnl_link *link) +{ + return link->l_link; +} + +/** + * Set the netnsid of the link + * @arg link Link object + * @link_netnsid the netnsid to set + * + * Sets the IFLA_LINK_NETNSID attribute of the link + * @returns 0 on success + */ +int rtnl_link_set_link_netnsid(struct rtnl_link *link, int32_t link_netnsid) +{ + link->l_link_netnsid = link_netnsid; + link->ce_mask |= LINK_ATTR_LINK_NETNSID; + return 0; +} + +/** + * Get the netnsid of the link + * @arg link Link object + * @out_link_netnsid the netnsid + * + * Gets the IFLA_LINK_NETNSID attribute of the link + * or returns an error if the value is unset. + * + * @returns 0 on success + */ +int rtnl_link_get_link_netnsid(const struct rtnl_link *link, int32_t *out_link_netnsid) +{ + if (!(link->ce_mask & LINK_ATTR_LINK_NETNSID)) + return -NLE_INVAL; + + *out_link_netnsid = link->l_link_netnsid; + return 0; +} + +/** + * Set master link of link object + * @arg link Link object + * @arg ifindex Interface index of master link + * + * @see rtnl_link_get_master() + */ +void rtnl_link_set_master(struct rtnl_link *link, int ifindex) +{ + link->l_master = ifindex; + link->ce_mask |= LINK_ATTR_MASTER; +} + +/** + * Return master link of link object + * @arg link Link object + * + * @see rtnl_link_set_master() + * @return Interface index of master link or 0 if not specified + */ +int rtnl_link_get_master(struct rtnl_link *link) +{ + return link->l_master; +} + +/** + * Set carrier of link object + * @arg link Link object + * @arg status New carrier status + * + * @see rtnl_link_get_carrier() + */ +void rtnl_link_set_carrier(struct rtnl_link *link, uint8_t status) +{ + link->l_carrier = status; + link->ce_mask |= LINK_ATTR_CARRIER; +} + +/** + * Return carrier status of link object + * @arg link Link object + * + * @see rtnl_link_set_master() + * @return Carrier state. + */ +uint8_t rtnl_link_get_carrier(struct rtnl_link *link) +{ + return link->l_carrier; +} + +/** + * Return carrier on/off changes of link object + * @arg link Link object + * @arg carrier_changes Pointer to store number of carrier changes + * + * @return 0 on success, negative error number otherwise + */ +int rtnl_link_get_carrier_changes(struct rtnl_link *link, uint32_t *carrier_changes) +{ + if (!(link->ce_mask & LINK_ATTR_CARRIER_CHANGES)) + return -NLE_NOATTR; + + if (carrier_changes) + *carrier_changes = link->l_carrier_changes; + + return 0; +} + +/** + * Set operational status of link object + * @arg link Link object + * @arg status New opertional status + * + * @route_doc{link_attr_operstate, Operational Status}} + * @see rtnl_link_get_operstate() + */ +void rtnl_link_set_operstate(struct rtnl_link *link, uint8_t status) +{ + link->l_operstate = status; + link->ce_mask |= LINK_ATTR_OPERSTATE; +} + +/** + * Return operational status of link object + * @arg link Link object + * + * @route_doc{link_attr_operstate, Operational Status} + * @see rtnl_link_set_operstate() + * @return Opertional state or \c IF_OPER_UNKNOWN + */ +uint8_t rtnl_link_get_operstate(struct rtnl_link *link) +{ + return link->l_operstate; +} + +/** + * Set link mode of link object + * @arg link Link object + * @arg mode New link mode + * + * @route_doc{link_attr_mode, Mode} + * @see rtnl_link_get_linkmode() + */ +void rtnl_link_set_linkmode(struct rtnl_link *link, uint8_t mode) +{ + link->l_linkmode = mode; + link->ce_mask |= LINK_ATTR_LINKMODE; +} + +/** + * Return link mode of link object + * @arg link Link object + * + * @route_doc{link_attr_mode, Mode} + * @see rtnl_link_get_linkmode() + * @return Link mode or \c IF_LINK_MODE_DEFAULT + */ +uint8_t rtnl_link_get_linkmode(struct rtnl_link *link) +{ + return link->l_linkmode; +} + +/** + * Return alias name of link object (SNMP IfAlias) + * @arg link Link object + * + * @route_doc{link_attr_alias, Alias} + * @see rtnl_link_set_ifalias() + * @return Alias name or NULL if not set. + */ +const char *rtnl_link_get_ifalias(struct rtnl_link *link) +{ + return link->l_ifalias; +} + +/** + * Set alias name of link object (SNMP IfAlias) + * @arg link Link object + * @arg alias Alias name or NULL to unset + * + * Sets the alias name of the link to the specified name. The alias + * name can be unset by specyfing NULL as the alias. The name will + * be strdup()ed, so no need to provide a persistent character string. + * + * @route_doc{link_attr_alias, Alias} + * @see rtnl_link_get_ifalias() + */ +void rtnl_link_set_ifalias(struct rtnl_link *link, const char *alias) +{ + free(link->l_ifalias); + + if (alias) { + link->l_ifalias = strdup(alias); + link->ce_mask |= LINK_ATTR_IFALIAS; + } else { + link->l_ifalias = NULL; + link->ce_mask &= ~LINK_ATTR_IFALIAS; + } +} + +/** + * Set queueing discipline name of link object + * @arg link Link object + * @arg name Name of queueing discipline + * + * @copydoc read_only_attribute + * + * For more information on how to modify the qdisc of a link, see section + * @ref_route{route_tc, Traffic Control}. + * + * @route_doc{link_attr_qdisc, Queueing Discipline Name} + * @see rtnl_link_get_qdisc() + */ +void rtnl_link_set_qdisc(struct rtnl_link *link, const char *name) +{ + _nl_strncpy_trunc(link->l_qdisc, name, sizeof(link->l_qdisc)); + link->ce_mask |= LINK_ATTR_QDISC; +} + +/** + * Return name of queueing discipline of link object + * @arg link Link object + * + * @route_doc{link_attr_qdisc, Queueing Discipline Name} + * @see rtnl_link_set_qdisc() + * @return Name of qdisc or NULL if not specified. + */ +char *rtnl_link_get_qdisc(struct rtnl_link *link) +{ + return link->ce_mask & LINK_ATTR_QDISC ? link->l_qdisc : NULL; +} + + +/** + * Return number of PCI virtual functions of link object + * @arg link Link object + * @arg num_vf Pointer to store number of VFs + * + * @return 0 on success or -NLE_OPNOTSUPP if not available + */ +int rtnl_link_get_num_vf(struct rtnl_link *link, uint32_t *num_vf) +{ + if (link->ce_mask & LINK_ATTR_NUM_VF) { + *num_vf = link->l_num_vf; + return 0; + } else + return -NLE_OPNOTSUPP; +} + +/** + * Return value of link statistics counter + * @arg link Link object + * @arg id Identifier of statistical counter + * + * @return Value of counter or 0 if not specified. + */ +uint64_t rtnl_link_get_stat(struct rtnl_link *link, rtnl_link_stat_id_t id) +{ + if (id > RTNL_LINK_STATS_MAX) + return 0; + + return link->l_stats[id]; +} + +/** + * Set value of link statistics counter + * @arg link Link object + * @arg id Identifier of statistical counter + * @arg value New value + * + * \note Changing the value of a statistical counter will not change the + * value in the kernel. + * + * @return 0 on success or a negative error code + */ +int rtnl_link_set_stat(struct rtnl_link *link, rtnl_link_stat_id_t id, + const uint64_t value) +{ + if (id > RTNL_LINK_STATS_MAX) + return -NLE_INVAL; + + link->l_stats[id] = value; + + return 0; +} + +/** + * Set type of link object + * @arg link Link object + * @arg type Name of link type + * + * Looks up the link type module and prepares the link to store type + * specific attributes. If a type has been assigned already it will + * be released with all link type specific attributes lost. + * + * @route_doc{link_modules, Link Modules} + * @return 0 on success or a negative error code. + */ +int rtnl_link_set_type(struct rtnl_link *link, const char *type) +{ + struct rtnl_link_info_ops *io; + _nl_auto_free char *kind = NULL; + int err; + + free(link->l_info_kind); + link->ce_mask &= ~LINK_ATTR_LINKINFO; + release_link_info(link); + + if (!type) + return 0; + + kind = strdup(type); + if (!kind) + return -NLE_NOMEM; + + io = rtnl_link_info_ops_lookup(type); + if (io) { + if ( io->io_alloc + && (err = io->io_alloc(link)) < 0) + return err; + + link->l_info_ops = io; + } + + link->l_info_kind = _nl_steal_pointer(&kind); + link->ce_mask |= LINK_ATTR_LINKINFO; + + return 0; +} + +/** + * Return type of link + * @arg link Link object + * + * @route_doc{link_modules, Link Modules} + * @return Name of link type or NULL if not specified. + */ +char *rtnl_link_get_type(struct rtnl_link *link) +{ + return link->l_info_kind; +} + +/** + * Set type of slave link object + * @arg link Link object (slave) + * @arg type Name of link type + * + * If a slave type has been assigned already it will be released. + * + * @route_doc{link_modules, Link Modules} + * @return 0 on success or a negative error code. + */ +int rtnl_link_set_slave_type(struct rtnl_link *link, const char *type) +{ + char *kind = NULL; + + if (type) { + kind = strdup(type); + if (!kind) + return -NLE_NOMEM; + } + + free(link->l_info_slave_kind); + link->l_info_slave_kind = kind; + + if (kind) + link->ce_mask |= LINK_ATTR_LINKINFO_SLAVE_KIND; + else + link->ce_mask &= ~LINK_ATTR_LINKINFO_SLAVE_KIND; + return 0; +} + +/** + * Return type of enslaved link + * @arg link Link object + * + * @route_doc{link_modules, Link Modules} + * @return Name of enslaved link type or NULL if not specified. + */ +const char *rtnl_link_get_slave_type(const struct rtnl_link *link) +{ + return link->l_info_slave_kind; +} + + +/** + * Set link promiscuity count + * @arg link Link object + * @arg count New promiscuity count + * + * @copydoc read_only_attribute + * + * @see rtnl_link_get_promiscuity() + */ +void rtnl_link_set_promiscuity(struct rtnl_link *link, uint32_t count) +{ + link->l_promiscuity = count; + link->ce_mask |= LINK_ATTR_PROMISCUITY; +} + +/** + * Return link promiscuity count + * @arg link Link object + * + * @see rtnl_link_set_promiscuity() + * @return Link promiscuity count or 0 + */ +uint32_t rtnl_link_get_promiscuity(struct rtnl_link *link) +{ + return link->l_promiscuity; +} + +/** + * Set number of TX queues + * @arg link Link object + * @arg nqueues Number of queues + * + * Sets the number of TX queues of the link object. The value is considered + * by the kernel when creating network devices that can be created via + * netlink. The value will be passed on to alloc_netdev_mqs() + * + * Therefore use of rtnl_link_set_num_tx_queues() only makes sense in + * combination with rtnl_link_add() or if the link object is used as a filter. + * + * @see rtnl_link_get_num_tx_queues() + */ +void rtnl_link_set_num_tx_queues(struct rtnl_link *link, uint32_t nqueues) +{ + link->l_num_tx_queues = nqueues; + link->ce_mask |= LINK_ATTR_NUM_TX_QUEUES; +} + +/** + * Return number of TX queues + * @arg link Link object + * + * @return Number of TX queues or 0 + */ +uint32_t rtnl_link_get_num_tx_queues(struct rtnl_link *link) +{ + return link->l_num_tx_queues; +} + +/** + * Set number of RX queues + * @arg link Link object + * @arg nqueues Number of queues + * + * Sets the number of RX queues of the link object. The value is considered + * by the kernel when creating network devices that can be created via + * netlink. The value will be passed on to alloc_netdev_mqs() + * + * Therefore use of rtnl_link_set_num_rx_queues() only makes sense in + * combination with rtnl_link_add() or if the link object is used as a filter. + * + * @see rtnl_link_get_num_rx_queues() + */ +void rtnl_link_set_num_rx_queues(struct rtnl_link *link, uint32_t nqueues) +{ + link->l_num_rx_queues = nqueues; + link->ce_mask |= LINK_ATTR_NUM_RX_QUEUES; +} + +/** + * Return number of RX queues + * @arg link Link object + * + * @return Number of RX queues or 0 + */ +uint32_t rtnl_link_get_num_rx_queues(struct rtnl_link *link) +{ + return link->l_num_rx_queues; +} + +/** + * Return maximum number of segments for generic segmentation offload + * @arg link Link object + * @arg gso_max_segs Pointer to store maximum number GSO segments + * + * @return 0 on success, negative error number otherwise + */ +int rtnl_link_get_gso_max_segs(struct rtnl_link *link, uint32_t *gso_max_segs) +{ + if (!(link->ce_mask & LINK_ATTR_GSO_MAX_SEGS)) + return -NLE_NOATTR; + + if (gso_max_segs) + *gso_max_segs = link->l_gso_max_segs; + + return 0; +} + +/** + * Return maximum size for generic segmentation offload + * @arg link Link object + * @arg gso_max_segs Pointer to store maximum GSO size + * + * @return 0 on success, negative error number otherwise + */ +int rtnl_link_get_gso_max_size(struct rtnl_link *link, uint32_t *gso_max_size) +{ + if (!(link->ce_mask & LINK_ATTR_GSO_MAX_SIZE)) + return -NLE_NOATTR; + + if (gso_max_size) + *gso_max_size = link->l_gso_max_size; + + return 0; +} + +/** + * Return physical port id of link object + * @arg link Link object + * + * @return Physical port id or NULL if not set. + */ +struct nl_data *rtnl_link_get_phys_port_id(struct rtnl_link *link) +{ + return link->l_phys_port_id; +} + +/** + * Return physical port name of link object + * @arg link Link object + * + * @return Physical port name or NULL if not set. + */ +char *rtnl_link_get_phys_port_name(struct rtnl_link *link) +{ + return link->l_phys_port_name; +} + +/* + * Return physical switch id of link object + * @arg link Link object + * + * @return Physical switch id or NULL if not set. + */ +struct nl_data *rtnl_link_get_phys_switch_id(struct rtnl_link *link) +{ + return link->l_phys_switch_id; +} + +void rtnl_link_set_ns_fd(struct rtnl_link *link, int fd) +{ + link->l_ns_fd = fd; + link->ce_mask |= LINK_ATTR_NS_FD; +} + +int rtnl_link_get_ns_fd(struct rtnl_link *link) +{ + return link->l_ns_fd; +} + +void rtnl_link_set_ns_pid(struct rtnl_link *link, pid_t pid) +{ + link->l_ns_pid = pid; + link->ce_mask |= LINK_ATTR_NS_PID; +} + +pid_t rtnl_link_get_ns_pid(struct rtnl_link *link) +{ + return link->l_ns_pid; +} + +/** @} */ + +/** + * @name Master/Slave + * @{ + */ + +/** + * Enslave slave link to master link + * @arg sock netlink socket + * @arg master ifindex of master link + * @arg slave ifindex of slave link + * + * This function is identical to rtnl_link_enslave() except that + * it takes interface indices instead of rtnl_link objects. + * + * @see rtnl_link_enslave() + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_enslave_ifindex(struct nl_sock *sock, int master, int slave) +{ + _nl_auto_rtnl_link struct rtnl_link *link = NULL; + int err; + + if (!(link = rtnl_link_alloc())) + return -NLE_NOMEM; + + rtnl_link_set_ifindex(link, slave); + rtnl_link_set_master(link, master); + + if ((err = rtnl_link_change(sock, link, link, 0)) < 0) + return err; + + _nl_clear_pointer(&link, rtnl_link_put); + + /* + * Due to the kernel not signaling whether this operation is + * supported or not, we will retrieve the attribute to see if the + * request was successful. If the master assigned remains unchanged + * we will return NLE_OPNOTSUPP to allow performing backwards + * compatibility of some sort. + */ + if ((err = rtnl_link_get_kernel(sock, slave, NULL, &link)) < 0) + return err; + + if (rtnl_link_get_master(link) != master) + return -NLE_OPNOTSUPP; + + return 0; +} + +/** + * Enslave slave link to master link + * @arg sock netlink socket + * @arg master master link + * @arg slave slave link + * + * Constructs a RTM_NEWLINK or RTM_SETLINK message adding the slave to + * the master and sends the request via the specified netlink socket. + * + * @note The feature of enslaving/releasing via netlink has only been added + * recently to the kernel (Feb 2011). Also, the kernel does not signal + * if the operation is not supported. Therefore this function will + * verify if the master assignment has changed and will return + * -NLE_OPNOTSUPP if it did not. + * + * @see rtnl_link_enslave_ifindex() + * @see rtnl_link_release() + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_enslave(struct nl_sock *sock, struct rtnl_link *master, + struct rtnl_link *slave) +{ + return rtnl_link_enslave_ifindex(sock, rtnl_link_get_ifindex(master), + rtnl_link_get_ifindex(slave)); +} + +/** + * Release slave link from its master + * @arg sock netlink socket + * @arg slave slave link + * + * This function is identical to rtnl_link_release() except that + * it takes an interface index instead of a rtnl_link object. + * + * @see rtnl_link_release() + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_release_ifindex(struct nl_sock *sock, int slave) +{ + return rtnl_link_enslave_ifindex(sock, 0, slave); +} + +/** + * Release slave link from its master + * @arg sock netlink socket + * @arg slave slave link + * + * Constructs a RTM_NEWLINK or RTM_SETLINK message releasing the slave from + * its master and sends the request via the specified netlink socket. + * + * @note The feature of enslaving/releasing via netlink has only been added + * recently to the kernel (Feb 2011). Also, the kernel does not signal + * if the operation is not supported. Therefore this function will + * verify if the master assignment has changed and will return + * -NLE_OPNOTSUPP if it did not. + * + * @see rtnl_link_release_ifindex() + * @see rtnl_link_enslave() + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_release(struct nl_sock *sock, struct rtnl_link *slave) +{ + return rtnl_link_release_ifindex(sock, rtnl_link_get_ifindex(slave)); +} + +/** @} */ + +/** + * @name Utilities + * @{ + */ + +static const struct trans_tbl link_flags[] = { + __ADD(IFF_LOOPBACK, loopback), + __ADD(IFF_BROADCAST, broadcast), + __ADD(IFF_POINTOPOINT, pointopoint), + __ADD(IFF_MULTICAST, multicast), + __ADD(IFF_NOARP, noarp), + __ADD(IFF_ALLMULTI, allmulti), + __ADD(IFF_PROMISC, promisc), + __ADD(IFF_MASTER, master), + __ADD(IFF_SLAVE, slave), + __ADD(IFF_DEBUG, debug), + __ADD(IFF_DYNAMIC, dynamic), + __ADD(IFF_AUTOMEDIA, automedia), + __ADD(IFF_PORTSEL, portsel), + __ADD(IFF_NOTRAILERS, notrailers), + __ADD(IFF_UP, up), + __ADD(IFF_RUNNING, running), + __ADD(IFF_LOWER_UP, lowerup), + __ADD(IFF_DORMANT, dormant), + __ADD(IFF_ECHO, echo), +}; + +char *rtnl_link_flags2str(int flags, char *buf, size_t len) +{ + return __flags2str(flags, buf, len, link_flags, + ARRAY_SIZE(link_flags)); +} + +int rtnl_link_str2flags(const char *name) +{ + return __str2flags(name, link_flags, ARRAY_SIZE(link_flags)); +} + +static const struct trans_tbl link_stats[] = { + __ADD(RTNL_LINK_RX_PACKETS, rx_packets), + __ADD(RTNL_LINK_TX_PACKETS, tx_packets), + __ADD(RTNL_LINK_RX_BYTES, rx_bytes), + __ADD(RTNL_LINK_TX_BYTES, tx_bytes), + __ADD(RTNL_LINK_RX_ERRORS, rx_errors), + __ADD(RTNL_LINK_TX_ERRORS, tx_errors), + __ADD(RTNL_LINK_RX_DROPPED, rx_dropped), + __ADD(RTNL_LINK_TX_DROPPED, tx_dropped), + __ADD(RTNL_LINK_RX_COMPRESSED, rx_compressed), + __ADD(RTNL_LINK_TX_COMPRESSED, tx_compressed), + __ADD(RTNL_LINK_RX_FIFO_ERR, rx_fifo_err), + __ADD(RTNL_LINK_TX_FIFO_ERR, tx_fifo_err), + __ADD(RTNL_LINK_RX_LEN_ERR, rx_len_err), + __ADD(RTNL_LINK_RX_OVER_ERR, rx_over_err), + __ADD(RTNL_LINK_RX_CRC_ERR, rx_crc_err), + __ADD(RTNL_LINK_RX_FRAME_ERR, rx_frame_err), + __ADD(RTNL_LINK_RX_MISSED_ERR, rx_missed_err), + __ADD(RTNL_LINK_TX_ABORT_ERR, tx_abort_err), + __ADD(RTNL_LINK_TX_CARRIER_ERR, tx_carrier_err), + __ADD(RTNL_LINK_TX_HBEAT_ERR, tx_hbeat_err), + __ADD(RTNL_LINK_TX_WIN_ERR, tx_win_err), + __ADD(RTNL_LINK_COLLISIONS, collisions), + __ADD(RTNL_LINK_MULTICAST, multicast), + __ADD(RTNL_LINK_IP6_INPKTS, Ip6InReceives), + __ADD(RTNL_LINK_IP6_INHDRERRORS, Ip6InHdrErrors), + __ADD(RTNL_LINK_IP6_INTOOBIGERRORS, Ip6InTooBigErrors), + __ADD(RTNL_LINK_IP6_INNOROUTES, Ip6InNoRoutes), + __ADD(RTNL_LINK_IP6_INADDRERRORS, Ip6InAddrErrors), + __ADD(RTNL_LINK_IP6_INUNKNOWNPROTOS, Ip6InUnknownProtos), + __ADD(RTNL_LINK_IP6_INTRUNCATEDPKTS, Ip6InTruncatedPkts), + __ADD(RTNL_LINK_IP6_INDISCARDS, Ip6InDiscards), + __ADD(RTNL_LINK_IP6_INDELIVERS, Ip6InDelivers), + __ADD(RTNL_LINK_IP6_OUTFORWDATAGRAMS, Ip6OutForwDatagrams), + __ADD(RTNL_LINK_IP6_OUTPKTS, Ip6OutRequests), + __ADD(RTNL_LINK_IP6_OUTDISCARDS, Ip6OutDiscards), + __ADD(RTNL_LINK_IP6_OUTNOROUTES, Ip6OutNoRoutes), + __ADD(RTNL_LINK_IP6_REASMTIMEOUT, Ip6ReasmTimeout), + __ADD(RTNL_LINK_IP6_REASMREQDS, Ip6ReasmReqds), + __ADD(RTNL_LINK_IP6_REASMOKS, Ip6ReasmOKs), + __ADD(RTNL_LINK_IP6_REASMFAILS, Ip6ReasmFails), + __ADD(RTNL_LINK_IP6_FRAGOKS, Ip6FragOKs), + __ADD(RTNL_LINK_IP6_FRAGFAILS, Ip6FragFails), + __ADD(RTNL_LINK_IP6_FRAGCREATES, Ip6FragCreates), + __ADD(RTNL_LINK_IP6_INMCASTPKTS, Ip6InMcastPkts), + __ADD(RTNL_LINK_IP6_OUTMCASTPKTS, Ip6OutMcastPkts), + __ADD(RTNL_LINK_IP6_INBCASTPKTS, Ip6InBcastPkts), + __ADD(RTNL_LINK_IP6_OUTBCASTPKTS, Ip6OutBcastPkts), + __ADD(RTNL_LINK_IP6_INOCTETS, Ip6InOctets), + __ADD(RTNL_LINK_IP6_OUTOCTETS, Ip6OutOctets), + __ADD(RTNL_LINK_IP6_INMCASTOCTETS, Ip6InMcastOctets), + __ADD(RTNL_LINK_IP6_OUTMCASTOCTETS, Ip6OutMcastOctets), + __ADD(RTNL_LINK_IP6_INBCASTOCTETS, Ip6InBcastOctets), + __ADD(RTNL_LINK_IP6_OUTBCASTOCTETS, Ip6OutBcastOctets), + __ADD(RTNL_LINK_ICMP6_INMSGS, ICMP6_InMsgs), + __ADD(RTNL_LINK_ICMP6_INERRORS, ICMP6_InErrors), + __ADD(RTNL_LINK_ICMP6_OUTMSGS, ICMP6_OutMsgs), + __ADD(RTNL_LINK_ICMP6_OUTERRORS, ICMP6_OutErrors), + __ADD(RTNL_LINK_ICMP6_CSUMERRORS, ICMP6_InCsumErrors), + __ADD(RTNL_LINK_IP6_CSUMERRORS, Ip6_InCsumErrors), + __ADD(RTNL_LINK_IP6_NOECTPKTS, Ip6_InNoECTPkts), + __ADD(RTNL_LINK_IP6_ECT1PKTS, Ip6_InECT1Pkts), + __ADD(RTNL_LINK_IP6_ECT0PKTS, Ip6_InECT0Pkts), + __ADD(RTNL_LINK_IP6_CEPKTS, Ip6_InCEPkts), + __ADD(RTNL_LINK_RX_NOHANDLER, rx_nohandler), + __ADD(RTNL_LINK_REASM_OVERLAPS, ReasmOverlaps), +}; + +char *rtnl_link_stat2str(int st, char *buf, size_t len) +{ + return __type2str(st, buf, len, link_stats, ARRAY_SIZE(link_stats)); +} + +int rtnl_link_str2stat(const char *name) +{ + return __str2type(name, link_stats, ARRAY_SIZE(link_stats)); +} + +static const struct trans_tbl link_operstates[] = { + __ADD(IF_OPER_UNKNOWN, unknown), + __ADD(IF_OPER_NOTPRESENT, notpresent), + __ADD(IF_OPER_DOWN, down), + __ADD(IF_OPER_LOWERLAYERDOWN, lowerlayerdown), + __ADD(IF_OPER_TESTING, testing), + __ADD(IF_OPER_DORMANT, dormant), + __ADD(IF_OPER_UP, up), +}; + +char *rtnl_link_operstate2str(uint8_t st, char *buf, size_t len) +{ + return __type2str(st, buf, len, link_operstates, + ARRAY_SIZE(link_operstates)); +} + +int rtnl_link_str2operstate(const char *name) +{ + return __str2type(name, link_operstates, + ARRAY_SIZE(link_operstates)); +} + +static const struct trans_tbl link_modes[] = { + __ADD(IF_LINK_MODE_DEFAULT, default), + __ADD(IF_LINK_MODE_DORMANT, dormant), +}; + +static const struct trans_tbl carrier_states[] = { + __ADD(0, down), + __ADD(1, up), +}; + +char *rtnl_link_mode2str(uint8_t st, char *buf, size_t len) +{ + return __type2str(st, buf, len, link_modes, ARRAY_SIZE(link_modes)); +} + +int rtnl_link_str2mode(const char *name) +{ + return __str2type(name, link_modes, ARRAY_SIZE(link_modes)); +} + +char *rtnl_link_carrier2str(uint8_t st, char *buf, size_t len) +{ + return __type2str(st, buf, len, carrier_states, + ARRAY_SIZE(carrier_states)); +} + +int rtnl_link_str2carrier(const char *name) +{ + return __str2type(name, carrier_states, ARRAY_SIZE(carrier_states)); +} + +int rtnl_link_has_vf_list(struct rtnl_link *link) { + if (link->ce_mask & LINK_ATTR_VF_LIST) + return 1; + else + return 0; +} + +void rtnl_link_set_vf_list(struct rtnl_link *link) { + int err; + + if (!(err = rtnl_link_has_vf_list(link))) + link->ce_mask |= LINK_ATTR_VF_LIST; + + return; +} + +void rtnl_link_unset_vf_list(struct rtnl_link *link) { + int err; + + if ((err = rtnl_link_has_vf_list(link))) + link->ce_mask &= ~LINK_ATTR_VF_LIST; + + return; +} + +/** @} */ + +/** + * @name Deprecated Functions + */ + +/** + * @deprecated Use of this function is deprecated, use rtnl_link_set_type() + */ +int rtnl_link_set_info_type(struct rtnl_link *link, const char *type) +{ + return rtnl_link_set_type(link, type); +} + +/** + * @deprecated Use of this function is deprecated, use rtnl_link_get_type() + */ +char *rtnl_link_get_info_type(struct rtnl_link *link) +{ + return rtnl_link_get_type(link); +} + +/** + * @deprecated The weight attribute is unused and obsoleted in all recent kernels + */ +void rtnl_link_set_weight(struct rtnl_link *link, unsigned int weight) +{ + link->l_weight = weight; + link->ce_mask |= LINK_ATTR_WEIGHT; +} + +/** + * @deprecated The weight attribute is unused and obsoleted in all recent kernels + */ +unsigned int rtnl_link_get_weight(struct rtnl_link *link) +{ + return link->l_weight; +} + +/** @} */ + +static struct nl_object_ops link_obj_ops = { + .oo_name = "route/link", + .oo_size = sizeof(struct rtnl_link), + .oo_free_data = link_free_data, + .oo_clone = link_clone, + .oo_dump = { + [NL_DUMP_LINE] = link_dump_line, + [NL_DUMP_DETAILS] = link_dump_details, + [NL_DUMP_STATS] = link_dump_stats, + }, + .oo_compare = link_compare, + .oo_keygen = link_keygen, + .oo_attrs2str = link_attrs2str, + .oo_id_attrs = LINK_ATTR_IFINDEX | LINK_ATTR_FAMILY, +}; + +static struct nl_af_group link_groups[] = { + { AF_UNSPEC, RTNLGRP_LINK }, + { AF_BRIDGE, RTNLGRP_LINK }, + { AF_INET6, RTNLGRP_IPV6_IFINFO }, + { END_OF_GROUP_LIST }, +}; + +static struct nl_cache_ops rtnl_link_ops = { + .co_name = "route/link", + .co_hdrsize = sizeof(struct ifinfomsg), + .co_msgtypes = { + { RTM_NEWLINK, NL_ACT_NEW, "new" }, + { RTM_DELLINK, NL_ACT_DEL, "del" }, + { RTM_GETLINK, NL_ACT_GET, "get" }, + { RTM_SETLINK, NL_ACT_CHANGE, "set" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_ROUTE, + .co_groups = link_groups, + .co_request_update = link_request_update, + .co_msg_parser = link_msg_parser, + .co_obj_ops = &link_obj_ops, +}; + +static void __init link_init(void) +{ + nl_cache_mngt_register(&rtnl_link_ops); +} + +static void __exit link_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_link_ops); +} + +/** @} */ diff --git a/libnl/lib/route/link/api.c b/libnl/lib/route/link/api.c new file mode 100644 index 0000000..5681d61 --- /dev/null +++ b/libnl/lib/route/link/api.c @@ -0,0 +1,415 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + */ + +/** + * @ingroup link + * @defgroup link_API Link Modules API + * @brief API for modules implementing specific link types/semantics. + * + * @par 1) Registering/Unregistering a new link info type + * @code + * static struct rtnl_link_info_ops vlan_info_ops = { + * .io_name = "vlan", + * .io_alloc = vlan_alloc, + * .io_parse = vlan_parse, + * .io_dump[NL_DUMP_BRIEF] = vlan_dump_brief, + * .io_dump[NL_DUMP_FULL] = vlan_dump_full, + * .io_free = vlan_free, + * }; + * + * static void __init vlan_init(void) + * { + * rtnl_link_register_info(&vlan_info_ops); + * } + * + * static void __exit vlan_exit(void) + * { + * rtnl_link_unregister_info(&vlan_info_ops); + * } + * @endcode + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include + +static NL_LIST_HEAD(info_ops); + +/* lock protecting info_ops and af_ops */ +static NL_RW_LOCK(info_lock); + +static struct rtnl_link_info_ops *__rtnl_link_info_ops_lookup(const char *name) +{ + struct rtnl_link_info_ops *ops; + + nl_list_for_each_entry(ops, &info_ops, io_list) + if (!strcmp(ops->io_name, name)) + return ops; + + return NULL; +} + +/** + * @name Link Info Modules + * @{ + */ + +/** + * Return operations of a specific link info type + * @arg name Name of link info type. + * + * @note The returned pointer must be given back using rtnl_link_info_ops_put() + * + * @return Pointer to operations or NULL if unavailable. + */ +struct rtnl_link_info_ops *rtnl_link_info_ops_lookup(const char *name) +{ + struct rtnl_link_info_ops *ops; + + nl_write_lock(&info_lock); + if ((ops = __rtnl_link_info_ops_lookup(name))) + ops->io_refcnt++; + nl_write_unlock(&info_lock); + + return ops; +} + +/** + * Give back reference to a set of operations. + * @arg ops Link info operations. + */ +void rtnl_link_info_ops_put(struct rtnl_link_info_ops *ops) +{ + if (ops) + ops->io_refcnt--; +} + +/** + * Register operations for a link info type + * @arg ops Link info operations + * + * This function must be called by modules implementing a specific link + * info type. It will make the operations implemented by the module + * available for everyone else. + * + * @return 0 on success or a negative error code. + * @return -NLE_INVAL Link info name not specified. + * @return -NLE_EXIST Operations for address family already registered. + */ +int rtnl_link_register_info(struct rtnl_link_info_ops *ops) +{ + int err = 0; + + if (ops->io_name == NULL) + return -NLE_INVAL; + + nl_write_lock(&info_lock); + if (__rtnl_link_info_ops_lookup(ops->io_name)) { + err = -NLE_EXIST; + goto errout; + } + + NL_DBG(1, "Registered link info operations %s\n", ops->io_name); + + nl_list_add_tail(&ops->io_list, &info_ops); +errout: + nl_write_unlock(&info_lock); + + return err; +} + +/** + * Unregister operations for a link info type + * @arg ops Link info operations + * + * This function must be called if a module implementing a specific link + * info type is unloaded or becomes unavailable. It must provide a + * set of operations which have previously been registered using + * rtnl_link_register_info(). + * + * @return 0 on success or a negative error code + * @return _NLE_OPNOTSUPP Link info operations not registered. + * @return -NLE_BUSY Link info operations still in use. + */ +int rtnl_link_unregister_info(struct rtnl_link_info_ops *ops) +{ + struct rtnl_link_info_ops *t; + int err = -NLE_OPNOTSUPP; + + nl_write_lock(&info_lock); + + nl_list_for_each_entry(t, &info_ops, io_list) { + if (t == ops) { + if (t->io_refcnt > 0) { + err = -NLE_BUSY; + goto errout; + } + + nl_list_del(&t->io_list); + + NL_DBG(1, "Unregistered link info operations %s\n", + ops->io_name); + err = 0; + goto errout; + } + } + +errout: + nl_write_unlock(&info_lock); + + return err; +} + +/** @} */ + +/** + * @name Link Address Family Modules + * @{ + */ + +static struct rtnl_link_af_ops *af_ops[AF_MAX]; + +/** + * Return operations of a specific link address family + * @arg family Address family + * + * @note The returned pointer must be given back using rtnl_link_af_ops_put() + * + * @return Pointer to operations or NULL if unavailable. + */ +struct rtnl_link_af_ops *rtnl_link_af_ops_lookup(const unsigned int family) +{ + if (family == AF_UNSPEC || family >= AF_MAX) + return NULL; + + nl_write_lock(&info_lock); + if (af_ops[family]) + af_ops[family]->ao_refcnt++; + nl_write_unlock(&info_lock); + + return af_ops[family]; +} + +/** + * Give back reference to a set of operations. + * @arg ops Address family operations. + */ +void rtnl_link_af_ops_put(struct rtnl_link_af_ops *ops) +{ + if (ops) + ops->ao_refcnt--; +} + +/** + * Allocate and return data buffer for link address family modules + * @arg link Link object + * @arg ops Address family operations + * + * This function must be called by link address family modules in all + * cases where the API does not provide the data buffer as argument + * already. This typically includes set functions the module provides. + * Calling this function is strictly required to ensure proper allocation + * of the buffer upon first use. Link objects will NOT proactively + * allocate a data buffer for each registered link address family. + * + * @return Pointer to data buffer or NULL on error. + */ +void *rtnl_link_af_alloc(struct rtnl_link *link, + const struct rtnl_link_af_ops *ops) +{ + int family; + + if (!link || !ops) + BUG(); + + family = ops->ao_family; + + if (!link->l_af_data[family]) { + if (!ops->ao_alloc) + BUG(); + + link->l_af_data[family] = ops->ao_alloc(link); + if (!link->l_af_data[family]) + return NULL; + } + + return link->l_af_data[family]; +} + +/** + * Return data buffer for link address family modules + * @arg link Link object + * @arg ops Address family operations + * + * This function returns a pointer to the data buffer for the specified link + * address family module or NULL if the buffer was not allocated yet. This + * function is typically used by get functions of modules which are not + * interested in having the data buffer allocated if no values have been set + * yet. + * + * @return Pointer to data buffer or NULL on error. + */ +void *rtnl_link_af_data(const struct rtnl_link *link, + const struct rtnl_link_af_ops *ops) +{ + if (!link || !ops) + BUG(); + + return link->l_af_data[ops->ao_family]; +} + +/** + * Register operations for a link address family + * @arg ops Address family operations + * + * This function must be called by modules implementing a specific link + * address family. It will make the operations implemented by the module + * available for everyone else. + * + * @return 0 on success or a negative error code. + * @return -NLE_INVAL Address family is out of range (0..AF_MAX) + * @return -NLE_EXIST Operations for address family already registered. + */ +int rtnl_link_af_register(struct rtnl_link_af_ops *ops) +{ + int err = 0; + + if (ops->ao_family == AF_UNSPEC || ops->ao_family >= AF_MAX) + return -NLE_INVAL; + + nl_write_lock(&info_lock); + if (af_ops[ops->ao_family]) { + err = -NLE_EXIST; + goto errout; + } + + ops->ao_refcnt = 0; + af_ops[ops->ao_family] = ops; + + NL_DBG(1, "Registered link address family operations %u\n", + ops->ao_family); + +errout: + nl_write_unlock(&info_lock); + + return err; +} + +/** + * Unregister operations for a link address family + * @arg ops Address family operations + * + * This function must be called if a module implementing a specific link + * address family is unloaded or becomes unavailable. It must provide a + * set of operations which have previously been registered using + * rtnl_link_af_register(). + * + * @return 0 on success or a negative error code + * @return -NLE_INVAL ops is NULL + * @return -NLE_OBJ_NOTFOUND Address family operations not registered. + * @return -NLE_BUSY Address family operations still in use. + */ +int rtnl_link_af_unregister(struct rtnl_link_af_ops *ops) +{ + int err = -NLE_INVAL; + + if (!ops) + return err; + + nl_write_lock(&info_lock); + if (!af_ops[ops->ao_family]) { + err = -NLE_OBJ_NOTFOUND; + goto errout; + } + + if (ops->ao_refcnt > 0) { + err = -NLE_BUSY; + goto errout; + } + + af_ops[ops->ao_family] = NULL; + + NL_DBG(1, "Unregistered link address family operations %u\n", + ops->ao_family); + +errout: + nl_write_unlock(&info_lock); + + return err; +} + +/** + * Compare af data for a link address family + * @arg a Link object a + * @arg b Link object b + * @arg family af data family + * + * This function will compare af_data between two links + * a and b of family given by arg family + * + * @return 0 if address family specific data matches or is not present + * or != 0 if it mismatches. + */ +int rtnl_link_af_data_compare(struct rtnl_link *a, struct rtnl_link *b, + int family) +{ + struct rtnl_link_af_ops *af_ops; + int ret = 0; + + if (!a->l_af_data[family] && !b->l_af_data[family]) + return 0; + + if (!a->l_af_data[family] || !b->l_af_data[family]) + return ~0; + + af_ops = rtnl_link_af_ops_lookup(family); + if (!af_ops) + return ~0; + + if (af_ops->ao_compare == NULL) { + ret = ~0; + goto out; + } + + ret = af_ops->ao_compare(a, b, family, ~0, 0); + +out: + rtnl_link_af_ops_put(af_ops); + + return ret; +} + +/** + * Compare link info data + * @arg a Link object a + * @arg b Link object b + * + * This function will compare link_info data between two links + * a and b + * + * @return 0 if link_info data matches or is not present + * or != 0 if it mismatches. + */ +int rtnl_link_info_data_compare(struct rtnl_link *a, struct rtnl_link *b, int flags) +{ + if (a->l_info_ops != b->l_info_ops) + return ~0; + + if (!a->l_info_ops || !a->l_info_ops->io_compare) + return 0; + + return a->l_info_ops->io_compare(a, b, flags); +} + +/** @} */ + +/** @} */ + diff --git a/libnl/lib/route/link/bonding.c b/libnl/lib/route/link/bonding.c new file mode 100644 index 0000000..5f78716 --- /dev/null +++ b/libnl/lib/route/link/bonding.c @@ -0,0 +1,224 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2011-2013 Thomas Graf + */ + +/** + * @ingroup link + * @defgroup bonding Bonding + * + * @details + * \b Link Type Name: "bond" + * + * @route_doc{link_bonding, Bonding Documentation} + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include + +/** + * Allocate link object of type bond + * + * @return Allocated link object or NULL. + */ +struct rtnl_link *rtnl_link_bond_alloc(void) +{ + struct rtnl_link *link; + int err; + + if (!(link = rtnl_link_alloc())) + return NULL; + + if ((err = rtnl_link_set_type(link, "bond")) < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Create a new kernel bonding device + * @arg sock netlink socket + * @arg name name of bonding device or NULL + * @arg opts bonding options (currently unused) + * + * Creates a new bonding device in the kernel. If no name is + * provided, the kernel will automatically pick a name of the + * form "type%d" (e.g. bond0, vlan1, etc.) + * + * The \a opts argument is currently unused. In the future, it + * may be used to carry additional bonding options to be set + * when creating the bonding device. + * + * @note When letting the kernel assign a name, it will become + * difficult to retrieve the interface afterwards because + * you have to guess the name the kernel has chosen. It is + * therefore not recommended to not provide a device name. + * + * @see rtnl_link_bond_enslave() + * @see rtnl_link_bond_release() + * + * @return 0 on success or a negative error code + */ +int rtnl_link_bond_add(struct nl_sock *sock, const char *name, + struct rtnl_link *opts) +{ + struct rtnl_link *link; + int err; + + if (!(link = rtnl_link_bond_alloc())) + return -NLE_NOMEM; + + if (!name && opts) + name = rtnl_link_get_name(opts); + + if (name) + rtnl_link_set_name(link, name); + + err = rtnl_link_add(sock, link, NLM_F_CREATE); + + rtnl_link_put(link); + + return err; +} + +/** + * Add a link to a bond (enslave) + * @arg sock netlink socket + * @arg master ifindex of bonding master + * @arg slave ifindex of slave link to add to bond + * + * This function is identical to rtnl_link_bond_enslave() except that + * it takes interface indices instead of rtnl_link objcets. + * + * @see rtnl_link_bond_enslave() + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_bond_enslave_ifindex(struct nl_sock *sock, int master, + int slave) +{ + struct rtnl_link *link; + int err; + + if (!(link = rtnl_link_bond_alloc())) + return -NLE_NOMEM; + + rtnl_link_set_ifindex(link, slave); + rtnl_link_set_master(link, master); + + if ((err = rtnl_link_change(sock, link, link, 0)) < 0) + goto errout; + + rtnl_link_put(link); + + /* + * Due to the kernel not signaling whether this opertion is + * supported or not, we will retrieve the attribute to see if the + * request was successful. If the master assigned remains unchanged + * we will return NLE_OPNOTSUPP to allow performing backwards + * compatibility of some sort. + */ + if ((err = rtnl_link_get_kernel(sock, slave, NULL, &link)) < 0) + return err; + + if (rtnl_link_get_master(link) != master) + err = -NLE_OPNOTSUPP; + +errout: + rtnl_link_put(link); + + return err; +} + +/** + * Add a link to a bond (enslave) + * @arg sock netlink socket + * @arg master bonding master + * @arg slave slave link to add to bond + * + * Constructs a RTM_NEWLINK or RTM_SETLINK message adding the slave to + * the master and sends the request via the specified netlink socket. + * + * @note The feature of enslaving/releasing via netlink has only been added + * recently to the kernel (Feb 2011). Also, the kernel does not signal + * if the operation is not supported. Therefore this function will + * verify if the master assignment has changed and will return + * -NLE_OPNOTSUPP if it did not. + * + * @see rtnl_link_bond_enslave_ifindex() + * @see rtnl_link_bond_release() + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_bond_enslave(struct nl_sock *sock, struct rtnl_link *master, + struct rtnl_link *slave) +{ + return rtnl_link_bond_enslave_ifindex(sock, + rtnl_link_get_ifindex(master), + rtnl_link_get_ifindex(slave)); +} + +/** + * Release a link from a bond + * @arg sock netlink socket + * @arg slave slave link to be released + * + * This function is identical to rtnl_link_bond_release() except that + * it takes an interface index instead of a rtnl_link object. + * + * @see rtnl_link_bond_release() + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_bond_release_ifindex(struct nl_sock *sock, int slave) +{ + return rtnl_link_bond_enslave_ifindex(sock, 0, slave); +} + +/** + * Release a link from a bond + * @arg sock netlink socket + * @arg slave slave link to be released + * + * Constructs a RTM_NEWLINK or RTM_SETLINK message releasing the slave from + * its master and sends the request via the specified netlink socket. + * + * @note The feature of enslaving/releasing via netlink has only been added + * recently to the kernel (Feb 2011). Also, the kernel does not signal + * if the operation is not supported. Therefore this function will + * verify if the master assignment has changed and will return + * -NLE_OPNOTSUPP if it did not. + * + * @see rtnl_link_bond_release_ifindex() + * @see rtnl_link_bond_enslave() + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_bond_release(struct nl_sock *sock, struct rtnl_link *slave) +{ + return rtnl_link_bond_release_ifindex(sock, + rtnl_link_get_ifindex(slave)); +} + +static struct rtnl_link_info_ops bonding_info_ops = { + .io_name = "bond", +}; + +static void __init bonding_init(void) +{ + rtnl_link_register_info(&bonding_info_ops); +} + +static void __exit bonding_exit(void) +{ + rtnl_link_unregister_info(&bonding_info_ops); +} + +/** @} */ diff --git a/libnl/lib/route/link/bridge.c b/libnl/lib/route/link/bridge.c new file mode 100644 index 0000000..2437db8 --- /dev/null +++ b/libnl/lib/route/link/bridge.c @@ -0,0 +1,988 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010-2013 Thomas Graf + */ + +/** + * @ingroup link + * @defgroup bridge Bridging + * + * @details + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +#define VLAN_VID_MASK 0x0fff /* VLAN Identifier */ + +/** @cond SKIP */ +#define BRIDGE_ATTR_PORT_STATE (1 << 0) +#define BRIDGE_ATTR_PRIORITY (1 << 1) +#define BRIDGE_ATTR_COST (1 << 2) +#define BRIDGE_ATTR_FLAGS (1 << 3) +#define BRIDGE_ATTR_PORT_VLAN (1 << 4) +#define BRIDGE_ATTR_HWMODE (1 << 5) +#define BRIDGE_ATTR_SELF (1 << 6) + +#define PRIV_FLAG_NEW_ATTRS (1 << 0) + +struct bridge_data +{ + uint8_t b_port_state; + uint8_t b_priv_flags; /* internal flags */ + uint16_t b_hwmode; + uint16_t b_priority; + uint16_t b_self; /* here for comparison reasons */ + uint32_t b_cost; + uint32_t b_flags; + uint32_t b_flags_mask; + uint32_t ce_mask; /* HACK to support attr macros */ + struct rtnl_link_bridge_vlan vlan_info; +}; + +static void set_bit(unsigned nr, uint32_t *addr) +{ + if (nr < RTNL_LINK_BRIDGE_VLAN_BITMAP_MAX) + addr[nr / 32] |= (((uint32_t) 1) << (nr % 32)); +} + +static int find_next_bit(int i, uint32_t x) +{ + int j; + + if (i >= 32) + return -1; + + /* find first bit */ + if (i < 0) + return __builtin_ffs(x); + + /* mask off prior finds to get next */ + j = __builtin_ffs(x >> i); + return j ? j + i : 0; +} + +static struct rtnl_link_af_ops bridge_ops; + +#define IS_BRIDGE_LINK_ASSERT(link) \ + if (!rtnl_link_is_bridge(link)) { \ + APPBUG("A function was expecting a link object of type bridge."); \ + return -NLE_OPNOTSUPP; \ + } + +static inline struct bridge_data *bridge_data(struct rtnl_link *link) +{ + return rtnl_link_af_data(link, &bridge_ops); +} + +static void *bridge_alloc(struct rtnl_link *link) +{ + return calloc(1, sizeof(struct bridge_data)); +} + +static void *bridge_clone(struct rtnl_link *link, void *data) +{ + struct bridge_data *bd; + + if ((bd = bridge_alloc(link))) + memcpy(bd, data, sizeof(*bd)); + + return bd; +} + +static void bridge_free(struct rtnl_link *link, void *data) +{ + free(data); +} + +static struct nla_policy br_attrs_policy[IFLA_BRPORT_MAX+1] = { + [IFLA_BRPORT_STATE] = { .type = NLA_U8 }, + [IFLA_BRPORT_PRIORITY] = { .type = NLA_U16 }, + [IFLA_BRPORT_COST] = { .type = NLA_U32 }, + [IFLA_BRPORT_MODE] = { .type = NLA_U8 }, + [IFLA_BRPORT_GUARD] = { .type = NLA_U8 }, + [IFLA_BRPORT_PROTECT] = { .type = NLA_U8 }, + [IFLA_BRPORT_FAST_LEAVE] = { .type = NLA_U8 }, + [IFLA_BRPORT_LEARNING] = { .type = NLA_U8 }, + [IFLA_BRPORT_LEARNING_SYNC] = { .type = NLA_U8 }, + [IFLA_BRPORT_UNICAST_FLOOD] = { .type = NLA_U8 }, +}; + +static void check_flag(struct rtnl_link *link, struct nlattr *attrs[], + int type, int flag) +{ + if (attrs[type] && nla_get_u8(attrs[type])) + rtnl_link_bridge_set_flags(link, flag); +} + +static int bridge_parse_protinfo(struct rtnl_link *link, struct nlattr *attr, + void *data) +{ + struct bridge_data *bd = data; + struct nlattr *br_attrs[IFLA_BRPORT_MAX+1]; + int err; + + /* Backwards compatibility */ + if (!nla_is_nested(attr)) { + if (nla_len(attr) < 1) + return -NLE_RANGE; + + bd->b_port_state = nla_get_u8(attr); + bd->ce_mask |= BRIDGE_ATTR_PORT_STATE; + + return 0; + } + + if ((err = nla_parse_nested(br_attrs, IFLA_BRPORT_MAX, attr, + br_attrs_policy)) < 0) + return err; + + bd->b_priv_flags |= PRIV_FLAG_NEW_ATTRS; + + if (br_attrs[IFLA_BRPORT_STATE]) { + bd->b_port_state = nla_get_u8(br_attrs[IFLA_BRPORT_STATE]); + bd->ce_mask |= BRIDGE_ATTR_PORT_STATE; + } + + if (br_attrs[IFLA_BRPORT_PRIORITY]) { + bd->b_priority = nla_get_u16(br_attrs[IFLA_BRPORT_PRIORITY]); + bd->ce_mask |= BRIDGE_ATTR_PRIORITY; + } + + if (br_attrs[IFLA_BRPORT_COST]) { + bd->b_cost = nla_get_u32(br_attrs[IFLA_BRPORT_COST]); + bd->ce_mask |= BRIDGE_ATTR_COST; + } + + check_flag(link, br_attrs, IFLA_BRPORT_MODE, RTNL_BRIDGE_HAIRPIN_MODE); + check_flag(link, br_attrs, IFLA_BRPORT_GUARD, RTNL_BRIDGE_BPDU_GUARD); + check_flag(link, br_attrs, IFLA_BRPORT_PROTECT, RTNL_BRIDGE_ROOT_BLOCK); + check_flag(link, br_attrs, IFLA_BRPORT_FAST_LEAVE, RTNL_BRIDGE_FAST_LEAVE); + check_flag(link, br_attrs, IFLA_BRPORT_UNICAST_FLOOD, + RTNL_BRIDGE_UNICAST_FLOOD); + check_flag(link, br_attrs, IFLA_BRPORT_LEARNING, RTNL_BRIDGE_LEARNING); + check_flag(link, br_attrs, IFLA_BRPORT_LEARNING_SYNC, + RTNL_BRIDGE_LEARNING_SYNC); + + return 0; +} + +static int bridge_parse_af_full(struct rtnl_link *link, struct nlattr *attr_full, + void *data) +{ + struct bridge_data *bd = data; + struct bridge_vlan_info *vinfo = NULL; + uint16_t vid_range_start = 0; + uint16_t vid_range_flags = -1; + + struct nlattr *attr; + int remaining; + + nla_for_each_nested(attr, attr_full, remaining) { + + if (nla_type(attr) == IFLA_BRIDGE_MODE) { + bd->b_hwmode = nla_get_u16(attr); + bd->ce_mask |= BRIDGE_ATTR_HWMODE; + } else if (nla_type(attr) != IFLA_BRIDGE_VLAN_INFO) + continue; + + if (nla_len(attr) != sizeof(struct bridge_vlan_info)) + return -EINVAL; + + vinfo = nla_data(attr); + if (!vinfo->vid || vinfo->vid >= VLAN_VID_MASK) + return -EINVAL; + + + if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) { + vid_range_start = vinfo->vid; + vid_range_flags = (vinfo->flags ^ BRIDGE_VLAN_INFO_RANGE_BEGIN); + continue; + } + + if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END) { + /* sanity check the range flags */ + if (vid_range_flags != (vinfo->flags ^ BRIDGE_VLAN_INFO_RANGE_END)) { + NL_DBG(1, "VLAN range flags differ; can not handle it.\n"); + return -EINVAL; + } + } else { + vid_range_start = vinfo->vid; + } + + for (; vid_range_start <= vinfo->vid; vid_range_start++) { + if (vinfo->flags & BRIDGE_VLAN_INFO_PVID) + bd->vlan_info.pvid = vinfo->vid; + + if (vinfo->flags & BRIDGE_VLAN_INFO_UNTAGGED) + set_bit(vid_range_start, bd->vlan_info.untagged_bitmap); + + set_bit(vid_range_start, bd->vlan_info.vlan_bitmap); + bd->ce_mask |= BRIDGE_ATTR_PORT_VLAN; + } + + vid_range_flags = -1; + } + + return 0; +} + +static int bridge_fill_af(struct rtnl_link *link, struct nl_msg *msg, + void *data) +{ + struct bridge_data *bd = data; + + if ((bd->ce_mask & BRIDGE_ATTR_SELF)||(bd->ce_mask & BRIDGE_ATTR_HWMODE)) + NLA_PUT_U16(msg, IFLA_BRIDGE_FLAGS, BRIDGE_FLAGS_SELF); + + if (bd->ce_mask & BRIDGE_ATTR_HWMODE) + NLA_PUT_U16(msg, IFLA_BRIDGE_MODE, bd->b_hwmode); + + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +static int bridge_fill_pi(struct rtnl_link *link, struct nl_msg *msg, + void *data) +{ + struct bridge_data *bd = data; + + if (bd->ce_mask & BRIDGE_ATTR_FLAGS) { + if (bd->b_flags_mask & RTNL_BRIDGE_BPDU_GUARD) { + NLA_PUT_U8(msg, IFLA_BRPORT_GUARD, + bd->b_flags & RTNL_BRIDGE_BPDU_GUARD); + } + if (bd->b_flags_mask & RTNL_BRIDGE_HAIRPIN_MODE) { + NLA_PUT_U8(msg, IFLA_BRPORT_MODE, + bd->b_flags & RTNL_BRIDGE_HAIRPIN_MODE); + } + if (bd->b_flags_mask & RTNL_BRIDGE_FAST_LEAVE) { + NLA_PUT_U8(msg, IFLA_BRPORT_FAST_LEAVE, + bd->b_flags & RTNL_BRIDGE_FAST_LEAVE); + } + if (bd->b_flags_mask & RTNL_BRIDGE_ROOT_BLOCK) { + NLA_PUT_U8(msg, IFLA_BRPORT_PROTECT, + bd->b_flags & RTNL_BRIDGE_ROOT_BLOCK); + } + if (bd->b_flags_mask & RTNL_BRIDGE_UNICAST_FLOOD) { + NLA_PUT_U8(msg, IFLA_BRPORT_UNICAST_FLOOD, + bd->b_flags & RTNL_BRIDGE_UNICAST_FLOOD); + } + if (bd->b_flags_mask & RTNL_BRIDGE_LEARNING) { + NLA_PUT_U8(msg, IFLA_BRPORT_LEARNING, + bd->b_flags & RTNL_BRIDGE_LEARNING); + } + if (bd->b_flags_mask & RTNL_BRIDGE_LEARNING_SYNC) { + NLA_PUT_U8(msg, IFLA_BRPORT_LEARNING_SYNC, + bd->b_flags & RTNL_BRIDGE_LEARNING_SYNC); + } + } + + if (bd->ce_mask & BRIDGE_ATTR_COST) + NLA_PUT_U32(msg, IFLA_BRPORT_COST, bd->b_cost); + + if (bd->ce_mask & BRIDGE_ATTR_PRIORITY) + NLA_PUT_U16(msg, IFLA_BRPORT_PRIORITY, bd->b_priority); + + if (bd->ce_mask & BRIDGE_ATTR_PORT_STATE) + NLA_PUT_U8(msg, IFLA_BRPORT_STATE, bd->b_port_state); + + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +static int bridge_override_rtm(struct rtnl_link *link) { + struct bridge_data *bd; + + if (!rtnl_link_is_bridge(link)) + return 0; + + bd = bridge_data(link); + + if (bd->ce_mask & BRIDGE_ATTR_FLAGS) + return 1; + + return 0; +} + +static int bridge_get_af(struct nl_msg *msg, uint32_t *ext_filter_mask) +{ + *ext_filter_mask |= RTEXT_FILTER_BRVLAN; + return 0; +} + +static void dump_bitmap(struct nl_dump_params *p, const uint32_t *b) +{ + int i = -1, j, k; + int start = -1, prev = -1; + int done, found = 0; + + for (k = 0; k < RTNL_LINK_BRIDGE_VLAN_BITMAP_LEN; k++) { + int base_bit; + uint32_t a = b[k]; + + base_bit = k * 32; + i = -1; + done = 0; + while (!done) { + j = find_next_bit(i, a); + if (j > 0) { + /* first hit of any bit */ + if (start < 0 && prev < 0) { + start = prev = j - 1 + base_bit; + goto next; + } + /* this bit is a continuation of prior bits */ + if (j - 2 + base_bit == prev) { + prev++; + goto next; + } + } else + done = 1; + + if (start >= 0) { + found++; + if (done && k < RTNL_LINK_BRIDGE_VLAN_BITMAP_LEN - 1) + break; + + nl_dump(p, " %d", start); + if (start != prev) + nl_dump(p, "-%d", prev); + + if (done) + break; + } + if (j > 0) + start = prev = j - 1 + base_bit; +next: + i = j; + } + } + if (!found) + nl_dump(p, " "); + + return; +} + +static void rtnl_link_bridge_dump_vlans(struct nl_dump_params *p, + struct bridge_data *bd) +{ + nl_dump(p, "pvid %u", bd->vlan_info.pvid); + + nl_dump(p, " all vlans:"); + dump_bitmap(p, bd->vlan_info.vlan_bitmap); + + nl_dump(p, " untagged vlans:"); + dump_bitmap(p, bd->vlan_info.untagged_bitmap); +} + +static void bridge_dump_details(struct rtnl_link *link, + struct nl_dump_params *p, void *data) +{ + struct bridge_data *bd = data; + + nl_dump_line(p, " bridge: "); + + if (bd->ce_mask & BRIDGE_ATTR_PORT_STATE) + nl_dump(p, "port-state %u ", bd->b_port_state); + + if (bd->ce_mask & BRIDGE_ATTR_PRIORITY) + nl_dump(p, "prio %u ", bd->b_priority); + + if (bd->ce_mask & BRIDGE_ATTR_COST) + nl_dump(p, "cost %u ", bd->b_cost); + + if (bd->ce_mask & BRIDGE_ATTR_HWMODE) { + char hbuf[32]; + + rtnl_link_bridge_hwmode2str(bd->b_hwmode, hbuf, sizeof(hbuf)); + nl_dump(p, "hwmode %s", hbuf); + } + + if (bd->ce_mask & BRIDGE_ATTR_PORT_VLAN) + rtnl_link_bridge_dump_vlans(p, bd); + + if (bd->ce_mask & BRIDGE_ATTR_FLAGS) { + char buf[256]; + + rtnl_link_bridge_flags2str(bd->b_flags & bd->b_flags_mask, + buf, sizeof(buf)); + nl_dump(p, "%s", buf); + } + + nl_dump(p, "\n"); +} + +static int bridge_compare(struct rtnl_link *_a, struct rtnl_link *_b, + int family, uint32_t attrs, int flags) +{ + struct bridge_data *a = bridge_data(_a); + struct bridge_data *b = bridge_data(_b); + int diff = 0; + +#define BRIDGE_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, BRIDGE_ATTR_##ATTR, a, b, EXPR) + diff |= BRIDGE_DIFF(PORT_STATE, a->b_port_state != b->b_port_state); + diff |= BRIDGE_DIFF(PRIORITY, a->b_priority != b->b_priority); + diff |= BRIDGE_DIFF(COST, a->b_cost != b->b_cost); + diff |= BRIDGE_DIFF(PORT_VLAN, memcmp(&a->vlan_info, &b->vlan_info, + sizeof(struct rtnl_link_bridge_vlan))); + diff |= BRIDGE_DIFF(HWMODE, a->b_hwmode != b->b_hwmode); + diff |= BRIDGE_DIFF(SELF, a->b_self != b->b_self); + + if (flags & LOOSE_COMPARISON) + diff |= BRIDGE_DIFF(FLAGS, + (a->b_flags ^ b->b_flags) & b->b_flags_mask); + else + diff |= BRIDGE_DIFF(FLAGS, a->b_flags != b->b_flags); +#undef BRIDGE_DIFF + + return diff; +} +/** @endcond */ + +/** + * Allocate link object of type bridge + * + * @return Allocated link object or NULL. + */ +struct rtnl_link *rtnl_link_bridge_alloc(void) +{ + struct rtnl_link *link; + int err; + + if (!(link = rtnl_link_alloc())) + return NULL; + + if ((err = rtnl_link_set_type(link, "bridge")) < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Create a new kernel bridge device + * @arg sk netlink socket + * @arg name name of the bridge device or NULL + * + * Creates a new bridge device in the kernel. If no name is + * provided, the kernel will automatically pick a name of the + * form "type%d" (e.g. bridge0, vlan1, etc.) + * + * @return 0 on success or a negative error code +*/ +int rtnl_link_bridge_add(struct nl_sock *sk, const char *name) +{ + int err; + struct rtnl_link *link; + + if (!(link = rtnl_link_bridge_alloc())) + return -NLE_NOMEM; + + if(name) + rtnl_link_set_name(link, name); + + err = rtnl_link_add(sk, link, NLM_F_CREATE); + rtnl_link_put(link); + + return err; +} + +/** + * Check if a link is a bridge + * @arg link Link object + * + * @return 1 if the link is a bridge, 0 otherwise. + */ +int rtnl_link_is_bridge(struct rtnl_link *link) +{ + return link->l_family == AF_BRIDGE && + link->l_af_ops == &bridge_ops; +} + +/** + * Check if bridge has extended information + * @arg link Link object of type bridge + * + * Checks if the bridge object has been constructed based on + * information that is only available in newer kernels. This + * affectes the following functions: + * - rtnl_link_bridge_get_cost() + * - rtnl_link_bridge_get_priority() + * - rtnl_link_bridge_get_flags() + * + * @return 1 if extended information is available, otherwise 0 is returned. + */ +int rtnl_link_bridge_has_ext_info(struct rtnl_link *link) +{ + struct bridge_data *bd; + + if (!rtnl_link_is_bridge(link)) + return 0; + + bd = bridge_data(link); + return !!(bd->b_priv_flags & PRIV_FLAG_NEW_ATTRS); +} + +/** + * Set Spanning Tree Protocol (STP) port state + * @arg link Link object of type bridge + * @arg state New STP port state + * + * The value of state must be one of the following: + * - BR_STATE_DISABLED + * - BR_STATE_LISTENING + * - BR_STATE_LEARNING + * - BR_STATE_FORWARDING + * - BR_STATE_BLOCKING + * + * @see rtnl_link_bridge_get_port_state() + * + * @return 0 on success or a negative error code. + * @retval -NLE_OPNOTSUPP Link is not a bridge + * @retval -NLE_INVAL Invalid state value (0..BR_STATE_BLOCKING) + */ +int rtnl_link_bridge_set_port_state(struct rtnl_link *link, uint8_t state) +{ + struct bridge_data *bd = bridge_data(link); + + IS_BRIDGE_LINK_ASSERT(link); + + if (state > BR_STATE_BLOCKING) + return -NLE_INVAL; + + bd->b_port_state = state; + bd->ce_mask |= BRIDGE_ATTR_PORT_STATE; + + return 0; +} + +/** + * Get Spanning Tree Protocol (STP) port state + * @arg link Link object of type bridge + * + * @see rtnl_link_bridge_set_port_state() + * + * @return The STP port state or a negative error code. + * @retval -NLE_OPNOTSUPP Link is not a bridge + */ +int rtnl_link_bridge_get_port_state(struct rtnl_link *link) +{ + struct bridge_data *bd = bridge_data(link); + + IS_BRIDGE_LINK_ASSERT(link); + + return bd->b_port_state; +} + +/** + * Set priority + * @arg link Link object of type bridge + * @arg prio Bridge priority + * + * @see rtnl_link_bridge_get_priority() + * + * @return 0 on success or a negative error code. + * @retval -NLE_OPNOTSUPP Link is not a bridge + */ +int rtnl_link_bridge_set_priority(struct rtnl_link *link, uint16_t prio) +{ + struct bridge_data *bd = bridge_data(link); + + IS_BRIDGE_LINK_ASSERT(link); + + bd->b_priority = prio; + bd->ce_mask |= BRIDGE_ATTR_PRIORITY; + + return 0; +} + +/** + * Get priority + * @arg link Link object of type bridge + * + * @see rtnl_link_bridge_set_priority() + * + * @return 0 on success or a negative error code. + * @retval -NLE_OPNOTSUPP Link is not a bridge + */ +int rtnl_link_bridge_get_priority(struct rtnl_link *link) +{ + struct bridge_data *bd = bridge_data(link); + + IS_BRIDGE_LINK_ASSERT(link); + + return bd->b_priority; +} + +/** + * Set Spanning Tree Protocol (STP) path cost + * @arg link Link object of type bridge + * @arg cost New STP path cost value + * + * @see rtnl_link_bridge_get_cost() + * + * @return The bridge priority or a negative error code. + * @retval -NLE_OPNOTSUPP Link is not a bridge + */ +int rtnl_link_bridge_set_cost(struct rtnl_link *link, uint32_t cost) +{ + struct bridge_data *bd = bridge_data(link); + + IS_BRIDGE_LINK_ASSERT(link); + + bd->b_cost = cost; + bd->ce_mask |= BRIDGE_ATTR_COST; + + return 0; +} + +/** + * Get Spanning Tree Protocol (STP) path cost + * @arg link Link object of type bridge + * @arg cost Pointer to store STP cost value + * + * @see rtnl_link_bridge_set_cost() + * + * @return 0 on success or a negative error code. + * @retval -NLE_OPNOTSUPP Link is not a bridge + * @retval -NLE_INVAL `cost` is not a valid pointer + */ +int rtnl_link_bridge_get_cost(struct rtnl_link *link, uint32_t *cost) +{ + struct bridge_data *bd = bridge_data(link); + + IS_BRIDGE_LINK_ASSERT(link); + + if (!cost) + return -NLE_INVAL; + + *cost = bd->b_cost; + + return 0; +} + +/** + * Unset flags + * @arg link Link object of type bridge + * @arg flags Bridging flags to unset + * + * @see rtnl_link_bridge_set_flags() + * @see rtnl_link_bridge_get_flags() + * + * @return 0 on success or a negative error code. + * @retval -NLE_OPNOTSUPP Link is not a bridge + */ +int rtnl_link_bridge_unset_flags(struct rtnl_link *link, unsigned int flags) +{ + struct bridge_data *bd = bridge_data(link); + + IS_BRIDGE_LINK_ASSERT(link); + + bd->b_flags_mask |= flags; + bd->b_flags &= ~flags; + bd->ce_mask |= BRIDGE_ATTR_FLAGS; + + return 0; +} + +/** + * Set flags + * @arg link Link object of type bridge + * @arg flags Bridging flags to set + * + * Valid flags are: + * - RTNL_BRIDGE_HAIRPIN_MODE + * - RTNL_BRIDGE_BPDU_GUARD + * - RTNL_BRIDGE_ROOT_BLOCK + * - RTNL_BRIDGE_FAST_LEAVE + * - RTNL_BRIDGE_UNICAST_FLOOD + * - RTNL_BRIDGE_LEARNING + * - RTNL_BRIDGE_LEARNING_SYNC + * + * @see rtnl_link_bridge_unset_flags() + * @see rtnl_link_bridge_get_flags() + * + * @return 0 on success or a negative error code. + * @retval -NLE_OPNOTSUPP Link is not a bridge + */ +int rtnl_link_bridge_set_flags(struct rtnl_link *link, unsigned int flags) +{ + struct bridge_data *bd = bridge_data(link); + + IS_BRIDGE_LINK_ASSERT(link); + + bd->b_flags_mask |= flags; + bd->b_flags |= flags; + bd->ce_mask |= BRIDGE_ATTR_FLAGS; + + return 0; +} + +/** + * Get flags + * @arg link Link object of type bridge + * + * @see rtnl_link_bridge_set_flags() + * @see rtnl_link_bridge_unset_flags() + * + * @return Flags or a negative error code. + * @retval -NLE_OPNOTSUPP Link is not a bridge + */ +int rtnl_link_bridge_get_flags(struct rtnl_link *link) +{ + struct bridge_data *bd = bridge_data(link); + + IS_BRIDGE_LINK_ASSERT(link); + + return bd->b_flags; +} + +/** + * Set link change type to self + * @arg link Link Object of type bridge + * + * This will set the bridge change flag to self, meaning that changes to + * be applied with this link object will be applied directly to the physical + * device in a bridge instead of the virtual device. + * + * @return 0 on success or negative error code + * @return -NLE_OPNOTSUP Link is not a bridge + */ +int rtnl_link_bridge_set_self(struct rtnl_link *link) +{ + struct bridge_data *bd = bridge_data(link); + + IS_BRIDGE_LINK_ASSERT(link); + + bd->b_self |= 1; + bd->ce_mask |= BRIDGE_ATTR_SELF; + + return 0; +} + +/** + * Get hardware mode + * @arg link Link object of type bridge + * @arg hwmode Output argument. + * + * @see rtnl_link_bridge_set_hwmode() + * + * @return 0 if hardware mode is present and returned in hwmode + * @return -NLE_NOATTR if hardware mode is not present + * @return -NLE_OPNOTSUP Link is not a bridge + */ +int rtnl_link_bridge_get_hwmode(struct rtnl_link *link, uint16_t *hwmode) +{ + struct bridge_data *bd = bridge_data(link); + + IS_BRIDGE_LINK_ASSERT(link); + + if (!(bd->ce_mask & BRIDGE_ATTR_HWMODE)) + return -NLE_NOATTR; + + *hwmode = bd->b_hwmode; + return 0; +} + +/** + * Set hardware mode + * @arg link Link object of type bridge + * @arg hwmode Hardware mode to set on link + * + * This will set the hardware mode of a link when it supports hardware + * offloads for bridging. + * @see rtnl_link_bridge_get_hwmode() + * + * Valid modes are: + * - RTNL_BRIDGE_HWMODE_VEB + * - RTNL_BRIDGE_HWMODE_VEPA + * + * When setting hardware mode, the change type will be set to self. + * @see rtnl_link_bridge_set_self() + * + * @return 0 on success or negative error code + * @return -NLE_OPNOTSUP Link is not a bridge + * @return -NLE_INVAL when specified hwmode is unsupported. + */ +int rtnl_link_bridge_set_hwmode(struct rtnl_link *link, uint16_t hwmode) +{ + int err; + struct bridge_data *bd = bridge_data(link); + + if (hwmode > RTNL_BRIDGE_HWMODE_MAX) + return -NLE_INVAL; + + if ((err = rtnl_link_bridge_set_self(link)) < 0) + return err; + + bd->b_hwmode = hwmode; + bd->ce_mask |= BRIDGE_ATTR_HWMODE; + + return 0; +} + + +static const struct trans_tbl bridge_flags[] = { + __ADD(RTNL_BRIDGE_HAIRPIN_MODE, hairpin_mode), + __ADD(RTNL_BRIDGE_BPDU_GUARD, bpdu_guard), + __ADD(RTNL_BRIDGE_ROOT_BLOCK, root_block), + __ADD(RTNL_BRIDGE_FAST_LEAVE, fast_leave), + __ADD(RTNL_BRIDGE_UNICAST_FLOOD, flood), + __ADD(RTNL_BRIDGE_LEARNING, learning), + __ADD(RTNL_BRIDGE_LEARNING_SYNC, learning_sync), +}; + +/** + * @name Flag Translation + * @{ + */ + +char *rtnl_link_bridge_flags2str(int flags, char *buf, size_t len) +{ + return __flags2str(flags, buf, len, bridge_flags, ARRAY_SIZE(bridge_flags)); +} + +int rtnl_link_bridge_str2flags(const char *name) +{ + return __str2flags(name, bridge_flags, ARRAY_SIZE(bridge_flags)); +} + +/** @} */ + +static const struct trans_tbl port_states[] = { + __ADD(BR_STATE_DISABLED, disabled), + __ADD(BR_STATE_LISTENING, listening), + __ADD(BR_STATE_LEARNING, learning), + __ADD(BR_STATE_FORWARDING, forwarding), + __ADD(BR_STATE_BLOCKING, blocking), +}; + +/** + * @name Port State Translation + * @{ + */ + +char *rtnl_link_bridge_portstate2str(int st, char *buf, size_t len) +{ + return __type2str(st, buf, len, port_states, ARRAY_SIZE(port_states)); +} + +int rtnl_link_bridge_str2portstate(const char *name) +{ + return __str2type(name, port_states, ARRAY_SIZE(port_states)); +} + +/** @} */ + +static const struct trans_tbl hw_modes[] = { + __ADD(RTNL_BRIDGE_HWMODE_VEB, veb), + __ADD(RTNL_BRIDGE_HWMODE_VEPA, vepa), + __ADD(RTNL_BRIDGE_HWMODE_UNDEF, undef), +}; + +/** + * @name Hardware Mode Translation + * @{ + */ + +char *rtnl_link_bridge_hwmode2str(uint16_t st, char *buf, size_t len) { + return __type2str(st, buf, len, hw_modes, ARRAY_SIZE(hw_modes)); +} + +uint16_t rtnl_link_bridge_str2hwmode(const char *name) +{ + return __str2type(name, hw_modes, ARRAY_SIZE(hw_modes)); +} + +/** @} */ + +int rtnl_link_bridge_pvid(struct rtnl_link *link) +{ + struct bridge_data *bd; + + IS_BRIDGE_LINK_ASSERT(link); + + bd = link->l_af_data[AF_BRIDGE]; + if (bd->ce_mask & BRIDGE_ATTR_PORT_VLAN) + return (int) bd->vlan_info.pvid; + + return -EINVAL; +} + +int rtnl_link_bridge_has_vlan(struct rtnl_link *link) +{ + struct bridge_data *bd; + int i; + + IS_BRIDGE_LINK_ASSERT(link); + + bd = link->l_af_data[AF_BRIDGE]; + if (bd->ce_mask & BRIDGE_ATTR_PORT_VLAN) { + if (bd->vlan_info.pvid) + return 1; + + for (i = 0; i < RTNL_LINK_BRIDGE_VLAN_BITMAP_LEN; ++i) { + if (bd->vlan_info.vlan_bitmap[i] || + bd->vlan_info.untagged_bitmap[i]) + return 1; + } + } + return 0; +} + +struct rtnl_link_bridge_vlan *rtnl_link_bridge_get_port_vlan(struct rtnl_link *link) +{ + struct bridge_data *data; + + if (!rtnl_link_is_bridge(link)) + return NULL; + + data = link->l_af_data[AF_BRIDGE]; + if (data && (data->ce_mask & BRIDGE_ATTR_PORT_VLAN)) + return &data->vlan_info; + + return NULL; +} + +static struct rtnl_link_af_ops bridge_ops = { + .ao_family = AF_BRIDGE, + .ao_alloc = &bridge_alloc, + .ao_clone = &bridge_clone, + .ao_free = &bridge_free, + .ao_parse_protinfo = &bridge_parse_protinfo, + .ao_dump[NL_DUMP_DETAILS] = &bridge_dump_details, + .ao_compare = &bridge_compare, + .ao_parse_af_full = &bridge_parse_af_full, + .ao_get_af = &bridge_get_af, + .ao_fill_af = &bridge_fill_af, + .ao_fill_pi = &bridge_fill_pi, + .ao_fill_pi_flags = NLA_F_NESTED, + .ao_override_rtm = &bridge_override_rtm, + .ao_fill_af_no_nest = 1, +}; + +static void __init bridge_init(void) +{ + rtnl_link_af_register(&bridge_ops); +} + +static void __exit bridge_exit(void) +{ + rtnl_link_af_unregister(&bridge_ops); +} + +/** @} */ diff --git a/libnl/lib/route/link/can.c b/libnl/lib/route/link/can.c new file mode 100644 index 0000000..cf16067 --- /dev/null +++ b/libnl/lib/route/link/can.c @@ -0,0 +1,784 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2012 Benedikt Spranger + */ + +/** + * @ingroup link + * @defgroup can CAN + * Controller Area Network link module + * + * @details + * \b Link Type Name: "can" + * + * @route_doc{link_can, CAN Documentation} + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/** @cond SKIP */ +#define CAN_HAS_BITTIMING (1<<0) +#define CAN_HAS_BITTIMING_CONST (1<<1) +#define CAN_HAS_CLOCK (1<<2) +#define CAN_HAS_STATE (1<<3) +#define CAN_HAS_CTRLMODE (1<<4) +#define CAN_HAS_RESTART_MS (1<<5) +#define CAN_HAS_RESTART (1<<6) +#define CAN_HAS_BERR_COUNTER (1<<7) + +struct can_info { + uint32_t ci_state; + uint32_t ci_restart; + uint32_t ci_restart_ms; + struct can_ctrlmode ci_ctrlmode; + struct can_bittiming ci_bittiming; + struct can_bittiming_const ci_bittiming_const; + struct can_clock ci_clock; + struct can_berr_counter ci_berr_counter; + uint32_t ci_mask; +}; + +/** @endcond */ + +static struct nla_policy can_policy[IFLA_CAN_MAX + 1] = { + [IFLA_CAN_STATE] = { .type = NLA_U32 }, + [IFLA_CAN_CTRLMODE] = { .minlen = sizeof(struct can_ctrlmode) }, + [IFLA_CAN_RESTART_MS] = { .type = NLA_U32 }, + [IFLA_CAN_RESTART] = { .type = NLA_U32 }, + [IFLA_CAN_BITTIMING] = { .minlen = sizeof(struct can_bittiming) }, + [IFLA_CAN_BITTIMING_CONST] + = { .minlen = sizeof(struct can_bittiming_const) }, + [IFLA_CAN_CLOCK] = { .minlen = sizeof(struct can_clock) }, + [IFLA_CAN_BERR_COUNTER] = { .minlen = sizeof(struct can_berr_counter) }, +}; + +static int can_alloc(struct rtnl_link *link) +{ + struct can_info *ci; + + if (link->l_info) + memset(link->l_info, 0, sizeof(*ci)); + else { + ci = calloc(1, sizeof(*ci)); + if (!ci) + return -NLE_NOMEM; + + link->l_info = ci; + } + + return 0; +} + +static int can_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_CAN_MAX+1]; + struct can_info *ci; + int err; + + NL_DBG(3, "Parsing CAN link info\n"); + + if ((err = nla_parse_nested(tb, IFLA_CAN_MAX, data, can_policy)) < 0) + goto errout; + + if ((err = can_alloc(link)) < 0) + goto errout; + + ci = link->l_info; + + if (tb[IFLA_CAN_STATE]) { + ci->ci_state = nla_get_u32(tb[IFLA_CAN_STATE]); + ci->ci_mask |= CAN_HAS_STATE; + } + + if (tb[IFLA_CAN_RESTART]) { + ci->ci_restart = nla_get_u32(tb[IFLA_CAN_RESTART]); + ci->ci_mask |= CAN_HAS_RESTART; + } + + if (tb[IFLA_CAN_RESTART_MS]) { + ci->ci_restart_ms = nla_get_u32(tb[IFLA_CAN_RESTART_MS]); + ci->ci_mask |= CAN_HAS_RESTART_MS; + } + + if (tb[IFLA_CAN_CTRLMODE]) { + nla_memcpy(&ci->ci_ctrlmode, tb[IFLA_CAN_CTRLMODE], + sizeof(ci->ci_ctrlmode)); + ci->ci_mask |= CAN_HAS_CTRLMODE; + } + + if (tb[IFLA_CAN_BITTIMING]) { + nla_memcpy(&ci->ci_bittiming, tb[IFLA_CAN_BITTIMING], + sizeof(ci->ci_bittiming)); + ci->ci_mask |= CAN_HAS_BITTIMING; + } + + if (tb[IFLA_CAN_BITTIMING_CONST]) { + nla_memcpy(&ci->ci_bittiming_const, + tb[IFLA_CAN_BITTIMING_CONST], + sizeof(ci->ci_bittiming_const)); + ci->ci_mask |= CAN_HAS_BITTIMING_CONST; + } + + if (tb[IFLA_CAN_CLOCK]) { + nla_memcpy(&ci->ci_clock, tb[IFLA_CAN_CLOCK], + sizeof(ci->ci_clock)); + ci->ci_mask |= CAN_HAS_CLOCK; + } + + if (tb[IFLA_CAN_BERR_COUNTER]) { + nla_memcpy(&ci->ci_berr_counter, tb[IFLA_CAN_BERR_COUNTER], + sizeof(ci->ci_berr_counter)); + ci->ci_mask |= CAN_HAS_BERR_COUNTER; + } + + err = 0; +errout: + return err; +} + +static void can_free(struct rtnl_link *link) +{ + struct can_info *ci = link->l_info; + + free(ci); + link->l_info = NULL; +} + +static char *print_can_state (uint32_t state) +{ + char *text; + + switch (state) + { + case CAN_STATE_ERROR_ACTIVE: + text = "error active"; + break; + case CAN_STATE_ERROR_WARNING: + text = "error warning"; + break; + case CAN_STATE_ERROR_PASSIVE: + text = "error passive"; + break; + case CAN_STATE_BUS_OFF: + text = "bus off"; + break; + case CAN_STATE_STOPPED: + text = "stopped"; + break; + case CAN_STATE_SLEEPING: + text = "sleeping"; + break; + default: + text = "unknown state"; + } + + return text; +} + +static void can_dump_line(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct can_info *ci = link->l_info; + char buf [64]; + + rtnl_link_can_ctrlmode2str(ci->ci_ctrlmode.flags, buf, sizeof(buf)); + nl_dump(p, "bitrate %d %s <%s>", + ci->ci_bittiming.bitrate, print_can_state(ci->ci_state), buf); +} + +static void can_dump_details(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct can_info *ci = link->l_info; + char buf [64]; + + rtnl_link_can_ctrlmode2str(ci->ci_ctrlmode.flags, buf, sizeof(buf)); + nl_dump(p, " bitrate %d %s <%s>", + ci->ci_bittiming.bitrate, print_can_state(ci->ci_state), buf); + + if (ci->ci_mask & CAN_HAS_RESTART) { + if (ci->ci_restart) + nl_dump_line(p," restarting\n"); + } + + if (ci->ci_mask & CAN_HAS_RESTART_MS) { + nl_dump_line(p," restart interval %d ms\n", + ci->ci_restart_ms); + } + + if (ci->ci_mask & CAN_HAS_BITTIMING) { + nl_dump_line(p," sample point %f %%\n", + ((float) ci->ci_bittiming.sample_point)/10); + nl_dump_line(p," time quanta %d ns\n", + ci->ci_bittiming.tq); + nl_dump_line(p," propagation segment %d tq\n", + ci->ci_bittiming.prop_seg); + nl_dump_line(p," phase buffer segment1 %d tq\n", + ci->ci_bittiming.phase_seg1); + nl_dump_line(p," phase buffer segment2 %d tq\n", + ci->ci_bittiming.phase_seg2); + nl_dump_line(p," synchronisation jump width %d tq\n", + ci->ci_bittiming.sjw); + nl_dump_line(p," bitrate prescaler %d\n", + ci->ci_bittiming.brp); + } + + if (ci->ci_mask & CAN_HAS_BITTIMING_CONST) { + nl_dump_line(p," minimum tsig1 %d tq\n", + ci->ci_bittiming_const.tseg1_min); + nl_dump_line(p," maximum tsig1 %d tq\n", + ci->ci_bittiming_const.tseg1_max); + nl_dump_line(p," minimum tsig2 %d tq\n", + ci->ci_bittiming_const.tseg2_min); + nl_dump_line(p," maximum tsig2 %d tq\n", + ci->ci_bittiming_const.tseg2_max); + nl_dump_line(p," maximum sjw %d tq\n", + ci->ci_bittiming_const.sjw_max); + nl_dump_line(p," minimum brp %d\n", + ci->ci_bittiming_const.brp_min); + nl_dump_line(p," maximum brp %d\n", + ci->ci_bittiming_const.brp_max); + nl_dump_line(p," brp increment %d\n", + ci->ci_bittiming_const.brp_inc); + } + + if (ci->ci_mask & CAN_HAS_CLOCK) { + nl_dump_line(p," base freq %d Hz\n", ci->ci_clock); + + } + + if (ci->ci_mask & CAN_HAS_BERR_COUNTER) { + nl_dump_line(p," bus error RX %d\n", + ci->ci_berr_counter.rxerr); + nl_dump_line(p," bus error TX %d\n", + ci->ci_berr_counter.txerr); + } + + return; +} + +static int can_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct can_info *cdst, *csrc = src->l_info; + int ret; + + dst->l_info = NULL; + ret = rtnl_link_set_type(dst, "can"); + if (ret < 0) + return ret; + + cdst = malloc(sizeof(*cdst)); + if (!cdst) + return -NLE_NOMEM; + + *cdst = *csrc; + dst->l_info = cdst; + + return 0; +} + +static int can_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct can_info *ci = link->l_info; + struct nlattr *data; + + data = nla_nest_start(msg, IFLA_INFO_DATA); + if (!data) + return -NLE_MSGSIZE; + + if (ci->ci_mask & CAN_HAS_RESTART) + NLA_PUT_U32(msg, CAN_HAS_RESTART, ci->ci_restart); + + if (ci->ci_mask & CAN_HAS_RESTART_MS) + NLA_PUT_U32(msg, CAN_HAS_RESTART_MS, ci->ci_restart_ms); + + if (ci->ci_mask & CAN_HAS_CTRLMODE) + NLA_PUT(msg, CAN_HAS_CTRLMODE, sizeof(ci->ci_ctrlmode), + &ci->ci_ctrlmode); + + if (ci->ci_mask & CAN_HAS_BITTIMING) + NLA_PUT(msg, CAN_HAS_BITTIMING, sizeof(ci->ci_bittiming), + &ci->ci_bittiming); + + if (ci->ci_mask & CAN_HAS_BITTIMING_CONST) + NLA_PUT(msg, CAN_HAS_BITTIMING_CONST, + sizeof(ci->ci_bittiming_const), + &ci->ci_bittiming_const); + + if (ci->ci_mask & CAN_HAS_CLOCK) + NLA_PUT(msg, CAN_HAS_CLOCK, sizeof(ci->ci_clock), + &ci->ci_clock); + + nla_nest_end(msg, data); + +nla_put_failure: + + return 0; +} + +static struct rtnl_link_info_ops can_info_ops = { + .io_name = "can", + .io_alloc = can_alloc, + .io_parse = can_parse, + .io_dump = { + [NL_DUMP_LINE] = can_dump_line, + [NL_DUMP_DETAILS] = can_dump_details, + }, + .io_clone = can_clone, + .io_put_attrs = can_put_attrs, + .io_free = can_free, +}; + +/** @cond SKIP */ +#define IS_CAN_LINK_ASSERT(link) \ + if ((link)->l_info_ops != &can_info_ops) { \ + APPBUG("Link is not a CAN link. set type \"can\" first."); \ + return -NLE_OPNOTSUPP; \ + } +/** @endcond */ + +/** + * @name CAN Object + * @{ + */ + +/** + * Check if link is a CAN link + * @arg link Link object + * + * @return True if link is a CAN link, otherwise false is returned. + */ +int rtnl_link_is_can(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "can"); +} + +/** + * Restart CAN device + * @arg link Link object + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_restart(struct rtnl_link *link) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + + ci->ci_restart = 1; + ci->ci_restart |= CAN_HAS_RESTART; + + return 0; +} + +/** + * Get CAN base frequency + * @arg link Link object + * @arg freq frequency in Hz + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_freq(struct rtnl_link *link, uint32_t *freq) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + if (!freq) + return -NLE_INVAL; + + if (ci->ci_mask & CAN_HAS_CLOCK) + *freq = ci->ci_clock.freq; + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Get CAN state + * @arg link Link object + * @arg state CAN bus state + * @return 0 on success or a negative error code + */ +int rtnl_link_can_state(struct rtnl_link *link, uint32_t *state) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + if (!state) + return -NLE_INVAL; + + *state = ci->ci_state; + + return 0; +} + +/** + * Get CAN RX bus error count + * @arg link Link object + * + * @return RX bus error count on success or a negative error code + */ +int rtnl_link_can_berr_rx(struct rtnl_link *link) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + + if (ci->ci_mask & CAN_HAS_BERR_COUNTER) + return ci->ci_berr_counter.rxerr; + else + return -NLE_AGAIN; +} + +/** + * Get CAN TX bus error count + * @arg link Link object + * + * @return TX bus error count on success or a negative error code + */ +int rtnl_link_can_berr_tx(struct rtnl_link *link) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + + if (ci->ci_mask & CAN_HAS_BERR_COUNTER) + return ci->ci_berr_counter.txerr; + else + return -NLE_AGAIN; +} + +/** + * Get CAN bus error count + * @arg link Link object + * @arg berr Bus error count + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_berr(struct rtnl_link *link, struct can_berr_counter *berr) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + if (!berr) + return -NLE_INVAL; + + if (ci->ci_mask & CAN_HAS_BERR_COUNTER) + *berr = ci->ci_berr_counter; + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Get CAN harware-dependent bit-timing constant + * @arg link Link object + * @arg bt_const Bit-timing constant + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_get_bt_const(struct rtnl_link *link, + struct can_bittiming_const *bt_const) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + if (!bt_const) + return -NLE_INVAL; + + if (ci->ci_mask & CAN_HAS_BITTIMING_CONST) + *bt_const = ci->ci_bittiming_const; + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Get CAN device bit-timing + * @arg link Link object + * @arg bit_timing CAN bit-timing + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_get_bittiming(struct rtnl_link *link, + struct can_bittiming *bit_timing) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + if (!bit_timing) + return -NLE_INVAL; + + if (ci->ci_mask & CAN_HAS_BITTIMING) + *bit_timing = ci->ci_bittiming; + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Set CAN device bit-timing + * @arg link Link object + * @arg bit_timing CAN bit-timing + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_set_bittiming(struct rtnl_link *link, + struct can_bittiming *bit_timing) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + if (!bit_timing) + return -NLE_INVAL; + + ci->ci_bittiming = *bit_timing; + ci->ci_mask |= CAN_HAS_BITTIMING; + + return 0; +} + +/** + * Get CAN device bit-timing + * @arg link Link object + * @arg bitrate CAN bitrate + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_get_bitrate(struct rtnl_link *link, uint32_t *bitrate) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + if (!bitrate) + return -NLE_INVAL; + + if (ci->ci_mask & CAN_HAS_BITTIMING) + *bitrate = ci->ci_bittiming.bitrate; + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Set CAN device bit-rate + * @arg link Link object + * @arg bitrate CAN bitrate + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_set_bitrate(struct rtnl_link *link, uint32_t bitrate) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + + ci->ci_bittiming.bitrate = bitrate; + ci->ci_mask |= CAN_HAS_BITTIMING; + + return 0; +} + +/** + * Get CAN device sample point + * @arg link Link object + * @arg sp CAN sample point + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_get_sample_point(struct rtnl_link *link, uint32_t *sp) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + if (!sp) + return -NLE_INVAL; + + if (ci->ci_mask & CAN_HAS_BITTIMING) + *sp = ci->ci_bittiming.sample_point; + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Set CAN device sample point + * @arg link Link object + * @arg sp CAN sample point + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_set_sample_point(struct rtnl_link *link, uint32_t sp) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + + ci->ci_bittiming.sample_point = sp; + ci->ci_mask |= CAN_HAS_BITTIMING; + + return 0; +} + +/** + * Get CAN device restart intervall + * @arg link Link object + * @arg interval Restart intervall in ms + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_get_restart_ms(struct rtnl_link *link, uint32_t *interval) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + if (!interval) + return -NLE_INVAL; + + if (ci->ci_mask & CAN_HAS_RESTART_MS) + *interval = ci->ci_restart_ms; + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Set CAN device restart intervall + * @arg link Link object + * @arg interval Restart intervall in ms + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_set_restart_ms(struct rtnl_link *link, uint32_t interval) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + + ci->ci_restart_ms = interval; + ci->ci_mask |= CAN_HAS_RESTART_MS; + + return 0; +} + +/** + * Get CAN control mode + * @arg link Link object + * @arg ctrlmode CAN control mode + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_get_ctrlmode(struct rtnl_link *link, uint32_t *ctrlmode) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + if (!ctrlmode) + return -NLE_INVAL; + + if (ci->ci_mask & CAN_HAS_CTRLMODE) + *ctrlmode = ci->ci_ctrlmode.flags; + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Set a CAN Control Mode + * @arg link Link object + * @arg ctrlmode CAN control mode + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_set_ctrlmode(struct rtnl_link *link, uint32_t ctrlmode) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + + ci->ci_ctrlmode.flags |= ctrlmode; + ci->ci_ctrlmode.mask |= ctrlmode; + ci->ci_mask |= CAN_HAS_CTRLMODE; + + return 0; +} + +/** + * Unset a CAN Control Mode + * @arg link Link object + * @arg ctrlmode CAN control mode + * + * @return 0 on success or a negative error code + */ +int rtnl_link_can_unset_ctrlmode(struct rtnl_link *link, uint32_t ctrlmode) +{ + struct can_info *ci = link->l_info; + + IS_CAN_LINK_ASSERT(link); + + ci->ci_ctrlmode.flags &= ~ctrlmode; + ci->ci_ctrlmode.mask |= ctrlmode; + ci->ci_mask |= CAN_HAS_CTRLMODE; + + return 0; +} + +/** @} */ + +/** + * @name Control Mode Translation + * @{ + */ + +static const struct trans_tbl can_ctrlmode[] = { + __ADD(CAN_CTRLMODE_LOOPBACK, loopback), + __ADD(CAN_CTRLMODE_LISTENONLY, listen-only), + __ADD(CAN_CTRLMODE_3_SAMPLES, triple-sampling), + __ADD(CAN_CTRLMODE_ONE_SHOT, one-shot), + __ADD(CAN_CTRLMODE_BERR_REPORTING, berr-reporting), +}; + +char *rtnl_link_can_ctrlmode2str(int ctrlmode, char *buf, size_t len) +{ + return __flags2str(ctrlmode, buf, len, can_ctrlmode, + ARRAY_SIZE(can_ctrlmode)); +} + +int rtnl_link_can_str2ctrlmode(const char *name) +{ + return __str2flags(name, can_ctrlmode, ARRAY_SIZE(can_ctrlmode)); +} + +/** @} */ + +static void __init can_init(void) +{ + rtnl_link_register_info(&can_info_ops); +} + +static void __exit can_exit(void) +{ + rtnl_link_unregister_info(&can_info_ops); +} + +/** @} */ diff --git a/libnl/lib/route/link/dummy.c b/libnl/lib/route/link/dummy.c new file mode 100644 index 0000000..8fbbdc9 --- /dev/null +++ b/libnl/lib/route/link/dummy.c @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2011 Thomas Graf + */ + +/** + * @ingroup link + * @defgroup dummy Dummy + * + * @details + * \b Link Type Name: "dummy" + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include + +static struct rtnl_link_info_ops dummy_info_ops = { + .io_name = "dummy", +}; + +static void __init dummy_init(void) +{ + rtnl_link_register_info(&dummy_info_ops); +} + +static void __exit dummy_exit(void) +{ + rtnl_link_unregister_info(&dummy_info_ops); +} + +/** @} */ diff --git a/libnl/lib/route/link/geneve.c b/libnl/lib/route/link/geneve.c new file mode 100644 index 0000000..7fff61d --- /dev/null +++ b/libnl/lib/route/link/geneve.c @@ -0,0 +1,809 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2018 Wang Jian + */ + +/** + * @ingroup link + * @defgroup geneve Geneve + * Generic Network Virtualization Encapsulation + * + * @details + * \b Link Type Name: "geneve" + * + * @route_doc{link_geneve, Geneve Documentation} + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + + +/** @cond SKIP */ +#define GENEVE_ATTR_ID (1<<0) +#define GENEVE_ATTR_REMOTE (1<<1) +#define GENEVE_ATTR_REMOTE6 (1<<2) +#define GENEVE_ATTR_TTL (1<<3) +#define GENEVE_ATTR_TOS (1<<4) +#define GENEVE_ATTR_LABEL (1<<5) +#define GENEVE_ATTR_PORT (1<<6) +#define GENEVE_ATTR_FLAGS (1<<7) +#define GENEVE_ATTR_UDP_CSUM (1<<8) +#define GENEVE_ATTR_UDP_ZERO_CSUM6_TX (1<<9) +#define GENEVE_ATTR_UDP_ZERO_CSUM6_RX (1<<10) + +struct geneve_info +{ + uint32_t id; + uint32_t remote; + struct in6_addr remote6; + uint8_t ttl; + uint8_t tos; + uint32_t label; + uint16_t port; + uint8_t flags; + uint8_t udp_csum; + uint8_t udp_zero_csum6_tx; + uint8_t udp_zero_csum6_rx; + uint32_t mask; +}; + +/** @endcond */ + +static struct nla_policy geneve_policy[IFLA_GENEVE_MAX + 1] = { + [IFLA_GENEVE_ID] = { .type = NLA_U32 }, + [IFLA_GENEVE_REMOTE] = { .minlen = sizeof(uint32_t) }, + [IFLA_GENEVE_REMOTE6] = { .minlen = sizeof(struct in6_addr) }, + [IFLA_GENEVE_TTL] = { .type = NLA_U8 }, + [IFLA_GENEVE_TOS] = { .type = NLA_U8 }, + [IFLA_GENEVE_LABEL] = { .type = NLA_U32 }, + [IFLA_GENEVE_PORT] = { .type = NLA_U16 }, + [IFLA_GENEVE_COLLECT_METADATA] = { .type = NLA_FLAG }, + [IFLA_GENEVE_UDP_CSUM] = { .type = NLA_U8 }, + [IFLA_GENEVE_UDP_ZERO_CSUM6_TX] = { .type = NLA_U8 }, + [IFLA_GENEVE_UDP_ZERO_CSUM6_RX] = { .type = NLA_U8 }, +}; + +static int geneve_alloc(struct rtnl_link *link) +{ + struct geneve_info *geneve; + + if (link->l_info) + memset(link->l_info, 0, sizeof(*geneve)); + else { + if ((geneve = calloc(1, sizeof(*geneve))) == NULL) + return -NLE_NOMEM; + link->l_info = geneve; + } + + return 0; +} + +static int geneve_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_GENEVE_MAX + 1]; + struct geneve_info *geneve; + int err = 0; + + NL_DBG(3, "Parsing Geneve link info\n"); + + err = nla_parse_nested(tb, IFLA_GENEVE_MAX, data, geneve_policy); + if (err < 0) + return err; + + err = geneve_alloc(link); + if (err < 0) + return err; + + geneve = link->l_info; + + if (tb[IFLA_GENEVE_ID]) { + geneve->id = nla_get_u32(tb[IFLA_GENEVE_ID]); + geneve->mask |= GENEVE_ATTR_ID; + } + + if (tb[IFLA_GENEVE_REMOTE]) { + nla_memcpy(&geneve->remote, tb[IFLA_GENEVE_REMOTE], + sizeof(geneve->remote)); + geneve->mask |= GENEVE_ATTR_REMOTE; + geneve->mask &= ~GENEVE_ATTR_REMOTE6; + } + if (tb[IFLA_GENEVE_REMOTE6]) { + nla_memcpy(&geneve->remote6, tb[IFLA_GENEVE_REMOTE6], + sizeof(geneve->remote6)); + geneve->mask |= GENEVE_ATTR_REMOTE6; + geneve->mask &= ~GENEVE_ATTR_REMOTE; + } + + if (tb[IFLA_GENEVE_TTL]) { + geneve->ttl = nla_get_u8(tb[IFLA_GENEVE_TTL]); + geneve->mask |= GENEVE_ATTR_TTL; + } + + if (tb[IFLA_GENEVE_TOS]) { + geneve->tos = nla_get_u8(tb[IFLA_GENEVE_TOS]); + geneve->mask |= GENEVE_ATTR_TOS; + } + + if (tb[IFLA_GENEVE_LABEL]) { + geneve->label = nla_get_u32(tb[IFLA_GENEVE_LABEL]); + geneve->mask |= GENEVE_ATTR_LABEL; + } + + if (tb[IFLA_GENEVE_PORT]) { + geneve->port = nla_get_u16(tb[IFLA_GENEVE_PORT]); + geneve->mask |= GENEVE_ATTR_PORT; + } + + if (tb[IFLA_GENEVE_COLLECT_METADATA]) + geneve->flags |= RTNL_LINK_GENEVE_F_COLLECT_METADATA; + + if (tb[IFLA_GENEVE_UDP_CSUM]) { + geneve->udp_csum = nla_get_u8(tb[IFLA_GENEVE_UDP_CSUM]); + geneve->mask |= GENEVE_ATTR_UDP_CSUM; + } + + if (tb[IFLA_GENEVE_UDP_ZERO_CSUM6_TX]) { + geneve->udp_zero_csum6_tx = nla_get_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_TX]); + geneve->mask |= GENEVE_ATTR_UDP_ZERO_CSUM6_TX; + } + + if (tb[IFLA_GENEVE_UDP_ZERO_CSUM6_RX]) { + geneve->udp_zero_csum6_rx = nla_get_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_RX]); + geneve->mask |= GENEVE_ATTR_UDP_ZERO_CSUM6_RX; + } + + return err; +} + +static void geneve_free(struct rtnl_link *link) +{ + struct geneve_info *geneve = link->l_info; + + free(geneve); + link->l_info = NULL; +} + +static void geneve_dump_line(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct geneve_info *geneve = link->l_info; + + nl_dump(p, "geneve-id %u", geneve->id); +} + +static void geneve_dump_details(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct geneve_info *geneve = link->l_info; + char addr[INET6_ADDRSTRLEN]; + + nl_dump_line(p, " geneve-id %u\n", geneve->id); + + if (geneve->mask & GENEVE_ATTR_REMOTE) { + nl_dump(p, " remote "); + if (inet_ntop(AF_INET, &geneve->remote, addr, sizeof(addr))) + nl_dump_line(p, "%s\n", addr); + else + nl_dump_line(p, "%#x\n", ntohs(geneve->remote)); + } else if (geneve->mask & GENEVE_ATTR_REMOTE6) { + nl_dump(p, " remote "); + if (inet_ntop(AF_INET6, &geneve->remote6, addr, sizeof(addr))) + nl_dump_line(p, "%s\n", addr); + else + nl_dump_line(p, "%#x\n", geneve->remote6); + } + + if (geneve->mask & GENEVE_ATTR_TTL) { + nl_dump(p, " ttl "); + nl_dump_line(p, "%u\n", geneve->ttl); + } + + if (geneve->mask & GENEVE_ATTR_TOS) { + nl_dump(p, " tos "); + nl_dump_line(p, "%u\n", geneve->tos); + } + + if (geneve->mask & GENEVE_ATTR_PORT) { + nl_dump(p, " port "); + nl_dump_line(p, "%u\n", ntohs(geneve->port)); + } + + if (geneve->mask & GENEVE_ATTR_LABEL) { + nl_dump(p, " label "); + nl_dump_line(p, "%u\n", ntohl(geneve->label)); + } + + if (geneve->mask & GENEVE_ATTR_UDP_CSUM) { + nl_dump(p, " UDP checksum "); + if (geneve->udp_csum) + nl_dump_line(p, "enabled (%#x)\n", geneve->udp_csum); + else + nl_dump_line(p, "disabled\n"); + } + + if (geneve->mask & GENEVE_ATTR_UDP_ZERO_CSUM6_TX) { + nl_dump(p, " udp-zero-csum6-tx "); + if (geneve->udp_zero_csum6_tx) + nl_dump_line(p, "enabled (%#x)\n", geneve->udp_zero_csum6_tx); + else + nl_dump_line(p, "disabled\n"); + } + + if (geneve->mask & GENEVE_ATTR_UDP_ZERO_CSUM6_RX) { + nl_dump(p, " udp-zero-csum6-rx "); + if (geneve->udp_zero_csum6_rx) + nl_dump_line(p, "enabled (%#x)\n", geneve->udp_zero_csum6_rx); + else + nl_dump_line(p, "disabled\n"); + } + + if (geneve->flags & RTNL_LINK_GENEVE_F_COLLECT_METADATA) + nl_dump(p, " collect-metadata\n"); +} + +static int geneve_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct geneve_info *gdst, *gsrc; + int err; + + gsrc = src->l_info; + dst->l_info = NULL; + err = rtnl_link_set_type(dst, "geneve"); + if (err < 0) + return err; + + gdst = dst->l_info; + + if (!gsrc || !gdst) + return -NLE_NOMEM; + + memcpy(gdst, gsrc, sizeof(struct geneve_info)); + + return 0; +} + +static int geneve_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct geneve_info *geneve = link->l_info; + struct nlattr *data; + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + return -NLE_MSGSIZE; + + if (geneve->mask & GENEVE_ATTR_ID) + NLA_PUT_U32(msg, IFLA_GENEVE_ID, geneve->id); + + if (geneve->mask & GENEVE_ATTR_REMOTE) + NLA_PUT(msg, IFLA_GENEVE_REMOTE, + sizeof(geneve->remote), &geneve->remote); + + if (geneve->mask & GENEVE_ATTR_REMOTE6) + NLA_PUT(msg, IFLA_GENEVE_REMOTE6, + sizeof(geneve->remote6), &geneve->remote6); + + if (geneve->mask & GENEVE_ATTR_TTL) + NLA_PUT_U8(msg, IFLA_GENEVE_TTL, geneve->ttl); + + if (geneve->mask & GENEVE_ATTR_TOS) + NLA_PUT_U8(msg, IFLA_GENEVE_TOS, geneve->tos); + + if (geneve->mask & GENEVE_ATTR_LABEL) + NLA_PUT_U32(msg, IFLA_GENEVE_LABEL, geneve->label); + + if (geneve->mask & GENEVE_ATTR_PORT) + NLA_PUT_U32(msg, IFLA_GENEVE_PORT, geneve->port); + + if (geneve->mask & GENEVE_ATTR_UDP_CSUM) + NLA_PUT_U8(msg, IFLA_GENEVE_UDP_CSUM, geneve->udp_csum); + + if (geneve->mask & GENEVE_ATTR_UDP_ZERO_CSUM6_TX) + NLA_PUT_U8(msg, IFLA_GENEVE_UDP_ZERO_CSUM6_TX, geneve->udp_zero_csum6_tx); + + if (geneve->mask & GENEVE_ATTR_UDP_ZERO_CSUM6_RX) + NLA_PUT_U8(msg, IFLA_GENEVE_UDP_ZERO_CSUM6_RX, geneve->udp_zero_csum6_rx); + + if (geneve->flags & RTNL_LINK_GENEVE_F_COLLECT_METADATA) + NLA_PUT_FLAG(msg, IFLA_GENEVE_COLLECT_METADATA); + + nla_nest_end(msg, data); + +nla_put_failure: + + return 0; +} + +static struct rtnl_link_info_ops geneve_info_ops = { + .io_name = "geneve", + .io_alloc = geneve_alloc, + .io_parse = geneve_parse, + .io_dump = { + [NL_DUMP_LINE] = geneve_dump_line, + [NL_DUMP_DETAILS] = geneve_dump_details, + }, + .io_clone = geneve_clone, + .io_put_attrs = geneve_put_attrs, + .io_free = geneve_free, +}; + + +/** @cond SKIP */ +#define IS_GENEVE_LINK_ASSERT(link) \ + if ((link)->l_info_ops != &geneve_info_ops) { \ + APPBUG("Link is not a geneve link. set type \"geneve\" first."); \ + return -NLE_OPNOTSUPP; \ + } +/** @endcond */ + +/** + * @name Geneve Object + * @{ + */ + +/** + * Allocate link object of type Geneve + * + * @return Allocated link object or NULL. + */ +struct rtnl_link *rtnl_link_geneve_alloc(void) +{ + struct rtnl_link *link; + int err; + + if (!(link = rtnl_link_alloc())) + return NULL; + + if ((err = rtnl_link_set_type(link, "geneve")) < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Check if link is a Geneve link + * @arg link Link object + * + * @return True if link is a Geneve link, otherwisee false is returned. + */ +int rtnl_link_is_geneve(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "geneve"); +} + +/** + * Set Geneve Network Indentifier + * @arg link Link object + * @arg id Geneve network identifier + * + * @return 0 on success or a negative error code + */ +int rtnl_link_geneve_set_id(struct rtnl_link *link, uint32_t id) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + if (id > RTNL_GENEVE_ID_MAX) + return -NLE_INVAL; + + geneve->id = id; + geneve->mask |= GENEVE_ATTR_ID; + + return 0; +} + +/** + * Get Geneve Network Identifier + * @arg link Link object + * @arg id Pointer to store network identifier + * + * @return 0 on success or a negative error code + */ +int rtnl_link_geneve_get_id(struct rtnl_link *link, uint32_t *id) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + if (!id) + return -NLE_INVAL; + + if (geneve->mask & GENEVE_ATTR_ID) + *id = geneve->id; + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Set Geneve unicast destination IP address + * @arg link Link object + * @arg addr The unicast destination IP address + * + * @return 0 on success or a negative error code + */ +int rtnl_link_geneve_set_remote(struct rtnl_link *link, struct nl_addr *addr) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + if ((nl_addr_get_family(addr) == AF_INET) && + (nl_addr_get_len(addr) == sizeof(geneve->remote))) { + memcpy(&geneve->remote, nl_addr_get_binary_addr(addr), + sizeof(geneve->remote)); + geneve->mask |= GENEVE_ATTR_REMOTE; + geneve->mask &= ~GENEVE_ATTR_REMOTE6; + } else if ((nl_addr_get_family(addr) == AF_INET6) && + (nl_addr_get_len(addr) == sizeof(geneve->remote6))) { + memcpy(&geneve->remote6, nl_addr_get_binary_addr(addr), + sizeof(geneve->remote6)); + geneve->mask |= GENEVE_ATTR_REMOTE6; + geneve->mask &= ~GENEVE_ATTR_REMOTE; + } else + return -NLE_INVAL; + + return 0; +} + +/** + * Get Geneve unicast destination IP address + * @arg link Link object + * @arg addr Pointer to store unicast destination IP addree + * + * @return 0 on success or a a negative error code + */ +int rtnl_link_geneve_get_remote(struct rtnl_link *link, struct nl_addr **addr) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + if (!addr) + return -NLE_INVAL; + + if (geneve->mask & GENEVE_ATTR_REMOTE) + *addr = nl_addr_build(AF_INET, &geneve->remote, sizeof(geneve->remote)); + else if (geneve->mask & GENEVE_ATTR_REMOTE6) + *addr = nl_addr_build(AF_INET6, &geneve->remote6, sizeof(geneve->remote6)); + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Set IP TTL value to use for Geneve + * @arg link Link object + * @arg ttl TTL value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_geneve_set_ttl(struct rtnl_link *link, uint8_t ttl) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + geneve->ttl = ttl; + geneve->mask |= GENEVE_ATTR_TTL; + + return 0; +} + +/** + * Get IP TTL value to use for Geneve + * @arg link Link object + * + * @return TTL value on success or a negative error code + */ +int rtnl_link_geneve_get_ttl(struct rtnl_link *link) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + if (!(geneve->mask & GENEVE_ATTR_TTL)) + return -NLE_AGAIN; + + return geneve->ttl; +} + +/** + * Set IP ToS value to use for Geneve + * @arg link Link object + * @arg tos ToS value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_geneve_set_tos(struct rtnl_link *link, uint8_t tos) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + geneve->tos = tos; + geneve->mask |= GENEVE_ATTR_TOS; + + return 0; +} + +/** + * Get IP ToS value to use for Geneve + * @arg link Link object + * + * @return ToS value on success or a negative error code + */ +int rtnl_link_geneve_get_tos(struct rtnl_link *link) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + if (!(geneve->mask & GENEVE_ATTR_TOS)) + return -NLE_AGAIN; + + return geneve->tos; +} + +/** + * Set UDP destination port to use for Geneve + * @arg link Link object + * @arg port Destination port + * + * @return 0 on success or a negative error code + */ + +int rtnl_link_geneve_set_port(struct rtnl_link *link, uint32_t port) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + geneve->port = htons(port); + geneve->mask |= GENEVE_ATTR_PORT; + + return 0; +} + +/** + * Get UDP destination port to use for Geneve + * @arg link Link object + * @arg port Pointer to store destination port + * + * @return 0 on success or a negative error code + */ +int rtnl_link_geneve_get_port(struct rtnl_link *link, uint32_t *port) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + if (!port) + return -NLE_INVAL; + + if (!(geneve->mask & GENEVE_ATTR_PORT)) + return -NLE_NOATTR; + + *port = ntohs(geneve->port); + + return 0; +} + +/** + * Set flow label to use for Geneve + * @arg link Link object + * @arg label Destination label + * + * @return 0 on success or a negative error code + */ +int rtnl_link_geneve_set_label(struct rtnl_link *link, uint32_t label) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + geneve->label = htonl(label); + geneve->mask |= GENEVE_ATTR_LABEL; + + return 0; +} + +/** + * Get flow label to use for Geneve + * @arg link Link object + * @arg label Pointer to store destination label + * + * @return 0 on success or a negative error code + */ +int rtnl_link_geneve_get_label(struct rtnl_link *link, uint32_t *label) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + if (!label) + return -NLE_INVAL; + if (!(geneve->mask & GENEVE_ATTR_LABEL)) + return -NLE_NOATTR; + + *label = ntohl(geneve->label); + + return 0; +} + +/** + * Set UDP checksum status to use for Geneve + * @arg link Link object + * @arg csum Status value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_geneve_set_udp_csum(struct rtnl_link *link, uint8_t csum) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + geneve->udp_csum = csum; + geneve->mask |= GENEVE_ATTR_UDP_CSUM; + + return 0; +} + +/** + * Get UDP checksum status to use for Geneve + * @arg link Link object + * + * @return status value on success or a negative error code + */ +int rtnl_link_geneve_get_udp_csum(struct rtnl_link *link) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + if (!(geneve->mask & GENEVE_ATTR_UDP_CSUM)) + return -NLE_NOATTR; + + return geneve->udp_csum; +} + +/** + * Set skip UDP checksum transmitted over IPv6 status to use for Geneve + * @arg link Link object + * @arg csum Status value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_geneve_set_udp_zero_csum6_tx(struct rtnl_link *link, uint8_t csum) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + geneve->udp_zero_csum6_tx = csum; + geneve->mask |= GENEVE_ATTR_UDP_ZERO_CSUM6_TX; + + return 0; +} + +/** + * Get skip UDP checksum transmitted over IPv6 status to use for Geneve + * @arg link Link object + * + * @return Status value on success or a negative error code + */ +int rtnl_link_geneve_get_udp_zero_csum6_tx(struct rtnl_link *link) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + if (!(geneve->mask & GENEVE_ATTR_UDP_ZERO_CSUM6_TX)) + return -NLE_NOATTR; + + return geneve->udp_zero_csum6_tx; +} + +/** + * Set skip UDP checksum received over IPv6 status to use for Geneve + * @arg link Link object + * @arg csum Status value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_geneve_set_udp_zero_csum6_rx(struct rtnl_link *link, uint8_t csum) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + geneve->udp_zero_csum6_rx = csum; + geneve->mask |= GENEVE_ATTR_UDP_ZERO_CSUM6_RX; + + return 0; +} + +/** + * Get skip UDP checksum received over IPv6 status to use for Geneve + * @arg link Link object + * + * @return Status value on success or a negative error code + */ +int rtnl_link_geneve_get_udp_zero_csum6_rx(struct rtnl_link *link) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + if (!(geneve->mask & GENEVE_ATTR_UDP_ZERO_CSUM6_RX)) + return -NLE_NOATTR; + + return geneve->udp_zero_csum6_rx; +} + +/** + * Set Geneve flags + * @arg link Link object + * @arg flags Which flags to set + * @arg enable Boolean enabling or disabling flag + * + * @return 0 on success or a negative error code + */ +int rtnl_link_geneve_set_flags(struct rtnl_link *link, uint8_t flags, int enable) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + if (flags & ~RTNL_LINK_GENEVE_F_COLLECT_METADATA) + return -NLE_INVAL; + + if (enable) + geneve->flags = flags; + else + geneve->flags &= ~flags; + + return 0; +} + +/** + * Get Geneve flags + * @arg link Link object + * @arg flags Pointer to store flags + * + * @return 0 on success or a negative error code + */ +int rtnl_link_geneve_get_flags(struct rtnl_link *link, uint8_t *flags) +{ + struct geneve_info *geneve = link->l_info; + + IS_GENEVE_LINK_ASSERT(link); + + *flags = geneve->flags; + return 0; +} + +/** @} */ +static void __init geneve_init(void) +{ + rtnl_link_register_info(&geneve_info_ops); +} + +static void __exit geneve_exit(void) +{ + rtnl_link_unregister_info(&geneve_info_ops); +} + +/** @} */ diff --git a/libnl/lib/route/link/ifb.c b/libnl/lib/route/link/ifb.c new file mode 100644 index 0000000..99eccf0 --- /dev/null +++ b/libnl/lib/route/link/ifb.c @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2014 Cong Wang + */ + +/** + * @ingroup link + * @defgroup ifb Intermediate Functional Block + * + * @details + * \b Link Type Name: "ifb" + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include + +static struct rtnl_link_info_ops ifb_info_ops = { + .io_name = "ifb", +}; + +static void __init ifb_init(void) +{ + rtnl_link_register_info(&ifb_info_ops); +} + +static void __exit ifb_exit(void) +{ + rtnl_link_unregister_info(&ifb_info_ops); +} + +/** @} */ diff --git a/libnl/lib/route/link/inet.c b/libnl/lib/route/link/inet.c new file mode 100644 index 0000000..660902b --- /dev/null +++ b/libnl/lib/route/link/inet.c @@ -0,0 +1,291 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010 Thomas Graf + */ + +/** + * @ingroup link_API + * @defgroup link_inet IPv4 Link Module + * @brief Implementation of IPv4 specific link attributes + * + * + * + * @par Example: Reading the value of IPV4_DEVCONF_FORWARDING + * @code + * struct nl_cache *cache; + * struct rtnl_link *link; + * uint32_t value; + * + * // Allocate a link cache + * rtnl_link_alloc_cache(sock, AF_UNSPEC, &cache); + * + * // Search for the link we wish to see the value from + * link = rtnl_link_get_by_name(cache, "eth0"); + * + * // Read the value of the setting IPV4_DEVCONF_FORWARDING + * if (rtnl_link_inet_get_conf(link, IPV4_DEVCONF_FORWARDING, &value) < 0) + * // Error: Unable to read config setting + * + * printf("forwarding is %s\n", value ? "enabled" : "disabled"); + * @endcode + * + * @par Example: Changing the value of IPV4_DEVCONF_FOWARDING + * @code + * // + * // ... Continueing from the previous example ... + * // + * + * struct rtnl_link *new; + * + * // Allocate a new link to store the changes we wish to make. + * new = rtnl_link_alloc(); + * + * // Set IPV4_DEVCONF_FORWARDING to '1' + * rtnl_link_inet_set_conf(new, IPV4_DEVCONF_FORWARDING, 1); + * + * // Send the change request to the kernel. + * rtnl_link_change(sock, link, new, 0); + * @endcode + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +struct inet_data +{ + uint8_t i_confset[IPV4_DEVCONF_MAX]; + uint32_t i_conf[IPV4_DEVCONF_MAX]; +}; +/** @endcond */ + +static void *inet_alloc(struct rtnl_link *link) +{ + return calloc(1, sizeof(struct inet_data)); +} + +static void *inet_clone(struct rtnl_link *link, void *data) +{ + struct inet_data *id; + + if ((id = inet_alloc(link))) + memcpy(id, data, sizeof(*id)); + + return id; +} + +static void inet_free(struct rtnl_link *link, void *data) +{ + free(data); +} + +static struct nla_policy inet_policy[IFLA_INET_MAX+1] = { + [IFLA_INET_CONF] = { .minlen = 4 }, +}; + +static int inet_parse_af(struct rtnl_link *link, struct nlattr *attr, void *data) +{ + struct inet_data *id = data; + struct nlattr *tb[IFLA_INET_MAX+1]; + int err; + + err = nla_parse_nested(tb, IFLA_INET_MAX, attr, inet_policy); + if (err < 0) + return err; + if (tb[IFLA_INET_CONF] && nla_len(tb[IFLA_INET_CONF]) % 4) + return -EINVAL; + + if (tb[IFLA_INET_CONF]) { + int i; + int len = min_t(int, IPV4_DEVCONF_MAX, nla_len(tb[IFLA_INET_CONF]) / 4); + + for (i = 0; i < len; i++) + id->i_confset[i] = 1; + nla_memcpy(&id->i_conf, tb[IFLA_INET_CONF], sizeof(id->i_conf)); + } + + return 0; +} + +static int inet_fill_af(struct rtnl_link *link, struct nl_msg *msg, void *data) +{ + struct inet_data *id = data; + struct nlattr *nla; + int i; + + if (!(nla = nla_nest_start(msg, IFLA_INET_CONF))) + return -NLE_MSGSIZE; + + for (i = 0; i < IPV4_DEVCONF_MAX; i++) + if (id->i_confset[i]) + NLA_PUT_U32(msg, i+1, id->i_conf[i]); + + nla_nest_end(msg, nla); + + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +static const struct trans_tbl inet_devconf[] = { + __ADD(IPV4_DEVCONF_FORWARDING, forwarding), + __ADD(IPV4_DEVCONF_MC_FORWARDING, mc_forwarding), + __ADD(IPV4_DEVCONF_PROXY_ARP, proxy_arp), + __ADD(IPV4_DEVCONF_ACCEPT_REDIRECTS, accept_redirects), + __ADD(IPV4_DEVCONF_SECURE_REDIRECTS, secure_redirects), + __ADD(IPV4_DEVCONF_SEND_REDIRECTS, send_redirects), + __ADD(IPV4_DEVCONF_SHARED_MEDIA, shared_media), + __ADD(IPV4_DEVCONF_RP_FILTER, rp_filter), + __ADD(IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE, accept_source_route), + __ADD(IPV4_DEVCONF_BOOTP_RELAY, bootp_relay), + __ADD(IPV4_DEVCONF_LOG_MARTIANS, log_martians), + __ADD(IPV4_DEVCONF_TAG, tag), + __ADD(IPV4_DEVCONF_ARPFILTER, arpfilter), + __ADD(IPV4_DEVCONF_MEDIUM_ID, medium_id), + __ADD(IPV4_DEVCONF_NOXFRM, noxfrm), + __ADD(IPV4_DEVCONF_NOPOLICY, nopolicy), + __ADD(IPV4_DEVCONF_FORCE_IGMP_VERSION, force_igmp_version), + __ADD(IPV4_DEVCONF_ARP_ANNOUNCE, arp_announce), + __ADD(IPV4_DEVCONF_ARP_IGNORE, arp_ignore), + __ADD(IPV4_DEVCONF_PROMOTE_SECONDARIES, promote_secondaries), + __ADD(IPV4_DEVCONF_ARP_ACCEPT, arp_accept), + __ADD(IPV4_DEVCONF_ARP_NOTIFY, arp_notify), + __ADD(IPV4_DEVCONF_ACCEPT_LOCAL, accept_local), + __ADD(IPV4_DEVCONF_SRC_VMARK, src_vmark), + __ADD(IPV4_DEVCONF_PROXY_ARP_PVLAN, proxy_arp_pvlan), + __ADD(IPV4_DEVCONF_ROUTE_LOCALNET, route_localnet), + __ADD(IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL, igmpv2_unsolicited_report_interval), + __ADD(IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL, igmpv3_unsolicited_report_interval), +}; + +const char *rtnl_link_inet_devconf2str(int type, char *buf, size_t len) +{ + return __type2str(type, buf, len, inet_devconf, + ARRAY_SIZE(inet_devconf)); +} + +int rtnl_link_inet_str2devconf(const char *name) +{ + return __str2type(name, inet_devconf, ARRAY_SIZE(inet_devconf)); +} + +static void inet_dump_details(struct rtnl_link *link, + struct nl_dump_params *p, void *data) +{ + struct inet_data *id = data; + char buf[64]; + int i, n = 0; + + nl_dump_line(p, " ipv4 devconf:\n"); + nl_dump_line(p, " "); + + for (i = 0; i < IPV4_DEVCONF_MAX; i++) { + nl_dump_line(p, "%-19s %3u", + rtnl_link_inet_devconf2str(i+1, buf, sizeof(buf)), + id->i_confset[i] ? id->i_conf[i] : 0); + + if (++n == 3) { + nl_dump(p, "\n"); + nl_dump_line(p, " "); + n = 0; + } else + nl_dump(p, " "); + } + + if (n != 0) + nl_dump(p, "\n"); +} + +static struct rtnl_link_af_ops inet_ops = { + .ao_family = AF_INET, + .ao_alloc = &inet_alloc, + .ao_clone = &inet_clone, + .ao_free = &inet_free, + .ao_parse_af = &inet_parse_af, + .ao_fill_af = &inet_fill_af, + .ao_dump[NL_DUMP_DETAILS] = &inet_dump_details, +}; + +/** + * Get value of a ipv4 link configuration setting + * @arg link Link object + * @arg cfgid Configuration identifier + * @arg res Result pointer + * + * Stores the value of the specified configuration setting in the provided + * result pointer. + * + * @return 0 on success or a negative error code. + * @return -NLE_RANGE cfgid is out of range, 1..IPV4_DEVCONF_MAX + * @return -NLE_NOATTR configuration setting not available + * @return -NLE_INVAL cfgid not set. If the link was received via netlink, + * it means that the cfgid is not supported. + */ +int rtnl_link_inet_get_conf(struct rtnl_link *link, const unsigned int cfgid, + uint32_t *res) +{ + struct inet_data *id; + + if (cfgid == 0 || cfgid > IPV4_DEVCONF_MAX) + return -NLE_RANGE; + + if (!(id = rtnl_link_af_data(link, &inet_ops))) + return -NLE_NOATTR; + + if (!id->i_confset[cfgid - 1]) + return -NLE_INVAL; + *res = id->i_conf[cfgid - 1]; + + return 0; +} + +/** + * Change value of a ipv4 link configuration setting + * @arg link Link object + * @arg cfgid Configuration identifier + * @arg value New value + * + * Changes the value in the per link ipv4 configuration array. + * + * @return 0 on success or a negative error code. + * @return -NLE_RANGE cfgid is out of range, 1..IPV4_DEVCONF_MAX + * @return -NLE_NOMEM memory allocation failed + */ +int rtnl_link_inet_set_conf(struct rtnl_link *link, const unsigned int cfgid, + uint32_t value) +{ + struct inet_data *id; + + if (!(id = rtnl_link_af_alloc(link, &inet_ops))) + return -NLE_NOMEM; + + if (cfgid == 0 || cfgid > IPV4_DEVCONF_MAX) + return -NLE_RANGE; + + id->i_confset[cfgid - 1] = 1; + id->i_conf[cfgid - 1] = value; + + return 0; +} + + +static void __init inet_init(void) +{ + rtnl_link_af_register(&inet_ops); +} + +static void __exit inet_exit(void) +{ + rtnl_link_af_unregister(&inet_ops); +} + +/** @} */ diff --git a/libnl/lib/route/link/inet6.c b/libnl/lib/route/link/inet6.c new file mode 100644 index 0000000..a06ad82 --- /dev/null +++ b/libnl/lib/route/link/inet6.c @@ -0,0 +1,702 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2010 Thomas Graf + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + +#include "netlink-private/route/utils.h" +#include "netlink-private/utils.h" + +#define I6_ADDR_GEN_MODE_UNKNOWN UINT8_MAX + +struct inet6_data +{ + uint32_t i6_flags; + struct ifla_cacheinfo i6_cacheinfo; + uint32_t i6_conf[DEVCONF_MAX]; + struct in6_addr i6_token; + uint8_t i6_addr_gen_mode; +}; + +static void *inet6_alloc(struct rtnl_link *link) +{ + struct inet6_data *i6; + + i6 = calloc(1, sizeof(struct inet6_data)); + if (i6) + i6->i6_addr_gen_mode = I6_ADDR_GEN_MODE_UNKNOWN; + + return i6; +} + +static void *inet6_clone(struct rtnl_link *link, void *data) +{ + struct inet6_data *i6; + + if ((i6 = inet6_alloc(link))) + memcpy(i6, data, sizeof(*i6)); + + return i6; +} + +static void inet6_free(struct rtnl_link *link, void *data) +{ + free(data); +} + +static struct nla_policy inet6_policy[IFLA_INET6_MAX+1] = { + [IFLA_INET6_FLAGS] = { .type = NLA_U32 }, + [IFLA_INET6_CACHEINFO] = { .minlen = sizeof(struct ifla_cacheinfo) }, + [IFLA_INET6_CONF] = { .minlen = 4 }, + [IFLA_INET6_STATS] = { .minlen = 8 }, + [IFLA_INET6_ICMP6STATS] = { .minlen = 8 }, + [IFLA_INET6_TOKEN] = { .minlen = sizeof(struct in6_addr) }, + [IFLA_INET6_ADDR_GEN_MODE] = { .type = NLA_U8 }, +}; + +static const uint8_t map_stat_id_from_IPSTATS_MIB_v1[__IPSTATS_MIB_MAX] = { + /* 14a196807482e6fc74f15fc03176d5c08880588f^:include/linux/snmp.h + * version before the API change in commit 14a196807482e6fc74f15fc03176d5c08880588f. + * This version was valid since commit edf391ff17232f097d72441c9ad467bcb3b5db18, which + * predates support for parsing IFLA_PROTINFO in libnl3. Such an even older meaning of + * the flags is not supported in libnl3. */ + [ 1] = RTNL_LINK_IP6_INPKTS, /* IPSTATS_MIB_INPKTS */ + [ 2] = RTNL_LINK_IP6_INHDRERRORS, /* IPSTATS_MIB_INHDRERRORS */ + [ 3] = RTNL_LINK_IP6_INTOOBIGERRORS, /* IPSTATS_MIB_INTOOBIGERRORS */ + [ 4] = RTNL_LINK_IP6_INNOROUTES, /* IPSTATS_MIB_INNOROUTES */ + [ 5] = RTNL_LINK_IP6_INADDRERRORS, /* IPSTATS_MIB_INADDRERRORS */ + [ 6] = RTNL_LINK_IP6_INUNKNOWNPROTOS, /* IPSTATS_MIB_INUNKNOWNPROTOS */ + [ 7] = RTNL_LINK_IP6_INTRUNCATEDPKTS, /* IPSTATS_MIB_INTRUNCATEDPKTS */ + [ 8] = RTNL_LINK_IP6_INDISCARDS, /* IPSTATS_MIB_INDISCARDS */ + [ 9] = RTNL_LINK_IP6_INDELIVERS, /* IPSTATS_MIB_INDELIVERS */ + [10] = RTNL_LINK_IP6_OUTFORWDATAGRAMS, /* IPSTATS_MIB_OUTFORWDATAGRAMS */ + [11] = RTNL_LINK_IP6_OUTPKTS, /* IPSTATS_MIB_OUTPKTS */ + [12] = RTNL_LINK_IP6_OUTDISCARDS, /* IPSTATS_MIB_OUTDISCARDS */ + [13] = RTNL_LINK_IP6_OUTNOROUTES, /* IPSTATS_MIB_OUTNOROUTES */ + [14] = RTNL_LINK_IP6_REASMTIMEOUT, /* IPSTATS_MIB_REASMTIMEOUT */ + [15] = RTNL_LINK_IP6_REASMREQDS, /* IPSTATS_MIB_REASMREQDS */ + [16] = RTNL_LINK_IP6_REASMOKS, /* IPSTATS_MIB_REASMOKS */ + [17] = RTNL_LINK_IP6_REASMFAILS, /* IPSTATS_MIB_REASMFAILS */ + [18] = RTNL_LINK_IP6_FRAGOKS, /* IPSTATS_MIB_FRAGOKS */ + [19] = RTNL_LINK_IP6_FRAGFAILS, /* IPSTATS_MIB_FRAGFAILS */ + [20] = RTNL_LINK_IP6_FRAGCREATES, /* IPSTATS_MIB_FRAGCREATES */ + [21] = RTNL_LINK_IP6_INMCASTPKTS, /* IPSTATS_MIB_INMCASTPKTS */ + [22] = RTNL_LINK_IP6_OUTMCASTPKTS, /* IPSTATS_MIB_OUTMCASTPKTS */ + [23] = RTNL_LINK_IP6_INBCASTPKTS, /* IPSTATS_MIB_INBCASTPKTS */ + [24] = RTNL_LINK_IP6_OUTBCASTPKTS, /* IPSTATS_MIB_OUTBCASTPKTS */ + [25] = RTNL_LINK_IP6_INOCTETS, /* IPSTATS_MIB_INOCTETS */ + [26] = RTNL_LINK_IP6_OUTOCTETS, /* IPSTATS_MIB_OUTOCTETS */ + [27] = RTNL_LINK_IP6_INMCASTOCTETS, /* IPSTATS_MIB_INMCASTOCTETS */ + [28] = RTNL_LINK_IP6_OUTMCASTOCTETS, /* IPSTATS_MIB_OUTMCASTOCTETS */ + [29] = RTNL_LINK_IP6_INBCASTOCTETS, /* IPSTATS_MIB_INBCASTOCTETS */ + [30] = RTNL_LINK_IP6_OUTBCASTOCTETS, /* IPSTATS_MIB_OUTBCASTOCTETS */ +}; + +static const uint8_t map_stat_id_from_IPSTATS_MIB_v2[__IPSTATS_MIB_MAX] = { + /* d8ec26d7f8287f5788a494f56e8814210f0e64be:include/uapi/linux/snmp.h + * version since the API change in commit 14a196807482e6fc74f15fc03176d5c08880588f */ + [ 1] = RTNL_LINK_IP6_INPKTS, /* IPSTATS_MIB_INPKTS */ + [ 2] = RTNL_LINK_IP6_INOCTETS, /* IPSTATS_MIB_INOCTETS */ + [ 3] = RTNL_LINK_IP6_INDELIVERS, /* IPSTATS_MIB_INDELIVERS */ + [ 4] = RTNL_LINK_IP6_OUTFORWDATAGRAMS, /* IPSTATS_MIB_OUTFORWDATAGRAMS */ + [ 5] = RTNL_LINK_IP6_OUTPKTS, /* IPSTATS_MIB_OUTPKTS */ + [ 6] = RTNL_LINK_IP6_OUTOCTETS, /* IPSTATS_MIB_OUTOCTETS */ + [ 7] = RTNL_LINK_IP6_INHDRERRORS, /* IPSTATS_MIB_INHDRERRORS */ + [ 8] = RTNL_LINK_IP6_INTOOBIGERRORS, /* IPSTATS_MIB_INTOOBIGERRORS */ + [ 9] = RTNL_LINK_IP6_INNOROUTES, /* IPSTATS_MIB_INNOROUTES */ + [10] = RTNL_LINK_IP6_INADDRERRORS, /* IPSTATS_MIB_INADDRERRORS */ + [11] = RTNL_LINK_IP6_INUNKNOWNPROTOS, /* IPSTATS_MIB_INUNKNOWNPROTOS */ + [12] = RTNL_LINK_IP6_INTRUNCATEDPKTS, /* IPSTATS_MIB_INTRUNCATEDPKTS */ + [13] = RTNL_LINK_IP6_INDISCARDS, /* IPSTATS_MIB_INDISCARDS */ + [14] = RTNL_LINK_IP6_OUTDISCARDS, /* IPSTATS_MIB_OUTDISCARDS */ + [15] = RTNL_LINK_IP6_OUTNOROUTES, /* IPSTATS_MIB_OUTNOROUTES */ + [16] = RTNL_LINK_IP6_REASMTIMEOUT, /* IPSTATS_MIB_REASMTIMEOUT */ + [17] = RTNL_LINK_IP6_REASMREQDS, /* IPSTATS_MIB_REASMREQDS */ + [18] = RTNL_LINK_IP6_REASMOKS, /* IPSTATS_MIB_REASMOKS */ + [19] = RTNL_LINK_IP6_REASMFAILS, /* IPSTATS_MIB_REASMFAILS */ + [20] = RTNL_LINK_IP6_FRAGOKS, /* IPSTATS_MIB_FRAGOKS */ + [21] = RTNL_LINK_IP6_FRAGFAILS, /* IPSTATS_MIB_FRAGFAILS */ + [22] = RTNL_LINK_IP6_FRAGCREATES, /* IPSTATS_MIB_FRAGCREATES */ + [23] = RTNL_LINK_IP6_INMCASTPKTS, /* IPSTATS_MIB_INMCASTPKTS */ + [24] = RTNL_LINK_IP6_OUTMCASTPKTS, /* IPSTATS_MIB_OUTMCASTPKTS */ + [25] = RTNL_LINK_IP6_INBCASTPKTS, /* IPSTATS_MIB_INBCASTPKTS */ + [26] = RTNL_LINK_IP6_OUTBCASTPKTS, /* IPSTATS_MIB_OUTBCASTPKTS */ + [27] = RTNL_LINK_IP6_INMCASTOCTETS, /* IPSTATS_MIB_INMCASTOCTETS */ + [28] = RTNL_LINK_IP6_OUTMCASTOCTETS, /* IPSTATS_MIB_OUTMCASTOCTETS */ + [29] = RTNL_LINK_IP6_INBCASTOCTETS, /* IPSTATS_MIB_INBCASTOCTETS */ + [30] = RTNL_LINK_IP6_OUTBCASTOCTETS, /* IPSTATS_MIB_OUTBCASTOCTETS */ + [31] = RTNL_LINK_IP6_CSUMERRORS, /* IPSTATS_MIB_CSUMERRORS */ + [32] = RTNL_LINK_IP6_NOECTPKTS, /* IPSTATS_MIB_NOECTPKTS */ + [33] = RTNL_LINK_IP6_ECT1PKTS, /* IPSTATS_MIB_ECT1PKTS */ + [34] = RTNL_LINK_IP6_ECT0PKTS, /* IPSTATS_MIB_ECT0PKTS */ + [35] = RTNL_LINK_IP6_CEPKTS, /* IPSTATS_MIB_CEPKTS */ + [36] = RTNL_LINK_REASM_OVERLAPS, /* IPSTATS_MIB_REASM_OVERLAPS */ +}; + +const uint8_t *const _nltst_map_stat_id_from_IPSTATS_MIB_v2 = map_stat_id_from_IPSTATS_MIB_v2; + +static int inet6_parse_protinfo(struct rtnl_link *link, struct nlattr *attr, + void *data) +{ + struct inet6_data *i6 = data; + struct nlattr *tb[IFLA_INET6_MAX+1]; + int err; + + err = nla_parse_nested(tb, IFLA_INET6_MAX, attr, inet6_policy); + if (err < 0) + return err; + if (tb[IFLA_INET6_CONF] && nla_len(tb[IFLA_INET6_CONF]) % 4) + return -EINVAL; + if (tb[IFLA_INET6_STATS] && nla_len(tb[IFLA_INET6_STATS]) % 8) + return -EINVAL; + if (tb[IFLA_INET6_ICMP6STATS] && nla_len(tb[IFLA_INET6_ICMP6STATS]) % 8) + return -EINVAL; + + if (tb[IFLA_INET6_FLAGS]) + i6->i6_flags = nla_get_u32(tb[IFLA_INET6_FLAGS]); + + if (tb[IFLA_INET6_CACHEINFO]) + nla_memcpy(&i6->i6_cacheinfo, tb[IFLA_INET6_CACHEINFO], + sizeof(i6->i6_cacheinfo)); + + if (tb[IFLA_INET6_CONF]) + nla_memcpy(&i6->i6_conf, tb[IFLA_INET6_CONF], + sizeof(i6->i6_conf)); + + if (tb[IFLA_INET6_TOKEN]) + nla_memcpy(&i6->i6_token, tb[IFLA_INET6_TOKEN], + sizeof(struct in6_addr)); + + if (tb[IFLA_INET6_ADDR_GEN_MODE]) + i6->i6_addr_gen_mode = nla_get_u8 (tb[IFLA_INET6_ADDR_GEN_MODE]); + + /* + * Due to 32bit data alignment, these addresses must be copied to an + * aligned location prior to access. + */ + if (tb[IFLA_INET6_STATS]) { + unsigned char *cnt = nla_data(tb[IFLA_INET6_STATS]); + uint64_t stat; + int i; + int len = nla_len(tb[IFLA_INET6_STATS]) / 8; + const uint8_t *map_stat_id = map_stat_id_from_IPSTATS_MIB_v2; + + if (len < 32 || + (tb[IFLA_INET6_ICMP6STATS] && nla_len(tb[IFLA_INET6_ICMP6STATS]) < 6)) { + /* kernel commit 14a196807482e6fc74f15fc03176d5c08880588f reordered the values. + * The later commit 6a5dc9e598fe90160fee7de098fa319665f5253e added values + * IPSTATS_MIB_CSUMERRORS/ICMP6_MIB_CSUMERRORS. If the netlink is shorter + * then this, assume that the kernel uses the previous meaning of the + * enumeration. */ + map_stat_id = map_stat_id_from_IPSTATS_MIB_v1; + } + + len = min_t(int, __IPSTATS_MIB_MAX, len); + for (i = 1; i < len; i++) { + memcpy(&stat, &cnt[i * sizeof(stat)], sizeof(stat)); + rtnl_link_set_stat(link, map_stat_id[i], stat); + } + } + + if (tb[IFLA_INET6_ICMP6STATS]) { + unsigned char *cnt = nla_data(tb[IFLA_INET6_ICMP6STATS]); + uint64_t stat; + int i; + int len = min_t(int, __ICMP6_MIB_MAX, nla_len(tb[IFLA_INET6_ICMP6STATS]) / 8); + + _NL_STATIC_ASSERT (__ICMP6_MIB_MAX == 6); + _NL_STATIC_ASSERT (RTNL_LINK_ICMP6_CSUMERRORS - RTNL_LINK_ICMP6_INMSGS + 1 == 5); + + for (i = 1; i < len; i++) { + memcpy(&stat, &cnt[i * sizeof(stat)], sizeof(stat)); + rtnl_link_set_stat(link, RTNL_LINK_ICMP6_INMSGS + i - 1, + stat); + } + } + + return 0; +} + +static int inet6_fill_af(struct rtnl_link *link, struct nl_msg *msg, void *data) +{ + struct inet6_data *id = data; + + if (id->i6_addr_gen_mode != I6_ADDR_GEN_MODE_UNKNOWN) + NLA_PUT_U8(msg, IFLA_INET6_ADDR_GEN_MODE, id->i6_addr_gen_mode); + + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +/* These live in include/net/if_inet6.h and should be moved to include/linux */ +#define IF_RA_OTHERCONF 0x80 +#define IF_RA_MANAGED 0x40 +#define IF_RA_RCVD 0x20 +#define IF_RS_SENT 0x10 +#define IF_READY 0x80000000 + +static const struct trans_tbl inet6_flags[] = { + __ADD(IF_RA_OTHERCONF, ra_otherconf), + __ADD(IF_RA_MANAGED, ra_managed), + __ADD(IF_RA_RCVD, ra_rcvd), + __ADD(IF_RS_SENT, rs_sent), + __ADD(IF_READY, ready), +}; + +char *rtnl_link_inet6_flags2str(int flags, char *buf, size_t len) +{ + return __flags2str(flags, buf, len, inet6_flags, + ARRAY_SIZE(inet6_flags)); +} + +int rtnl_link_inet6_str2flags(const char *name) +{ + return __str2flags(name, inet6_flags, ARRAY_SIZE(inet6_flags)); +} + +static const struct trans_tbl inet6_devconf[] = { + __ADD(DEVCONF_FORWARDING, forwarding), + __ADD(DEVCONF_HOPLIMIT, hoplimit), + __ADD(DEVCONF_MTU6, mtu6), + __ADD(DEVCONF_ACCEPT_RA, accept_ra), + __ADD(DEVCONF_ACCEPT_REDIRECTS, accept_redirects), + __ADD(DEVCONF_AUTOCONF, autoconf), + __ADD(DEVCONF_DAD_TRANSMITS, dad_transmits), + __ADD(DEVCONF_RTR_SOLICITS, rtr_solicits), + __ADD(DEVCONF_RTR_SOLICIT_INTERVAL, rtr_solicit_interval), + __ADD(DEVCONF_RTR_SOLICIT_DELAY, rtr_solicit_delay), + __ADD(DEVCONF_USE_TEMPADDR, use_tempaddr), + __ADD(DEVCONF_TEMP_VALID_LFT, temp_valid_lft), + __ADD(DEVCONF_TEMP_PREFERED_LFT, temp_prefered_lft), + __ADD(DEVCONF_REGEN_MAX_RETRY, regen_max_retry), + __ADD(DEVCONF_MAX_DESYNC_FACTOR, max_desync_factor), + __ADD(DEVCONF_MAX_ADDRESSES, max_addresses), + __ADD(DEVCONF_FORCE_MLD_VERSION, force_mld_version), + __ADD(DEVCONF_ACCEPT_RA_DEFRTR, accept_ra_defrtr), + __ADD(DEVCONF_ACCEPT_RA_PINFO, accept_ra_pinfo), + __ADD(DEVCONF_ACCEPT_RA_RTR_PREF, accept_ra_rtr_pref), + __ADD(DEVCONF_RTR_PROBE_INTERVAL, rtr_probe_interval), + __ADD(DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN, accept_ra_rt_info), + __ADD(DEVCONF_PROXY_NDP, proxy_ndp), + __ADD(DEVCONF_OPTIMISTIC_DAD, optimistic_dad), + __ADD(DEVCONF_ACCEPT_SOURCE_ROUTE, accept_source_route), + __ADD(DEVCONF_MC_FORWARDING, mc_forwarding), + __ADD(DEVCONF_DISABLE_IPV6, disable_ipv6), + __ADD(DEVCONF_ACCEPT_DAD, accept_dad), + __ADD(DEVCONF_FORCE_TLLAO, force_tllao), +}; + +static char *inet6_devconf2str(int type, char *buf, size_t len) +{ + return __type2str(type, buf, len, inet6_devconf, + ARRAY_SIZE(inet6_devconf)); +} + +static const struct trans_tbl inet6_addr_gen_mode[] = { + __ADD(IN6_ADDR_GEN_MODE_EUI64, eui64), + __ADD(IN6_ADDR_GEN_MODE_NONE, none), + __ADD(IN6_ADDR_GEN_MODE_STABLE_PRIVACY, stable_privacy), +}; + +const char *rtnl_link_inet6_addrgenmode2str(uint8_t mode, char *buf, size_t len) +{ + return __type2str(mode, buf, len, inet6_addr_gen_mode, + ARRAY_SIZE(inet6_addr_gen_mode)); +} + +uint8_t rtnl_link_inet6_str2addrgenmode(const char *mode) +{ + return (uint8_t) __str2type(mode, inet6_addr_gen_mode, + ARRAY_SIZE(inet6_addr_gen_mode)); +} + +static void inet6_dump_details(struct rtnl_link *link, + struct nl_dump_params *p, void *data) +{ + struct inet6_data *i6 = data; + struct nl_addr *addr; + int i, n = 0; + char buf[64]; + + nl_dump_line(p, " ipv6 max-reasm-len %s", + nl_size2str(i6->i6_cacheinfo.max_reasm_len, buf, sizeof(buf))); + + nl_dump(p, " <%s>\n", + rtnl_link_inet6_flags2str(i6->i6_flags, buf, sizeof(buf))); + + nl_dump_line(p, " create-stamp %.2fs reachable-time %s", + (double) i6->i6_cacheinfo.tstamp / 100., + nl_msec2str(i6->i6_cacheinfo.reachable_time, buf, sizeof(buf))); + + nl_dump(p, " retrans-time %s\n", + nl_msec2str(i6->i6_cacheinfo.retrans_time, buf, sizeof(buf))); + + addr = nl_addr_build(AF_INET6, &i6->i6_token, sizeof(i6->i6_token)); + nl_dump(p, " token %s\n", + nl_addr2str(addr, buf, sizeof(buf))); + nl_addr_put(addr); + + nl_dump(p, " link-local address mode %s\n", + rtnl_link_inet6_addrgenmode2str(i6->i6_addr_gen_mode, + buf, sizeof(buf))); + + nl_dump_line(p, " devconf:\n"); + nl_dump_line(p, " "); + + for (i = 0; i < DEVCONF_MAX; i++) { + char buf2[64]; + uint32_t value = i6->i6_conf[i]; + int x, offset; + + switch (i) { + case DEVCONF_TEMP_VALID_LFT: + case DEVCONF_TEMP_PREFERED_LFT: + nl_msec2str((uint64_t) value * 1000., buf2, sizeof(buf2)); + break; + + case DEVCONF_RTR_PROBE_INTERVAL: + case DEVCONF_RTR_SOLICIT_INTERVAL: + case DEVCONF_RTR_SOLICIT_DELAY: + nl_msec2str(value, buf2, sizeof(buf2)); + break; + + default: + snprintf(buf2, sizeof(buf2), "%u", value); + break; + } + + inet6_devconf2str(i, buf, sizeof(buf)); + + offset = 23 - strlen(buf2); + if (offset < 0) + offset = 0; + + for (x = strlen(buf); x < offset; x++) + buf[x] = ' '; + + _nl_strncpy_trunc(&buf[offset], buf2, sizeof(buf) - offset); + + nl_dump_line(p, "%s", buf); + + if (++n == 3) { + nl_dump(p, "\n"); + nl_dump_line(p, " "); + n = 0; + } else + nl_dump(p, " "); + } + + if (n != 0) + nl_dump(p, "\n"); +} + +static void inet6_dump_stats(struct rtnl_link *link, + struct nl_dump_params *p, void *data) +{ + double octets; + char *octetsUnit; + + nl_dump(p, " IPv6: InPkts InOctets " + " InDiscards InDelivers\n"); + nl_dump(p, " %18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_INPKTS]); + + octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_INOCTETS], + &octetsUnit); + if (octets) + nl_dump(p, "%14.2f %3s ", octets, octetsUnit); + else + nl_dump(p, "%16" PRIu64 " B ", 0); + + nl_dump(p, "%18" PRIu64 " %18" PRIu64 "\n", + link->l_stats[RTNL_LINK_IP6_INDISCARDS], + link->l_stats[RTNL_LINK_IP6_INDELIVERS]); + + nl_dump(p, " OutPkts OutOctets " + " OutDiscards OutForwards\n"); + + nl_dump(p, " %18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_OUTPKTS]); + + octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_OUTOCTETS], + &octetsUnit); + if (octets) + nl_dump(p, "%14.2f %3s ", octets, octetsUnit); + else + nl_dump(p, "%16" PRIu64 " B ", 0); + + nl_dump(p, "%18" PRIu64 " %18" PRIu64 "\n", + link->l_stats[RTNL_LINK_IP6_OUTDISCARDS], + link->l_stats[RTNL_LINK_IP6_OUTFORWDATAGRAMS]); + + nl_dump(p, " InMcastPkts InMcastOctets " + " InBcastPkts InBcastOctests\n"); + + nl_dump(p, " %18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_INMCASTPKTS]); + + octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_INMCASTOCTETS], + &octetsUnit); + if (octets) + nl_dump(p, "%14.2f %3s ", octets, octetsUnit); + else + nl_dump(p, "%16" PRIu64 " B ", 0); + + nl_dump(p, "%18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_INBCASTPKTS]); + octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_INBCASTOCTETS], + &octetsUnit); + if (octets) + nl_dump(p, "%14.2f %3s\n", octets, octetsUnit); + else + nl_dump(p, "%16" PRIu64 " B\n", 0); + + nl_dump(p, " OutMcastPkts OutMcastOctets " + " OutBcastPkts OutBcastOctests\n"); + + nl_dump(p, " %18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_OUTMCASTPKTS]); + + octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_OUTMCASTOCTETS], + &octetsUnit); + if (octets) + nl_dump(p, "%14.2f %3s ", octets, octetsUnit); + else + nl_dump(p, "%16" PRIu64 " B ", 0); + + nl_dump(p, "%18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_OUTBCASTPKTS]); + octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_OUTBCASTOCTETS], + &octetsUnit); + if (octets) + nl_dump(p, "%14.2f %3s\n", octets, octetsUnit); + else + nl_dump(p, "%16" PRIu64 " B\n", 0); + + nl_dump(p, " ReasmOKs ReasmFails " + " ReasmReqds ReasmTimeout\n"); + nl_dump(p, " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n", + link->l_stats[RTNL_LINK_IP6_REASMOKS], + link->l_stats[RTNL_LINK_IP6_REASMFAILS], + link->l_stats[RTNL_LINK_IP6_REASMREQDS], + link->l_stats[RTNL_LINK_IP6_REASMTIMEOUT]); + + nl_dump(p, " FragOKs FragFails " + " FragCreates\n"); + nl_dump(p, " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n", + link->l_stats[RTNL_LINK_IP6_FRAGOKS], + link->l_stats[RTNL_LINK_IP6_FRAGFAILS], + link->l_stats[RTNL_LINK_IP6_FRAGCREATES]); + + nl_dump(p, " InHdrErrors InTooBigErrors " + " InNoRoutes InAddrErrors\n"); + nl_dump(p, " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n", + link->l_stats[RTNL_LINK_IP6_INHDRERRORS], + link->l_stats[RTNL_LINK_IP6_INTOOBIGERRORS], + link->l_stats[RTNL_LINK_IP6_INNOROUTES], + link->l_stats[RTNL_LINK_IP6_INADDRERRORS]); + + nl_dump(p, " InUnknownProtos InTruncatedPkts " + " OutNoRoutes InCsumErrors\n"); + nl_dump(p, " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n", + link->l_stats[RTNL_LINK_IP6_INUNKNOWNPROTOS], + link->l_stats[RTNL_LINK_IP6_INTRUNCATEDPKTS], + link->l_stats[RTNL_LINK_IP6_OUTNOROUTES], + link->l_stats[RTNL_LINK_IP6_CSUMERRORS]); + + nl_dump(p, " InNoECTPkts InECT1Pkts " + " InECT0Pkts InCEPkts\n"); + nl_dump(p, " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n", + link->l_stats[RTNL_LINK_IP6_NOECTPKTS], + link->l_stats[RTNL_LINK_IP6_ECT1PKTS], + link->l_stats[RTNL_LINK_IP6_ECT0PKTS], + link->l_stats[RTNL_LINK_IP6_CEPKTS]); + + nl_dump(p, " ICMPv6: InMsgs InErrors " + " OutMsgs OutErrors InCsumErrors\n"); + nl_dump(p, " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n", + link->l_stats[RTNL_LINK_ICMP6_INMSGS], + link->l_stats[RTNL_LINK_ICMP6_INERRORS], + link->l_stats[RTNL_LINK_ICMP6_OUTMSGS], + link->l_stats[RTNL_LINK_ICMP6_OUTERRORS], + link->l_stats[RTNL_LINK_ICMP6_CSUMERRORS]); +} + +static const struct nla_policy protinfo_policy = { + .type = NLA_NESTED, +}; + +static struct rtnl_link_af_ops inet6_ops = { + .ao_family = AF_INET6, + .ao_alloc = &inet6_alloc, + .ao_clone = &inet6_clone, + .ao_free = &inet6_free, + .ao_parse_protinfo = &inet6_parse_protinfo, + .ao_parse_af = &inet6_parse_protinfo, + .ao_fill_af = &inet6_fill_af, + .ao_dump[NL_DUMP_DETAILS] = &inet6_dump_details, + .ao_dump[NL_DUMP_STATS] = &inet6_dump_stats, + .ao_protinfo_policy = &protinfo_policy, +}; + +/** + * Return IPv6 specific flags + * @arg link Link object + * @arg out_flags Flags on success + * + * Returns the link's IPv6 flags. + * + * @return 0 on success + * @return -NLE_NOATTR configuration setting not available + */ +int rtnl_link_inet6_get_flags(struct rtnl_link *link, uint32_t* out_flags) +{ + struct inet6_data *id = NULL; + + if (!(id = rtnl_link_af_data(link, &inet6_ops))) + return -NLE_NOATTR; + + *out_flags = id->i6_flags; + return 0; +} + +/** + * Set IPv6 specific flags + * @arg link Link object + * @arg flags Flags to set + * + * Sets the link's IPv6 specific flags. Overwrites currently set flags. + * + * @return 0 on success + * @return -NLE_NOMEM could not allocate inet6 data + */ +int rtnl_link_inet6_set_flags(struct rtnl_link *link, uint32_t flags) +{ + struct inet6_data *id; + + if (!(id = rtnl_link_af_alloc(link, &inet6_ops))) + return -NLE_NOMEM; + + id->i6_flags = flags; + return 0; +} + +/** + * Get IPv6 tokenized interface identifier + * @arg link Link object + * @arg token Tokenized interface identifier on success + * + * Returns the link's IPv6 tokenized interface identifier. + * + * @return 0 on success + * @return -NLE_NOMEM failure to allocate struct nl_addr result + * @return -NLE_NOATTR configuration setting not available + * @return -NLE_NOADDR tokenized interface identifier is not set + */ +int rtnl_link_inet6_get_token(struct rtnl_link *link, struct nl_addr **addr) +{ + struct inet6_data *id; + + if (!(id = rtnl_link_af_data(link, &inet6_ops))) + return -NLE_NOATTR; + + *addr = nl_addr_build(AF_INET6, &id->i6_token, sizeof(id->i6_token)); + if (!*addr) + return -NLE_NOMEM; + if (nl_addr_iszero(*addr)) { + nl_addr_put(*addr); + *addr = NULL; + return -NLE_NOADDR; + } + + return 0; +} + +/** + * Set IPv6 tokenized interface identifier + * @arg link Link object + * @arg token Tokenized interface identifier + * + * Sets the link's IPv6 tokenized interface identifier. + * + * @return 0 on success + * @return -NLE_NOMEM could not allocate inet6 data + * @return -NLE_INVAL addr is not a valid inet6 address + */ +int rtnl_link_inet6_set_token(struct rtnl_link *link, struct nl_addr *addr) +{ + struct inet6_data *id; + + if ((nl_addr_get_family(addr) != AF_INET6) || + (nl_addr_get_len(addr) != sizeof(id->i6_token))) + return -NLE_INVAL; + + if (!(id = rtnl_link_af_alloc(link, &inet6_ops))) + return -NLE_NOMEM; + + memcpy(&id->i6_token, nl_addr_get_binary_addr(addr), + sizeof(id->i6_token)); + return 0; +} + +/** + * Get IPv6 link-local address generation mode + * @arg link Link object + * @arg mode Generation mode on success + * + * Returns the link's IPv6 link-local address generation mode. + * + * @return 0 on success + * @return -NLE_NOATTR configuration setting not available + * @return -NLE_INVAL generation mode unknown. If the link was received via + * netlink, it means that address generation mode is not + * supported by the kernel. + */ +int rtnl_link_inet6_get_addr_gen_mode(struct rtnl_link *link, uint8_t *mode) +{ + struct inet6_data *id; + + if (!(id = rtnl_link_af_data(link, &inet6_ops))) + return -NLE_NOATTR; + + if (id->i6_addr_gen_mode == I6_ADDR_GEN_MODE_UNKNOWN) + return -NLE_INVAL; + + *mode = id->i6_addr_gen_mode; + return 0; +} + +/** + * Set IPv6 link-local address generation mode + * @arg link Link object + * @arg mode Generation mode + * + * Sets the link's IPv6 link-local address generation mode. + * + * @return 0 on success + * @return -NLE_NOMEM could not allocate inet6 data + */ +int rtnl_link_inet6_set_addr_gen_mode(struct rtnl_link *link, uint8_t mode) +{ + struct inet6_data *id; + + if (!(id = rtnl_link_af_alloc(link, &inet6_ops))) + return -NLE_NOMEM; + + id->i6_addr_gen_mode = mode; + return 0; +} + +static void __init inet6_init(void) +{ + rtnl_link_af_register(&inet6_ops); +} + +static void __exit inet6_exit(void) +{ + rtnl_link_af_unregister(&inet6_ops); +} diff --git a/libnl/lib/route/link/ip6tnl.c b/libnl/lib/route/link/ip6tnl.c new file mode 100644 index 0000000..0b939f5 --- /dev/null +++ b/libnl/lib/route/link/ip6tnl.c @@ -0,0 +1,695 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2014 Susant Sahani + */ + +/** + * @ingroup link + * @defgroup ip6tnl IP6TNL + * ip6tnl link module + * + * @details + * \b Link Type Name: "ip6tnl" + * + * @route_doc{link_ip6tnl, IP6TNL Documentation} + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IP6_TNL_ATTR_LINK (1 << 0) +#define IP6_TNL_ATTR_LOCAL (1 << 1) +#define IP6_TNL_ATTR_REMOTE (1 << 2) +#define IP6_TNL_ATTR_TTL (1 << 3) +#define IP6_TNL_ATTR_TOS (1 << 4) +#define IP6_TNL_ATTR_ENCAPLIMIT (1 << 5) +#define IP6_TNL_ATTR_FLAGS (1 << 6) +#define IP6_TNL_ATTR_PROTO (1 << 7) +#define IP6_TNL_ATTR_FLOWINFO (1 << 8) + +struct ip6_tnl_info +{ + uint8_t ttl; + uint8_t tos; + uint8_t encap_limit; + uint8_t proto; + uint32_t flags; + uint32_t link; + uint32_t flowinfo; + struct in6_addr local; + struct in6_addr remote; + uint32_t ip6_tnl_mask; +}; + +static struct nla_policy ip6_tnl_policy[IFLA_IPTUN_MAX + 1] = { + [IFLA_IPTUN_LINK] = { .type = NLA_U32 }, + [IFLA_IPTUN_LOCAL] = { .minlen = sizeof(struct in6_addr) }, + [IFLA_IPTUN_REMOTE] = { .minlen = sizeof(struct in6_addr) }, + [IFLA_IPTUN_TTL] = { .type = NLA_U8 }, + [IFLA_IPTUN_TOS] = { .type = NLA_U8 }, + [IFLA_IPTUN_ENCAP_LIMIT] = { .type = NLA_U8 }, + [IFLA_IPTUN_FLOWINFO] = { .type = NLA_U32 }, + [IFLA_IPTUN_FLAGS] = { .type = NLA_U32 }, + [IFLA_IPTUN_PROTO] = { .type = NLA_U8 }, +}; + +static int ip6_tnl_alloc(struct rtnl_link *link) +{ + struct ip6_tnl_info *ip6_tnl; + + if (link->l_info) + memset(link->l_info, 0, sizeof(*ip6_tnl)); + else { + ip6_tnl = calloc(1, sizeof(*ip6_tnl)); + if (!ip6_tnl) + return -NLE_NOMEM; + + link->l_info = ip6_tnl; + } + + return 0; +} + +static int ip6_tnl_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_IPTUN_MAX + 1]; + struct ip6_tnl_info *ip6_tnl; + int err; + + NL_DBG(3, "Parsing IP6_TNL link info\n"); + + err = nla_parse_nested(tb, IFLA_IPTUN_MAX, data, ip6_tnl_policy); + if (err < 0) + goto errout; + + err = ip6_tnl_alloc(link); + if (err < 0) + goto errout; + + ip6_tnl = link->l_info; + + if (tb[IFLA_IPTUN_LINK]) { + ip6_tnl->link = nla_get_u32(tb[IFLA_IPTUN_LINK]); + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_LINK; + } + + if (tb[IFLA_IPTUN_LOCAL]) { + nla_memcpy(&ip6_tnl->local, tb[IFLA_IPTUN_LOCAL], sizeof(struct in6_addr)); + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_LOCAL; + } + + if (tb[IFLA_IPTUN_REMOTE]) { + nla_memcpy(&ip6_tnl->remote, tb[IFLA_IPTUN_REMOTE], sizeof(struct in6_addr)); + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_REMOTE; + } + + if (tb[IFLA_IPTUN_TTL]) { + ip6_tnl->ttl = nla_get_u8(tb[IFLA_IPTUN_TTL]); + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_TTL; + } + + if (tb[IFLA_IPTUN_TOS]) { + ip6_tnl->tos = nla_get_u8(tb[IFLA_IPTUN_TOS]); + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_TOS; + } + + if (tb[IFLA_IPTUN_ENCAP_LIMIT]) { + ip6_tnl->encap_limit = nla_get_u8(tb[IFLA_IPTUN_ENCAP_LIMIT]); + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_ENCAPLIMIT; + } + + if (tb[IFLA_IPTUN_FLAGS]) { + ip6_tnl->flags = nla_get_u32(tb[IFLA_IPTUN_FLAGS]); + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_FLAGS; + } + + if (tb[IFLA_IPTUN_FLOWINFO]) { + ip6_tnl->flowinfo = nla_get_u32(tb[IFLA_IPTUN_FLOWINFO]); + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_FLOWINFO; + } + + if (tb[IFLA_IPTUN_PROTO]) { + ip6_tnl->proto = nla_get_u8(tb[IFLA_IPTUN_PROTO]); + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_PROTO; + } + + err = 0; + +errout: + return err; +} + +static int ip6_tnl_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + struct nlattr *data; + + data = nla_nest_start(msg, IFLA_INFO_DATA); + if (!data) + return -NLE_MSGSIZE; + + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_LINK) + NLA_PUT_U32(msg, IFLA_IPTUN_LINK, ip6_tnl->link); + + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_LOCAL) + NLA_PUT(msg, IFLA_IPTUN_LOCAL, sizeof(struct in6_addr), &ip6_tnl->local); + + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_REMOTE) + NLA_PUT(msg, IFLA_IPTUN_REMOTE, sizeof(struct in6_addr), &ip6_tnl->remote); + + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_TTL) + NLA_PUT_U8(msg, IFLA_IPTUN_TTL, ip6_tnl->ttl); + + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_TOS) + NLA_PUT_U8(msg, IFLA_IPTUN_TOS, ip6_tnl->tos); + + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_ENCAPLIMIT) + NLA_PUT_U8(msg, IFLA_IPTUN_ENCAP_LIMIT, ip6_tnl->encap_limit); + + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_FLAGS) + NLA_PUT_U32(msg, IFLA_IPTUN_FLAGS, ip6_tnl->flags); + + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_FLOWINFO) + NLA_PUT_U32(msg, IFLA_IPTUN_FLOWINFO, ip6_tnl->flowinfo); + + /* kernel crashes if this attribure is missing temporary fix */ + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_PROTO) + NLA_PUT_U8(msg, IFLA_IPTUN_PROTO, ip6_tnl->proto); + else + NLA_PUT_U8(msg, IFLA_IPTUN_PROTO, 0); + + nla_nest_end(msg, data); + +nla_put_failure: + return 0; +} + +static void ip6_tnl_free(struct rtnl_link *link) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + free(ip6_tnl); + link->l_info = NULL; +} + +static void ip6_tnl_dump_line(struct rtnl_link *link, struct nl_dump_params *p) +{ + nl_dump(p, "ip6_tnl : %s", link->l_name); +} + +static void ip6_tnl_dump_details(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + char *name, addr[INET6_ADDRSTRLEN]; + struct rtnl_link *parent; + + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_LINK) { + nl_dump(p, " link "); + + name = NULL; + parent = link_lookup(link->ce_cache, ip6_tnl->link); + if (parent) + name = rtnl_link_get_name(parent); + + if (name) + nl_dump_line(p, "%s\n", name); + else + nl_dump_line(p, "%u\n", ip6_tnl->link); + } + + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_LOCAL) { + nl_dump(p, " local "); + + if(inet_ntop(AF_INET6, &ip6_tnl->local, addr, INET6_ADDRSTRLEN)) + nl_dump_line(p, "%s\n", addr); + else + nl_dump_line(p, "%#x\n", ip6_tnl->local); + } + + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_REMOTE) { + nl_dump(p, " remote "); + + if(inet_ntop(AF_INET6, &ip6_tnl->remote, addr, INET6_ADDRSTRLEN)) + nl_dump_line(p, "%s\n", addr); + else + nl_dump_line(p, "%#x\n", ip6_tnl->remote); + } + + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_TTL) { + nl_dump(p, " ttl "); + nl_dump_line(p, "%d\n", ip6_tnl->ttl); + } + + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_TOS) { + nl_dump(p, " tos "); + nl_dump_line(p, "%d\n", ip6_tnl->tos); + } + + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_ENCAPLIMIT) { + nl_dump(p, " encaplimit "); + nl_dump_line(p, "%d\n", ip6_tnl->encap_limit); + } + + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_FLAGS) { + nl_dump(p, " flags "); + nl_dump_line(p, " (%x)\n", ip6_tnl->flags); + } + + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_FLOWINFO) { + nl_dump(p, " flowinfo "); + nl_dump_line(p, " (%x)\n", ip6_tnl->flowinfo); + } + + if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_PROTO) { + nl_dump(p, " proto "); + nl_dump_line(p, " (%x)\n", ip6_tnl->proto); + } +} + +static int ip6_tnl_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct ip6_tnl_info *ip6_tnl_dst, *ip6_tnl_src = src->l_info; + int err; + + dst->l_info = NULL; + + err = rtnl_link_set_type(dst, "ip6tnl"); + if (err < 0) + return err; + + ip6_tnl_dst = dst->l_info; + + if (!ip6_tnl_dst || !ip6_tnl_src) + BUG(); + + memcpy(ip6_tnl_dst, ip6_tnl_src, sizeof(struct ip6_tnl_info)); + + return 0; +} + +static struct rtnl_link_info_ops ip6_tnl_info_ops = { + .io_name = "ip6tnl", + .io_alloc = ip6_tnl_alloc, + .io_parse = ip6_tnl_parse, + .io_dump = { + [NL_DUMP_LINE] = ip6_tnl_dump_line, + [NL_DUMP_DETAILS] = ip6_tnl_dump_details, + }, + .io_clone = ip6_tnl_clone, + .io_put_attrs = ip6_tnl_put_attrs, + .io_free = ip6_tnl_free, +}; + +#define IS_IP6_TNL_LINK_ASSERT(link)\ + if ((link)->l_info_ops != &ip6_tnl_info_ops) {\ + APPBUG("Link is not a ip6_tnl link. set type \"ip6tnl\" first.");\ + return -NLE_OPNOTSUPP;\ + } + +struct rtnl_link *rtnl_link_ip6_tnl_alloc(void) +{ + struct rtnl_link *link; + int err; + + link = rtnl_link_alloc(); + if (!link) + return NULL; + + err = rtnl_link_set_type(link, "ip6tnl"); + if (err < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Check if link is a IP6_TNL link + * @arg link Link object + * + * @return True if link is a IP6_TNL link, otherwise false is returned. + */ +int rtnl_link_is_ip6_tnl(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "ip6tnl"); +} + +/** + * Create a new ip6_tnl tunnel device + * @arg sock netlink socket + * @arg name name of the tunnel device + * + * Creates a new ip6_tnl tunnel device in the kernel + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6_tnl_add(struct nl_sock *sk, const char *name) +{ + struct rtnl_link *link; + int err; + + link = rtnl_link_ip6_tnl_alloc(); + if (!link) + return -NLE_NOMEM; + + if(name) + rtnl_link_set_name(link, name); + + err = rtnl_link_add(sk, link, NLM_F_CREATE); + rtnl_link_put(link); + + return err; +} + +/** + * Set IP6_TNL tunnel interface index + * @arg link Link object + * @arg index interface index + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6_tnl_set_link(struct rtnl_link *link, uint32_t index) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + ip6_tnl->link = index; + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_LINK; + + return 0; +} + +/** + * Get IP6_TNL tunnel interface index + * @arg link Link object + * + * @return interface index value + */ +uint32_t rtnl_link_ip6_tnl_get_link(struct rtnl_link *link) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + return ip6_tnl->link; +} + +/** + * Set IP6_TNL tunnel local address + * @arg link Link object + * @arg addr local address + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6_tnl_set_local(struct rtnl_link *link, struct in6_addr *addr) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + memcpy(&ip6_tnl->local, addr, sizeof(struct in6_addr)); + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_LOCAL; + + return 0; +} + +/** + * Get IP6_TNL tunnel local address + * @arg link Link object + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6_tnl_get_local(struct rtnl_link *link, struct in6_addr *addr) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + memcpy(addr, &ip6_tnl->local, sizeof(struct in6_addr)); + + return 0; +} + +/** + * Set IP6_TNL tunnel remote address + * @arg link Link object + * @arg remote remote address + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6_tnl_set_remote(struct rtnl_link *link, struct in6_addr *addr) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + memcpy(&ip6_tnl->remote, addr, sizeof(struct in6_addr)); + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_REMOTE; + + return 0; +} + +/** + * Get IP6_TNL tunnel remote address + * @arg link Link object + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6_tnl_get_remote(struct rtnl_link *link, struct in6_addr *addr) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + memcpy(addr, &ip6_tnl->remote, sizeof(struct in6_addr)); + + return 0; +} + +/** + * Set IP6_TNL tunnel ttl + * @arg link Link object + * @arg ttl tunnel ttl + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6_tnl_set_ttl(struct rtnl_link *link, uint8_t ttl) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + ip6_tnl->ttl = ttl; + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_TTL; + + return 0; +} + +/** + * Get IP6_TNL tunnel ttl + * @arg link Link object + * + * @return ttl value + */ +uint8_t rtnl_link_ip6_tnl_get_ttl(struct rtnl_link *link) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + return ip6_tnl->ttl; +} + +/** + * Set IP6_TNL tunnel tos + * @arg link Link object + * @arg tos tunnel tos + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6_tnl_set_tos(struct rtnl_link *link, uint8_t tos) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + ip6_tnl->tos = tos; + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_TOS; + + return 0; +} + +/** + * Get IP6_TNL tunnel tos + * @arg link Link object + * + * @return tos value + */ +uint8_t rtnl_link_ip6_tnl_get_tos(struct rtnl_link *link) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + return ip6_tnl->tos; +} + +/** + * Set IP6_TNL tunnel encap limit + * @arg link Link object + * @arg encap_limit encaplimit value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6_tnl_set_encaplimit(struct rtnl_link *link, uint8_t encap_limit) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + ip6_tnl->encap_limit = encap_limit; + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_ENCAPLIMIT; + + return 0; +} + +/** + * Get IP6_TNL encaplimit + * @arg link Link object + * + * @return encaplimit value + */ +uint8_t rtnl_link_ip6_tnl_get_encaplimit(struct rtnl_link *link) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + return ip6_tnl->encap_limit; +} + +/** + * Set IP6_TNL tunnel flowinfo + * @arg link Link object + * @arg flowinfo flowinfo value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6_tnl_set_flowinfo(struct rtnl_link *link, uint32_t flowinfo) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + ip6_tnl->flowinfo = flowinfo; + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_FLOWINFO; + + return 0; +} + +/** + * Get IP6_TNL flowinfo + * @arg link Link object + * + * @return flowinfo value + */ +uint32_t rtnl_link_ip6_tnl_get_flowinfo(struct rtnl_link *link) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + return ip6_tnl->flowinfo; +} + +/** + * Set IP6_TNL tunnel flags + * @arg link Link object + * @arg flags tunnel flags + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6_tnl_set_flags(struct rtnl_link *link, uint32_t flags) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + ip6_tnl->flags = flags; + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_FLAGS; + + return 0; +} + +/** + * Get IP6_TNL path flags + * @arg link Link object + * + * @return flags value + */ +uint32_t rtnl_link_ip6_tnl_get_flags(struct rtnl_link *link) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + return ip6_tnl->flags; +} + +/** + * Set IP6_TNL tunnel proto + * @arg link Link object + * @arg proto tunnel proto + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ip6_tnl_set_proto(struct rtnl_link *link, uint8_t proto) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + ip6_tnl->proto = proto; + ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_PROTO; + + return 0; +} + +/** + * Get IP6_TNL proto + * @arg link Link object + * + * @return proto value + */ +uint8_t rtnl_link_ip6_tnl_get_proto(struct rtnl_link *link) +{ + struct ip6_tnl_info *ip6_tnl = link->l_info; + + IS_IP6_TNL_LINK_ASSERT(link); + + return ip6_tnl->proto; +} + +static void __init ip6_tnl_init(void) +{ + rtnl_link_register_info(&ip6_tnl_info_ops); +} + +static void __exit ip6_tnl_exit(void) +{ + rtnl_link_unregister_info(&ip6_tnl_info_ops); +} diff --git a/libnl/lib/route/link/ipgre.c b/libnl/lib/route/link/ipgre.c new file mode 100644 index 0000000..1000a0c --- /dev/null +++ b/libnl/lib/route/link/ipgre.c @@ -0,0 +1,838 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2014 Susant Sahani + */ + +/** + * @ingroup link + * @defgroup ipgre IPGRE + * ipgre link module + * + * @details + * \b Link Type Name: "ipgre" + * + * @route_doc{link_ipgre, IPGRE Documentation} + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IPGRE_ATTR_LINK (1 << 0) +#define IPGRE_ATTR_IFLAGS (1 << 1) +#define IPGRE_ATTR_OFLAGS (1 << 2) +#define IPGRE_ATTR_IKEY (1 << 3) +#define IPGRE_ATTR_OKEY (1 << 4) +#define IPGRE_ATTR_LOCAL (1 << 5) +#define IPGRE_ATTR_REMOTE (1 << 6) +#define IPGRE_ATTR_TTL (1 << 7) +#define IPGRE_ATTR_TOS (1 << 8) +#define IPGRE_ATTR_PMTUDISC (1 << 9) + +struct ipgre_info +{ + uint8_t ttl; + uint8_t tos; + uint8_t pmtudisc; + uint16_t iflags; + uint16_t oflags; + uint32_t ikey; + uint32_t okey; + uint32_t link; + uint32_t local; + uint32_t remote; + uint32_t ipgre_mask; +}; + +static struct nla_policy ipgre_policy[IFLA_GRE_MAX + 1] = { + [IFLA_GRE_LINK] = { .type = NLA_U32 }, + [IFLA_GRE_IFLAGS] = { .type = NLA_U16 }, + [IFLA_GRE_OFLAGS] = { .type = NLA_U16 }, + [IFLA_GRE_IKEY] = { .type = NLA_U32 }, + [IFLA_GRE_OKEY] = { .type = NLA_U32 }, + [IFLA_GRE_LOCAL] = { .type = NLA_U32 }, + [IFLA_GRE_REMOTE] = { .type = NLA_U32 }, + [IFLA_GRE_TTL] = { .type = NLA_U8 }, + [IFLA_GRE_TOS] = { .type = NLA_U8 }, + [IFLA_GRE_PMTUDISC] = { .type = NLA_U8 }, +}; + +static int ipgre_alloc(struct rtnl_link *link) +{ + struct ipgre_info *ipgre; + + if (link->l_info) + memset(link->l_info, 0, sizeof(*ipgre)); + else { + ipgre = calloc(1, sizeof(*ipgre)); + if (!ipgre) + return -NLE_NOMEM; + + link->l_info = ipgre; + } + + return 0; +} + +static int ipgre_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_GRE_MAX + 1]; + struct ipgre_info *ipgre; + int err; + + NL_DBG(3, "Parsing IPGRE link info\n"); + + err = nla_parse_nested(tb, IFLA_GRE_MAX, data, ipgre_policy); + if (err < 0) + goto errout; + + err = ipgre_alloc(link); + if (err < 0) + goto errout; + + ipgre = link->l_info; + + if (tb[IFLA_GRE_LINK]) { + ipgre->link = nla_get_u32(tb[IFLA_GRE_LINK]); + ipgre->ipgre_mask |= IPGRE_ATTR_LINK; + } + + if (tb[IFLA_GRE_IFLAGS]) { + ipgre->iflags = nla_get_u16(tb[IFLA_GRE_IFLAGS]); + ipgre->ipgre_mask |= IPGRE_ATTR_IFLAGS; + } + + if (tb[IFLA_GRE_OFLAGS]) { + ipgre->oflags = nla_get_u16(tb[IFLA_GRE_OFLAGS]); + ipgre->ipgre_mask |= IPGRE_ATTR_OFLAGS; + } + + if (tb[IFLA_GRE_IKEY]) { + ipgre->ikey = nla_get_u32(tb[IFLA_GRE_IKEY]); + ipgre->ipgre_mask |= IPGRE_ATTR_IKEY; + } + + if (tb[IFLA_GRE_OKEY]) { + ipgre->okey = nla_get_u32(tb[IFLA_GRE_OKEY]); + ipgre->ipgre_mask |= IPGRE_ATTR_OKEY; + } + + if (tb[IFLA_GRE_LOCAL]) { + ipgre->local = nla_get_u32(tb[IFLA_GRE_LOCAL]); + ipgre->ipgre_mask |= IPGRE_ATTR_LOCAL; + } + + if (tb[IFLA_GRE_REMOTE]) { + ipgre->remote = nla_get_u32(tb[IFLA_GRE_REMOTE]); + ipgre->ipgre_mask |= IPGRE_ATTR_REMOTE; + } + + if (tb[IFLA_GRE_TTL]) { + ipgre->ttl = nla_get_u8(tb[IFLA_GRE_TTL]); + ipgre->ipgre_mask |= IPGRE_ATTR_TTL; + } + + if (tb[IFLA_GRE_TOS]) { + ipgre->tos = nla_get_u8(tb[IFLA_GRE_TOS]); + ipgre->ipgre_mask |= IPGRE_ATTR_TOS; + } + + if (tb[IFLA_GRE_PMTUDISC]) { + ipgre->pmtudisc = nla_get_u8(tb[IFLA_GRE_PMTUDISC]); + ipgre->ipgre_mask |= IPGRE_ATTR_PMTUDISC; + } + + err = 0; + +errout: + return err; +} + +static int ipgre_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct ipgre_info *ipgre = link->l_info; + struct nlattr *data; + + data = nla_nest_start(msg, IFLA_INFO_DATA); + if (!data) + return -NLE_MSGSIZE; + + if (ipgre->ipgre_mask & IPGRE_ATTR_LINK) + NLA_PUT_U32(msg, IFLA_GRE_LINK, ipgre->link); + + if (ipgre->ipgre_mask & IFLA_GRE_IFLAGS) + NLA_PUT_U16(msg, IFLA_GRE_IFLAGS, ipgre->iflags); + + if (ipgre->ipgre_mask & IFLA_GRE_OFLAGS) + NLA_PUT_U16(msg, IFLA_GRE_OFLAGS, ipgre->oflags); + + if (ipgre->ipgre_mask & IPGRE_ATTR_IKEY) + NLA_PUT_U32(msg, IFLA_GRE_IKEY, ipgre->ikey); + + if (ipgre->ipgre_mask & IPGRE_ATTR_OKEY) + NLA_PUT_U32(msg, IFLA_GRE_OKEY, ipgre->okey); + + if (ipgre->ipgre_mask & IPGRE_ATTR_LOCAL) + NLA_PUT_U32(msg, IFLA_GRE_LOCAL, ipgre->local); + + if (ipgre->ipgre_mask & IPGRE_ATTR_REMOTE) + NLA_PUT_U32(msg, IFLA_GRE_REMOTE, ipgre->remote); + + if (ipgre->ipgre_mask & IPGRE_ATTR_TTL) + NLA_PUT_U8(msg, IFLA_GRE_TTL, ipgre->ttl); + + if (ipgre->ipgre_mask & IPGRE_ATTR_TOS) + NLA_PUT_U8(msg, IFLA_GRE_TOS, ipgre->tos); + + if (ipgre->ipgre_mask & IPGRE_ATTR_PMTUDISC) + NLA_PUT_U8(msg, IFLA_GRE_PMTUDISC, ipgre->pmtudisc); + + nla_nest_end(msg, data); + +nla_put_failure: + + return 0; +} + +static void ipgre_free(struct rtnl_link *link) +{ + struct ipgre_info *ipgre = link->l_info; + + free(ipgre); + link->l_info = NULL; +} + +static void ipgre_dump_line(struct rtnl_link *link, struct nl_dump_params *p) +{ + nl_dump(p, "ipgre : %s", link->l_name); +} + +static void ipgre_dump_details(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct ipgre_info *ipgre = link->l_info; + char *name, addr[INET_ADDRSTRLEN]; + struct rtnl_link *parent; + + if (ipgre->ipgre_mask & IPGRE_ATTR_LINK) { + nl_dump(p, " link "); + + name = NULL; + parent = link_lookup(link->ce_cache, ipgre->link); + if (parent) + name = rtnl_link_get_name(parent); + + if (name) + nl_dump_line(p, "%s\n", name); + else + nl_dump_line(p, "%u\n", ipgre->link); + } + + if (ipgre->ipgre_mask & IPGRE_ATTR_IFLAGS) { + nl_dump(p, " iflags "); + nl_dump_line(p, "%x\n", ipgre->iflags); + } + + if (ipgre->ipgre_mask & IPGRE_ATTR_OFLAGS) { + nl_dump(p, " oflags "); + nl_dump_line(p, "%x\n", ipgre->oflags); + } + + if (ipgre->ipgre_mask & IPGRE_ATTR_IKEY) { + nl_dump(p, " ikey "); + nl_dump_line(p, "%x\n",ipgre->ikey); + } + + if (ipgre->ipgre_mask & IPGRE_ATTR_OKEY) { + nl_dump(p, " okey "); + nl_dump_line(p, "%x\n", ipgre->okey); + } + + if (ipgre->ipgre_mask & IPGRE_ATTR_LOCAL) { + nl_dump(p, " local "); + if(inet_ntop(AF_INET, &ipgre->local, addr, sizeof(addr))) + nl_dump_line(p, "%s\n", addr); + else + nl_dump_line(p, "%#x\n", ntohs(ipgre->local)); + } + + if (ipgre->ipgre_mask & IPGRE_ATTR_REMOTE) { + nl_dump(p, " remote "); + if(inet_ntop(AF_INET, &ipgre->remote, addr, sizeof(addr))) + nl_dump_line(p, "%s\n", addr); + else + nl_dump_line(p, "%#x\n", ntohs(ipgre->remote)); + } + + if (ipgre->ipgre_mask & IPGRE_ATTR_TTL) { + nl_dump(p, " ttl "); + nl_dump_line(p, "%u\n", ipgre->ttl); + } + + if (ipgre->ipgre_mask & IPGRE_ATTR_TOS) { + nl_dump(p, " tos "); + nl_dump_line(p, "%u\n", ipgre->tos); + } + + if (ipgre->ipgre_mask & IPGRE_ATTR_PMTUDISC) { + nl_dump(p, " pmtudisc "); + nl_dump_line(p, "enabled (%#x)\n", ipgre->pmtudisc); + } +} + +static int ipgre_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct ipgre_info *ipgre_dst, *ipgre_src = src->l_info; + int err; + + dst->l_info = NULL; + + err = rtnl_link_set_type(dst, "gre"); + if (err < 0) + return err; + + ipgre_dst = dst->l_info; + + if (!ipgre_dst || !ipgre_src) + BUG(); + + memcpy(ipgre_dst, ipgre_src, sizeof(struct ipgre_info)); + + return 0; +} + +static int ipgretap_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct ipgre_info *ipgre_dst, *ipgre_src = src->l_info; + int err; + + dst->l_info = NULL; + + err = rtnl_link_set_type(dst, "gretap"); + if (err < 0) + return err; + + ipgre_dst = dst->l_info; + + if (!ipgre_dst || !ipgre_src) + BUG(); + + memcpy(ipgre_dst, ipgre_src, sizeof(struct ipgre_info)); + + return 0; +} + +static struct rtnl_link_info_ops ipgre_info_ops = { + .io_name = "gre", + .io_alloc = ipgre_alloc, + .io_parse = ipgre_parse, + .io_dump = { + [NL_DUMP_LINE] = ipgre_dump_line, + [NL_DUMP_DETAILS] = ipgre_dump_details, + }, + .io_clone = ipgre_clone, + .io_put_attrs = ipgre_put_attrs, + .io_free = ipgre_free, +}; + +static struct rtnl_link_info_ops ipgretap_info_ops = { + .io_name = "gretap", + .io_alloc = ipgre_alloc, + .io_parse = ipgre_parse, + .io_dump = { + [NL_DUMP_LINE] = ipgre_dump_line, + [NL_DUMP_DETAILS] = ipgre_dump_details, + }, + .io_clone = ipgretap_clone, + .io_put_attrs = ipgre_put_attrs, + .io_free = ipgre_free, +}; + +#define IS_IPGRE_LINK_ASSERT(link) \ + if ((link)->l_info_ops != &ipgre_info_ops && \ + (link)->l_info_ops != &ipgretap_info_ops) { \ + APPBUG("Link is not a ipgre link. set type \"gre/gretap\" first.");\ + return -NLE_OPNOTSUPP; \ + } + +struct rtnl_link *rtnl_link_ipgre_alloc(void) +{ + struct rtnl_link *link; + int err; + + link = rtnl_link_alloc(); + if (!link) + return NULL; + + err = rtnl_link_set_type(link, "gre"); + if (err < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Check if link is a IPGRE link + * @arg link Link object + * + * @return True if link is a IPGRE link, otherwise 0 is returned. + */ +int rtnl_link_is_ipgre(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "gre"); +} + +/** + * Create a new IPGRE tunnel device + * @arg sock netlink socket + * @arg name name of the tunnel deviceL + * + * Creates a new ipip tunnel device in the kernel + * @return 0 on success or a negative error code + */ +int rtnl_link_ipgre_add(struct nl_sock *sk, const char *name) +{ + struct rtnl_link *link; + int err; + + link = rtnl_link_ipgre_alloc(); + if (!link) + return -NLE_NOMEM; + + if(name) + rtnl_link_set_name(link, name); + + err = rtnl_link_add(sk, link, NLM_F_CREATE); + rtnl_link_put(link); + + return err; +} + +struct rtnl_link *rtnl_link_ipgretap_alloc(void) +{ + struct rtnl_link *link; + int err; + + link = rtnl_link_alloc(); + if (!link) + return NULL; + + err = rtnl_link_set_type(link, "gretap"); + if (err < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Check if link is a IPGRETAP link + * @arg link Link object + * + * @return True if link is a IPGRETAP link, otherwise 0 is returned. + */ +int rtnl_link_is_ipgretap(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "gretap"); +} +/** + * Create a new IPGRETAP tunnel device + * @arg sock netlink socket + * @arg name name of the tunnel deviceL + * + * Creates a new IPGRETAP tunnel device in the kernel + * @return 0 on success or a negative error code + */ +int rtnl_link_ipgretap_add(struct nl_sock *sk, const char *name) +{ + struct rtnl_link *link; + int err; + + link = rtnl_link_ipgretap_alloc(); + if (!link) + return -NLE_NOMEM; + + if(name) + rtnl_link_set_name(link, name); + + err = rtnl_link_add(sk, link, NLM_F_CREATE); + rtnl_link_put(link); + + return err; +} + +/** + * Set IPGRE tunnel interface index + * @arg link Link object + * @arg index interface index + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipgre_set_link(struct rtnl_link *link, uint32_t index) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + ipgre->link = index; + ipgre->ipgre_mask |= IPGRE_ATTR_LINK; + + return 0; +} + +/** + * Get IPGRE tunnel interface index + * @arg link Link object + * + * @return interface index + */ +uint32_t rtnl_link_ipgre_get_link(struct rtnl_link *link) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + return ipgre->link; +} + +/** + * Set IPGRE tunnel set iflags + * @arg link Link object + * @arg iflags gre iflags + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipgre_set_iflags(struct rtnl_link *link, uint16_t iflags) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + ipgre->iflags = iflags; + ipgre->ipgre_mask |= IPGRE_ATTR_IFLAGS; + + return 0; +} + +/** + * Get IPGRE tunnel iflags + * @arg link Link object + * + * @return iflags + */ +uint16_t rtnl_link_ipgre_get_iflags(struct rtnl_link *link) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + return ipgre->iflags; +} + +/** + * Set IPGRE tunnel set oflags + * @arg link Link object + * @arg iflags gre oflags + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipgre_set_oflags(struct rtnl_link *link, uint16_t oflags) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + ipgre->oflags = oflags; + ipgre->ipgre_mask |= IPGRE_ATTR_OFLAGS; + + return 0; +} + +/** + * Get IPGRE tunnel oflags + * @arg link Link object + * + * @return oflags + */ +uint16_t rtnl_link_ipgre_get_oflags(struct rtnl_link *link) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + return ipgre->oflags; +} + +/** + * Set IPGRE tunnel set ikey + * @arg link Link object + * @arg ikey gre ikey + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipgre_set_ikey(struct rtnl_link *link, uint32_t ikey) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + ipgre->ikey = ikey; + ipgre->ipgre_mask |= IPGRE_ATTR_IKEY; + + return 0; +} + +/** + * Get IPGRE tunnel ikey + * @arg link Link object + * + * @return ikey + */ +uint32_t rtnl_link_ipgre_get_ikey(struct rtnl_link *link) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + return ipgre->ikey; +} + +/** + * Set IPGRE tunnel set okey + * @arg link Link object + * @arg okey gre okey + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipgre_set_okey(struct rtnl_link *link, uint32_t okey) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + ipgre->okey = okey; + ipgre->ipgre_mask |= IPGRE_ATTR_OKEY; + + return 0; +} + +/** + * Get IPGRE tunnel okey + * @arg link Link object + * + * @return okey value + */ +uint32_t rtnl_link_ipgre_get_okey(struct rtnl_link *link) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + return ipgre->okey; +} + +/** + * Set IPGRE tunnel local address + * @arg link Link object + * @arg addr local address + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipgre_set_local(struct rtnl_link *link, uint32_t addr) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + ipgre->local = addr; + ipgre->ipgre_mask |= IPGRE_ATTR_LOCAL; + + return 0; +} + +/** + * Get IPGRE tunnel local address + * @arg link Link object + * + * @return local address + */ +uint32_t rtnl_link_ipgre_get_local(struct rtnl_link *link) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + return ipgre->local; +} + +/** + * Set IPGRE tunnel remote address + * @arg link Link object + * @arg remote remote address + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipgre_set_remote(struct rtnl_link *link, uint32_t remote) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + ipgre->remote = remote; + ipgre->ipgre_mask |= IPGRE_ATTR_REMOTE; + + return 0; +} + +/** + * Get IPGRE tunnel remote address + * @arg link Link object + * + * @return remote address on success or a negative error code + */ +uint32_t rtnl_link_ipgre_get_remote(struct rtnl_link *link) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + return ipgre->remote; +} + +/** + * Set IPGRE tunnel ttl + * @arg link Link object + * @arg ttl tunnel ttl + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipgre_set_ttl(struct rtnl_link *link, uint8_t ttl) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + ipgre->ttl = ttl; + ipgre->ipgre_mask |= IPGRE_ATTR_TTL; + + return 0; +} + +/** + * Set IPGRE tunnel ttl + * @arg link Link object + * + * @return ttl value + */ +uint8_t rtnl_link_ipgre_get_ttl(struct rtnl_link *link) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + return ipgre->ttl; +} + +/** + * Set IPGRE tunnel tos + * @arg link Link object + * @arg tos tunnel tos + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipgre_set_tos(struct rtnl_link *link, uint8_t tos) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + ipgre->tos = tos; + ipgre->ipgre_mask |= IPGRE_ATTR_TOS; + + return 0; +} + +/** + * Get IPGRE tunnel tos + * @arg link Link object + * + * @return tos value + */ +uint8_t rtnl_link_ipgre_get_tos(struct rtnl_link *link) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + return ipgre->tos; +} + +/** + * Set IPGRE tunnel path MTU discovery + * @arg link Link object + * @arg pmtudisc path MTU discovery + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipgre_set_pmtudisc(struct rtnl_link *link, uint8_t pmtudisc) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + ipgre->pmtudisc = pmtudisc; + ipgre->ipgre_mask |= IPGRE_ATTR_PMTUDISC; + + return 0; +} + +/** + * Get IPGRE path MTU discovery + * @arg link Link object + * + * @return pmtudisc value + */ +uint8_t rtnl_link_ipgre_get_pmtudisc(struct rtnl_link *link) +{ + struct ipgre_info *ipgre = link->l_info; + + IS_IPGRE_LINK_ASSERT(link); + + return ipgre->pmtudisc; +} + +/* Function prototype for ABI-preserving wrapper (not in public header) to avoid + * GCC warning about missing prototype. */ +uint8_t rtnl_link_get_pmtudisc(struct rtnl_link *link); + +uint8_t rtnl_link_get_pmtudisc(struct rtnl_link *link) +{ + /* rtnl_link_ipgre_get_pmtudisc() was wrongly named. Keep this + * to preserve ABI. */ + return rtnl_link_ipgre_get_pmtudisc (link); +} + +static void __init ipgre_init(void) +{ + rtnl_link_register_info(&ipgre_info_ops); + rtnl_link_register_info(&ipgretap_info_ops); +} + +static void __exit ipgre_exit(void) +{ + rtnl_link_unregister_info(&ipgre_info_ops); + rtnl_link_unregister_info(&ipgretap_info_ops); +} diff --git a/libnl/lib/route/link/ipip.c b/libnl/lib/route/link/ipip.c new file mode 100644 index 0000000..111c244 --- /dev/null +++ b/libnl/lib/route/link/ipip.c @@ -0,0 +1,535 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2014 Susant Sahani + */ + +/** + * @ingroup link + * @defgroup ipip IPIP + * ipip link module + * + * @details + * \b Link Type Name: "ipip" + * + * @route_doc{link_ipip, IPIP Documentation} + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IPIP_ATTR_LINK (1 << 0) +#define IPIP_ATTR_LOCAL (1 << 1) +#define IPIP_ATTR_REMOTE (1 << 2) +#define IPIP_ATTR_TTL (1 << 3) +#define IPIP_ATTR_TOS (1 << 4) +#define IPIP_ATTR_PMTUDISC (1 << 5) + +struct ipip_info +{ + uint8_t ttl; + uint8_t tos; + uint8_t pmtudisc; + uint32_t link; + uint32_t local; + uint32_t remote; + uint32_t ipip_mask; +}; + +static struct nla_policy ipip_policy[IFLA_IPTUN_MAX + 1] = { + [IFLA_IPTUN_LINK] = { .type = NLA_U32 }, + [IFLA_IPTUN_LOCAL] = { .type = NLA_U32 }, + [IFLA_IPTUN_REMOTE] = { .type = NLA_U32 }, + [IFLA_IPTUN_TTL] = { .type = NLA_U8 }, + [IFLA_IPTUN_TOS] = { .type = NLA_U8 }, + [IFLA_IPTUN_PMTUDISC] = { .type = NLA_U8 }, +}; + +static int ipip_alloc(struct rtnl_link *link) +{ + struct ipip_info *ipip; + + if (link->l_info) + memset(link->l_info, 0, sizeof(*ipip)); + else { + ipip = calloc(1, sizeof(*ipip)); + if (!ipip) + return -NLE_NOMEM; + + link->l_info = ipip; + } + + return 0; +} + +static int ipip_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_IPTUN_MAX + 1]; + struct ipip_info *ipip; + int err; + + NL_DBG(3, "Parsing IPIP link info\n"); + + err = nla_parse_nested(tb, IFLA_IPTUN_MAX, data, ipip_policy); + if (err < 0) + goto errout; + + err = ipip_alloc(link); + if (err < 0) + goto errout; + + ipip = link->l_info; + + if (tb[IFLA_IPTUN_LINK]) { + ipip->link = nla_get_u32(tb[IFLA_IPTUN_LINK]); + ipip->ipip_mask |= IPIP_ATTR_LINK; + } + + if (tb[IFLA_IPTUN_LOCAL]) { + ipip->local = nla_get_u32(tb[IFLA_IPTUN_LOCAL]); + ipip->ipip_mask |= IPIP_ATTR_LOCAL; + } + + if (tb[IFLA_IPTUN_REMOTE]) { + ipip->remote = nla_get_u32(tb[IFLA_IPTUN_REMOTE]); + ipip->ipip_mask |= IPIP_ATTR_REMOTE; + } + + if (tb[IFLA_IPTUN_TTL]) { + ipip->ttl = nla_get_u8(tb[IFLA_IPTUN_TTL]); + ipip->ipip_mask |= IPIP_ATTR_TTL; + } + + if (tb[IFLA_IPTUN_TOS]) { + ipip->tos = nla_get_u8(tb[IFLA_IPTUN_TOS]); + ipip->ipip_mask |= IPIP_ATTR_TOS; + } + + if (tb[IFLA_IPTUN_PMTUDISC]) { + ipip->pmtudisc = nla_get_u8(tb[IFLA_IPTUN_PMTUDISC]); + ipip->ipip_mask |= IPIP_ATTR_PMTUDISC; + } + + err = 0; + +errout: + return err; +} + +static int ipip_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct ipip_info *ipip = link->l_info; + struct nlattr *data; + + data = nla_nest_start(msg, IFLA_INFO_DATA); + if (!data) + return -NLE_MSGSIZE; + + if (ipip->ipip_mask & IPIP_ATTR_LINK) + NLA_PUT_U32(msg, IFLA_IPTUN_LINK, ipip->link); + + if (ipip->ipip_mask & IPIP_ATTR_LOCAL) + NLA_PUT_U32(msg, IFLA_IPTUN_LOCAL, ipip->local); + + if (ipip->ipip_mask & IPIP_ATTR_REMOTE) + NLA_PUT_U32(msg, IFLA_IPTUN_REMOTE, ipip->remote); + + if (ipip->ipip_mask & IPIP_ATTR_TTL) + NLA_PUT_U8(msg, IFLA_IPTUN_TTL, ipip->ttl); + + if (ipip->ipip_mask & IPIP_ATTR_TOS) + NLA_PUT_U8(msg, IFLA_IPTUN_TOS, ipip->tos); + + if (ipip->ipip_mask & IPIP_ATTR_PMTUDISC) + NLA_PUT_U8(msg, IFLA_IPTUN_PMTUDISC, ipip->pmtudisc); + + nla_nest_end(msg, data); + +nla_put_failure: + return 0; +} + +static void ipip_free(struct rtnl_link *link) +{ + struct ipip_info *ipip = link->l_info; + + free(ipip); + link->l_info = NULL; +} + +static void ipip_dump_line(struct rtnl_link *link, struct nl_dump_params *p) +{ + nl_dump(p, "ipip : %s", link->l_name); +} + +static void ipip_dump_details(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct ipip_info *ipip = link->l_info; + char *name, addr[INET_ADDRSTRLEN]; + struct rtnl_link *parent; + + if (ipip->ipip_mask & IPIP_ATTR_LINK) { + nl_dump(p, " link "); + + name = NULL; + parent = link_lookup(link->ce_cache, ipip->link); + if (parent) + name = rtnl_link_get_name(parent); + + if (name) + nl_dump_line(p, "%s\n", name); + else + nl_dump_line(p, "%u\n", ipip->link); + } + + if (ipip->ipip_mask & IPIP_ATTR_LOCAL) { + nl_dump(p, " local "); + if(inet_ntop(AF_INET, &ipip->local, addr, sizeof(addr))) + nl_dump_line(p, "%s\n", addr); + else + nl_dump_line(p, "%#x\n", ntohs(ipip->local)); + } + + if (ipip->ipip_mask & IPIP_ATTR_REMOTE) { + nl_dump(p, " remote "); + if(inet_ntop(AF_INET, &ipip->remote, addr, sizeof(addr))) + nl_dump_line(p, "%s\n", addr); + else + nl_dump_line(p, "%#x\n", ntohs(ipip->remote)); + } + + if (ipip->ipip_mask & IPIP_ATTR_TTL) { + nl_dump(p, " ttl "); + nl_dump_line(p, "%u\n", ipip->ttl); + } + + if (ipip->ipip_mask & IPIP_ATTR_TOS) { + nl_dump(p, " tos "); + nl_dump_line(p, "%u\n", ipip->tos); + } + + if (ipip->ipip_mask & IPIP_ATTR_PMTUDISC) { + nl_dump(p, " pmtudisc "); + nl_dump_line(p, "enabled (%#x)\n", ipip->pmtudisc); + } +} + +static int ipip_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct ipip_info *ipip_dst, *ipip_src = src->l_info; + int err; + + dst->l_info = NULL; + + err = rtnl_link_set_type(dst, "ipip"); + if (err < 0) + return err; + + ipip_dst = dst->l_info; + + if (!ipip_dst || !ipip_src) + BUG(); + + memcpy(ipip_dst, ipip_src, sizeof(struct ipip_info)); + + return 0; +} + +static struct rtnl_link_info_ops ipip_info_ops = { + .io_name = "ipip", + .io_alloc = ipip_alloc, + .io_parse = ipip_parse, + .io_dump = { + [NL_DUMP_LINE] = ipip_dump_line, + [NL_DUMP_DETAILS] = ipip_dump_details, + }, + .io_clone = ipip_clone, + .io_put_attrs = ipip_put_attrs, + .io_free = ipip_free, +}; + +#define IS_IPIP_LINK_ASSERT(link) \ + if ((link)->l_info_ops != &ipip_info_ops) { \ + APPBUG("Link is not a ipip link. set type \"ipip\" first."); \ + return -NLE_OPNOTSUPP; \ + } + +struct rtnl_link *rtnl_link_ipip_alloc(void) +{ + struct rtnl_link *link; + int err; + + link = rtnl_link_alloc(); + if (!link) + return NULL; + + err = rtnl_link_set_type(link, "ipip"); + if (err < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Check if link is a IPIP link + * @arg link Link object + * + * @return True if link is a IPIP link, otherwise false is returned. + */ +int rtnl_link_is_ipip(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "ipip"); +} + +/** + * Create a new ipip tunnel device + * @arg sock netlink socket + * @arg name name of the tunnel deviceL + * + * Creates a new ipip tunnel device in the kernel + * @return 0 on success or a negative error code + */ +int rtnl_link_ipip_add(struct nl_sock *sk, const char *name) +{ + struct rtnl_link *link; + int err; + + link = rtnl_link_ipip_alloc(); + if (!link) + return -NLE_NOMEM; + + if(name) + rtnl_link_set_name(link, name); + + err = rtnl_link_add(sk, link, NLM_F_CREATE); + rtnl_link_put(link); + + return err; +} + +/** + * Set IPIP tunnel interface index + * @arg link Link object + * @arg index interface index + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipip_set_link(struct rtnl_link *link, uint32_t index) +{ + struct ipip_info *ipip = link->l_info; + + IS_IPIP_LINK_ASSERT(link); + + ipip->link = index; + ipip->ipip_mask |= IPIP_ATTR_LINK; + + return 0; +} + +/** + * Get IPIP tunnel interface index + * @arg link Link object + * + * @return interface index value + */ +uint32_t rtnl_link_ipip_get_link(struct rtnl_link *link) +{ + struct ipip_info *ipip = link->l_info; + + IS_IPIP_LINK_ASSERT(link); + + return ipip->link; +} + +/** + * Set IPIP tunnel local address + * @arg link Link object + * @arg addr local address + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipip_set_local(struct rtnl_link *link, uint32_t addr) +{ + struct ipip_info *ipip = link->l_info; + + IS_IPIP_LINK_ASSERT(link); + + ipip->local = addr; + ipip->ipip_mask |= IPIP_ATTR_LOCAL; + + return 0; +} + +/** + * Get IPIP tunnel local address + * @arg link Link object + * + * @return local address value + */ +uint32_t rtnl_link_ipip_get_local(struct rtnl_link *link) +{ + struct ipip_info *ipip = link->l_info; + + IS_IPIP_LINK_ASSERT(link); + + return ipip->local; +} + +/** + * Set IPIP tunnel remote address + * @arg link Link object + * @arg remote remote address + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipip_set_remote(struct rtnl_link *link, uint32_t addr) +{ + struct ipip_info *ipip = link->l_info; + + IS_IPIP_LINK_ASSERT(link); + + ipip->remote = addr; + ipip->ipip_mask |= IPIP_ATTR_REMOTE; + + return 0; +} + +/** + * Get IPIP tunnel remote address + * @arg link Link object + * + * @return remote address + */ +uint32_t rtnl_link_ipip_get_remote(struct rtnl_link *link) +{ + struct ipip_info *ipip = link->l_info; + + IS_IPIP_LINK_ASSERT(link); + + return ipip->remote; +} + +/** + * Set IPIP tunnel ttl + * @arg link Link object + * @arg ttl tunnel ttl + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipip_set_ttl(struct rtnl_link *link, uint8_t ttl) +{ + struct ipip_info *ipip = link->l_info; + + IS_IPIP_LINK_ASSERT(link); + + ipip->ttl = ttl; + ipip->ipip_mask |= IPIP_ATTR_TTL; + + return 0; +} + +/** + * Get IPIP tunnel ttl + * @arg link Link object + * + * @return ttl value + */ +uint8_t rtnl_link_ipip_get_ttl(struct rtnl_link *link) +{ + struct ipip_info *ipip = link->l_info; + + IS_IPIP_LINK_ASSERT(link); + + return ipip->ttl; +} + +/** + * Set IPIP tunnel tos + * @arg link Link object + * @arg tos tunnel tos + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipip_set_tos(struct rtnl_link *link, uint8_t tos) +{ + struct ipip_info *ipip = link->l_info; + + IS_IPIP_LINK_ASSERT(link); + + ipip->tos = tos; + ipip->ipip_mask |= IPIP_ATTR_TOS; + + return 0; +} + +/** + * Get IPIP tunnel tos + * @arg link Link object + * + * @return tos value + */ +uint8_t rtnl_link_ipip_get_tos(struct rtnl_link *link) +{ + struct ipip_info *ipip = link->l_info; + + IS_IPIP_LINK_ASSERT(link); + + return ipip->tos; +} + +/** + * Set IPIP tunnel path MTU discovery + * @arg link Link object + * @arg pmtudisc path MTU discovery + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipip_set_pmtudisc(struct rtnl_link *link, uint8_t pmtudisc) +{ + struct ipip_info *ipip = link->l_info; + + IS_IPIP_LINK_ASSERT(link); + + ipip->pmtudisc = pmtudisc; + ipip->ipip_mask |= IPIP_ATTR_PMTUDISC; + + return 0; +} + +/** + * Get IPIP path MTU discovery + * @arg link Link object + * + * @return pmtudisc value + */ +uint8_t rtnl_link_ipip_get_pmtudisc(struct rtnl_link *link) +{ + struct ipip_info *ipip = link->l_info; + + IS_IPIP_LINK_ASSERT(link); + + return ipip->pmtudisc; +} + +static void __init ipip_init(void) +{ + rtnl_link_register_info(&ipip_info_ops); +} + +static void __exit ipip_exit(void) +{ + rtnl_link_unregister_info(&ipip_info_ops); +} diff --git a/libnl/lib/route/link/ipvlan.c b/libnl/lib/route/link/ipvlan.c new file mode 100644 index 0000000..e9a5b0c --- /dev/null +++ b/libnl/lib/route/link/ipvlan.c @@ -0,0 +1,273 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2015 Cong Wang + */ + +/** + * @ingroup link + * @defgroup ipvlan IPVLAN + * IP-based Virtual LAN link module + * + * @details + * \b Link Type Name: "ipvlan" + * + * @route_doc{link_ipvlan, IPVLAN Documentation} + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/** @cond SKIP */ +#define IPVLAN_HAS_MODE (1<<0) + +struct ipvlan_info +{ + uint16_t ipi_mode; + uint32_t ipi_mask; +}; + +/** @endcond */ + +static struct nla_policy ipvlan_policy[IFLA_IPVLAN_MAX+1] = { + [IFLA_IPVLAN_MODE] = { .type = NLA_U16 }, +}; + +static int ipvlan_alloc(struct rtnl_link *link) +{ + struct ipvlan_info *ipi; + + if (link->l_info) + memset(link->l_info, 0, sizeof(*ipi)); + else { + if ((ipi = calloc(1, sizeof(*ipi))) == NULL) + return -NLE_NOMEM; + + link->l_info = ipi; + } + + return 0; +} + +static int ipvlan_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_IPVLAN_MAX+1]; + struct ipvlan_info *ipi; + int err; + + NL_DBG(3, "Parsing IPVLAN link info\n"); + + if ((err = nla_parse_nested(tb, IFLA_IPVLAN_MAX, data, ipvlan_policy)) < 0) + goto errout; + + if ((err = ipvlan_alloc(link)) < 0) + goto errout; + + ipi = link->l_info; + + if (tb[IFLA_IPVLAN_MODE]) { + ipi->ipi_mode = nla_get_u16(tb[IFLA_IPVLAN_MODE]); + ipi->ipi_mask |= IPVLAN_HAS_MODE; + } + + err = 0; +errout: + return err; +} + +static void ipvlan_free(struct rtnl_link *link) +{ + free(link->l_info); + link->l_info = NULL; +} + +static void ipvlan_dump(struct rtnl_link *link, struct nl_dump_params *p) +{ + char buf[64]; + struct ipvlan_info *ipi = link->l_info; + + if (ipi->ipi_mask & IPVLAN_HAS_MODE) { + rtnl_link_ipvlan_mode2str(ipi->ipi_mode, buf, sizeof(buf)); + nl_dump(p, "ipvlan-mode %s", buf); + } +} + +static int ipvlan_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct ipvlan_info *vdst, *vsrc = src->l_info; + int err; + + dst->l_info = NULL; + if ((err = rtnl_link_set_type(dst, "ipvlan")) < 0) + return err; + vdst = dst->l_info; + + if (!vdst || !vsrc) + return -NLE_NOMEM; + + memcpy(vdst, vsrc, sizeof(struct ipvlan_info)); + + return 0; +} + +static int ipvlan_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct ipvlan_info *ipi = link->l_info; + struct nlattr *data; + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + return -NLE_MSGSIZE; + + if (ipi->ipi_mask & IPVLAN_HAS_MODE) + NLA_PUT_U16(msg, IFLA_IPVLAN_MODE, ipi->ipi_mode); + + nla_nest_end(msg, data); + +nla_put_failure: + + return 0; +} + +static struct rtnl_link_info_ops ipvlan_info_ops = { + .io_name = "ipvlan", + .io_alloc = ipvlan_alloc, + .io_parse = ipvlan_parse, + .io_dump = { + [NL_DUMP_LINE] = ipvlan_dump, + [NL_DUMP_DETAILS] = ipvlan_dump, + }, + .io_clone = ipvlan_clone, + .io_put_attrs = ipvlan_put_attrs, + .io_free = ipvlan_free, +}; + +/** @cond SKIP */ +#define IS_IPVLAN_LINK_ASSERT(link) \ + if ((link)->l_info_ops != &ipvlan_info_ops) { \ + APPBUG("Link is not a ipvlan link. set type \"ipvlan\" first."); \ + return -NLE_OPNOTSUPP; \ + } +/** @endcond */ + +/** + * @name IPVLAN Object + * @{ + */ + +/** + * Allocate link object of type IPVLAN + * + * @return Allocated link object or NULL. + */ +struct rtnl_link *rtnl_link_ipvlan_alloc(void) +{ + struct rtnl_link *link; + int err; + + if (!(link = rtnl_link_alloc())) + return NULL; + + if ((err = rtnl_link_set_type(link, "ipvlan")) < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Check if link is a IPVLAN link + * @arg link Link object + * + * @return True if link is a IPVLAN link, otherwise false is returned. + */ +int rtnl_link_is_ipvlan(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "ipvlan"); +} + +/** + * Set IPVLAN MODE + * @arg link Link object + * @arg mode IPVLAN mode + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipvlan_set_mode(struct rtnl_link *link, uint16_t mode) +{ + struct ipvlan_info *ipi = link->l_info; + + IS_IPVLAN_LINK_ASSERT(link); + + ipi->ipi_mode = mode; + ipi->ipi_mask |= IPVLAN_HAS_MODE; + + return 0; +} + +/** + * Get IPVLAN Mode + * @arg link Link object + * @arg out_mode on success, return the mode + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_ipvlan_get_mode(struct rtnl_link *link, uint16_t *out_mode) +{ + struct ipvlan_info *ipi = link->l_info; + + IS_IPVLAN_LINK_ASSERT(link); + + if (!(ipi->ipi_mask & IPVLAN_HAS_MODE)) + return -NLE_INVAL; + *out_mode = ipi->ipi_mode; + return 0; +} + +/** @} */ + +static const struct trans_tbl ipvlan_modes[] = { + __ADD(IPVLAN_MODE_L2, l2), + __ADD(IPVLAN_MODE_L3, l3), +}; + +/** + * @name Mode Translation + * @{ + */ + +char *rtnl_link_ipvlan_mode2str(int mode, char *buf, size_t len) +{ + return __type2str(mode, buf, len, ipvlan_modes, ARRAY_SIZE(ipvlan_modes)); +} + +int rtnl_link_ipvlan_str2mode(const char *name) +{ + return __str2type(name, ipvlan_modes, ARRAY_SIZE(ipvlan_modes)); +} + +/** @} */ + +static void __init ipvlan_init(void) +{ + rtnl_link_register_info(&ipvlan_info_ops); +} + +static void __exit ipvlan_exit(void) +{ + rtnl_link_unregister_info(&ipvlan_info_ops); +} + +/** @} */ diff --git a/libnl/lib/route/link/ipvti.c b/libnl/lib/route/link/ipvti.c new file mode 100644 index 0000000..64b6757 --- /dev/null +++ b/libnl/lib/route/link/ipvti.c @@ -0,0 +1,484 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2014 Susant Sahani + */ + +/** + * @ingroup link + * @defgroup ipvti IPVTI + * ipvti link module + * + * @details + * \b Link Type Name: "ipvti" + * + * @route_doc{link_ipvti, IPVTI Documentation} + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IPVTI_ATTR_LINK (1 << 0) +#define IPVTI_ATTR_IKEY (1 << 1) +#define IPVTI_ATTR_OKEY (1 << 2) +#define IPVTI_ATTR_LOCAL (1 << 3) +#define IPVTI_ATTR_REMOTE (1 << 4) + +struct ipvti_info +{ + uint32_t link; + uint32_t ikey; + uint32_t okey; + uint32_t local; + uint32_t remote; + uint32_t ipvti_mask; +}; + +static struct nla_policy ipvti_policy[IFLA_VTI_MAX + 1] = { + [IFLA_VTI_LINK] = { .type = NLA_U32 }, + [IFLA_VTI_IKEY] = { .type = NLA_U32 }, + [IFLA_VTI_OKEY] = { .type = NLA_U32 }, + [IFLA_VTI_LOCAL] = { .type = NLA_U32 }, + [IFLA_VTI_REMOTE] = { .type = NLA_U32 }, +}; + +static int ipvti_alloc(struct rtnl_link *link) +{ + struct ipvti_info *ipvti; + + if (link->l_info) + memset(link->l_info, 0, sizeof(*ipvti)); + else { + ipvti = calloc(1, sizeof(*ipvti)); + if (!ipvti) + return -NLE_NOMEM; + + link->l_info = ipvti; + } + + return 0; +} + +static int ipvti_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_VTI_MAX + 1]; + struct ipvti_info *ipvti; + int err; + + NL_DBG(3, "Parsing IPVTI link info\n"); + + err = nla_parse_nested(tb, IFLA_VTI_MAX, data, ipvti_policy); + if (err < 0) + goto errout; + + err = ipvti_alloc(link); + if (err < 0) + goto errout; + + ipvti = link->l_info; + + if (tb[IFLA_VTI_LINK]) { + ipvti->link = nla_get_u32(tb[IFLA_VTI_LINK]); + ipvti->ipvti_mask |= IPVTI_ATTR_LINK; + } + + if (tb[IFLA_VTI_IKEY]) { + ipvti->ikey = nla_get_u32(tb[IFLA_VTI_IKEY]); + ipvti->ipvti_mask |= IPVTI_ATTR_IKEY; + } + + if (tb[IFLA_VTI_OKEY]) { + ipvti->okey = nla_get_u32(tb[IFLA_VTI_OKEY]); + ipvti->ipvti_mask |= IPVTI_ATTR_OKEY; + } + + if (tb[IFLA_VTI_LOCAL]) { + ipvti->local = nla_get_u32(tb[IFLA_VTI_LOCAL]); + ipvti->ipvti_mask |= IPVTI_ATTR_LOCAL; + } + + if (tb[IFLA_VTI_REMOTE]) { + ipvti->remote = nla_get_u32(tb[IFLA_VTI_REMOTE]); + ipvti->ipvti_mask |= IPVTI_ATTR_REMOTE; + } + + err = 0; + +errout: + return err; +} + +static int ipvti_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct ipvti_info *ipvti = link->l_info; + struct nlattr *data; + + data = nla_nest_start(msg, IFLA_INFO_DATA); + if (!data) + return -NLE_MSGSIZE; + + if (ipvti->ipvti_mask & IPVTI_ATTR_LINK) + NLA_PUT_U32(msg, IFLA_VTI_LINK, ipvti->link); + + if (ipvti->ipvti_mask & IPVTI_ATTR_IKEY) + NLA_PUT_U32(msg, IFLA_VTI_IKEY, ipvti->ikey); + + if (ipvti->ipvti_mask & IFLA_VTI_IKEY) + NLA_PUT_U32(msg, IFLA_VTI_OKEY, ipvti->okey); + + if (ipvti->ipvti_mask & IPVTI_ATTR_LOCAL) + NLA_PUT_U32(msg, IFLA_VTI_LOCAL, ipvti->local); + + if (ipvti->ipvti_mask & IPVTI_ATTR_REMOTE) + NLA_PUT_U32(msg, IFLA_VTI_REMOTE, ipvti->remote); + + nla_nest_end(msg, data); + +nla_put_failure: + + return 0; +} + +static void ipvti_free(struct rtnl_link *link) +{ + struct ipvti_info *ipvti = link->l_info; + + free(ipvti); + link->l_info = NULL; +} + +static void ipvti_dump_line(struct rtnl_link *link, struct nl_dump_params *p) +{ + nl_dump(p, "ipvti : %s", link->l_name); +} + +static void ipvti_dump_details(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct ipvti_info *ipvti = link->l_info; + char *name, addr[INET_ADDRSTRLEN]; + struct rtnl_link *parent; + + if (ipvti->ipvti_mask & IPVTI_ATTR_LINK) { + nl_dump(p, " link "); + + name = NULL; + parent = link_lookup(link->ce_cache, ipvti->link); + if (parent) + name = rtnl_link_get_name(parent); + + if (name) + nl_dump_line(p, "%s\n", name); + else + nl_dump_line(p, "%u\n", ipvti->link); + } + + if (ipvti->ipvti_mask & IPVTI_ATTR_IKEY) { + nl_dump(p, " ikey "); + nl_dump_line(p, "%x\n",ipvti->ikey); + } + + if (ipvti->ipvti_mask & IPVTI_ATTR_OKEY) { + nl_dump(p, " okey "); + nl_dump_line(p, "%x\n", ipvti->okey); + } + + if (ipvti->ipvti_mask & IPVTI_ATTR_LOCAL) { + nl_dump(p, " local "); + if(inet_ntop(AF_INET, &ipvti->local, addr, sizeof(addr))) + nl_dump_line(p, "%s\n", addr); + else + nl_dump_line(p, "%#x\n", ntohs(ipvti->local)); + } + + if (ipvti->ipvti_mask & IPVTI_ATTR_REMOTE) { + nl_dump(p, " remote "); + if(inet_ntop(AF_INET, &ipvti->remote, addr, sizeof(addr))) + nl_dump_line(p, "%s\n", addr); + else + nl_dump_line(p, "%#x\n", ntohs(ipvti->remote)); + } +} + +static int ipvti_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct ipvti_info *ipvti_dst, *ipvti_src = src->l_info; + int err; + + dst->l_info = NULL; + + err = rtnl_link_set_type(dst, "vti"); + if (err < 0) + return err; + + ipvti_dst = dst->l_info; + + if (!ipvti_dst || !ipvti_src) + BUG(); + + memcpy(ipvti_dst, ipvti_src, sizeof(struct ipvti_info)); + + return 0; +} + +static struct rtnl_link_info_ops ipvti_info_ops = { + .io_name = "vti", + .io_alloc = ipvti_alloc, + .io_parse = ipvti_parse, + .io_dump = { + [NL_DUMP_LINE] = ipvti_dump_line, + [NL_DUMP_DETAILS] = ipvti_dump_details, + }, + .io_clone = ipvti_clone, + .io_put_attrs = ipvti_put_attrs, + .io_free = ipvti_free, +}; + +#define IS_IPVTI_LINK_ASSERT(link) \ + if ((link)->l_info_ops != &ipvti_info_ops) { \ + APPBUG("Link is not a ipvti link. set type \vti\" first."); \ + return -NLE_OPNOTSUPP; \ + } + +struct rtnl_link *rtnl_link_ipvti_alloc(void) +{ + struct rtnl_link *link; + int err; + + link = rtnl_link_alloc(); + if (!link) + return NULL; + + err = rtnl_link_set_type(link, "vti"); + if (err < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Check if link is a IPVTI link + * @arg link Link object + * + * @return True if link is a IPVTI link, otherwise 0 is returned. + */ +int rtnl_link_is_ipvti(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "vti"); +} +/** + * Create a new ipvti tunnel device + * @arg sock netlink socket + * @arg name name of the tunnel deviceL + * + * Creates a new ipvti tunnel device in the kernel + * @return 0 on success or a negative error code + */ +int rtnl_link_ipvti_add(struct nl_sock *sk, const char *name) +{ + struct rtnl_link *link; + int err; + + link = rtnl_link_ipvti_alloc(); + if (!link) + return -NLE_NOMEM; + + if(name) + rtnl_link_set_name(link, name); + + err = rtnl_link_add(sk, link, NLM_F_CREATE); + rtnl_link_put(link); + + return err; +} +/** + * Set IPVTI tunnel interface index + * @arg link Link object + * @arg index interface index + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipvti_set_link(struct rtnl_link *link, uint32_t index) +{ + struct ipvti_info *ipvti = link->l_info; + + IS_IPVTI_LINK_ASSERT(link); + + ipvti->link = index; + ipvti->ipvti_mask |= IPVTI_ATTR_LINK; + + return 0; +} + +/** + * Get IPVTI tunnel interface index + * @arg link Link object + * + * @return interface index + */ +uint32_t rtnl_link_ipvti_get_link(struct rtnl_link *link) +{ + struct ipvti_info *ipvti = link->l_info; + + IS_IPVTI_LINK_ASSERT(link); + + return ipvti->link; +} + +/** + * Set IPVTI tunnel set ikey + * @arg link Link object + * @arg ikey gre ikey + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipvti_set_ikey(struct rtnl_link *link, uint32_t ikey) +{ + struct ipvti_info *ipvti = link->l_info; + + IS_IPVTI_LINK_ASSERT(link); + + ipvti->ikey = ikey; + ipvti->ipvti_mask |= IPVTI_ATTR_IKEY; + + return 0; +} + +/** + * Get IPVTI tunnel ikey + * @arg link Link object + * + * @return ikey + */ +uint32_t rtnl_link_ipvti_get_ikey(struct rtnl_link *link) +{ + struct ipvti_info *ipvti = link->l_info; + + IS_IPVTI_LINK_ASSERT(link); + + return ipvti->ikey; +} + +/** + * Set IPVTI tunnel set okey + * @arg link Link object + * @arg okey gre okey + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipvti_set_okey(struct rtnl_link *link, uint32_t okey) +{ + struct ipvti_info *ipvti = link->l_info; + + IS_IPVTI_LINK_ASSERT(link); + + ipvti->okey = okey; + ipvti->ipvti_mask |= IPVTI_ATTR_OKEY; + + return 0; +} + +/** + * Get IPVTI tunnel okey + * @arg link Link object + * + * @return okey value + */ +uint32_t rtnl_link_ipvti_get_okey(struct rtnl_link *link) +{ + struct ipvti_info *ipvti = link->l_info; + + IS_IPVTI_LINK_ASSERT(link); + + return ipvti->okey; +} + +/** + * Set IPVTI tunnel local address + * @arg link Link object + * @arg addr local address + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipvti_set_local(struct rtnl_link *link, uint32_t addr) +{ + struct ipvti_info *ipvti = link->l_info; + + IS_IPVTI_LINK_ASSERT(link); + + ipvti->local = addr; + ipvti->ipvti_mask |= IPVTI_ATTR_LOCAL; + + return 0; +} + +/** + * Get IPVTI tunnel local address + * @arg link Link object + * + * @return local address + */ +uint32_t rtnl_link_ipvti_get_local(struct rtnl_link *link) +{ + struct ipvti_info *ipvti = link->l_info; + + IS_IPVTI_LINK_ASSERT(link); + + return ipvti->local; +} + +/** + * Set IPVTI tunnel remote address + * @arg link Link object + * @arg remote remote address + * + * @return 0 on success or a negative error code + */ +int rtnl_link_ipvti_set_remote(struct rtnl_link *link, uint32_t remote) +{ + struct ipvti_info *ipvti = link->l_info; + + IS_IPVTI_LINK_ASSERT(link); + + ipvti->remote = remote; + ipvti->ipvti_mask |= IPVTI_ATTR_REMOTE; + + return 0; +} + +/** + * Get IPVTI tunnel remote address + * @arg link Link object + * + * @return remote address on success or a negative error code + */ +uint32_t rtnl_link_ipvti_get_remote(struct rtnl_link *link) +{ + struct ipvti_info *ipvti = link->l_info; + + IS_IPVTI_LINK_ASSERT(link); + + return ipvti->remote; +} + +static void __init ipvti_init(void) +{ + rtnl_link_register_info(&ipvti_info_ops); +} + +static void __exit ipvti_exit(void) +{ + rtnl_link_unregister_info(&ipvti_info_ops); +} diff --git a/libnl/lib/route/link/macsec.c b/libnl/lib/route/link/macsec.c new file mode 100644 index 0000000..625eb2e --- /dev/null +++ b/libnl/lib/route/link/macsec.c @@ -0,0 +1,847 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2016 Sabrina Dubroca + */ + +/** + * @ingroup link + * @defgroup macsec MACsec + * MACsec link module + * + * @details + * \b Link Type Name: "macsec" + * + * @route_doc{link_macsec, MACsec Documentation} + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/** @cond SKIP */ +#define MACSEC_ATTR_SCI (1 << 0) +#define MACSEC_ATTR_ICV_LEN (1 << 1) +#define MACSEC_ATTR_CIPHER_SUITE (1 << 2) +#define MACSEC_ATTR_WINDOW (1 << 3) +#define MACSEC_ATTR_ENCODING_SA (1 << 4) +#define MACSEC_ATTR_ENCRYPT (1 << 5) +#define MACSEC_ATTR_PROTECT (1 << 6) +#define MACSEC_ATTR_INC_SCI (1 << 7) +#define MACSEC_ATTR_ES (1 << 8) +#define MACSEC_ATTR_SCB (1 << 9) +#define MACSEC_ATTR_REPLAY_PROTECT (1 << 10) +#define MACSEC_ATTR_VALIDATION (1 << 11) +#define MACSEC_ATTR_PORT (1 << 12) + +struct macsec_info { + int ifindex; + uint64_t sci; + uint16_t port; + uint64_t cipher_suite; + uint16_t icv_len; + uint32_t window; + enum macsec_validation_type validate; + uint8_t encoding_sa; + + uint8_t send_sci, end_station, scb, replay_protect, protect, encrypt; + + uint32_t ce_mask; +}; + +#define DEFAULT_ICV_LEN 16 + +/** @endcond */ + +static struct nla_policy macsec_policy[IFLA_MACSEC_MAX+1] = { + [IFLA_MACSEC_SCI] = { .type = NLA_U64 }, + [IFLA_MACSEC_ICV_LEN] = { .type = NLA_U8 }, + [IFLA_MACSEC_CIPHER_SUITE] = { .type = NLA_U64 }, + [IFLA_MACSEC_WINDOW] = { .type = NLA_U32 }, + [IFLA_MACSEC_ENCODING_SA] = { .type = NLA_U8 }, + [IFLA_MACSEC_ENCRYPT] = { .type = NLA_U8 }, + [IFLA_MACSEC_PROTECT] = { .type = NLA_U8 }, + [IFLA_MACSEC_INC_SCI] = { .type = NLA_U8 }, + [IFLA_MACSEC_ES] = { .type = NLA_U8 }, + [IFLA_MACSEC_SCB] = { .type = NLA_U8 }, + [IFLA_MACSEC_REPLAY_PROTECT] = { .type = NLA_U8 }, + [IFLA_MACSEC_VALIDATION] = { .type = NLA_U8 }, +}; + +/** + * @name MACsec Object + * @{ + */ + +/** + * Allocate link object of type MACsec + * + * @return Allocated link object or NULL. + */ +static int macsec_alloc(struct rtnl_link *link) +{ + struct macsec_info *info; + + if (!link->l_info) { + link->l_info = malloc(sizeof(struct macsec_info)); + if (!link->l_info) + return -NLE_NOMEM; + } + + memset(link->l_info, 0, sizeof(struct macsec_info)); + info = link->l_info; + + info->cipher_suite = MACSEC_DEFAULT_CIPHER_ID; + info->icv_len = DEFAULT_ICV_LEN; + info->ce_mask = MACSEC_ATTR_CIPHER_SUITE | MACSEC_ATTR_ICV_LEN; + + return 0; +} + +static int macsec_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_MACSEC_MAX+1]; + struct macsec_info *info; + int err; + + NL_DBG(3, "Parsing MACsec link info\n"); + + if ((err = nla_parse_nested(tb, IFLA_MACSEC_MAX, data, macsec_policy)) < 0) + goto errout; + + if ((err = macsec_alloc(link)) < 0) + goto errout; + + info = link->l_info; + + if (tb[IFLA_MACSEC_SCI]) { + info->sci = nla_get_u64(tb[IFLA_MACSEC_SCI]); + info->ce_mask |= MACSEC_ATTR_SCI; + } + + if (tb[IFLA_MACSEC_PROTECT]) { + info->protect = nla_get_u8(tb[IFLA_MACSEC_PROTECT]); + info->ce_mask |= MACSEC_ATTR_PROTECT; + } + + if (tb[IFLA_MACSEC_CIPHER_SUITE]) { + info->cipher_suite = nla_get_u64(tb[IFLA_MACSEC_CIPHER_SUITE]); + info->ce_mask |= MACSEC_ATTR_CIPHER_SUITE; + } + + if (tb[IFLA_MACSEC_ICV_LEN]) { + info->icv_len = nla_get_u8(tb[IFLA_MACSEC_ICV_LEN]); + info->ce_mask |= MACSEC_ATTR_ICV_LEN; + } + + if (tb[IFLA_MACSEC_ENCODING_SA]) { + info->encoding_sa = nla_get_u8(tb[IFLA_MACSEC_ENCODING_SA]); + info->ce_mask |= MACSEC_ATTR_ENCODING_SA; + } + + if (tb[IFLA_MACSEC_VALIDATION]) { + info->validate = nla_get_u8(tb[IFLA_MACSEC_VALIDATION]); + info->ce_mask |= MACSEC_ATTR_VALIDATION; + } + + if (tb[IFLA_MACSEC_ENCRYPT]) { + info->encrypt = nla_get_u8(tb[IFLA_MACSEC_ENCRYPT]); + info->ce_mask |= MACSEC_ATTR_ENCRYPT; + } + + if (tb[IFLA_MACSEC_INC_SCI]) { + info->send_sci = nla_get_u8(tb[IFLA_MACSEC_INC_SCI]); + info->ce_mask |= MACSEC_ATTR_INC_SCI; + } + + if (tb[IFLA_MACSEC_ES]) { + info->end_station = nla_get_u8(tb[IFLA_MACSEC_ES]); + info->ce_mask |= MACSEC_ATTR_ES; + } + + if (tb[IFLA_MACSEC_SCB]) { + info->scb = nla_get_u8(tb[IFLA_MACSEC_SCB]); + info->ce_mask |= MACSEC_ATTR_SCB; + } + + if (tb[IFLA_MACSEC_REPLAY_PROTECT]) { + info->replay_protect = nla_get_u8(tb[IFLA_MACSEC_REPLAY_PROTECT]); + info->ce_mask |= MACSEC_ATTR_REPLAY_PROTECT; + } + + if (tb[IFLA_MACSEC_WINDOW]) { + info->window = nla_get_u32(tb[IFLA_MACSEC_WINDOW]); + info->ce_mask |= MACSEC_ATTR_WINDOW; + } + + err = 0; +errout: + return err; +} + +static void macsec_free(struct rtnl_link *link) +{ + free(link->l_info); + link->l_info = NULL; +} + +static const char *values_on_off[] = { "off", "on" }; + +static const char *VALIDATE_STR[] = { + [MACSEC_VALIDATE_DISABLED] = "disabled", + [MACSEC_VALIDATE_CHECK] = "check", + [MACSEC_VALIDATE_STRICT] = "strict", +}; + +static char *replay_protect_str(char *buf, uint8_t replay_protect, uint8_t window) +{ + if (replay_protect == 1) { + sprintf(buf, "replay_protect on window %d", window); + } else if (replay_protect == 0) { + sprintf(buf, "replay_protect off"); + } else { + buf[0] = '\0'; + } + + return buf; +} + +/** @cond SKIP */ +#define PRINT_FLAG(buf, i, field, c) ({ if (i->field == 1) *buf++ = c; }) +/** @endcond */ +static char *flags_str(char *buf, unsigned char len, struct macsec_info *info) +{ + char *tmp = buf; + memset(tmp, 0, len); + + PRINT_FLAG(tmp, info, protect, 'P'); + PRINT_FLAG(tmp, info, encrypt, 'E'); + PRINT_FLAG(tmp, info, send_sci, 'S'); + PRINT_FLAG(tmp, info, end_station, 'e'); + PRINT_FLAG(tmp, info, scb, 's'); + PRINT_FLAG(tmp, info, replay_protect, 'R'); + + *tmp++ = ' '; + *tmp++ = 'v'; + switch (info->validate) { + case MACSEC_VALIDATE_DISABLED: + *tmp++ = 'd'; + break; + case MACSEC_VALIDATE_CHECK: + *tmp++ = 'c'; + break; + case MACSEC_VALIDATE_STRICT: + *tmp++ = 's'; + break; + default: + break; + } + + sprintf(tmp, " %d", info->encoding_sa); + + return buf; +} + +static void macsec_dump_line(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct macsec_info *info = link->l_info; + char tmp[128]; + + nl_dump(p, "sci %016llx <%s>", ntohll(info->sci), flags_str(tmp, sizeof(tmp), info)); +} + +static void macsec_dump_details(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct macsec_info *info = link->l_info; + char tmp[128]; + + nl_dump(p, " sci %016llx protect %s encoding_sa %d encrypt %s send_sci %s validate %s %s\n", + ntohll(info->sci), values_on_off[info->protect], info->encoding_sa, values_on_off[info->encrypt], values_on_off[info->send_sci], + VALIDATE_STR[info->validate], + replay_protect_str(tmp, info->replay_protect, info->window)); + nl_dump(p, " cipher suite: %016llx, icv_len %d\n", + info->cipher_suite, info->icv_len); +} + +static int macsec_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct macsec_info *copy, *info = src->l_info; + int err; + + dst->l_info = NULL; + if ((err = rtnl_link_set_type(dst, "macsec")) < 0) + return err; + copy = dst->l_info; + + if (!info || !copy) + return -NLE_NOMEM; + + memcpy(copy, info, sizeof(struct macsec_info)); + + return 0; +} + +static int macsec_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct macsec_info *info = link->l_info; + struct nlattr *data; + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + return -NLE_MSGSIZE; + + if (info->ce_mask & MACSEC_ATTR_SCI) + NLA_PUT_U64(msg, IFLA_MACSEC_SCI, info->sci); + else if (info->ce_mask & MACSEC_ATTR_PORT) + NLA_PUT_U16(msg, IFLA_MACSEC_PORT, htons(info->port)); + + if ((info->ce_mask & MACSEC_ATTR_ENCRYPT)) + NLA_PUT_U8(msg, IFLA_MACSEC_ENCRYPT, info->encrypt); + + if (info->cipher_suite != MACSEC_DEFAULT_CIPHER_ID || info->icv_len != DEFAULT_ICV_LEN) { + NLA_PUT_U64(msg, IFLA_MACSEC_CIPHER_SUITE, info->cipher_suite); + NLA_PUT_U8(msg, IFLA_MACSEC_ICV_LEN, info->icv_len); + } + + if ((info->ce_mask & MACSEC_ATTR_INC_SCI)) + NLA_PUT_U8(msg, IFLA_MACSEC_INC_SCI, info->send_sci); + + if ((info->ce_mask & MACSEC_ATTR_ES)) + NLA_PUT_U8(msg, IFLA_MACSEC_ES, info->end_station); + + if ((info->ce_mask & MACSEC_ATTR_SCB)) + NLA_PUT_U8(msg, IFLA_MACSEC_SCB, info->scb); + + if ((info->ce_mask & MACSEC_ATTR_PROTECT)) + NLA_PUT_U8(msg, IFLA_MACSEC_PROTECT, info->protect); + + if ((info->ce_mask & MACSEC_ATTR_REPLAY_PROTECT)) { + if (info->replay_protect && !(info->ce_mask & MACSEC_ATTR_WINDOW)) + return -NLE_INVAL; + + NLA_PUT_U8(msg, IFLA_MACSEC_REPLAY_PROTECT, info->replay_protect); + NLA_PUT_U32(msg, IFLA_MACSEC_WINDOW, info->window); + } + + if ((info->ce_mask & MACSEC_ATTR_VALIDATION)) + NLA_PUT_U8(msg, IFLA_MACSEC_VALIDATION, info->validate); + + if ((info->ce_mask & MACSEC_ATTR_ENCODING_SA)) + NLA_PUT_U8(msg, IFLA_MACSEC_ENCODING_SA, info->encoding_sa); + + nla_nest_end(msg, data); + + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +static int macsec_compare(struct rtnl_link *link_a, struct rtnl_link *link_b, + int flags) +{ + struct macsec_info *a = link_a->l_info; + struct macsec_info *b = link_b->l_info; + int diff = 0; + uint32_t attrs = flags & LOOSE_COMPARISON ? b->ce_mask : ~0; + +#define MACSEC_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, MACSEC_ATTR_##ATTR, a, b, EXPR) + + if (a->ce_mask & MACSEC_ATTR_SCI && b->ce_mask & MACSEC_ATTR_SCI) + diff |= MACSEC_DIFF(SCI, a->sci != b->sci); + else if (a->ce_mask & MACSEC_ATTR_PORT && b->ce_mask & MACSEC_ATTR_PORT) + diff |= MACSEC_DIFF(PORT, a->port != b->port); + + if (a->ce_mask & MACSEC_ATTR_CIPHER_SUITE && b->ce_mask & MACSEC_ATTR_CIPHER_SUITE) { + diff |= MACSEC_DIFF(ICV_LEN, a->icv_len != b->icv_len); + diff |= MACSEC_DIFF(CIPHER_SUITE, a->cipher_suite != b->cipher_suite); + } + + if (a->ce_mask & MACSEC_ATTR_REPLAY_PROTECT && b->ce_mask & MACSEC_ATTR_REPLAY_PROTECT) { + int d = MACSEC_DIFF(REPLAY_PROTECT, a->replay_protect != b->replay_protect); + if (a->replay_protect && b->replay_protect) + d |= MACSEC_DIFF(WINDOW, a->window != b->window); + diff |= d; + } + + diff |= MACSEC_DIFF(ENCODING_SA, a->encoding_sa != b->encoding_sa); + diff |= MACSEC_DIFF(ENCRYPT, a->encrypt != b->encrypt); + diff |= MACSEC_DIFF(PROTECT, a->protect != b->protect); + diff |= MACSEC_DIFF(INC_SCI, a->send_sci != b->send_sci); + diff |= MACSEC_DIFF(ES, a->end_station != b->end_station); + diff |= MACSEC_DIFF(SCB, a->scb != b->scb); + diff |= MACSEC_DIFF(VALIDATION, a->validate != b->validate); +#undef MACSEC_DIFF + + return diff; +} + + +static struct rtnl_link_info_ops macsec_info_ops = { + .io_name = "macsec", + .io_alloc = macsec_alloc, + .io_parse = macsec_parse, + .io_dump = { + [NL_DUMP_LINE] = macsec_dump_line, + [NL_DUMP_DETAILS] = macsec_dump_details, + }, + .io_clone = macsec_clone, + .io_put_attrs = macsec_put_attrs, + .io_free = macsec_free, + .io_compare = macsec_compare, +}; + +static void __init macsec_init(void) +{ + rtnl_link_register_info(&macsec_info_ops); +} + +static void __exit macsec_exit(void) +{ + rtnl_link_unregister_info(&macsec_info_ops); +} + +/** @cond SKIP */ +#define IS_MACSEC_LINK_ASSERT(link) \ + if ((link)->l_info_ops != &macsec_info_ops) { \ + APPBUG("Link is not a MACsec link. set type \"macsec\" first."); \ + return -NLE_OPNOTSUPP; \ + } +/** @endcond */ + +struct rtnl_link *rtnl_link_macsec_alloc(void) +{ + struct rtnl_link *link = rtnl_link_alloc(); + + if (!link) + return NULL; + + if (rtnl_link_set_type(link, "macsec") < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Set SCI + * @arg link Link object + * @arg sci Secure Channel Identifier in network byte order + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_macsec_set_sci(struct rtnl_link *link, uint64_t sci) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + info->sci = sci; + info->ce_mask |= MACSEC_ATTR_SCI; + + return 0; +} + +/** + * Get SCI + * @arg link Link object + * @arg sci On return points to the Secure Channel Identifier + * in network byte order + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_macsec_get_sci(struct rtnl_link *link, uint64_t *sci) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_SCI)) + return -NLE_NOATTR; + + if (sci) + *sci = info->sci; + + return 0; +} + +/** + * Set port identifier + * @arg link Link object + * @arg port Port identifier in host byte order + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_macsec_set_port(struct rtnl_link *link, uint16_t port) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + info->port = port; + info->ce_mask |= MACSEC_ATTR_PORT; + + return 0; +} + +/** + * Get port identifier + * @arg link Link object + * @arg port On return points to the port identifier in host byte order + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_macsec_get_port(struct rtnl_link *link, uint16_t *port) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_PORT)) + return -NLE_NOATTR; + + if (port) + *port = info->port; + + return 0; +} + +int rtnl_link_macsec_set_cipher_suite(struct rtnl_link *link, uint64_t cipher_suite) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + info->cipher_suite = cipher_suite; + info->ce_mask |= MACSEC_ATTR_CIPHER_SUITE; + + return 0; +} + +int rtnl_link_macsec_get_cipher_suite(struct rtnl_link *link, uint64_t *cs) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_CIPHER_SUITE)) + return -NLE_NOATTR; + + if (cs) + *cs = info->cipher_suite; + + return 0; +} + +int rtnl_link_macsec_set_icv_len(struct rtnl_link *link, uint16_t icv_len) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (icv_len > MACSEC_STD_ICV_LEN) + return -NLE_INVAL; + + info->icv_len = icv_len; + info->ce_mask |= MACSEC_ATTR_ICV_LEN; + + return 0; +} + +int rtnl_link_macsec_get_icv_len(struct rtnl_link *link, uint16_t *icv_len) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_ICV_LEN)) + return -NLE_NOATTR; + + if (icv_len) + *icv_len = info->icv_len; + + return 0; +} + +int rtnl_link_macsec_set_protect(struct rtnl_link *link, uint8_t protect) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (protect > 1) + return -NLE_INVAL; + + info->protect = protect; + info->ce_mask |= MACSEC_ATTR_PROTECT; + + return 0; +} + +int rtnl_link_macsec_get_protect(struct rtnl_link *link, uint8_t *protect) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_PROTECT)) + return -NLE_NOATTR; + + if (protect) + *protect = info->protect; + + return 0; +} + +int rtnl_link_macsec_set_encrypt(struct rtnl_link *link, uint8_t encrypt) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (encrypt > 1) + return -NLE_INVAL; + + info->encrypt = encrypt; + info->ce_mask |= MACSEC_ATTR_ENCRYPT; + + return 0; +} + +int rtnl_link_macsec_get_encrypt(struct rtnl_link *link, uint8_t *encrypt) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_ENCRYPT)) + return -NLE_NOATTR; + + if (encrypt) + *encrypt = info->encrypt; + + return 0; +} + +int rtnl_link_macsec_set_encoding_sa(struct rtnl_link *link, uint8_t encoding_sa) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (encoding_sa > 3) + return -NLE_INVAL; + + info->encoding_sa = encoding_sa; + info->ce_mask |= MACSEC_ATTR_ENCODING_SA; + + return 0; +} + +int rtnl_link_macsec_get_encoding_sa(struct rtnl_link *link, uint8_t *encoding_sa) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_ENCODING_SA)) + return -NLE_NOATTR; + + if (encoding_sa) + *encoding_sa = info->encoding_sa; + + return 0; +} + +int rtnl_link_macsec_set_validation_type(struct rtnl_link *link, enum macsec_validation_type validate) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (validate > 1) + return -NLE_INVAL; + + info->validate = validate; + info->ce_mask |= MACSEC_ATTR_VALIDATION; + + return 0; +} + +int rtnl_link_macsec_get_validation_type(struct rtnl_link *link, enum macsec_validation_type *validate) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_VALIDATION)) + return -NLE_NOATTR; + + if (validate) + *validate = info->validate; + + return 0; +} + +int rtnl_link_macsec_set_replay_protect(struct rtnl_link *link, uint8_t replay_protect) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (replay_protect > 1) + return -NLE_INVAL; + + info->replay_protect = replay_protect; + info->ce_mask |= MACSEC_ATTR_REPLAY_PROTECT; + + return 0; +} + +int rtnl_link_macsec_get_replay_protect(struct rtnl_link *link, uint8_t *replay_protect) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_REPLAY_PROTECT)) + return -NLE_NOATTR; + + if (replay_protect) + *replay_protect = info->replay_protect; + + return 0; +} + +int rtnl_link_macsec_set_window(struct rtnl_link *link, uint32_t window) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + info->window = window; + info->ce_mask |= MACSEC_ATTR_WINDOW; + + return 0; +} + +int rtnl_link_macsec_get_window(struct rtnl_link *link, uint32_t *window) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_WINDOW)) + return -NLE_NOATTR; + + if (window) + *window = info->window; + + return 0; +} + +int rtnl_link_macsec_set_send_sci(struct rtnl_link *link, uint8_t send_sci) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (send_sci > 1) + return -NLE_INVAL; + + info->send_sci = send_sci; + info->ce_mask |= MACSEC_ATTR_INC_SCI; + + return 0; +} + +int rtnl_link_macsec_get_send_sci(struct rtnl_link *link, uint8_t *send_sci) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_INC_SCI)) + return -NLE_NOATTR; + + if (send_sci) + *send_sci = info->send_sci; + + return 0; +} + +int rtnl_link_macsec_set_end_station(struct rtnl_link *link, uint8_t end_station) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (end_station > 1) + return -NLE_INVAL; + + info->end_station = end_station; + info->ce_mask |= MACSEC_ATTR_ES; + + return 0; +} + +int rtnl_link_macsec_get_end_station(struct rtnl_link *link, uint8_t *es) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_ES)) + return -NLE_NOATTR; + + if (es) + *es = info->end_station; + + return 0; +} + +int rtnl_link_macsec_set_scb(struct rtnl_link *link, uint8_t scb) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (scb > 1) + return -NLE_INVAL; + + info->scb = scb; + info->ce_mask |= MACSEC_ATTR_SCB; + + return 0; +} + +int rtnl_link_macsec_get_scb(struct rtnl_link *link, uint8_t *scb) +{ + struct macsec_info *info = link->l_info; + + IS_MACSEC_LINK_ASSERT(link); + + if (!(info->ce_mask & MACSEC_ATTR_SCB)) + return -NLE_NOATTR; + + if (scb) + *scb = info->scb; + + return 0; +} + +/** @} */ + +/** @} */ diff --git a/libnl/lib/route/link/macvlan.c b/libnl/lib/route/link/macvlan.c new file mode 100644 index 0000000..0689840 --- /dev/null +++ b/libnl/lib/route/link/macvlan.c @@ -0,0 +1,870 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Michael Braun + */ + +/** + * @ingroup link + * @defgroup macvlan MACVLAN/MACVTAP + * MAC-based Virtual LAN link module + * + * @details + * \b Link Type Name: "macvlan" + * + * @route_doc{link_macvlan, MACVLAN Documentation} + * @route_doc{link_macvtap, MACVTAP Documentation} + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/** @cond SKIP */ +#define MACVLAN_HAS_MODE (1<<0) +#define MACVLAN_HAS_FLAGS (1<<1) +#define MACVLAN_HAS_MACADDR (1<<2) + +struct macvlan_info +{ + uint32_t mvi_mode; + uint16_t mvi_flags; // there currently is only one flag and kernel has no flags_mask yet + uint32_t mvi_mask; + uint32_t mvi_maccount; + uint32_t mvi_macmode; + struct nl_addr **mvi_macaddr; +}; + +/** @endcond */ + +static struct nla_policy macvlan_policy[IFLA_MACVLAN_MAX+1] = { + [IFLA_MACVLAN_MODE] = { .type = NLA_U32 }, + [IFLA_MACVLAN_FLAGS] = { .type = NLA_U16 }, + [IFLA_MACVLAN_MACADDR_MODE] = { .type = NLA_U32 }, + [IFLA_MACVLAN_MACADDR] = { .type = NLA_UNSPEC }, + [IFLA_MACVLAN_MACADDR_DATA] = { .type = NLA_NESTED }, + [IFLA_MACVLAN_MACADDR_COUNT] = { .type = NLA_U32 }, +}; + +static int macvlan_alloc(struct rtnl_link *link) +{ + struct macvlan_info *mvi; + uint32_t i; + + if (link->l_info) { + mvi = link->l_info; + for (i = 0; i < mvi->mvi_maccount; i++) + nl_addr_put(mvi->mvi_macaddr[i]); + free(mvi->mvi_macaddr); + memset(mvi, 0, sizeof(*mvi)); + } else { + if ((mvi = calloc(1, sizeof(*mvi))) == NULL) + return -NLE_NOMEM; + + link->l_info = mvi; + } + mvi->mvi_macmode = MACVLAN_MACADDR_SET; + + return 0; +} + +static int macvlan_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_MACVLAN_MAX+1]; + struct macvlan_info *mvi; + struct nlattr *nla; + int len; + int err; + + NL_DBG(3, "Parsing %s link info", link->l_info_ops->io_name); + + if ((err = nla_parse_nested(tb, IFLA_MACVLAN_MAX, data, macvlan_policy)) < 0) + goto errout; + + if ((err = macvlan_alloc(link)) < 0) + goto errout; + + mvi = link->l_info; + + if (tb[IFLA_MACVLAN_MODE]) { + mvi->mvi_mode = nla_get_u32(tb[IFLA_MACVLAN_MODE]); + mvi->mvi_mask |= MACVLAN_HAS_MODE; + } + + if (tb[IFLA_MACVLAN_FLAGS]) { + mvi->mvi_flags = nla_get_u16(tb[IFLA_MACVLAN_FLAGS]); + mvi->mvi_mask |= MACVLAN_HAS_FLAGS; + } + + if ( tb[IFLA_MACVLAN_MACADDR_COUNT] + && tb[IFLA_MACVLAN_MACADDR_DATA]) { + mvi->mvi_maccount = nla_get_u32(tb[IFLA_MACVLAN_MACADDR_COUNT]); + if (mvi->mvi_maccount > 0) { + uint32_t i; + + nla = nla_data(tb[IFLA_MACVLAN_MACADDR_DATA]); + len = nla_len(tb[IFLA_MACVLAN_MACADDR_DATA]); + + mvi->mvi_macaddr = calloc(mvi->mvi_maccount, + sizeof(*(mvi->mvi_macaddr))); + + i = 0; + for (; nla_ok(nla, len); nla = nla_next(nla, &len)) { + if (i >= mvi->mvi_maccount) + break; + if (nla_type(nla) != IFLA_MACVLAN_MACADDR || + nla_len(nla) < ETH_ALEN) + continue; + mvi->mvi_macaddr[i] = nl_addr_alloc_attr(nla, AF_LLC); + i++; + } + } + mvi->mvi_mask |= MACVLAN_HAS_MACADDR; + } + + err = 0; +errout: + return err; +} + +static void macvlan_free(struct rtnl_link *link) +{ + struct macvlan_info *mvi; + uint32_t i; + + mvi = link->l_info; + if (!mvi) + return; + + for (i = 0; i < mvi->mvi_maccount; i++) + nl_addr_put(mvi->mvi_macaddr[i]); + free(mvi->mvi_macaddr); + free(mvi); + + link->l_info = NULL; +} + +static void macvlan_dump_details(struct rtnl_link *link, struct nl_dump_params *p) +{ + char buf[64]; + uint32_t i; + struct macvlan_info *mvi = link->l_info; + + if (mvi->mvi_mask & MACVLAN_HAS_MODE) { + rtnl_link_macvlan_mode2str(mvi->mvi_mode, buf, sizeof(buf)); + nl_dump(p, " %s-mode %s", link->l_info_ops->io_name, buf); + } + + if (mvi->mvi_mask & MACVLAN_HAS_FLAGS) { + rtnl_link_macvlan_flags2str(mvi->mvi_flags, buf, sizeof(buf)); + nl_dump(p, " %s-flags %s", link->l_info_ops->io_name, buf); + } + + if (mvi->mvi_mask & MACVLAN_HAS_MACADDR) { + nl_dump(p, " macvlan-count %u", (unsigned) mvi->mvi_maccount); + + if (mvi->mvi_maccount) + nl_dump(p, " macvlan-sourcemac"); + + for (i = 0; i < mvi->mvi_maccount; i++) { + nl_dump(p, " %s", nl_addr2str(mvi->mvi_macaddr[i], buf, + sizeof(buf))); + } + } + nl_dump(p, "\n"); +} + +static int macvlan_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct macvlan_info *vdst, *vsrc = src->l_info; + int err; + uint32_t i; + + dst->l_info = NULL; + if ((err = rtnl_link_set_type(dst, "macvlan")) < 0) + return err; + vdst = dst->l_info; + + if (!vdst || !vsrc) + return -NLE_NOMEM; + + memcpy(vdst, vsrc, sizeof(struct macvlan_info)); + + if ( vsrc->mvi_mask & MACVLAN_HAS_MACADDR + && vsrc->mvi_maccount > 0) { + vdst->mvi_macaddr = calloc(vdst->mvi_maccount, + sizeof(*(vdst->mvi_macaddr))); + for (i = 0; i < vdst->mvi_maccount; i++) + vdst->mvi_macaddr[i] = nl_addr_clone(vsrc->mvi_macaddr[i]); + } else + vdst->mvi_macaddr = NULL; + + return 0; +} + +static int macvlan_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct macvlan_info *mvi = link->l_info; + struct nlattr *data, *datamac = NULL; + int i, ret; + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + return -NLE_MSGSIZE; + + ret = -NLE_NOMEM; + + if (mvi->mvi_mask & MACVLAN_HAS_MODE) + NLA_PUT_U32(msg, IFLA_MACVLAN_MODE, mvi->mvi_mode); + + if (mvi->mvi_mask & MACVLAN_HAS_FLAGS) + NLA_PUT_U16(msg, IFLA_MACVLAN_FLAGS, mvi->mvi_flags); + + if (mvi->mvi_mask & MACVLAN_HAS_MACADDR) { + NLA_PUT_U32(msg, IFLA_MACVLAN_MACADDR_MODE, mvi->mvi_macmode); + datamac = nla_nest_start(msg, IFLA_MACVLAN_MACADDR_DATA); + if (!datamac) + goto nla_put_failure; + + for (i = 0; i < mvi->mvi_maccount; i++) { + NLA_PUT_ADDR(msg, IFLA_MACVLAN_MACADDR, + mvi->mvi_macaddr[i]); + } + } + + ret = 0; + +nla_put_failure: + if (datamac) + nla_nest_end(msg, datamac); + + nla_nest_end(msg, data); + + return ret; +} + +static struct rtnl_link_info_ops macvlan_info_ops = { + .io_name = "macvlan", + .io_alloc = macvlan_alloc, + .io_parse = macvlan_parse, + .io_dump = { + [NL_DUMP_DETAILS] = macvlan_dump_details, + }, + .io_clone = macvlan_clone, + .io_put_attrs = macvlan_put_attrs, + .io_free = macvlan_free, +}; + +static struct rtnl_link_info_ops macvtap_info_ops = { + .io_name = "macvtap", + .io_alloc = macvlan_alloc, + .io_parse = macvlan_parse, + .io_dump = { + [NL_DUMP_DETAILS] = macvlan_dump_details, + }, + .io_clone = macvlan_clone, + .io_put_attrs = macvlan_put_attrs, + .io_free = macvlan_free, +}; + +/** @cond SKIP */ +#define IS_MACVLAN_LINK_ASSERT(link) \ + if ((link)->l_info_ops != &macvlan_info_ops) { \ + APPBUG("Link is not a macvlan link. set type \"macvlan\" first."); \ + return -NLE_OPNOTSUPP; \ + } + +#define IS_MACVTAP_LINK_ASSERT(link) \ + if ((link)->l_info_ops != &macvtap_info_ops) { \ + APPBUG("Link is not a macvtap link. set type \"macvtap\" first."); \ + return -NLE_OPNOTSUPP; \ + } +/** @endcond */ + +/** + * @name MACVLAN Object + * @{ + */ + +/** + * Allocate link object of type MACVLAN + * + * @return Allocated link object or NULL. + */ +struct rtnl_link *rtnl_link_macvlan_alloc(void) +{ + struct rtnl_link *link; + int err; + + if (!(link = rtnl_link_alloc())) + return NULL; + + if ((err = rtnl_link_set_type(link, "macvlan")) < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Check if link is a MACVLAN link + * @arg link Link object + * + * @return True if link is a MACVLAN link, otherwise false is returned. + */ +int rtnl_link_is_macvlan(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "macvlan"); +} + +/** + * Set MACVLAN MODE + * @arg link Link object + * @arg mode MACVLAN mode + * + * @return 0 on success or a negative error code + */ +int rtnl_link_macvlan_set_mode(struct rtnl_link *link, uint32_t mode) +{ + struct macvlan_info *mvi = link->l_info; + int i; + + IS_MACVLAN_LINK_ASSERT(link); + + mvi->mvi_mode = mode; + mvi->mvi_mask |= MACVLAN_HAS_MODE; + + if (mode != MACVLAN_MODE_SOURCE) { + for (i = 0; i < mvi->mvi_maccount; i++) + nl_addr_put(mvi->mvi_macaddr[i]); + free(mvi->mvi_macaddr); + mvi->mvi_maccount = 0; + mvi->mvi_macaddr = NULL; + mvi->mvi_macmode = MACVLAN_MACADDR_SET; + mvi->mvi_mask &= ~MACVLAN_HAS_MACADDR; + } + + return 0; +} + +/** + * Get MACVLAN Mode + * @arg link Link object + * + * @return MACVLAN mode, 0 if not set or a negative error code. + */ +uint32_t rtnl_link_macvlan_get_mode(struct rtnl_link *link) +{ + struct macvlan_info *mvi = link->l_info; + + IS_MACVLAN_LINK_ASSERT(link); + + if (mvi->mvi_mask & MACVLAN_HAS_MODE) + return mvi->mvi_mode; + else + return 0; +} + +/** + * Set MACVLAN MACMODE + * @arg link Link object + * @arg mode MACVLAN mac list modification mode + * + * Only for macvlan SOURCE mode. + * + * @return 0 on success or a negative error code + */ +int rtnl_link_macvlan_set_macmode(struct rtnl_link *link, uint32_t macmode) +{ + struct macvlan_info *mvi = link->l_info; + + IS_MACVLAN_LINK_ASSERT(link); + + if (!(mvi->mvi_mask & MACVLAN_HAS_MODE) || + (mvi->mvi_mode != MACVLAN_MODE_SOURCE)) + return -NLE_INVAL; + + mvi->mvi_macmode = macmode; + mvi->mvi_mask |= MACVLAN_HAS_MACADDR; + + return 0; +} + +/** + * Get MACVLAN MACMODE + * @arg link Link object + * @arg out_macmode mac list modification mode + * + * Only for SOURCE mode. + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_macvlan_get_macmode(struct rtnl_link *link, uint32_t *out_macmode) +{ + struct macvlan_info *mvi = link->l_info; + + IS_MACVLAN_LINK_ASSERT(link); + + if (!(mvi->mvi_mask & MACVLAN_HAS_MODE) || + (mvi->mvi_mode != MACVLAN_MODE_SOURCE)) + return -NLE_INVAL; + + if (!(mvi->mvi_mask & MACVLAN_HAS_MACADDR)) + return -NLE_INVAL; + + *out_macmode = mvi->mvi_macmode; + + return 0; +} + +/** + * Set MACVLAN flags + * @arg link Link object + * @arg flags MACVLAN flags + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_macvlan_set_flags(struct rtnl_link *link, uint16_t flags) +{ + struct macvlan_info *mvi = link->l_info; + + IS_MACVLAN_LINK_ASSERT(link); + + mvi->mvi_flags |= flags; + mvi->mvi_mask |= MACVLAN_HAS_FLAGS; + + return 0; +} + +/** + * Unset MACVLAN flags + * @arg link Link object + * @arg flags MACVLAN flags + * + * Note: kernel currently only has a single flag and lacks flags_mask to + * indicate which flags shall be changed (it always all). + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_macvlan_unset_flags(struct rtnl_link *link, uint16_t flags) +{ + struct macvlan_info *mvi = link->l_info; + + IS_MACVLAN_LINK_ASSERT(link); + + mvi->mvi_flags &= ~flags; + mvi->mvi_mask |= MACVLAN_HAS_FLAGS; + + return 0; +} + +/** + * Get MACVLAN flags + * @arg link Link object + * + * @return MACVLAN flags, 0 if none set, or a negative error code. + */ +uint16_t rtnl_link_macvlan_get_flags(struct rtnl_link *link) +{ + struct macvlan_info *mvi = link->l_info; + + IS_MACVLAN_LINK_ASSERT(link); + + return mvi->mvi_flags; +} + +/** + * Get number of MAC-Addr for MACVLAN device in source mode + * @arg link Link object + * @arg out_count number of mac addresses + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_macvlan_count_macaddr(struct rtnl_link *link, uint32_t *out_count) +{ + struct macvlan_info *mvi = link->l_info; + + IS_MACVLAN_LINK_ASSERT(link); + + if (!(mvi->mvi_mask & MACVLAN_HAS_MODE) || + (mvi->mvi_mode != MACVLAN_MODE_SOURCE)) + return -NLE_INVAL; + + if (!(mvi->mvi_mask & MACVLAN_HAS_MACADDR)) + return -NLE_INVAL; + + *out_count = mvi->mvi_maccount; + + return 0; +} + +/** + * Get configured remote MAC-Addr from MACVLAN device in source mode + * @arg link Link object + * @arg out_addr address object + * + * The returned nl_addr struct needs NOT to be released using nl_addr_put. + * It is only valid until the address is not removed from this link object + * or its mode is changed to non-source. + * + * @return 0 on success or negative error code + */ +int rtnl_link_macvlan_get_macaddr(struct rtnl_link *link, uint32_t idx, + const struct nl_addr **out_addr) +{ + struct macvlan_info *mvi = link->l_info; + + IS_MACVLAN_LINK_ASSERT(link); + + if (!(mvi->mvi_mask & MACVLAN_HAS_MODE) || + (mvi->mvi_mode != MACVLAN_MODE_SOURCE)) + return -NLE_INVAL; + + if (!(mvi->mvi_mask & MACVLAN_HAS_MACADDR)) + return -NLE_INVAL; + + if (idx >= mvi->mvi_maccount) + return -NLE_INVAL; + + *out_addr = mvi->mvi_macaddr[idx]; + return 0; +} + +/** + * Add MAC-Addr to MACVLAN device in source mode + * @arg link Link object + * @arg addr MAC-Addr + * + * addr is not release but cloned by this method. + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_macvlan_add_macaddr(struct rtnl_link *link, struct nl_addr *addr) +{ + struct macvlan_info *mvi = link->l_info; + struct nl_addr **mvi_macaddr; + size_t newsize; + + IS_MACVLAN_LINK_ASSERT(link); + + if (nl_addr_get_family(addr) != AF_LLC) + return -NLE_INVAL; + + if (!(mvi->mvi_mask & MACVLAN_HAS_MODE) || + (mvi->mvi_mode != MACVLAN_MODE_SOURCE)) + return -NLE_INVAL; + + if (!(mvi->mvi_mask & MACVLAN_HAS_MACADDR)) + return -NLE_INVAL; + + if (mvi->mvi_maccount >= UINT32_MAX) + return -NLE_INVAL; + + newsize = (mvi->mvi_maccount + 1) * sizeof(*(mvi->mvi_macaddr)); + mvi_macaddr = realloc(mvi->mvi_macaddr, newsize); + if (!mvi_macaddr) + return -NLE_NOMEM; + + mvi->mvi_macaddr = mvi_macaddr; + mvi->mvi_macaddr[mvi->mvi_maccount] = nl_addr_clone(addr); + mvi->mvi_maccount++; + + mvi->mvi_mask |= MACVLAN_HAS_MACADDR; + + return 0; +} + +/** + * Remove MAC-Addr from MACVLAN device in source mode + * @arg link Link object + * @arg addr MAC-Addr + * + * addr is not release by this method. + * + * @return a negative error code on failure, or the number + * of deleted addresses on success. + */ +int rtnl_link_macvlan_del_macaddr(struct rtnl_link *link, struct nl_addr *addr) +{ + struct macvlan_info *mvi = link->l_info; + uint32_t found, i; + + IS_MACVLAN_LINK_ASSERT(link); + + if (nl_addr_get_family(addr) != AF_LLC) + return -NLE_INVAL; + + if (!(mvi->mvi_mask & MACVLAN_HAS_MODE) || + (mvi->mvi_mode != MACVLAN_MODE_SOURCE)) + return -NLE_INVAL; + + if (!(mvi->mvi_mask & MACVLAN_HAS_MACADDR)) + return -NLE_INVAL; + + nl_addr_get(addr); + + found = 0; i = 0; + while (i + found < mvi->mvi_maccount) { + mvi->mvi_macaddr[i] = mvi->mvi_macaddr[i + found]; + if (found > 0) + mvi->mvi_macaddr[i + found] = NULL; + if (nl_addr_cmp(addr, mvi->mvi_macaddr[i]) == 0) { + nl_addr_put(mvi->mvi_macaddr[i]); + mvi->mvi_macaddr[i] = NULL; + found++; + } else + i++; + } + + nl_addr_put(addr); + + mvi->mvi_maccount -= found; + + return found > INT_MAX ? INT_MAX : (int) found; +} + +/** @} */ + + +/** + * @name MACVTAP Object + * @{ + */ + +/** + * Allocate link object of type MACVTAP + * + * @return Allocated link object or NULL. + */ +struct rtnl_link *rtnl_link_macvtap_alloc(void) +{ + struct rtnl_link *link; + int err; + + if (!(link = rtnl_link_alloc())) + return NULL; + + if ((err = rtnl_link_set_type(link, "macvtap")) < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Check if link is a MACVTAP link + * @arg link Link object + * + * @return True if link is a MACVTAP link, otherwise false is returned. + */ +int rtnl_link_is_macvtap(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "macvtap"); +} + +/** + * Set MACVTAP MODE + * @arg link Link object + * @arg mode MACVTAP mode + * + * @return 0 on success or a negative error code + */ +int rtnl_link_macvtap_set_mode(struct rtnl_link *link, uint32_t mode) +{ + struct macvlan_info *mvi = link->l_info; + + IS_MACVTAP_LINK_ASSERT(link); + + mvi->mvi_mode = mode; + mvi->mvi_mask |= MACVLAN_HAS_MODE; + + return 0; +} + +/** + * Get MACVTAP Mode + * @arg link Link object + * + * @return MACVTAP mode, 0 if not set or a negative error code. + */ +uint32_t rtnl_link_macvtap_get_mode(struct rtnl_link *link) +{ + struct macvlan_info *mvi = link->l_info; + + IS_MACVTAP_LINK_ASSERT(link); + + if (mvi->mvi_mask & MACVLAN_HAS_MODE) + return mvi->mvi_mode; + else + return 0; +} + +/** + * Set MACVTAP flags + * @arg link Link object + * @arg flags MACVTAP flags + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_macvtap_set_flags(struct rtnl_link *link, uint16_t flags) +{ + struct macvlan_info *mvi = link->l_info; + + IS_MACVTAP_LINK_ASSERT(link); + + mvi->mvi_flags |= flags; + mvi->mvi_mask |= MACVLAN_HAS_FLAGS; + + return 0; +} + +/** + * Unset MACVTAP flags + * @arg link Link object + * @arg flags MACVTAP flags + * + * Note: kernel currently only has a single flag and lacks flags_mask to + * indicate which flags shall be changed (it always all). + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_macvtap_unset_flags(struct rtnl_link *link, uint16_t flags) +{ + struct macvlan_info *mvi = link->l_info; + + IS_MACVTAP_LINK_ASSERT(link); + + mvi->mvi_flags &= ~flags; + mvi->mvi_mask |= MACVLAN_HAS_FLAGS; + + return 0; +} + +/** + * Get MACVTAP flags + * @arg link Link object + * + * @return MACVTAP flags, 0 if none set, or a negative error code. + */ +uint16_t rtnl_link_macvtap_get_flags(struct rtnl_link *link) +{ + struct macvlan_info *mvi = link->l_info; + + IS_MACVTAP_LINK_ASSERT(link); + + return mvi->mvi_flags; +} + +/** @} */ + + +static const struct trans_tbl macvlan_flags[] = { + __ADD(MACVLAN_FLAG_NOPROMISC, nopromisc), +}; + +static const struct trans_tbl macvlan_modes[] = { + __ADD(MACVLAN_MODE_PRIVATE, private), + __ADD(MACVLAN_MODE_VEPA, vepa), + __ADD(MACVLAN_MODE_BRIDGE, bridge), + __ADD(MACVLAN_MODE_PASSTHRU, passthru), + __ADD(MACVLAN_MODE_SOURCE, source), +}; + +static const struct trans_tbl macvlan_macmodes[] = { + __ADD(MACVLAN_MACADDR_ADD, "add"), + __ADD(MACVLAN_MACADDR_DEL, "del"), + __ADD(MACVLAN_MACADDR_SET, "set"), + __ADD(MACVLAN_MACADDR_FLUSH, "flush"), +}; + +/** + * @name Flag Translation + * @{ + */ + +char *rtnl_link_macvlan_flags2str(int flags, char *buf, size_t len) +{ + return __flags2str(flags, buf, len, macvlan_flags, ARRAY_SIZE(macvlan_flags)); +} + +int rtnl_link_macvlan_str2flags(const char *name) +{ + return __str2flags(name, macvlan_flags, ARRAY_SIZE(macvlan_flags)); +} + +char *rtnl_link_macvtap_flags2str(int flags, char *buf, size_t len) +{ + return __flags2str(flags, buf, len, macvlan_flags, ARRAY_SIZE(macvlan_flags)); +} + +int rtnl_link_macvtap_str2flags(const char *name) +{ + return __str2flags(name, macvlan_flags, ARRAY_SIZE(macvlan_flags)); +} + +/** @} */ + +/** + * @name Mode Translation + * @{ + */ + +char *rtnl_link_macvlan_mode2str(int mode, char *buf, size_t len) +{ + return __type2str(mode, buf, len, macvlan_modes, ARRAY_SIZE(macvlan_modes)); +} + +int rtnl_link_macvlan_str2mode(const char *name) +{ + return __str2type(name, macvlan_modes, ARRAY_SIZE(macvlan_modes)); +} + +char *rtnl_link_macvlan_macmode2str(int mode, char *buf, size_t len) +{ + return __type2str(mode, buf, len, macvlan_macmodes, + ARRAY_SIZE(macvlan_macmodes)); +} + +int rtnl_link_macvlan_str2macmode(const char *name) +{ + return __str2type(name, macvlan_macmodes, ARRAY_SIZE(macvlan_macmodes)); +} + +char *rtnl_link_macvtap_mode2str(int mode, char *buf, size_t len) +{ + return __type2str(mode, buf, len, macvlan_modes, ARRAY_SIZE(macvlan_modes)); +} + +int rtnl_link_macvtap_str2mode(const char *name) +{ + return __str2type(name, macvlan_modes, ARRAY_SIZE(macvlan_modes)); +} + +/** @} */ + +static void __init macvlan_init(void) +{ + rtnl_link_register_info(&macvlan_info_ops); + rtnl_link_register_info(&macvtap_info_ops); +} + +static void __exit macvlan_exit(void) +{ + rtnl_link_unregister_info(&macvlan_info_ops); + rtnl_link_unregister_info(&macvtap_info_ops); +} + +/** @} */ diff --git a/libnl/lib/route/link/ppp.c b/libnl/lib/route/link/ppp.c new file mode 100644 index 0000000..0bf1c4c --- /dev/null +++ b/libnl/lib/route/link/ppp.c @@ -0,0 +1,220 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2016 Jonas Johansson + */ + +/** + * @ingroup link + * @defgroup ppp PPP + * + * @details + * \b Link Type Name: "ppp" + * + * @route_doc{link_ppp, PPP Documentation} + * @{ + */ + +#define _GNU_SOURCE + +#include + +#include +#include +#include + +/** @cond SKIP */ +#define PPP_ATTR_FD (1<<0) + +struct ppp_info +{ + int32_t pi_fd; + uint32_t ce_mask; +}; + +/** @endcond */ + +static struct nla_policy ppp_nl_policy[IFLA_PPP_MAX+1] = { + [IFLA_PPP_DEV_FD] = { .type = NLA_S32 }, +}; + +static int ppp_alloc(struct rtnl_link *link) +{ + struct ppp_info *info; + + if (link->l_info) + memset(link->l_info, 0, sizeof(*info)); + else { + if ((info = calloc(1, sizeof(*info))) == NULL) + return -NLE_NOMEM; + + link->l_info = info; + } + + return 0; +} + +static int ppp_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_PPP_MAX+1]; + struct ppp_info *info; + int err; + + NL_DBG(3, "Parsing PPP link info\n"); + + if ((err = nla_parse_nested(tb, IFLA_PPP_MAX, data, ppp_nl_policy)) < 0) + goto errout; + + if ((err = ppp_alloc(link)) < 0) + goto errout; + + info = link->l_info; + + if (tb[IFLA_PPP_DEV_FD]) { + info->pi_fd = nla_get_s32(tb[IFLA_PPP_DEV_FD]); + info->ce_mask |= PPP_ATTR_FD; + } + + err = 0; +errout: + return err; +} + +static void ppp_free(struct rtnl_link *link) +{ + free(link->l_info); + link->l_info = NULL; +} + +static int ppp_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct ppp_info *vdst, *vsrc = src->l_info; + int err; + + dst->l_info = NULL; + if ((err = rtnl_link_set_type(dst, "ppp")) < 0) + return err; + vdst = dst->l_info; + + if (!vdst || !vsrc) + return -NLE_NOMEM; + + memcpy(vdst, vsrc, sizeof(struct ppp_info)); + + return 0; +} + +static int ppp_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct ppp_info *info = link->l_info; + struct nlattr *data; + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + return -NLE_MSGSIZE; + + if (info->ce_mask & PPP_ATTR_FD) + NLA_PUT_S32(msg, IFLA_PPP_DEV_FD, info->pi_fd); + + nla_nest_end(msg, data); + +nla_put_failure: + + return 0; +} + +static struct rtnl_link_info_ops ppp_info_ops = { + .io_name = "ppp", + .io_alloc = ppp_alloc, + .io_parse = ppp_parse, + .io_clone = ppp_clone, + .io_put_attrs = ppp_put_attrs, + .io_free = ppp_free, +}; + +/** @cond SKIP */ +#define IS_PPP_LINK_ASSERT(link) \ + if ((link)->l_info_ops != &ppp_info_ops) { \ + APPBUG("Link is not a PPP link. set type \"ppp\" first."); \ + return -NLE_OPNOTSUPP; \ + } +/** @endcond */ + +/** + * @name PPP Object + * @{ + */ + +/** + * Allocate link object of type PPP + * + * @return Allocated link object or NULL. + */ +struct rtnl_link *rtnl_link_ppp_alloc(void) +{ + struct rtnl_link *link; + int err; + + if (!(link = rtnl_link_alloc())) + return NULL; + + if ((err = rtnl_link_set_type(link, "ppp")) < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Set PPP file descriptor + * @arg link Link object + * @arg flags PPP file descriptor + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_ppp_set_fd(struct rtnl_link *link, int32_t fd) +{ + struct ppp_info *info = link->l_info; + + IS_PPP_LINK_ASSERT(link); + + info->pi_fd |= fd; + info->ce_mask |= PPP_ATTR_FD; + + return 0; +} + +/** + * Get PPP file descriptor + * @arg link Link object + * + * @return PPP file descriptor, 0 if not set or a negative error code. + */ +int rtnl_link_ppp_get_fd(struct rtnl_link *link, int32_t *fd) +{ + struct ppp_info *info = link->l_info; + + IS_PPP_LINK_ASSERT(link); + + if (!(info->ce_mask & PPP_ATTR_FD)) + return -NLE_NOATTR; + + if (fd) + *fd = info->pi_fd; + + return 0; +} + +/** @} */ + +static void __init ppp_init(void) +{ + rtnl_link_register_info(&ppp_info_ops); +} + +static void __exit ppp_exit(void) +{ + rtnl_link_unregister_info(&ppp_info_ops); +} + +/** @} */ diff --git a/libnl/lib/route/link/sit.c b/libnl/lib/route/link/sit.c new file mode 100644 index 0000000..6d81af0 --- /dev/null +++ b/libnl/lib/route/link/sit.c @@ -0,0 +1,819 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2014 Susant Sahani + */ + +/** + * @ingroup link + * @defgroup sit SIT + * sit link module + * + * @details + * \b Link Type Name: "sit" + * + * @route_doc{link_sit, SIT Documentation} + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SIT_ATTR_LINK (1 << 0) +#define SIT_ATTR_LOCAL (1 << 1) +#define SIT_ATTR_REMOTE (1 << 2) +#define SIT_ATTR_TTL (1 << 3) +#define SIT_ATTR_TOS (1 << 4) +#define SIT_ATTR_PMTUDISC (1 << 5) +#define SIT_ATTR_FLAGS (1 << 6) +#define SIT_ATTR_PROTO (1 << 7) +#define SIT_ATTR_6RD_PREFIX (1 << 8) +#define SIT_ATTR_6RD_RELAY_PREFIX (1 << 9) +#define SIT_ATTR_6RD_PREFIXLEN (1 << 10) +#define SIT_ATTR_6RD_RELAY_PREFIXLEN (1 << 11) + +struct sit_info +{ + uint8_t ttl; + uint8_t tos; + uint8_t pmtudisc; + uint8_t proto; + uint16_t flags; + uint32_t link; + uint32_t local; + uint32_t remote; + struct in6_addr ip6rd_prefix; + uint32_t ip6rd_relay_prefix; + uint16_t ip6rd_prefixlen; + uint16_t ip6rd_relay_prefixlen; + uint32_t sit_mask; +}; + +static struct nla_policy sit_policy[IFLA_IPTUN_MAX + 1] = { + [IFLA_IPTUN_LINK] = { .type = NLA_U32 }, + [IFLA_IPTUN_LOCAL] = { .type = NLA_U32 }, + [IFLA_IPTUN_REMOTE] = { .type = NLA_U32 }, + [IFLA_IPTUN_TTL] = { .type = NLA_U8 }, + [IFLA_IPTUN_TOS] = { .type = NLA_U8 }, + [IFLA_IPTUN_PMTUDISC] = { .type = NLA_U8 }, + [IFLA_IPTUN_FLAGS] = { .type = NLA_U16 }, + [IFLA_IPTUN_PROTO] = { .type = NLA_U8 }, + [IFLA_IPTUN_6RD_PREFIX] = { .minlen = sizeof(struct in6_addr) }, + [IFLA_IPTUN_6RD_RELAY_PREFIX] = { .type = NLA_U32 }, + [IFLA_IPTUN_6RD_PREFIXLEN] = { .type = NLA_U16 }, + [IFLA_IPTUN_6RD_RELAY_PREFIXLEN] = { .type = NLA_U16 }, +}; + +static int sit_alloc(struct rtnl_link *link) +{ + struct sit_info *sit; + + if (link->l_info) + memset(link->l_info, 0, sizeof(*sit)); + else { + sit = calloc(1, sizeof(*sit)); + if (!sit) + return -NLE_NOMEM; + + link->l_info = sit; + } + + return 0; +} + +static int sit_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_IPTUN_MAX + 1]; + struct sit_info *sit; + int err; + + NL_DBG(3, "Parsing SIT link info\n"); + + err = nla_parse_nested(tb, IFLA_IPTUN_MAX, data, sit_policy); + if (err < 0) + goto errout; + + err = sit_alloc(link); + if (err < 0) + goto errout; + + sit = link->l_info; + + if (tb[IFLA_IPTUN_LINK]) { + sit->link = nla_get_u32(tb[IFLA_IPTUN_LINK]); + sit->sit_mask |= SIT_ATTR_LINK; + } + + if (tb[IFLA_IPTUN_LOCAL]) { + sit->local = nla_get_u32(tb[IFLA_IPTUN_LOCAL]); + sit->sit_mask |= SIT_ATTR_LOCAL; + } + + if (tb[IFLA_IPTUN_REMOTE]) { + sit->remote = nla_get_u32(tb[IFLA_IPTUN_REMOTE]); + sit->sit_mask |= SIT_ATTR_REMOTE; + } + + if (tb[IFLA_IPTUN_TTL]) { + sit->ttl = nla_get_u8(tb[IFLA_IPTUN_TTL]); + sit->sit_mask |= SIT_ATTR_TTL; + } + + if (tb[IFLA_IPTUN_TOS]) { + sit->tos = nla_get_u8(tb[IFLA_IPTUN_TOS]); + sit->sit_mask |= SIT_ATTR_TOS; + } + + if (tb[IFLA_IPTUN_PMTUDISC]) { + sit->pmtudisc = nla_get_u8(tb[IFLA_IPTUN_PMTUDISC]); + sit->sit_mask |= SIT_ATTR_PMTUDISC; + } + + if (tb[IFLA_IPTUN_FLAGS]) { + sit->flags = nla_get_u16(tb[IFLA_IPTUN_FLAGS]); + sit->sit_mask |= SIT_ATTR_FLAGS; + } + + if (tb[IFLA_IPTUN_PROTO]) { + sit->proto = nla_get_u8(tb[IFLA_IPTUN_PROTO]); + sit->sit_mask |= SIT_ATTR_PROTO; + } + + if (tb[IFLA_IPTUN_6RD_PREFIX]) { + nla_memcpy(&sit->ip6rd_prefix, tb[IFLA_IPTUN_6RD_PREFIX], + sizeof(struct in6_addr)); + sit->sit_mask |= SIT_ATTR_6RD_PREFIX; + } + + if (tb[IFLA_IPTUN_6RD_RELAY_PREFIX]) { + sit->ip6rd_relay_prefix = nla_get_u32(tb[IFLA_IPTUN_6RD_RELAY_PREFIX]); + sit->sit_mask |= SIT_ATTR_6RD_RELAY_PREFIX; + } + + if (tb[IFLA_IPTUN_6RD_PREFIXLEN]) { + sit->ip6rd_prefixlen = nla_get_u16(tb[IFLA_IPTUN_6RD_PREFIXLEN]); + sit->sit_mask |= SIT_ATTR_6RD_PREFIXLEN; + } + + if (tb[IFLA_IPTUN_6RD_RELAY_PREFIXLEN]) { + sit->ip6rd_relay_prefixlen = nla_get_u16(tb[IFLA_IPTUN_6RD_RELAY_PREFIXLEN]); + sit->sit_mask |= SIT_ATTR_6RD_RELAY_PREFIXLEN; + } + + err = 0; + +errout: + return err; +} + +static int sit_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct sit_info *sit = link->l_info; + struct nlattr *data; + + data = nla_nest_start(msg, IFLA_INFO_DATA); + if (!data) + return -NLE_MSGSIZE; + + if (sit->sit_mask & SIT_ATTR_LINK) + NLA_PUT_U32(msg, IFLA_IPTUN_LINK, sit->link); + + if (sit->sit_mask & SIT_ATTR_LOCAL) + NLA_PUT_U32(msg, IFLA_IPTUN_LOCAL, sit->local); + + if (sit->sit_mask & SIT_ATTR_REMOTE) + NLA_PUT_U32(msg, IFLA_IPTUN_REMOTE, sit->remote); + + if (sit->sit_mask & SIT_ATTR_TTL) + NLA_PUT_U8(msg, IFLA_IPTUN_TTL, sit->ttl); + + if (sit->sit_mask & SIT_ATTR_TOS) + NLA_PUT_U8(msg, IFLA_IPTUN_TOS, sit->tos); + + if (sit->sit_mask & SIT_ATTR_PMTUDISC) + NLA_PUT_U8(msg, IFLA_IPTUN_PMTUDISC, sit->pmtudisc); + + if (sit->sit_mask & SIT_ATTR_FLAGS) + NLA_PUT_U16(msg, IFLA_IPTUN_FLAGS, sit->flags); + + if (sit->sit_mask & SIT_ATTR_PROTO) + NLA_PUT_U8(msg, IFLA_IPTUN_PROTO, sit->proto); + + if (sit->sit_mask & SIT_ATTR_6RD_PREFIX) + NLA_PUT(msg, IFLA_IPTUN_6RD_PREFIX, sizeof(struct in6_addr), &sit->ip6rd_prefix); + + if (sit->sit_mask & SIT_ATTR_6RD_RELAY_PREFIX) + NLA_PUT_U32(msg, IFLA_IPTUN_6RD_RELAY_PREFIX, sit->ip6rd_relay_prefix); + + if (sit->sit_mask & SIT_ATTR_6RD_PREFIXLEN) + NLA_PUT_U16(msg, IFLA_IPTUN_6RD_PREFIXLEN, sit->ip6rd_prefixlen); + + if (sit->sit_mask & SIT_ATTR_6RD_RELAY_PREFIXLEN) + NLA_PUT_U16(msg, IFLA_IPTUN_6RD_RELAY_PREFIXLEN, sit->ip6rd_relay_prefixlen); + + nla_nest_end(msg, data); + +nla_put_failure: + + return 0; +} + +static void sit_free(struct rtnl_link *link) +{ + struct sit_info *sit = link->l_info; + + free(sit); + link->l_info = NULL; +} + +static void sit_dump_line(struct rtnl_link *link, struct nl_dump_params *p) +{ + nl_dump(p, "sit : %s", link->l_name); +} + +static void sit_dump_details(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct sit_info *sit = link->l_info; + char *name, addr[INET_ADDRSTRLEN], addr6[INET6_ADDRSTRLEN]; + struct rtnl_link *parent; + + if (sit->sit_mask & SIT_ATTR_LINK) { + nl_dump(p, " link "); + + name = NULL; + parent = link_lookup(link->ce_cache, sit->link); + if (parent) + name = rtnl_link_get_name(parent); + + if (name) + nl_dump_line(p, "%s\n", name); + else + nl_dump_line(p, "%u\n", sit->link); + } + + if (sit->sit_mask & SIT_ATTR_LOCAL) { + nl_dump(p, " local "); + if(inet_ntop(AF_INET, &sit->local, addr, sizeof(addr))) + nl_dump_line(p, "%s\n", addr); + else + nl_dump_line(p, "%#x\n", ntohs(sit->local)); + } + + if (sit->sit_mask & SIT_ATTR_REMOTE) { + nl_dump(p, " remote "); + if(inet_ntop(AF_INET, &sit->remote, addr, sizeof(addr))) + nl_dump_line(p, "%s\n", addr); + else + nl_dump_line(p, "%#x\n", ntohs(sit->remote)); + } + + if (sit->sit_mask & SIT_ATTR_TTL) { + nl_dump(p, " ttl "); + nl_dump_line(p, "%u\n", sit->ttl); + } + + if (sit->sit_mask & SIT_ATTR_TOS) { + nl_dump(p, " tos "); + nl_dump_line(p, "%u\n", sit->tos); + } + + if (sit->sit_mask & SIT_ATTR_FLAGS) { + nl_dump(p, " flags "); + nl_dump_line(p, " (%x)\n", sit->flags); + } + + if (sit->sit_mask & SIT_ATTR_PROTO) { + nl_dump(p, " proto "); + nl_dump_line(p, " (%x)\n", sit->proto); + } + + if (sit->sit_mask & SIT_ATTR_6RD_PREFIX) { + nl_dump(p, " 6rd_prefix "); + if(inet_ntop(AF_INET6, &sit->ip6rd_prefix, addr6, INET6_ADDRSTRLEN)) + nl_dump_line(p, "%s\n", addr6); + else + nl_dump_line(p, "[unknown]\n"); + } + + if (sit->sit_mask & SIT_ATTR_6RD_RELAY_PREFIX) { + nl_dump(p, " 6rd_relay_prefix "); + if(inet_ntop(AF_INET, &sit->ip6rd_relay_prefix, addr, sizeof(addr))) + nl_dump_line(p, "%s\n", addr); + else + nl_dump_line(p, "[unknown]\n"); + } + + if (sit->sit_mask & SIT_ATTR_6RD_PREFIXLEN) { + nl_dump(p, " 6rd_prefixlen "); + nl_dump_line(p, "%d\n", sit->ip6rd_prefixlen); + } + + if (sit->sit_mask & SIT_ATTR_6RD_RELAY_PREFIXLEN) { + nl_dump(p, " 6rd_relay_prefixlen "); + nl_dump_line(p, "%d\n", sit->ip6rd_relay_prefixlen); + } +} + +static int sit_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct sit_info *sit_dst, *sit_src = src->l_info; + int err; + + dst->l_info = NULL; + + err = rtnl_link_set_type(dst, "sit"); + if (err < 0) + return err; + + sit_dst = dst->l_info; + + if (!sit_dst || !sit_src) + return -NLE_NOMEM; + + memcpy(sit_dst, sit_src, sizeof(struct sit_info)); + + return 0; +} + +static struct rtnl_link_info_ops sit_info_ops = { + .io_name = "sit", + .io_alloc = sit_alloc, + .io_parse = sit_parse, + .io_dump = { + [NL_DUMP_LINE] = sit_dump_line, + [NL_DUMP_DETAILS] = sit_dump_details, + }, + .io_clone = sit_clone, + .io_put_attrs = sit_put_attrs, + .io_free = sit_free, +}; + +#define IS_SIT_LINK_ASSERT(link, sit) \ + struct sit_info *sit; \ + do { \ + const struct rtnl_link *_link = (link); \ + if (!_link || _link->l_info_ops != &sit_info_ops) { \ + APPBUG("Link is not a sit link. set type \"sit\" first."); \ + return -NLE_OPNOTSUPP; \ + } \ + (sit) = _link->l_info; \ + } while (0) + +struct rtnl_link *rtnl_link_sit_alloc(void) +{ + struct rtnl_link *link; + int err; + + link = rtnl_link_alloc(); + if (!link) + return NULL; + + err = rtnl_link_set_type(link, "sit"); + if (err < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Check if link is a SIT link + * @arg link Link object + * + * @return True if link is a SIT link, otherwise false is returned. + */ +int rtnl_link_is_sit(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "sit"); +} + +/** + * Create a new sit tunnel device + * @arg sock netlink socket + * @arg name name of the tunnel device + * + * Creates a new sit tunnel device in the kernel + * @return 0 on success or a negative error code + */ +int rtnl_link_sit_add(struct nl_sock *sk, const char *name) +{ + struct rtnl_link *link; + int err; + + link = rtnl_link_sit_alloc(); + if (!link) + return -NLE_NOMEM; + + if(name) + rtnl_link_set_name(link, name); + + err = rtnl_link_add(sk, link, NLM_F_CREATE); + rtnl_link_put(link); + + return err; +} + +/** + * Set SIT tunnel interface index + * @arg link Link object + * @arg index interface index + * + * @return 0 on success or a negative error code + */ +int rtnl_link_sit_set_link(struct rtnl_link *link, uint32_t index) +{ + IS_SIT_LINK_ASSERT(link, sit); + + sit->link = index; + sit->sit_mask |= SIT_ATTR_LINK; + + return 0; +} + +/** + * Get SIT tunnel interface index + * @arg link Link object + * + * @return interface index value + */ +uint32_t rtnl_link_sit_get_link(struct rtnl_link *link) +{ + IS_SIT_LINK_ASSERT(link, sit); + + return sit->link; +} + +/** + * Set SIT tunnel local address + * @arg link Link object + * @arg addr local address + * + * @return 0 on success or a negative error code + */ +int rtnl_link_sit_set_local(struct rtnl_link *link, uint32_t addr) +{ + IS_SIT_LINK_ASSERT(link, sit); + + sit->local = addr; + sit->sit_mask |= SIT_ATTR_LOCAL; + + return 0; +} + +/** + * Get SIT tunnel local address + * @arg link Link object + * + * @return local address value + */ +uint32_t rtnl_link_sit_get_local(struct rtnl_link *link) +{ + IS_SIT_LINK_ASSERT(link, sit); + + return sit->local; +} + +/** + * Set SIT tunnel remote address + * @arg link Link object + * @arg remote remote address + * + * @return 0 on success or a negative error code + */ +int rtnl_link_sit_set_remote(struct rtnl_link *link, uint32_t addr) +{ + IS_SIT_LINK_ASSERT(link, sit); + + sit->remote = addr; + sit->sit_mask |= SIT_ATTR_REMOTE; + + return 0; +} + +/** + * Get SIT tunnel remote address + * @arg link Link object + * + * @return remote address + */ +uint32_t rtnl_link_sit_get_remote(struct rtnl_link *link) +{ + IS_SIT_LINK_ASSERT(link, sit); + + return sit->remote; +} + +/** + * Set SIT tunnel ttl + * @arg link Link object + * @arg ttl tunnel ttl + * + * @return 0 on success or a negative error code + */ +int rtnl_link_sit_set_ttl(struct rtnl_link *link, uint8_t ttl) +{ + IS_SIT_LINK_ASSERT(link, sit); + + sit->ttl = ttl; + sit->sit_mask |= SIT_ATTR_TTL; + + return 0; +} + +/** + * Get SIT tunnel ttl + * @arg link Link object + * + * @return ttl value + */ +uint8_t rtnl_link_sit_get_ttl(struct rtnl_link *link) +{ + IS_SIT_LINK_ASSERT(link, sit); + + return sit->ttl; +} + +/** + * Set SIT tunnel tos + * @arg link Link object + * @arg tos tunnel tos + * + * @return 0 on success or a negative error code + */ +int rtnl_link_sit_set_tos(struct rtnl_link *link, uint8_t tos) +{ + IS_SIT_LINK_ASSERT(link, sit); + + sit->tos = tos; + sit->sit_mask |= SIT_ATTR_TOS; + + return 0; +} + +/** + * Get SIT tunnel tos + * @arg link Link object + * + * @return tos value + */ +uint8_t rtnl_link_sit_get_tos(struct rtnl_link *link) +{ + IS_SIT_LINK_ASSERT(link, sit); + + return sit->tos; +} + +/** + * Set SIT tunnel path MTU discovery + * @arg link Link object + * @arg pmtudisc path MTU discovery + * + * @return 0 on success or a negative error code + */ +int rtnl_link_sit_set_pmtudisc(struct rtnl_link *link, uint8_t pmtudisc) +{ + IS_SIT_LINK_ASSERT(link, sit); + + sit->pmtudisc = pmtudisc; + sit->sit_mask |= SIT_ATTR_PMTUDISC; + + return 0; +} + +/** + * Get SIT path MTU discovery + * @arg link Link object + * + * @return pmtudisc value + */ +uint8_t rtnl_link_sit_get_pmtudisc(struct rtnl_link *link) +{ + IS_SIT_LINK_ASSERT(link, sit); + + return sit->pmtudisc; +} + +/** + * Set SIT tunnel flags + * @arg link Link object + * @arg flags tunnel flags + * + * @return 0 on success or a negative error code + */ +int rtnl_link_sit_set_flags(struct rtnl_link *link, uint16_t flags) +{ + IS_SIT_LINK_ASSERT(link, sit); + + sit->flags = flags; + sit->sit_mask |= SIT_ATTR_FLAGS; + + return 0; +} + +/** + * Get SIT path flags + * @arg link Link object + * + * @return flags value + */ +uint16_t rtnl_link_sit_get_flags(struct rtnl_link *link) +{ + IS_SIT_LINK_ASSERT(link, sit); + + return sit->flags; +} + +/** + * Set SIT tunnel proto + * @arg link Link object + * @arg proto tunnel proto + * + * @return 0 on success or a negative error code + */ +int rtnl_link_sit_set_proto(struct rtnl_link *link, uint8_t proto) +{ + IS_SIT_LINK_ASSERT(link, sit); + + sit->proto = proto; + sit->sit_mask |= SIT_ATTR_PROTO; + + return 0; +} + +/** + * Get SIT proto + * @arg link Link object + * + * @return proto value + */ +uint8_t rtnl_link_sit_get_proto(struct rtnl_link *link) +{ + IS_SIT_LINK_ASSERT(link, sit); + + return sit->proto; +} + +/** + * Set ip6rd prefix + * @arg link Link object + * @arg prefix The IPv6 prefix + * + * @return 0 on success or an error code. + */ +int rtnl_link_sit_set_ip6rd_prefix(struct rtnl_link *link, const struct in6_addr *prefix) +{ + IS_SIT_LINK_ASSERT(link, sit); + + sit->ip6rd_prefix = *prefix; + sit->sit_mask |= SIT_ATTR_6RD_PREFIX; + return 0; +} + +/** + * Get ip6rd prefix + * @arg link Link object + * @arg prefix The output IPv6 prefix + * + * @return 0 on success or an error code. If the property is unset, + * this call fails too. + */ +int rtnl_link_sit_get_ip6rd_prefix(const struct rtnl_link *link, struct in6_addr *prefix) +{ + IS_SIT_LINK_ASSERT(link, sit); + + if (!(sit->sit_mask & SIT_ATTR_6RD_PREFIX)) + return -NLE_NOATTR; + + if (prefix) + *prefix = sit->ip6rd_prefix; + return 0; +} + +/** + * Set ip6rd prefix length + * @arg link Link object + * @arg prefixlen The IPv6 prefix length + * + * @return 0 on success or an error code. + */ +int rtnl_link_sit_set_ip6rd_prefixlen(struct rtnl_link *link, uint16_t prefixlen) +{ + IS_SIT_LINK_ASSERT(link, sit); + + sit->sit_mask |= SIT_ATTR_6RD_PREFIXLEN; + sit->ip6rd_prefixlen = prefixlen; + return 0; +} + +/** + * Get ip6rd prefix length + * @arg link Link object + * @arg prefixlen Output pointer for the prefix length + * + * @return 0 on success or an error code. If the property is unset, + * this call fails. + */ +int rtnl_link_sit_get_ip6rd_prefixlen(struct rtnl_link *link, uint16_t *prefixlen) +{ + IS_SIT_LINK_ASSERT(link, sit); + + if (!(sit->sit_mask & SIT_ATTR_6RD_PREFIXLEN)) + return -NLE_NOATTR; + + if (prefixlen) + *prefixlen = sit->ip6rd_prefixlen; + return 0; +} + +/** + * Set ip6rd relay prefix + * @arg link Link object + * @arg prefix The IPv6 prefix length + * + * @return 0 on success or an error code. + */ +int rtnl_link_sit_set_ip6rd_relay_prefix(struct rtnl_link *link, uint32_t prefix) +{ + IS_SIT_LINK_ASSERT(link, sit); + + sit->sit_mask |= SIT_ATTR_6RD_RELAY_PREFIX; + sit->ip6rd_relay_prefix = prefix; + return 0; +} + +/** + * Get ip6rd prefix length + * @arg link Link object + * @arg prefixlen Output pointer for the prefix length + * + * @return 0 on success or an error code. If the property is unset, + * this call fails. + */ +int rtnl_link_sit_get_ip6rd_relay_prefix(const struct rtnl_link *link, uint32_t *prefix) +{ + IS_SIT_LINK_ASSERT(link, sit); + + if (!(sit->sit_mask & SIT_ATTR_6RD_RELAY_PREFIX)) + return -NLE_NOATTR; + + if (prefix) + *prefix = sit->ip6rd_relay_prefix; + return 0; +} + +/** + * Set ip6rd relay prefix length + * @arg link Link object + * @arg prefixlen The IPv6 prefix length + * + * @return 0 on success or an error code. + */ +int rtnl_link_sit_set_ip6rd_relay_prefixlen(struct rtnl_link *link, uint16_t prefixlen) +{ + IS_SIT_LINK_ASSERT(link, sit); + + sit->sit_mask |= SIT_ATTR_6RD_RELAY_PREFIXLEN; + sit->ip6rd_relay_prefixlen = prefixlen; + return 0; +} + +/** + * Get ip6rd relay prefix length + * @arg link Link object + * @arg prefixlen Output pointer for the prefix length + * + * @return 0 on success or an error code. If the property is unset, + * this call fails. + */ +int rtnl_link_sit_get_ip6rd_relay_prefixlen(struct rtnl_link *link, uint16_t *prefixlen) +{ + IS_SIT_LINK_ASSERT(link, sit); + + if (!(sit->sit_mask & SIT_ATTR_6RD_RELAY_PREFIX)) + return -NLE_NOATTR; + + if (prefixlen) + *prefixlen = sit->ip6rd_relay_prefixlen; + return 0; +} + +static void __init sit_init(void) +{ + rtnl_link_register_info(&sit_info_ops); +} + +static void __exit sit_exit(void) +{ + rtnl_link_unregister_info(&sit_info_ops); +} diff --git a/libnl/lib/route/link/sriov.c b/libnl/lib/route/link/sriov.c new file mode 100644 index 0000000..bedf395 --- /dev/null +++ b/libnl/lib/route/link/sriov.c @@ -0,0 +1,1462 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2016 Intel Corp. All rights reserved. + * Copyright (c) 2016 Jef Oliver + */ + +/** + * @ingroup link + * @defgroup sriov SRIOV + * SR-IOV VF link module + * + * @details + * SR-IOV (Single Root Input/Output Virtualization) is a network interface + * that allows for the isolation of the PCI Express resources. In a virtual + * environment, SR-IOV allows multiple virtual machines can share a single + * PCI Express hardware interface. This is done via VFs (Virtual Functions), + * virtual hardware devices with their own PCI address. + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include + +#include +#include +#include +#include + +/** @cond SKIP */ + +#define SRIOVON "on" +#define SRIOVOFF "off" + +#define SET_VF_STAT(link, vf_num, stb, stat, attr) \ + vf_data->vf_stats[stat] = nla_get_u64(stb[attr]) + +/* SRIOV-VF Attributes */ +#define SRIOV_ATTR_INDEX (1 << 0) +#define SRIOV_ATTR_ADDR (1 << 1) +#define SRIOV_ATTR_VLAN (1 << 2) +#define SRIOV_ATTR_TX_RATE (1 << 3) +#define SRIOV_ATTR_SPOOFCHK (1 << 4) +#define SRIOV_ATTR_RATE_MAX (1 << 5) +#define SRIOV_ATTR_RATE_MIN (1 << 6) +#define SRIOV_ATTR_LINK_STATE (1 << 7) +#define SRIOV_ATTR_RSS_QUERY_EN (1 << 8) +#define SRIOV_ATTR_STATS (1 << 9) +#define SRIOV_ATTR_TRUST (1 << 10) +#define SRIOV_ATTR_IB_NODE_GUID (1 << 11) +#define SRIOV_ATTR_IB_PORT_GUID (1 << 12) + +static struct nla_policy sriov_info_policy[IFLA_VF_MAX+1] = { + [IFLA_VF_MAC] = { .minlen = sizeof(struct ifla_vf_mac) }, + [IFLA_VF_VLAN] = { .minlen = sizeof(struct ifla_vf_vlan) }, + [IFLA_VF_VLAN_LIST] = { .type = NLA_NESTED }, + [IFLA_VF_TX_RATE] = { .minlen = sizeof(struct ifla_vf_tx_rate) }, + [IFLA_VF_SPOOFCHK] = { .minlen = sizeof(struct ifla_vf_spoofchk) }, + [IFLA_VF_RATE] = { .minlen = sizeof(struct ifla_vf_rate) }, + [IFLA_VF_LINK_STATE] = { .minlen = sizeof(struct ifla_vf_link_state) }, + [IFLA_VF_RSS_QUERY_EN] = { .minlen = sizeof(struct ifla_vf_rss_query_en) }, + [IFLA_VF_STATS] = { .type = NLA_NESTED }, + [IFLA_VF_TRUST] = { .minlen = sizeof(struct ifla_vf_trust) }, + [IFLA_VF_IB_NODE_GUID] = { .minlen = sizeof(struct ifla_vf_guid) }, + [IFLA_VF_IB_PORT_GUID] = { .minlen = sizeof(struct ifla_vf_guid) }, +}; + +static struct nla_policy sriov_stats_policy[IFLA_VF_STATS_MAX+1] = { + [IFLA_VF_STATS_RX_PACKETS] = { .type = NLA_U64 }, + [IFLA_VF_STATS_TX_PACKETS] = { .type = NLA_U64 }, + [IFLA_VF_STATS_RX_BYTES] = { .type = NLA_U64 }, + [IFLA_VF_STATS_TX_BYTES] = { .type = NLA_U64 }, + [IFLA_VF_STATS_BROADCAST] = { .type = NLA_U64 }, + [IFLA_VF_STATS_MULTICAST] = { .type = NLA_U64 }, +}; + +/** @endcond */ + +/* Clone SRIOV VF list in link object */ +int rtnl_link_sriov_clone(struct rtnl_link *dst, struct rtnl_link *src) { + int err = 0; + struct nl_addr *vf_addr; + struct rtnl_link_vf *s_list, *d_vf, *s_vf, *next, *dest_h = NULL; + nl_vf_vlans_t *src_vlans = NULL, *dst_vlans = NULL; + nl_vf_vlan_info_t *src_vlan_info = NULL, *dst_vlan_info = NULL; + + if (!(err = rtnl_link_has_vf_list(src))) + return 0; + + dst->l_vf_list = rtnl_link_vf_alloc(); + if (!dst->l_vf_list) + return -NLE_NOMEM; + dest_h = dst->l_vf_list; + s_list = src->l_vf_list; + + nl_list_for_each_entry_safe(s_vf, next, &s_list->vf_list, vf_list) { + if (!(d_vf = rtnl_link_vf_alloc())) + return -NLE_NOMEM; + + memcpy(d_vf, s_vf, sizeof(*s_vf)); + + if (s_vf->ce_mask & SRIOV_ATTR_ADDR) { + vf_addr = nl_addr_clone(s_vf->vf_lladdr); + if (!vf_addr) { + rtnl_link_vf_put(d_vf); + return -NLE_NOMEM; + } + d_vf->vf_lladdr = vf_addr; + } + + if (s_vf->ce_mask & SRIOV_ATTR_VLAN) { + src_vlans = s_vf->vf_vlans; + src_vlan_info = src_vlans->vlans; + + err = rtnl_link_vf_vlan_alloc(&dst_vlans, + src_vlans->size); + if (err < 0) { + rtnl_link_vf_put(d_vf); + return err; + } + dst_vlan_info = dst_vlans->vlans; + memcpy(dst_vlans, src_vlans, sizeof(nl_vf_vlans_t)); + memcpy(dst_vlan_info, src_vlan_info, + dst_vlans->size * sizeof(dst_vlan_info)); + d_vf->vf_vlans = dst_vlans; + } + + nl_list_add_head(&d_vf->vf_list, &dest_h->vf_list); + dest_h = d_vf; + } + + return 0; +} + +/* Dump VLAN details for each SRIOV VF */ +static void dump_sriov_vlans(nl_vf_vlans_t *vlans, + struct nl_dump_params *p) { + char buf[64]; + int cur = 0; + nl_vf_vlan_info_t *vlan_data; + uint16_t prot; + + vlan_data = vlans->vlans; + nl_dump(p, "\t VLANS:\n"); + while (cur < vlans->size) { + nl_dump(p, "\t vlan %u", vlan_data[cur].vf_vlan); + if (vlan_data[cur].vf_vlan_qos) + nl_dump(p, " qos %u", vlan_data[cur].vf_vlan_qos); + if (vlan_data[cur].vf_vlan_proto) { + prot = vlan_data[cur].vf_vlan_proto; + nl_dump(p, " proto %s", + rtnl_link_vf_vlanproto2str(prot, buf, + sizeof(buf))); + } + nl_dump(p, "\n"); + cur++; + } + + return; +} + +/* Dump details for each SRIOV VF */ +static void dump_vf_details(struct rtnl_link_vf *vf_data, + struct nl_dump_params *p) { + char buf[64]; + int err = 0; + struct nl_vf_rate vf_rate; + uint32_t v = 0; + + nl_dump(p, "\tvf %u: ", vf_data->vf_index); + if (vf_data->ce_mask & SRIOV_ATTR_LINK_STATE) { + v = vf_data->vf_linkstate; + nl_dump(p, "state %s ", + rtnl_link_vf_linkstate2str(v, buf, sizeof(buf))); + } + if (vf_data->ce_mask & SRIOV_ATTR_ADDR) { + nl_dump(p, "addr %s ", + nl_addr2str(vf_data->vf_lladdr, buf, sizeof(buf))); + } + nl_dump(p, "\n"); + + v = vf_data->vf_spoofchk; + nl_dump(p, "\t spoofchk %s ", v ? SRIOVON : SRIOVOFF); + v = vf_data->vf_trust; + nl_dump(p, "trust %s ", v ? SRIOVON : SRIOVOFF); + v = vf_data->vf_rss_query_en; + nl_dump(p, "rss_query %s\n", v ? SRIOVON : SRIOVOFF); + + err = rtnl_link_vf_get_rate(vf_data, &vf_rate); + if (!err) { + if (vf_rate.api == RTNL_LINK_VF_RATE_API_OLD) + nl_dump(p, "\t rate_api old rate %u\n", + vf_rate.rate); + else if (vf_rate.api == RTNL_LINK_VF_RATE_API_NEW) + nl_dump(p, "\t rate_api new min_rate %u " + "max_rate %u\n", vf_rate.min_tx_rate, + vf_rate.max_tx_rate); + } + if (vf_data->ce_mask & SRIOV_ATTR_VLAN) + dump_sriov_vlans(vf_data->vf_vlans, p); + + return; +} + +/* Loop through SRIOV VF list dump details */ +void rtnl_link_sriov_dump_details(struct rtnl_link *link, + struct nl_dump_params *p) { + int err; + struct rtnl_link_vf *vf_data, *list, *next; + + if (!(err = rtnl_link_has_vf_list(link))) + BUG(); + + nl_dump(p, " SRIOV VF List\n"); + list = link->l_vf_list; + nl_list_for_each_entry_safe(vf_data, next, &list->vf_list, vf_list) { + if (vf_data->ce_mask & SRIOV_ATTR_INDEX) + dump_vf_details(vf_data, p); + } + + return; +} + +/* Dump stats for each SRIOV VF */ +static void dump_vf_stats(struct rtnl_link_vf *vf_data, + struct nl_dump_params *p) { + char *unit; + float res; + + nl_dump(p, " VF %" PRIu64 " Stats:\n", vf_data->vf_index); + nl_dump_line(p, "\tRX: %-14s %-10s %-10s %-10s\n", + "bytes", "packets", "multicast", "broadcast"); + + res = nl_cancel_down_bytes(vf_data->vf_stats[RTNL_LINK_VF_STATS_RX_BYTES], + &unit); + + nl_dump_line(p, + "\t%10.2f %3s %10" PRIu64 " %10" PRIu64 " %10" PRIu64 "\n", + res, unit, + vf_data->vf_stats[RTNL_LINK_VF_STATS_RX_PACKETS], + vf_data->vf_stats[RTNL_LINK_VF_STATS_MULTICAST], + vf_data->vf_stats[RTNL_LINK_VF_STATS_BROADCAST]); + + nl_dump_line(p, "\tTX: %-14s %-10s\n", "bytes", "packets"); + + res = nl_cancel_down_bytes(vf_data->vf_stats[RTNL_LINK_VF_STATS_TX_BYTES], + &unit); + + nl_dump_line(p, "\t%10.2f %3s %10" PRIu64 "\n", res, unit, + vf_data->vf_stats[RTNL_LINK_VF_STATS_TX_PACKETS]); + + return; +} + +/* Loop through SRIOV VF list dump stats */ +void rtnl_link_sriov_dump_stats(struct rtnl_link *link, + struct nl_dump_params *p) { + struct rtnl_link_vf *vf_data, *list, *next; + + list = link->l_vf_list; + nl_list_for_each_entry_safe(vf_data, next, &list->vf_list, vf_list) { + if (vf_data->ce_mask & SRIOV_ATTR_INDEX) + dump_vf_stats(vf_data, p); + } + nl_dump(p, "\n"); + + return; +} + +/* Free stored SRIOV VF data */ +void rtnl_link_sriov_free_data(struct rtnl_link *link) { + int err = 0; + struct rtnl_link_vf *list, *vf, *next; + + if (!(err = rtnl_link_has_vf_list(link))) + return; + + list = link->l_vf_list; + nl_list_for_each_entry_safe(vf, next, &list->vf_list, vf_list) { + nl_list_del(&vf->vf_list); + rtnl_link_vf_put(vf); + } + + rtnl_link_vf_put(link->l_vf_list); + + return; +} + +/* Fill VLAN info array */ +static int rtnl_link_vf_vlan_info(int len, struct ifla_vf_vlan_info **vi, + nl_vf_vlans_t **nvi) { + int cur = 0, err; + nl_vf_vlans_t *vlans; + + if (len <= 0) + return 0; + + if ((err = rtnl_link_vf_vlan_alloc(&vlans, len)) < 0) + return err; + + cur = 0; + while (cur < len) { + vlans->vlans[cur].vf_vlan = vi[cur]->vlan ? vi[cur]->vlan : 0; + vlans->vlans[cur].vf_vlan_qos = vi[cur]->qos ? vi[cur]->qos : 0; + if (vi[cur]->vlan_proto) { + vlans->vlans[cur].vf_vlan_proto = ntohs(vi[cur]->vlan_proto); + } else { + vlans->vlans[cur].vf_vlan_proto = ETH_P_8021Q; + } + cur++; + } + + *nvi = vlans; + return 0; +} + +/* Fill the IFLA_VF_VLAN attribute */ +static void sriov_fill_vf_vlan(struct nl_msg *msg, nl_vf_vlan_info_t *vinfo, + uint32_t index) { + struct ifla_vf_vlan vlan; + + vlan.vf = index; + vlan.vlan = vinfo[0].vf_vlan; + vlan.qos = vinfo[0].vf_vlan_qos; + NLA_PUT(msg, IFLA_VF_VLAN, sizeof(vlan), &vlan); + +nla_put_failure: + return; +} + +/* Fill the IFLA_VF_VLAN_LIST attribute */ +static int sriov_fill_vf_vlan_list(struct nl_msg *msg, nl_vf_vlans_t *vlans, + uint32_t index) { + int cur = 0; + nl_vf_vlan_info_t *vlan_info = vlans->vlans; + struct ifla_vf_vlan_info vlan; + struct nlattr *list; + + if (!(list = nla_nest_start(msg, IFLA_VF_VLAN_LIST))) + return -NLE_MSGSIZE; + + vlan.vf = index; + while (cur < vlans->size) { + vlan.vlan = vlan_info[cur].vf_vlan; + vlan.qos = vlan_info[cur].vf_vlan_qos; + vlan.vlan_proto = vlan_info[cur].vf_vlan_proto; + + NLA_PUT(msg, IFLA_VF_VLAN_INFO, sizeof(vlan), &vlan); + + cur++; + } + +nla_put_failure: + nla_nest_end(msg, list); + + return 0; +} + +/* Fill individual IFLA_VF_INFO attributes */ +static int sriov_fill_vfinfo(struct nl_msg *msg, + struct rtnl_link_vf *vf_data) { + int err = 0, new_rate = 0; + nl_vf_vlans_t *vlan_list; + nl_vf_vlan_info_t *vlan_info; + struct ifla_vf_guid vf_node_guid; + struct ifla_vf_guid vf_port_guid; + struct ifla_vf_link_state vf_link_state; + struct ifla_vf_mac vf_mac; + struct ifla_vf_rate new_vf_rate; + struct ifla_vf_rss_query_en vf_rss_query_en; + struct ifla_vf_spoofchk vf_spoofchk; + struct ifla_vf_trust vf_trust; + struct ifla_vf_tx_rate vf_rate; + struct nlattr *list; + uint16_t proto; + + if (!(vf_data->ce_mask & SRIOV_ATTR_INDEX)) + return -NLE_MISSING_ATTR; + + if (!(list = nla_nest_start(msg, IFLA_VF_INFO))) + return -NLE_MSGSIZE; + + /* IFLA_VF_MAC */ + if (vf_data->ce_mask & SRIOV_ATTR_ADDR) { + vf_mac.vf = vf_data->vf_index; + memset(vf_mac.mac, 0, sizeof(vf_mac.mac)); + memcpy(vf_mac.mac, nl_addr_get_binary_addr(vf_data->vf_lladdr), + nl_addr_get_len(vf_data->vf_lladdr)); + NLA_PUT(msg, IFLA_VF_MAC, sizeof(vf_mac), &vf_mac); + } + + /* IFLA_VF_VLAN IFLA_VF_VLAN_LIST */ + if (vf_data->ce_mask & SRIOV_ATTR_VLAN) { + vlan_list = vf_data->vf_vlans; + vlan_info = vlan_list->vlans; + proto = vlan_info[0].vf_vlan_proto; + if (!proto) + proto = ETH_P_8021Q; + + if ((vlan_list->size == 1) && (proto == ETH_P_8021Q)) + sriov_fill_vf_vlan(msg, vlan_info, vf_data->vf_index); + else + err = sriov_fill_vf_vlan_list(msg, vlan_list, + vf_data->vf_index); + } + + /* IFLA_VF_TX_RATE */ + if (vf_data->ce_mask & SRIOV_ATTR_TX_RATE) { + vf_rate.vf = vf_data->vf_index; + vf_rate.rate = vf_data->vf_rate; + + NLA_PUT(msg, IFLA_VF_TX_RATE, sizeof(vf_rate), &vf_rate); + } + + /* IFLA_VF_RATE */ + new_vf_rate.min_tx_rate = 0; + new_vf_rate.max_tx_rate = 0; + new_vf_rate.vf = vf_data->vf_index; + if (vf_data->ce_mask & SRIOV_ATTR_RATE_MIN) { + new_vf_rate.min_tx_rate = vf_data->vf_min_tx_rate; + new_rate = 1; + } + if (vf_data->ce_mask & SRIOV_ATTR_RATE_MAX) { + new_vf_rate.max_tx_rate = vf_data->vf_max_tx_rate; + new_rate = 1; + } + if (new_rate) + NLA_PUT(msg, IFLA_VF_RATE, sizeof(new_vf_rate), &new_vf_rate); + + /* IFLA_VF_SPOOFCHK */ + if (vf_data->ce_mask & SRIOV_ATTR_SPOOFCHK) { + vf_spoofchk.vf = vf_data->vf_index; + vf_spoofchk.setting = vf_data->vf_spoofchk; + + NLA_PUT(msg, IFLA_VF_SPOOFCHK, sizeof(vf_spoofchk), + &vf_spoofchk); + } + + /* IFLA_VF_LINK_STATE */ + if (vf_data->ce_mask & SRIOV_ATTR_LINK_STATE) { + vf_link_state.vf = vf_data->vf_index; + vf_link_state.link_state = vf_data->vf_linkstate; + + NLA_PUT(msg, IFLA_VF_LINK_STATE, sizeof(vf_link_state), + &vf_link_state); + } + + /* IFLA_VF_RSS_QUERY_EN */ + if (vf_data->ce_mask & SRIOV_ATTR_RSS_QUERY_EN) { + vf_rss_query_en.vf = vf_data->vf_index; + vf_rss_query_en.setting = vf_data->vf_rss_query_en; + + NLA_PUT(msg, IFLA_VF_RSS_QUERY_EN, sizeof(vf_rss_query_en), + &vf_rss_query_en); + } + + /* IFLA_VF_TRUST */ + if (vf_data->ce_mask & SRIOV_ATTR_TRUST) { + vf_trust.vf = vf_data->vf_index; + vf_trust.setting = vf_data->vf_trust; + + NLA_PUT(msg, IFLA_VF_TRUST, sizeof(vf_trust), &vf_trust); + } + + /* IFLA_VF_IB_NODE_GUID */ + if (vf_data->ce_mask & SRIOV_ATTR_IB_NODE_GUID) { + vf_node_guid.vf = vf_data->vf_index; + vf_node_guid.guid = vf_data->vf_guid_node; + + NLA_PUT(msg, IFLA_VF_IB_NODE_GUID, sizeof(vf_node_guid), + &vf_node_guid); + } + + /* IFLA_VF_IB_PORT_GUID */ + if (vf_data->ce_mask & SRIOV_ATTR_IB_PORT_GUID) { + vf_port_guid.vf = vf_data->vf_index; + vf_port_guid.guid = vf_data->vf_guid_port; + + NLA_PUT(msg, IFLA_VF_IB_PORT_GUID, sizeof(vf_port_guid), + &vf_port_guid); + } + +nla_put_failure: + nla_nest_end(msg, list); + + return err; +} + +/* Fill the IFLA_VFINFO_LIST attribute */ +int rtnl_link_sriov_fill_vflist(struct nl_msg *msg, struct rtnl_link *link) { + int err = 0; + struct nlattr *data; + struct rtnl_link_vf *list, *vf, *next; + + if (!(err = rtnl_link_has_vf_list(link))) + return 0; + + if (!(data = nla_nest_start(msg, IFLA_VFINFO_LIST))) + return -NLE_MSGSIZE; + + list = link->l_vf_list; + nl_list_for_each_entry_safe(vf, next, &list->vf_list, vf_list) { + if (vf->ce_mask & SRIOV_ATTR_INDEX) { + if ((err = sriov_fill_vfinfo(msg, vf)) < 0) + goto nla_nest_list_failure; + } + } + +nla_nest_list_failure: + nla_nest_end(msg, data); + + return err; +} + +/* Parse IFLA_VFINFO_LIST and IFLA_VF_INFO attributes */ +int rtnl_link_sriov_parse_vflist(struct rtnl_link *link, struct nlattr **tb) { + int err, len, list_len, list_rem; + struct ifla_vf_mac *vf_lladdr; + struct ifla_vf_vlan *vf_vlan; + struct ifla_vf_vlan_info *vf_vlan_info[MAX_VLAN_LIST_LEN]; + struct ifla_vf_tx_rate *vf_tx_rate; + struct ifla_vf_spoofchk *vf_spoofchk; + struct ifla_vf_link_state *vf_linkstate; + struct ifla_vf_rate *vf_rate; + struct ifla_vf_rss_query_en *vf_rss_query; + struct ifla_vf_trust *vf_trust; + struct nlattr *nla, *nla_list, *t[IFLA_VF_MAX+1], + *stb[RTNL_LINK_VF_STATS_MAX+1]; + nl_vf_vlans_t *vf_vlans = NULL; + struct rtnl_link_vf *vf_data, *vf_head = NULL; + + len = nla_len(tb[IFLA_VFINFO_LIST]); + link->l_vf_list = rtnl_link_vf_alloc(); + if (!link->l_vf_list) + return -NLE_NOMEM; + vf_head = link->l_vf_list; + + for (nla = nla_data(tb[IFLA_VFINFO_LIST]); nla_ok(nla, len); + nla = nla_next(nla, &len)) { + err = nla_parse(t, IFLA_VF_MAX, nla_data(nla), nla_len(nla), + sriov_info_policy); + if (err < 0) + return err; + + vf_data = rtnl_link_vf_alloc(); + if (!vf_data) + return -NLE_NOMEM; + + if (t[IFLA_VF_MAC]) { + vf_lladdr = nla_data(t[IFLA_VF_MAC]); + + vf_data->vf_index = vf_lladdr->vf; + vf_data->ce_mask |= SRIOV_ATTR_INDEX; + + vf_data->vf_lladdr = nl_addr_build(AF_LLC, + vf_lladdr->mac, 6); + if (vf_data->vf_lladdr == NULL) { + rtnl_link_vf_put(vf_data); + return -NLE_NOMEM; + } + nl_addr_set_family(vf_data->vf_lladdr, AF_LLC); + vf_data->ce_mask |= SRIOV_ATTR_ADDR; + } + + if (t[IFLA_VF_VLAN_LIST]) { + list_len = 0; + nla_for_each_nested(nla_list, t[IFLA_VF_VLAN_LIST], + list_rem) { + if (list_len >= MAX_VLAN_LIST_LEN) + break; + vf_vlan_info[list_len] = nla_data(nla_list); + list_len++; + } + + err = rtnl_link_vf_vlan_info(list_len, vf_vlan_info, + &vf_vlans); + if (err < 0) { + rtnl_link_vf_put(vf_data); + return err; + } + + vf_data->vf_vlans = vf_vlans; + vf_data->ce_mask |= SRIOV_ATTR_VLAN; + } else if (t[IFLA_VF_VLAN]) { + vf_vlan = nla_data(t[IFLA_VF_VLAN]); + + if (vf_vlan->vlan) { + err = rtnl_link_vf_vlan_alloc(&vf_vlans, 1); + if (err < 0) { + rtnl_link_vf_put(vf_data); + return err; + } + + vf_vlans->vlans[0].vf_vlan = vf_vlan->vlan; + vf_vlans->vlans[0].vf_vlan_qos = vf_vlan->qos; + vf_vlans->vlans[0].vf_vlan_proto = ETH_P_8021Q; + + vf_data->vf_vlans = vf_vlans; + vf_data->ce_mask |= SRIOV_ATTR_VLAN; + } + } + + if (t[IFLA_VF_TX_RATE]) { + vf_tx_rate = nla_data(t[IFLA_VF_TX_RATE]); + + if (vf_tx_rate->rate) { + vf_data->vf_rate = vf_tx_rate->rate; + vf_data->ce_mask |= SRIOV_ATTR_TX_RATE; + } + } + + if (t[IFLA_VF_SPOOFCHK]) { + vf_spoofchk = nla_data(t[IFLA_VF_SPOOFCHK]); + + if (vf_spoofchk->setting != -1) { + vf_data->vf_spoofchk = vf_spoofchk->setting ? 1 : 0; + vf_data->ce_mask |= SRIOV_ATTR_SPOOFCHK; + } + } + + if (t[IFLA_VF_LINK_STATE]) { + vf_linkstate = nla_data(t[IFLA_VF_LINK_STATE]); + + vf_data->vf_linkstate = vf_linkstate->link_state; + vf_data->ce_mask |= SRIOV_ATTR_LINK_STATE; + } + + if (t[IFLA_VF_RATE]) { + vf_rate = nla_data(t[IFLA_VF_RATE]); + + if (vf_rate->max_tx_rate) { + vf_data->vf_max_tx_rate = vf_rate->max_tx_rate; + vf_data->ce_mask |= SRIOV_ATTR_RATE_MAX; + } + if (vf_rate->min_tx_rate) { + vf_data->vf_min_tx_rate = vf_rate->min_tx_rate; + vf_data->ce_mask |= SRIOV_ATTR_RATE_MIN; + } + } + + if (t[IFLA_VF_RSS_QUERY_EN]) { + vf_rss_query = nla_data(t[IFLA_VF_RSS_QUERY_EN]); + + if (vf_rss_query->setting != -1) { + vf_data->vf_rss_query_en = vf_rss_query->setting ? 1 : 0; + vf_data->ce_mask |= SRIOV_ATTR_RSS_QUERY_EN; + } + } + + if (t[IFLA_VF_STATS]) { + err = nla_parse_nested(stb, IFLA_VF_STATS_MAX, + t[IFLA_VF_STATS], + sriov_stats_policy); + if (err < 0) { + rtnl_link_vf_put(vf_data); + return err; + } + + SET_VF_STAT(link, cur, stb, + RTNL_LINK_VF_STATS_RX_PACKETS, + IFLA_VF_STATS_RX_PACKETS); + SET_VF_STAT(link, cur, stb, + RTNL_LINK_VF_STATS_TX_PACKETS, + IFLA_VF_STATS_TX_PACKETS); + SET_VF_STAT(link, cur, stb, + RTNL_LINK_VF_STATS_RX_BYTES, + IFLA_VF_STATS_RX_BYTES); + SET_VF_STAT(link, cur, stb, + RTNL_LINK_VF_STATS_TX_BYTES, + IFLA_VF_STATS_TX_BYTES); + SET_VF_STAT(link, cur, stb, + RTNL_LINK_VF_STATS_BROADCAST, + IFLA_VF_STATS_BROADCAST); + SET_VF_STAT(link, cur, stb, + RTNL_LINK_VF_STATS_MULTICAST, + IFLA_VF_STATS_MULTICAST); + + vf_data->ce_mask |= IFLA_VF_STATS; + } + + if (t[IFLA_VF_TRUST]) { + vf_trust = nla_data(t[IFLA_VF_TRUST]); + + if (vf_trust->setting != -1) { + vf_data->vf_trust = vf_trust->setting ? 1 : 0; + vf_data->ce_mask |= SRIOV_ATTR_TRUST; + } + } + + nl_list_add_head(&vf_data->vf_list, &vf_head->vf_list); + vf_head = vf_data; + } + + return 0; +} + +/** + * @name SR-IOV Sub-Object + * @{ + */ + +/** + * Add a SRIOV VF object to a link object + * @param link Link object to add to + * @param vf_data SRIOV VF object to add + * + * @return 0 if SRIOV VF object added successfully + * @return -NLE_OBJ_NOTFOUND if \p link or \p vf_data not provided + * @return -NLE_NOMEM if out of memory + */ +int rtnl_link_vf_add(struct rtnl_link *link, struct rtnl_link_vf *vf_data) { + struct rtnl_link_vf *vf_head = NULL; + + if (!link||!vf_data) + return -NLE_OBJ_NOTFOUND; + + if (!link->l_vf_list) { + link->l_vf_list = rtnl_link_vf_alloc(); + if (!link->l_vf_list) + return -NLE_NOMEM; + } + + vf_head = vf_data; + vf_head->ce_refcnt++; + + vf_head = link->l_vf_list; + nl_list_add_head(&vf_data->vf_list, &vf_head->vf_list); + link->l_vf_list = vf_head; + + rtnl_link_set_vf_list(link); + + return 0; +} + +/** + * Allocate a new SRIOV VF object + * + * @return NULL if out of memory + * @return New VF Object + * + * @see rtnl_link_vf_put() + * + * The SRIOV VF object must be returned to the link object with + * rtnl_link_vf_put() when operations are done to prevent memory leaks. + */ +struct rtnl_link_vf *rtnl_link_vf_alloc(void) { + struct rtnl_link_vf *vf; + + if (!(vf = calloc(1, sizeof(*vf)))) + return NULL; + + NL_INIT_LIST_HEAD(&vf->vf_list); + vf->ce_refcnt = 1; + + NL_DBG(4, "Allocated new SRIOV VF object %p\n", vf); + + return vf; +} + +/** + * Free SRIOV VF object. + * @arg vf_data SRIOV VF data object + */ +void rtnl_link_vf_free(struct rtnl_link_vf *vf_data) { + if (!vf_data) + return; + + if (vf_data->ce_refcnt > 0) + NL_DBG(1, "Warning: Freeing SRIOV VF object in use...\n"); + + if (vf_data->ce_mask & SRIOV_ATTR_ADDR) + nl_addr_put(vf_data->vf_lladdr); + if (vf_data->ce_mask & SRIOV_ATTR_VLAN) + rtnl_link_vf_vlan_put(vf_data->vf_vlans); + + NL_DBG(4, "Freed SRIOV VF object %p\n", vf_data); + free(vf_data); + + return; +} + +/** + * Lookup SRIOV VF in link object by VF index. + * + * @return NULL if VF not found + * @return VF Object + * + * @see rtnl_link_vf_put() + * + * The SRIOV VF object must be returned to the link object with + * rtnl_link_vf_put() when operations are done to prevent memory leaks. + */ +struct rtnl_link_vf *rtnl_link_vf_get(struct rtnl_link *link, uint32_t vf_num) { + struct rtnl_link_vf *list, *vf, *next, *ret = NULL; + + list = link->l_vf_list; + nl_list_for_each_entry_safe(vf, next, &list->vf_list, vf_list) { + if (vf->vf_index == vf_num) { + ret = vf; + break; + } + } + + if (ret) { + ret->ce_refcnt++; + NL_DBG(4, "New reference to SRIOV VF object %p, total %i\n", + ret, ret->ce_refcnt); + } + + return ret; +} + +/** + * Return SRIOV VF object to the owning link object. + * @arg vf_data SRIOV VF data object + * + * @see rtnl_link_vf_alloc() + * @see rtnl_link_vf_get() + */ +void rtnl_link_vf_put(struct rtnl_link_vf *vf_data) { + if (!vf_data) + return; + + vf_data->ce_refcnt--; + NL_DBG(4, "Returned SRIOV VF object reference %p, %i remaining\n", + vf_data, vf_data->ce_refcnt); + + if (vf_data->ce_refcnt < 0) + BUG(); + + if (vf_data->ce_refcnt <= 0) + rtnl_link_vf_free(vf_data); + + return; +} + +/** + * Get link layer address of SRIOV Virtual Function + * @arg vf_data SRIOV VF object + * @arg addr Pointer to store Link Layer address + * + * @see rtnl_link_get_num_vf() + * @see rtnl_link_vf_set_addr() + * + * @copydoc pointer_lifetime_warning + * @return 0 if addr is present and addr is set to pointer containing address + * @return -NLE_OBJ_NOTFOUND if information for VF info is not found + * @return -NLE_NOATTR if the link layer address is not set + */ +int rtnl_link_vf_get_addr(struct rtnl_link_vf *vf_data, struct nl_addr **addr) +{ + if (!vf_data) + return -NLE_OBJ_NOTFOUND; + + if (vf_data->ce_mask & SRIOV_ATTR_ADDR) + *addr = vf_data->vf_lladdr; + else + return -NLE_NOATTR; + + return 0; +} + +/** + * Set link layer address of SRIOV Virtual Function object + * @param vf_data SRIOV VF object + * @param addr New link layer address + * + * This function increments the reference counter of the address object + * and overwrites any existing link layer address previously assigned. + * + * @see rtnl_link_vf_get_addr() + */ +void rtnl_link_vf_set_addr(struct rtnl_link_vf *vf_data, struct nl_addr *addr) { + if (vf_data->vf_lladdr) + nl_addr_put(vf_data->vf_lladdr); + + nl_addr_get(addr); + vf_data->vf_lladdr = addr; + vf_data->ce_mask |= SRIOV_ATTR_ADDR; + + return; +} + +/** + * Set the Infiniband node GUID for the SRIOV Virtual Function object + * @param vf_data SRIOV VF object + * @param guid node GUID + */ +void rtnl_link_vf_set_ib_node_guid(struct rtnl_link_vf *vf_data, + uint64_t guid) { + vf_data->vf_guid_node = guid; + vf_data->ce_mask |= SRIOV_ATTR_IB_NODE_GUID; + + return; +} + +/** + * Set the Infiniband port GUID for the SRIOV Virtual Function object + * @param vf_data SRIOV VF object + * @param guid port GUID + */ +void rtnl_link_vf_set_ib_port_guid(struct rtnl_link_vf *vf_data, + uint64_t guid) { + vf_data->vf_guid_port = guid; + vf_data->ce_mask |= SRIOV_ATTR_IB_PORT_GUID; + + return; +} + +/** + * Get index of SRIOV Virtual Function + * @arg vf_data SRIOV VF object + * @arg vf_index Pointer to store VF index + * + * @see rtnl_link_get_num_vf() + * + * @return 0 if index is present and vf_index is set + * @return -NLE_OBJ_NOTFOUND if information for VF info is not found + * @return -NLE_NOATTR if the VF index is not set + */ +int rtnl_link_vf_get_index(struct rtnl_link_vf *vf_data, uint32_t *vf_index) +{ + if (!vf_data) + return -NLE_OBJ_NOTFOUND; + + if (vf_data->ce_mask & SRIOV_ATTR_INDEX) + *vf_index = vf_data->vf_index; + else + return -NLE_NOATTR; + + return 0; +} + +/** + * Set index of SRIOV Virtual Function object + * @param vf_data SRIOV VF object + * @param vf_index Index value + * + * @see rtnl_link_vf_get_index() + */ +void rtnl_link_vf_set_index(struct rtnl_link_vf *vf_data, uint32_t vf_index) +{ + vf_data->vf_index = vf_index; + vf_data->ce_mask |= SRIOV_ATTR_INDEX; + + return; +} + +/** + * Get link state of SRIOV Virtual Function + * @arg vf_data SRIOV VF object + * @arg vf_linkstate Pointer to store VF link state + * + * @see rtnl_link_get_num_vf() + * @see rtnl_link_set_linkstate() + * + * @return 0 if link state is present and vf_linkstate is set + * @return -NLE_OBJ_NOTFOUND if information for VF info is not found + * @return -NLE_NOATTR if the VF link state is not set + */ +int rtnl_link_vf_get_linkstate(struct rtnl_link_vf *vf_data, + uint32_t *vf_linkstate) +{ + if (!vf_data) + return -NLE_OBJ_NOTFOUND; + + if (vf_data->ce_mask & SRIOV_ATTR_LINK_STATE) + *vf_linkstate = vf_data->vf_linkstate; + else + return -NLE_NOATTR; + + return 0; +} + +/** + * Set link state of SRIOV Virtual Function object + * @param vf_data SRIOV VF object + * @param vf_linkstate Link state value + * + * @see rtnl_link_get_linkstate() + * + * Not all hardware supports setting link state. If the feature is unsupported, + * the link change request will fail with -NLE_OPNOTSUPP + */ +void rtnl_link_vf_set_linkstate(struct rtnl_link_vf *vf_data, + uint32_t vf_linkstate) { + vf_data->vf_linkstate = vf_linkstate; + vf_data->ce_mask |= SRIOV_ATTR_LINK_STATE; + + return; +} + +/** + * Get TX Rate Limit of SRIOV Virtual Function + * @arg vf_data SRIOV VF object + * @arg vf_rate Pointer to store VF rate limiting data + * + * @see rtnl_link_get_num_vf() + * @see rtnl_link_set_rate() + * + * When the older rate API has been implemented, the rate member of the struct + * will be set, and the api member will be set to RTNL_LINK_VF_API_OLD. + * When the newer rate API has been implemented, the max_tx_rate + * and/or the minx_tx_rate will be set, and the api member will be set to + * RTNL_LINK_VF_API_NEW. + * + * Old rate API supports only a maximum TX rate. + * ip link set dev vf 0 rate + * New rate API supports minumum and maximum TX rates. + * ip link set dev vf 0 min_tx_rate + * ip link set dev vf 0 max_tx_rate + * + * @return 0 if rate is present and vf_rate is set + * @return -NLE_OBJ_NOTFOUND if information for VF info is not found + * @return -NLE_NOATTR if the VF rate is not set + */ +int rtnl_link_vf_get_rate(struct rtnl_link_vf *vf_data, + struct nl_vf_rate *vf_rate) +{ + int set = 0; + + if (!vf_data) + return -NLE_OBJ_NOTFOUND; + + vf_rate->api = RTNL_LINK_VF_RATE_API_UNSPEC; + vf_rate->rate = 0; + vf_rate->max_tx_rate = 0; + vf_rate->min_tx_rate = 0; + + if (vf_data->ce_mask & SRIOV_ATTR_RATE_MAX) { + if (vf_data->vf_max_tx_rate) { + vf_rate->api = RTNL_LINK_VF_RATE_API_NEW; + vf_rate->max_tx_rate = vf_data->vf_max_tx_rate; + set = 1; + } + } + if (vf_data->ce_mask & SRIOV_ATTR_RATE_MIN) { + if (vf_data->vf_min_tx_rate) { + vf_rate->api = RTNL_LINK_VF_RATE_API_NEW; + vf_rate->min_tx_rate = vf_data->vf_min_tx_rate; + set = 1; + } + } + if ((!set) && (vf_data->ce_mask & SRIOV_ATTR_TX_RATE)) { + if (vf_data->vf_rate) { + vf_rate->api = RTNL_LINK_VF_RATE_API_OLD; + vf_rate->rate = vf_data->vf_rate; + set = 1; + } + } + + if (!set) + return -NLE_NOATTR; + + return 0; +} + +/** + * Set TX Rate Limit of SRIOV Virtual Function object + * @param vf_data SRIOV VF object + * @param vf_rate Rate limiting structure + * + * @see rtnl_link_vf_get_rate() + * + * When setting the rate, the API level must be specificed. + * Valid API levels: + * RTNL_LINK_VF_RATE_API_NEW + * RTNL_LINK_VF_RATE_API_OLD + * + * When using the new API, if either the min_tx_rate or + * max_tx_rate has been set, and the other is being changed, + * you must specify the currently set values to preserve + * them. If this is not done, that setting will be disabled. + * + * Old rate API supports only a maximum TX rate. + * ip link set dev vf 0 rate + * New rate API supports minumum and maximum TX rates. + * ip link set dev vf 0 min_tx_rate + * ip link set dev vf 0 max_tx_rate + * + * Not all hardware supports min_tx_rate. + */ +void rtnl_link_vf_set_rate(struct rtnl_link_vf *vf_data, + struct nl_vf_rate *vf_rate) { + if (vf_rate->api == RTNL_LINK_VF_RATE_API_OLD) { + vf_data->vf_rate = vf_rate->rate; + vf_data->ce_mask |= SRIOV_ATTR_TX_RATE; + } else if (vf_rate->api == RTNL_LINK_VF_RATE_API_NEW) { + vf_data->vf_max_tx_rate = vf_rate->max_tx_rate; + vf_data->ce_mask |= SRIOV_ATTR_RATE_MAX; + + vf_data->vf_min_tx_rate = vf_rate->min_tx_rate; + vf_data->ce_mask |= SRIOV_ATTR_RATE_MIN; + } + + return; +} + +/** + * Get RSS Query EN value of SRIOV Virtual Function + * @arg vf_data SRIOV VF object + * @arg vf_rss_query_en Pointer to store VF RSS Query value + * + * @see rtnl_link_get_num_vf() + * @see rtnl_link_vf_set_rss_query_en() + * + * @return 0 if rss_query_en is present and vf_rss_query_en is set + * @return -NLE_OBJ_NOTFOUND if information for VF info is not found + * @return -NLE_NOATTR if the VF RSS Query EN value is not set + */ +int rtnl_link_vf_get_rss_query_en(struct rtnl_link_vf *vf_data, + uint32_t *vf_rss_query_en) +{ + if (!vf_data) + return -NLE_OBJ_NOTFOUND; + + if (vf_data->ce_mask & SRIOV_ATTR_RSS_QUERY_EN) + *vf_rss_query_en = vf_data->vf_rss_query_en; + else + return -NLE_NOATTR; + + return 0; +} + +/** + * Set RSS configuration querying of SRIOV Virtual Function Object + * @arg vf_data SRIOV VF object + * @arg vf_rss_query_en RSS Query value + * + * @see rtnl_link_vf_get_rss_query_en() + */ +void rtnl_link_vf_set_rss_query_en(struct rtnl_link_vf *vf_data, + uint32_t vf_rss_query_en) { + vf_data->vf_rss_query_en = vf_rss_query_en; + vf_data->ce_mask |= SRIOV_ATTR_RSS_QUERY_EN; + + return; +} + +/** + * Get spoof checking value of SRIOV Virtual Function + * @arg vf_data SRIOV VF object + * @arg vf_spoofchk Pointer to store VF spoofchk value + * + * @see rtnl_link_get_num_vf() + * @see rtnl_link_set_spoofchk() + * + * @return 0 if spoofchk is present and vf_spoofchk is set + * @return -NLE_OBJ_NOTFOUND if information for VF info is not found + * @return -NLE_NOATTR if the VF spoofcheck is not set + */ +int rtnl_link_vf_get_spoofchk(struct rtnl_link_vf *vf_data, + uint32_t *vf_spoofchk) +{ + if (!vf_data) + return -NLE_OBJ_NOTFOUND; + + if (vf_data->ce_mask & SRIOV_ATTR_SPOOFCHK) + *vf_spoofchk = vf_data->vf_spoofchk; + else + return -NLE_NOATTR; + + return 0; +} + +/** + * Set spoof checking value of SRIOV Virtual Function Object + * @param vf_data + * @param vf_spoofchk + * + * @see rtnl_link_vf_get_spoofchk() + */ +void rtnl_link_vf_set_spoofchk(struct rtnl_link_vf *vf_data, + uint32_t vf_spoofchk) { + vf_data->vf_spoofchk = vf_spoofchk; + vf_data->ce_mask |= SRIOV_ATTR_SPOOFCHK; + + return; +} + +/** + * Get value of stat counter for SRIOV Virtual Function + * @arg vf_data SRIOV VF object + * @arg stat Identifier of statistical counter + * @arg vf_stat Pointer to store VF stat value in + * + * @see rtnl_link_get_num_vf() + * + * @return 0 if stat is present and vf_stat is set + * @return -NLE_OBJ_NOTFOUND if information for VF info is not found + * @return -NLE_NOATTR if the VF stat is not set + */ +int rtnl_link_vf_get_stat(struct rtnl_link_vf *vf_data, + rtnl_link_vf_stats_t stat, uint64_t *vf_stat) +{ + if (!vf_data) + return -NLE_OBJ_NOTFOUND; + + if (vf_data->ce_mask & SRIOV_ATTR_STATS) + *vf_stat = vf_data->vf_stats[stat]; + else + return -NLE_NOATTR; + + return 0; +} + +/** + * Get trust setting of SRIOV Virtual Function + * @arg vf_data SRIOV VF object + * @arg vf_trust Pointer to store VF trust value + * + * @see rtnl_link_get_num_vf() + * @see rtnl_link_set_trust() + * + * @return 0 if trust is present and vf_trust is set + * @return -NLE_OBJ_NOTFOUND if information for VF info is not found + * @return -NLE_NOATTR if the VF trust setting is not set + */ +int rtnl_link_vf_get_trust(struct rtnl_link_vf *vf_data, uint32_t *vf_trust) +{ + if (!vf_data) + return -NLE_OBJ_NOTFOUND; + + if (vf_data->ce_mask & SRIOV_ATTR_TRUST) + *vf_trust = vf_data->vf_trust; + else + return -NLE_NOATTR; + + return 0; +} + +/** + * Set user trust setting on SRIOV Virtual Function Object + * @param vf_data + * @param vf_trust + * + * @see rtnl_link_vf_get_trust() + */ +void rtnl_link_vf_set_trust(struct rtnl_link_vf *vf_data, uint32_t vf_trust) { + vf_data->vf_trust = vf_trust; + vf_data->ce_mask |= SRIOV_ATTR_TRUST; + + return; +} + +/** + * Get an array of VLANS on SRIOV Virtual Function + * @arg vf_data SRIOV VF object + * @arg vf_vlans Pointer to nl_vf_vlans_t struct to store vlan info. + * + * @see rtnl_link_get_num_vf() + * + * The SRIOV VF VLANs object must be returned to the SRIOV VF object with + * rtnl_link_vf_vlans_put() when operations are done to prevent memory leaks. + * + * @copydoc pointer_lifetime_warning + * @return 0 if VLAN info is present and vf_vlans is set + * @return -NLE_OBJ_NOTFOUND if information for VF info is not found + * @return -NLE_NOATTR if the VF vlans is not set + */ +int rtnl_link_vf_get_vlans(struct rtnl_link_vf *vf_data, + nl_vf_vlans_t **vf_vlans) { + nl_vf_vlans_t *vf; + + if (!vf_data) + return -NLE_OBJ_NOTFOUND; + + if (vf_data->ce_mask & SRIOV_ATTR_VLAN) { + vf = vf_data->vf_vlans; + vf->ce_refcnt++; + *vf_vlans = vf; + } else + return -NLE_NOATTR; + + return 0; +} + +/** + * Add a SRIOV VF VLANs object to the SRIOV Virtual Function Object + * @param vf_data SRIOV VF object + * @param vf_vlans SRIOV VF VLANs object + * + * @see rtnl_link_vf_get_vlans() + * @see rtnl_link_vf_vlan_alloc() + * + * This function assigns ownership of the SRIOV VF object \p vf_vlans + * to the SRIOV Virtual Function object \p vf_data. Do not use + * rtnl_link_vf_vlan_put() on \p vf_vlans after this. + */ +void rtnl_link_vf_set_vlans(struct rtnl_link_vf *vf_data, + nl_vf_vlans_t *vf_vlans) { + if (!vf_data||!vf_vlans) + return; + + vf_data->vf_vlans = vf_vlans; + vf_data->vf_vlans->ce_refcnt++; + vf_data->ce_mask |= SRIOV_ATTR_VLAN; + + return; +} + +/** + * Allocate a SRIOV VF VLAN object + * @param vf_vlans Pointer to store VLAN object at + * @param vlan_count Number of VLANs that will be stored in VLAN object + * + * The SRIOV VF VLANs object must be returned to the sRIOV VF object with + * rtnl_link_vf_vlan_put() when operations are done to prevent memory leaks. + * + * @return 0 if VLAN object is created and vf_vlans is set. + * @return -NLE_NOMEM if object could not be allocated. + * @return -NLE_INVAL if vlan_count is more than supported by SRIOV VF + */ +int rtnl_link_vf_vlan_alloc(nl_vf_vlans_t **vf_vlans, int vlan_count) { + nl_vf_vlans_t *vlans; + nl_vf_vlan_info_t *vlan_info; + + if (vlan_count > MAX_VLAN_LIST_LEN) + return -NLE_INVAL; + + vlans = calloc(1, sizeof(*vlans)); + if (!vlans) + return -NLE_NOMEM; + + vlan_info = calloc(vlan_count+1, sizeof(*vlan_info)); + if (!vlan_info) { + free(vlans); + return -NLE_NOMEM; + } + + NL_DBG(4, "Allocated new SRIOV VF VLANs object %p\n", vlans); + + vlans->ce_refcnt = 1; + vlans->size = vlan_count; + vlans->vlans = vlan_info; + *vf_vlans = vlans; + + return 0; +} + +/** + * Free an allocated SRIOV VF VLANs object + * @param vf_vlans SRIOV VF VLANs object + */ +void rtnl_link_vf_vlan_free(nl_vf_vlans_t *vf_vlans) { + if (!vf_vlans) + return; + + if (vf_vlans->ce_refcnt > 0) + NL_DBG(1, "Warning: Freeing SRIOV VF VLANs object in use...\n"); + + NL_DBG(4, "Freed SRIOV VF object %p\n", vf_vlans); + free(vf_vlans->vlans); + free(vf_vlans); + + return; +} + +/** + * Return SRIOV VF VLANs object to the owning SRIOV VF object. + * @param vf_vlans SRIOV VF VLANs object + */ +void rtnl_link_vf_vlan_put(nl_vf_vlans_t *vf_vlans) { + if (!vf_vlans) + return; + + vf_vlans->ce_refcnt--; + NL_DBG(4, "Returned SRIOV VF VLANs object reference %p, %i remaining\n", + vf_vlans, vf_vlans->ce_refcnt); + + if (vf_vlans->ce_refcnt < 0) + BUG(); + + if (vf_vlans->ce_refcnt <= 0) + rtnl_link_vf_vlan_free(vf_vlans); + + return; +} + +/** @} */ + +/** + * @name Utilities + * @{ + */ + +static const struct trans_tbl vf_link_states[] = { + __ADD(IFLA_VF_LINK_STATE_AUTO, autodetect), + __ADD(IFLA_VF_LINK_STATE_ENABLE, up), + __ADD(IFLA_VF_LINK_STATE_DISABLE, down), +}; + +char *rtnl_link_vf_linkstate2str(uint32_t ls, char *buf, size_t len) +{ + return __type2str(ls, buf, len, vf_link_states, + ARRAY_SIZE(vf_link_states)); +} + +int rtnl_link_vf_str2linkstate(const char *name) +{ + return __str2type(name, vf_link_states, ARRAY_SIZE(vf_link_states)); +} + +static const struct trans_tbl vf_vlan_proto[] = { + __ADD(ETH_P_8021Q, 8021Q), + __ADD(ETH_P_8021AD, 8021AD), +}; + +char *rtnl_link_vf_vlanproto2str(uint16_t proto, char *buf, size_t len) +{ + return __type2str(proto, buf, len, vf_vlan_proto, + ARRAY_SIZE(vf_vlan_proto)); +} + +int rtnl_link_vf_str2vlanproto(const char *name) +{ + return __str2type(name, vf_vlan_proto, ARRAY_SIZE(vf_vlan_proto)); +} + +/* Return a guid from a format checked string. + * Format string must be xx:xx:xx:xx:xx:xx:xx:xx where XX can be an + * arbitrary hex digit + * + * Function modified from original at iproute2/lib/utils.c:get_guid() + * Original by Eli Cohen . + * iproute2 git commit d91fb3f4c7e4dba806541bdc90b1fb60a3581541 + */ +int rtnl_link_vf_str2guid(uint64_t *guid, const char *guid_s) { + unsigned long int tmp; + char *endptr; + int i; + + if (strlen(guid_s) != RTNL_VF_GUID_STR_LEN) + return -1; + + for (i = 0; i < 7; i++) { + if (guid_s[2 + i * 3] != ':') + return -1; + } + + *guid = 0; + for (i = 0; i < 8; i++) { + tmp = strtoul(guid_s + i * 3, &endptr, 16); + if (endptr != guid_s + i * 3 + 2) + return -1; + + if (tmp > 255) + return -1; + + *guid |= tmp << (56 - 8 * i); + } + + return 0; +} + +/** @} */ + +/** @} */ diff --git a/libnl/lib/route/link/veth.c b/libnl/lib/route/link/veth.c new file mode 100644 index 0000000..a192966 --- /dev/null +++ b/libnl/lib/route/link/veth.c @@ -0,0 +1,305 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Cong Wang + */ + +/** + * @ingroup link + * @defgroup veth VETH + * Virtual Ethernet + * + * @details + * \b Link Type Name: "veth" + * + * @route_doc{link_veth, VETH Documentation} + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static struct nla_policy veth_policy[VETH_INFO_MAX+1] = { + [VETH_INFO_PEER] = { .minlen = sizeof(struct ifinfomsg) }, +}; + +static int veth_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[VETH_INFO_MAX+1]; + struct nlattr *peer_tb[IFLA_MAX + 1]; + struct rtnl_link *peer = link->l_info; + int err; + + NL_DBG(3, "Parsing veth link info\n"); + + if ((err = nla_parse_nested(tb, VETH_INFO_MAX, data, veth_policy)) < 0) + goto errout; + + if (tb[VETH_INFO_PEER]) { + struct nlattr *nla_peer; + struct ifinfomsg *ifi; + + nla_peer = tb[VETH_INFO_PEER]; + ifi = nla_data(nla_peer); + + peer->l_family = ifi->ifi_family; + peer->l_arptype = ifi->ifi_type; + peer->l_index = ifi->ifi_index; + peer->l_flags = ifi->ifi_flags; + peer->l_change = ifi->ifi_change; + err = nla_parse(peer_tb, IFLA_MAX, (struct nlattr *) + ((char *) nla_data(nla_peer) + sizeof(struct ifinfomsg)), + nla_len(nla_peer) - sizeof(struct ifinfomsg), + rtln_link_policy); + if (err < 0) + goto errout; + + err = rtnl_link_info_parse(peer, peer_tb); + if (err < 0) + goto errout; + } + + err = 0; + +errout: + return err; +} + +static void veth_dump_line(struct rtnl_link *link, struct nl_dump_params *p) +{ +} + +static void veth_dump_details(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct rtnl_link *peer = link->l_info; + char *name; + name = rtnl_link_get_name(peer); + nl_dump(p, " peer "); + if (name) + nl_dump_line(p, "%s\n", name); + else + nl_dump_line(p, "%u\n", peer->l_index); +} + +static int veth_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct rtnl_link *dst_peer = NULL, *src_peer = src->l_info; + + /* we are calling nl_object_clone() recursively, this should + * happen only once */ + if (src_peer) { + src_peer->l_info = NULL; + dst_peer = (struct rtnl_link *)nl_object_clone(OBJ_CAST(src_peer)); + if (!dst_peer) + return -NLE_NOMEM; + src_peer->l_info = src; + dst_peer->l_info = dst; + } + dst->l_info = dst_peer; + return 0; +} + +static int veth_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct rtnl_link *peer = link->l_info; + struct ifinfomsg ifi; + struct nlattr *data, *info_peer; + + memset(&ifi, 0, sizeof ifi); + ifi.ifi_family = peer->l_family; + ifi.ifi_type = peer->l_arptype; + ifi.ifi_index = peer->l_index; + ifi.ifi_flags = peer->l_flags; + ifi.ifi_change = peer->l_change; + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + return -NLE_MSGSIZE; + if (!(info_peer = nla_nest_start(msg, VETH_INFO_PEER))) + return -NLE_MSGSIZE; + if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0) + return -NLE_MSGSIZE; + rtnl_link_fill_info(msg, peer); + nla_nest_end(msg, info_peer); + nla_nest_end(msg, data); + + return 0; +} + +static int veth_alloc(struct rtnl_link *link) +{ + struct rtnl_link *peer; + int err; + + /* return early if we are in recursion */ + if (link->l_info) + return 0; + + if (!(peer = rtnl_link_alloc())) + return -NLE_NOMEM; + + /* We don't need to hold a reference here, as link and + * its peer should always be freed together. + */ + peer->l_info = link; + if ((err = rtnl_link_set_type(peer, "veth")) < 0) { + rtnl_link_put(peer); + return err; + } + + link->l_info = peer; + return 0; +} + +static void veth_free(struct rtnl_link *link) +{ + struct rtnl_link *peer = link->l_info; + if (peer) { + link->l_info = NULL; + /* avoid calling this recursively */ + peer->l_info = NULL; + rtnl_link_put(peer); + } + /* the caller should finally free link */ +} + +static struct rtnl_link_info_ops veth_info_ops = { + .io_name = "veth", + .io_parse = veth_parse, + .io_dump = { + [NL_DUMP_LINE] = veth_dump_line, + [NL_DUMP_DETAILS] = veth_dump_details, + }, + .io_alloc = veth_alloc, + .io_clone = veth_clone, + .io_put_attrs = veth_put_attrs, + .io_free = veth_free, +}; + +/** @cond SKIP */ + +#define IS_VETH_LINK_ASSERT(link) \ + if ((link)->l_info_ops != &veth_info_ops) { \ + APPBUG("Link is not a veth link. set type \"veth\" first."); \ + return NULL; \ + } +/** @endcond */ + +/** + * @name VETH Object + * @{ + */ + +/** + * Allocate link object of type veth + * + * @return Allocated link object or NULL. + */ +struct rtnl_link *rtnl_link_veth_alloc(void) +{ + struct rtnl_link *link; + int err; + + if (!(link = rtnl_link_alloc())) + return NULL; + if ((err = rtnl_link_set_type(link, "veth")) < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Get the peer link of a veth link + * + * @return the peer link object. + */ +struct rtnl_link *rtnl_link_veth_get_peer(struct rtnl_link *link) +{ + IS_VETH_LINK_ASSERT(link); + nl_object_get(OBJ_CAST(link->l_info)); + return link->l_info; +} + +/** + * Release a veth link and its peer + * + */ +void rtnl_link_veth_release(struct rtnl_link *link) +{ + veth_free(link); + rtnl_link_put(link); +} + +/** + * Check if link is a veth link + * @arg link Link object + * + * @return True if link is a veth link, otherwise false is returned. + */ +int rtnl_link_is_veth(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "veth"); +} + +/** + * Create a new kernel veth device + * @arg sock netlink socket + * @arg name name of the veth device or NULL + * @arg peer_name name of its peer or NULL + * @arg pid pid of the process in the new netns + * + * Creates a new veth device pair in the kernel and move the peer + * to the network namespace where the process is. If no name is + * provided, the kernel will automatically pick a name of the + * form "veth%d" (e.g. veth0, veth1, etc.) + * + * @return 0 on success or a negative error code + */ +int rtnl_link_veth_add(struct nl_sock *sock, const char *name, + const char *peer_name, pid_t pid) +{ + struct rtnl_link *link, *peer; + int err = -NLE_NOMEM; + + if (!(link = rtnl_link_veth_alloc())) + return -NLE_NOMEM; + peer = link->l_info; + + if (name) + rtnl_link_set_name(link, name); + if (peer_name) + rtnl_link_set_name(peer, peer_name); + + rtnl_link_set_ns_pid(peer, pid); + err = rtnl_link_add(sock, link, NLM_F_CREATE | NLM_F_EXCL); + + rtnl_link_put(link); + return err; +} + +/** @} */ + +static void __init veth_init(void) +{ + rtnl_link_register_info(&veth_info_ops); +} + +static void __exit veth_exit(void) +{ + rtnl_link_unregister_info(&veth_info_ops); +} + +/** @} */ diff --git a/libnl/lib/route/link/vlan.c b/libnl/lib/route/link/vlan.c new file mode 100644 index 0000000..8893411 --- /dev/null +++ b/libnl/lib/route/link/vlan.c @@ -0,0 +1,676 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2013 Thomas Graf + */ + +/** + * @ingroup link + * @defgroup vlan VLAN + * Virtual LAN link module + * + * @details + * \b Link Type Name: "vlan" + * + * @route_doc{link_vlan, VLAN Documentation} + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/** @cond SKIP */ +#define VLAN_HAS_ID (1<<0) +#define VLAN_HAS_FLAGS (1<<1) +#define VLAN_HAS_INGRESS_QOS (1<<2) +#define VLAN_HAS_EGRESS_QOS (1<<3) +#define VLAN_HAS_PROTOCOL (1<<4) + +struct vlan_info +{ + uint16_t vi_vlan_id; + uint16_t vi_protocol; + unsigned int vi_ingress_qos_mask:(VLAN_PRIO_MAX+1); + uint32_t vi_flags; + uint32_t vi_flags_mask; + uint32_t vi_ingress_qos[VLAN_PRIO_MAX+1]; + uint32_t vi_negress; + uint32_t vi_egress_size; + struct vlan_map * vi_egress_qos; + uint32_t vi_mask; +}; + +/** @endcond */ + +static struct nla_policy vlan_policy[IFLA_VLAN_MAX+1] = { + [IFLA_VLAN_ID] = { .type = NLA_U16 }, + [IFLA_VLAN_FLAGS] = { .minlen = sizeof(struct ifla_vlan_flags) }, + [IFLA_VLAN_INGRESS_QOS] = { .type = NLA_NESTED }, + [IFLA_VLAN_EGRESS_QOS] = { .type = NLA_NESTED }, + [IFLA_VLAN_PROTOCOL] = { .type = NLA_U16 }, +}; + +static int vlan_alloc(struct rtnl_link *link) +{ + struct vlan_info *vi; + + if (link->l_info) { + vi = link->l_info; + free(vi->vi_egress_qos); + memset(link->l_info, 0, sizeof(*vi)); + } else { + if ((vi = calloc(1, sizeof(*vi))) == NULL) + return -NLE_NOMEM; + + link->l_info = vi; + } + + return 0; +} + +static int vlan_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_VLAN_MAX+1]; + struct vlan_info *vi; + int err; + + NL_DBG(3, "Parsing VLAN link info\n"); + + if ((err = nla_parse_nested(tb, IFLA_VLAN_MAX, data, vlan_policy)) < 0) + goto errout; + + if ((err = vlan_alloc(link)) < 0) + goto errout; + + vi = link->l_info; + + if (tb[IFLA_VLAN_ID]) { + vi->vi_vlan_id = nla_get_u16(tb[IFLA_VLAN_ID]); + vi->vi_mask |= VLAN_HAS_ID; + } + + if (tb[IFLA_VLAN_PROTOCOL]) { + vi->vi_protocol = nla_get_u16(tb[IFLA_VLAN_PROTOCOL]); + vi->vi_mask |= VLAN_HAS_PROTOCOL; + } + + if (tb[IFLA_VLAN_FLAGS]) { + struct ifla_vlan_flags flags; + nla_memcpy(&flags, tb[IFLA_VLAN_FLAGS], sizeof(flags)); + + vi->vi_flags = flags.flags; + vi->vi_mask |= VLAN_HAS_FLAGS; + } + + if (tb[IFLA_VLAN_INGRESS_QOS]) { + struct ifla_vlan_qos_mapping *map; + struct nlattr *nla; + int remaining; + + vi->vi_ingress_qos_mask = 0; + memset(vi->vi_ingress_qos, 0, sizeof(vi->vi_ingress_qos)); + + nla_for_each_nested(nla, tb[IFLA_VLAN_INGRESS_QOS], remaining) { + if (nla_len(nla) < sizeof(*map)) + return -NLE_INVAL; + + map = nla_data(nla); + if (map->from > VLAN_PRIO_MAX) { + return -NLE_INVAL; + } + + /* Kernel will not explicitly serialize mappings with "to" zero + * (although they are implicitly set). + * + * Thus we only mark those as "set" which are explicitly sent. + * That is similar to what we do with the egress map and it preserves + * previous behavior before NL_CAPABILITY_RTNL_LINK_VLAN_INGRESS_MAP_CLEAR. + * + * It matters only when a received object is send back to kernel to modify + * the link. + */ + vi->vi_ingress_qos_mask |= (1 << map->from); + vi->vi_ingress_qos[map->from] = map->to; + } + + vi->vi_mask |= VLAN_HAS_INGRESS_QOS; + } + + if (tb[IFLA_VLAN_EGRESS_QOS]) { + struct ifla_vlan_qos_mapping *map; + struct nlattr *nla; + int remaining, i = 0; + + nla_for_each_nested(nla, tb[IFLA_VLAN_EGRESS_QOS], remaining) { + if (nla_len(nla) < sizeof(*map)) + return -NLE_INVAL; + i++; + } + + /* align to have a little reserve */ + vi->vi_egress_size = (i + 32) & ~31; + vi->vi_egress_qos = calloc(vi->vi_egress_size, sizeof(*vi->vi_egress_qos)); + if (vi->vi_egress_qos == NULL) + return -NLE_NOMEM; + + i = 0; + nla_for_each_nested(nla, tb[IFLA_VLAN_EGRESS_QOS], remaining) { + map = nla_data(nla); + NL_DBG(4, "Assigning egress qos mapping %d\n", i); + vi->vi_egress_qos[i].vm_from = map->from; + vi->vi_egress_qos[i++].vm_to = map->to; + } + + vi->vi_negress = i; + vi->vi_mask |= VLAN_HAS_EGRESS_QOS; + } + + err = 0; +errout: + return err; +} + +static void vlan_free(struct rtnl_link *link) +{ + struct vlan_info *vi = link->l_info; + + if (vi) { + free(vi->vi_egress_qos); + vi->vi_egress_qos = NULL; + } + + free(vi); + link->l_info = NULL; +} + +static void vlan_dump_line(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct vlan_info *vi = link->l_info; + + nl_dump(p, "vlan-id %d", vi->vi_vlan_id); +} + +static void vlan_dump_details(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct vlan_info *vi = link->l_info; + int printed; + uint32_t i; + char buf[64]; + + rtnl_link_vlan_flags2str(vi->vi_flags, buf, sizeof(buf)); + nl_dump_line(p, " vlan-info id %d <%s>", vi->vi_vlan_id, buf); + + if (vi->vi_mask & VLAN_HAS_PROTOCOL) + nl_dump_line(p, " vlan protocol <%d>", ntohs(vi->vi_protocol)); + + nl_dump(p, "\n"); + + if (vi->vi_mask & VLAN_HAS_INGRESS_QOS) { + nl_dump_line(p, + " ingress vlan prio -> qos/socket prio mapping:\n"); + for (i = 0, printed = 0; i <= VLAN_PRIO_MAX; i++) { + if (vi->vi_ingress_qos_mask & (1 << i)) { + if (printed == 0) + nl_dump_line(p, " "); + nl_dump(p, "%x -> %#08x, ", + i, vi->vi_ingress_qos[i]); + if (printed++ == 3) { + nl_dump(p, "\n"); + printed = 0; + } + } + } + + if (printed > 0 && printed != 4) + nl_dump(p, "\n"); + } + + if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) { + nl_dump_line(p, + " egress qos/socket prio -> vlan prio mapping:\n"); + for (i = 0, printed = 0; i < vi->vi_negress; i++) { + if (printed == 0) + nl_dump_line(p, " "); + nl_dump(p, "%#08x -> %x, ", + vi->vi_egress_qos[i].vm_from, + vi->vi_egress_qos[i].vm_to); + if (printed++ == 3) { + nl_dump(p, "\n"); + printed = 0; + } + } + + if (printed > 0 && printed != 4) + nl_dump(p, "\n"); + } +} + +static int vlan_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct vlan_info *vdst, *vsrc = src->l_info; + int err; + struct vlan_map *p = NULL; + + dst->l_info = NULL; + if ((err = rtnl_link_set_type(dst, "vlan")) < 0) + return err; + vdst = dst->l_info; + + if (vsrc->vi_negress) { + p = calloc(vsrc->vi_negress, + sizeof(struct vlan_map)); + if (!p) + return -NLE_NOMEM; + } + + *vdst = *vsrc; + + if (vsrc->vi_negress) { + vdst->vi_egress_size = vsrc->vi_negress; + vdst->vi_egress_qos = p; + memcpy(vdst->vi_egress_qos, vsrc->vi_egress_qos, + vsrc->vi_negress * sizeof(struct vlan_map)); + } + + return 0; +} + +static int vlan_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct vlan_info *vi = link->l_info; + struct nlattr *data; + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + return -NLE_MSGSIZE; + + if (vi->vi_mask & VLAN_HAS_ID) + NLA_PUT_U16(msg, IFLA_VLAN_ID, vi->vi_vlan_id); + + if (vi->vi_mask & VLAN_HAS_PROTOCOL) + NLA_PUT_U16(msg, IFLA_VLAN_PROTOCOL, vi->vi_protocol); + + if (vi->vi_mask & VLAN_HAS_FLAGS) { + struct ifla_vlan_flags flags = { + .flags = vi->vi_flags, + .mask = vi->vi_flags_mask, + }; + + NLA_PUT(msg, IFLA_VLAN_FLAGS, sizeof(flags), &flags); + } + + if (vi->vi_mask & VLAN_HAS_INGRESS_QOS) { + struct ifla_vlan_qos_mapping map; + struct nlattr *qos; + int i; + + if (!(qos = nla_nest_start(msg, IFLA_VLAN_INGRESS_QOS))) + goto nla_put_failure; + + for (i = 0; i <= VLAN_PRIO_MAX; i++) { + if (vi->vi_ingress_qos_mask & (1 << i)) { + map.from = i; + map.to = vi->vi_ingress_qos[i]; + + NLA_PUT(msg, i, sizeof(map), &map); + } + } + + nla_nest_end(msg, qos); + } + + if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) { + struct ifla_vlan_qos_mapping map; + struct nlattr *qos; + uint32_t i; + + if (!(qos = nla_nest_start(msg, IFLA_VLAN_EGRESS_QOS))) + goto nla_put_failure; + + for (i = 0; i < vi->vi_negress; i++) { + map.from = vi->vi_egress_qos[i].vm_from; + map.to = vi->vi_egress_qos[i].vm_to; + + NLA_PUT(msg, i, sizeof(map), &map); + } + + nla_nest_end(msg, qos); + } + + nla_nest_end(msg, data); + +nla_put_failure: + + return 0; +} + +static struct rtnl_link_info_ops vlan_info_ops = { + .io_name = "vlan", + .io_alloc = vlan_alloc, + .io_parse = vlan_parse, + .io_dump = { + [NL_DUMP_LINE] = vlan_dump_line, + [NL_DUMP_DETAILS] = vlan_dump_details, + }, + .io_clone = vlan_clone, + .io_put_attrs = vlan_put_attrs, + .io_free = vlan_free, +}; + +/** @cond SKIP */ +#define IS_VLAN_LINK_ASSERT(link) \ + if ((link)->l_info_ops != &vlan_info_ops) { \ + APPBUG("Link is not a vlan link. set type \"vlan\" first."); \ + return -NLE_OPNOTSUPP; \ + } +/** @endcond */ + +/** + * @name VLAN Object + * @{ + */ + +/** + * Allocate link object of type VLAN + * + * @return Allocated link object or NULL. + */ +struct rtnl_link *rtnl_link_vlan_alloc(void) +{ + struct rtnl_link *link; + int err; + + if (!(link = rtnl_link_alloc())) + return NULL; + + if ((err = rtnl_link_set_type(link, "vlan")) < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Check if link is a VLAN link + * @arg link Link object + * + * @return True if link is a VLAN link, otherwise false is returned. + */ +int rtnl_link_is_vlan(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "vlan"); +} + +/** + * Set VLAN ID + * @arg link Link object + * @arg id VLAN identifier + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vlan_set_id(struct rtnl_link *link, uint16_t id) +{ + struct vlan_info *vi = link->l_info; + + IS_VLAN_LINK_ASSERT(link); + + vi->vi_vlan_id = id; + vi->vi_mask |= VLAN_HAS_ID; + + return 0; +} + +/** + * Get VLAN Id + * @arg link Link object + * + * @return VLAN id, 0 if not set or a negative error code. + */ +int rtnl_link_vlan_get_id(struct rtnl_link *link) +{ + struct vlan_info *vi = link->l_info; + + IS_VLAN_LINK_ASSERT(link); + + if (vi->vi_mask & VLAN_HAS_ID) + return vi->vi_vlan_id; + else + return 0; +} + +/** + * Set VLAN protocol + * @arg link Link object + * @arg protocol VLAN protocol in network byte order. + * Probably you want to set it to something like htons(ETH_P_8021Q). + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vlan_set_protocol(struct rtnl_link *link, uint16_t protocol) +{ + struct vlan_info *vi = link->l_info; + + IS_VLAN_LINK_ASSERT(link); + + vi->vi_protocol = protocol; + vi->vi_mask |= VLAN_HAS_PROTOCOL; + + return 0; +} + +/** + * Get VLAN protocol + * @arg link Link object + * + * @return VLAN protocol in network byte order like htons(ETH_P_8021Q), + * 0 if not set or a negative error code. + */ +int rtnl_link_vlan_get_protocol(struct rtnl_link *link) +{ + struct vlan_info *vi = link->l_info; + + IS_VLAN_LINK_ASSERT(link); + + if (vi->vi_mask & VLAN_HAS_PROTOCOL) + return vi->vi_protocol; + else + return 0; +} + +/** + * Set VLAN flags + * @arg link Link object + * @arg flags VLAN flags + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_vlan_set_flags(struct rtnl_link *link, unsigned int flags) +{ + struct vlan_info *vi = link->l_info; + + IS_VLAN_LINK_ASSERT(link); + + vi->vi_flags_mask |= flags; + vi->vi_flags |= flags; + vi->vi_mask |= VLAN_HAS_FLAGS; + + return 0; +} + +/** + * Unset VLAN flags + * @arg link Link object + * @arg flags VLAN flags + * + * @return 0 on success or a negative error code. + */ +int rtnl_link_vlan_unset_flags(struct rtnl_link *link, unsigned int flags) +{ + struct vlan_info *vi = link->l_info; + + IS_VLAN_LINK_ASSERT(link); + + vi->vi_flags_mask |= flags; + vi->vi_flags &= ~flags; + vi->vi_mask |= VLAN_HAS_FLAGS; + + return 0; +} + +/** + * Get VLAN flags + * @arg link Link object + * + * @return VLAN flags, 0 if none set, or a negative error code. + */ +int rtnl_link_vlan_get_flags(struct rtnl_link *link) +{ + struct vlan_info *vi = link->l_info; + + IS_VLAN_LINK_ASSERT(link); + + return vi->vi_flags; +} + +/** @} */ + +/** + * @name Quality of Service + * @{ + */ + +int rtnl_link_vlan_set_ingress_map(struct rtnl_link *link, int from, + uint32_t to) +{ + struct vlan_info *vi = link->l_info; + + IS_VLAN_LINK_ASSERT(link); + + if (from < 0 || from > VLAN_PRIO_MAX) + return -NLE_INVAL; + + vi->vi_ingress_qos_mask |= (1 << from); + vi->vi_ingress_qos[from] = to; + vi->vi_mask |= VLAN_HAS_INGRESS_QOS; + + return 0; +} + +uint32_t *rtnl_link_vlan_get_ingress_map(struct rtnl_link *link) +{ + struct vlan_info *vi = link->l_info; + + if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) + return NULL; + + if (vi->vi_mask & VLAN_HAS_INGRESS_QOS) + return vi->vi_ingress_qos; + else + return NULL; +} + +int rtnl_link_vlan_set_egress_map(struct rtnl_link *link, uint32_t from, int to) +{ + struct vlan_info *vi = link->l_info; + + if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) + return -NLE_OPNOTSUPP; + + if (to < 0 || to > VLAN_PRIO_MAX) + return -NLE_INVAL; + + if (vi->vi_negress >= vi->vi_egress_size) { + uint32_t new_size = vi->vi_egress_size + 1 + vi->vi_egress_size / 2; + size_t bytes; + void *ptr; + + if (new_size < vi->vi_egress_size) + return -NLE_NOMEM; + bytes = (size_t) new_size * sizeof(struct vlan_map); + if (bytes / sizeof (struct vlan_map) != new_size) + return -NLE_NOMEM; + ptr = realloc(vi->vi_egress_qos, bytes); + if (!ptr) + return -NLE_NOMEM; + + vi->vi_egress_qos = ptr; + vi->vi_egress_size = new_size; + } + + vi->vi_egress_qos[vi->vi_negress].vm_from = from; + vi->vi_egress_qos[vi->vi_negress].vm_to = to; + vi->vi_negress++; + vi->vi_mask |= VLAN_HAS_EGRESS_QOS; + + return 0; +} + +struct vlan_map *rtnl_link_vlan_get_egress_map(struct rtnl_link *link, + int *negress) +{ + struct vlan_info *vi = link->l_info; + + if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) + return NULL; + + if (negress == NULL) + return NULL; + + if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) { + *negress = vi->vi_negress; + return vi->vi_egress_qos; + } else { + *negress = 0; + return NULL; + } +} + +/** @} */ + +static const struct trans_tbl vlan_flags[] = { + __ADD(VLAN_FLAG_REORDER_HDR, reorder_hdr), + __ADD(VLAN_FLAG_GVRP, gvrp), + __ADD(VLAN_FLAG_LOOSE_BINDING, loose_binding), + __ADD(VLAN_FLAG_MVRP, mvrp), +}; + +/** + * @name Flag Translation + * @{ + */ + +char *rtnl_link_vlan_flags2str(int flags, char *buf, size_t len) +{ + return __flags2str(flags, buf, len, vlan_flags, ARRAY_SIZE(vlan_flags)); +} + +int rtnl_link_vlan_str2flags(const char *name) +{ + return __str2flags(name, vlan_flags, ARRAY_SIZE(vlan_flags)); +} + +/** @} */ + + +static void __init vlan_init(void) +{ + rtnl_link_register_info(&vlan_info_ops); +} + +static void __exit vlan_exit(void) +{ + rtnl_link_unregister_info(&vlan_info_ops); +} + +/** @} */ diff --git a/libnl/lib/route/link/vrf.c b/libnl/lib/route/link/vrf.c new file mode 100644 index 0000000..ecbece7 --- /dev/null +++ b/libnl/lib/route/link/vrf.c @@ -0,0 +1,263 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2015 Cumulus Networks. All rights reserved. + * Copyright (c) 2015 David Ahern + */ + +/** + * @ingroup link + * @defgroup vrf VRF + * Virtual Routing and Forwarding link module + * + * @details + * \b Link Type Name: "vrf" + * + * @route_doc{link_vrf, VRF Documentation} + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifndef __ANDROID__ +#include +#endif + +#define VRF_TABLE_ID_MAX RT_TABLE_MAX + +/** @cond SKIP */ +#define VRF_HAS_TABLE_ID (1<<0) + +struct vrf_info { + uint32_t table_id; + uint32_t vi_mask; +}; + +/** @endcond */ + +static struct nla_policy vrf_policy[IFLA_VRF_MAX + 1] = { + [IFLA_VRF_TABLE] = { .type = NLA_U32 }, +}; + +static int vrf_alloc(struct rtnl_link *link) +{ + struct vrf_info *vi; + + if (link->l_info) { + memset(link->l_info, 0, sizeof (*vi)); + return 0; + } + + if ((vi = calloc(1, sizeof(*vi))) == NULL) + return -NLE_NOMEM; + + link->l_info = vi; + + return 0; +} + +static int vrf_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_VRF_MAX+1]; + struct vrf_info *vi; + int err; + + NL_DBG(3, "Parsing VRF link info"); + + if ((err = nla_parse_nested(tb, IFLA_VRF_MAX, data, vrf_policy)) < 0) + goto errout; + + if ((err = vrf_alloc(link)) < 0) + goto errout; + + vi = link->l_info; + + if (tb[IFLA_VRF_TABLE]) { + vi->table_id = nla_get_u32(tb[IFLA_VRF_TABLE]); + vi->vi_mask |= VRF_HAS_TABLE_ID; + } + + err = 0; + +errout: + return err; +} + +static void vrf_free(struct rtnl_link *link) +{ + free(link->l_info); + link->l_info = NULL; +} + +static int vrf_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct vrf_info *vdst, *vsrc = src->l_info; + int err; + + dst->l_info = NULL; + if ((err = rtnl_link_set_type(dst, "vrf")) < 0) + return err; + vdst = dst->l_info; + + BUG_ON(!vdst || !vsrc); + + memcpy(vdst, vsrc, sizeof(struct vrf_info)); + + return 0; +} + +static int vrf_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct vrf_info *vi = link->l_info; + struct nlattr *data; + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + return -NLE_NOMEM; + + if (vi->vi_mask & VRF_HAS_TABLE_ID) { + NLA_PUT_U32(msg, IFLA_VRF_TABLE, vi->table_id); + } + + nla_nest_end(msg, data); + +nla_put_failure: + + return 0; +} + +static void vrf_dump(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct vrf_info *vi = link->l_info; + + if (vi->vi_mask & VRF_HAS_TABLE_ID) { + nl_dump(p, "table-id %u", vi->table_id); + } +} + +static struct rtnl_link_info_ops vrf_info_ops = { + .io_name = "vrf", + .io_alloc = vrf_alloc, + .io_parse = vrf_parse, + .io_dump = { + [NL_DUMP_LINE] = vrf_dump, + [NL_DUMP_DETAILS] = vrf_dump, + }, + .io_clone = vrf_clone, + .io_put_attrs = vrf_put_attrs, + .io_free = vrf_free, +}; + +/** @cond SKIP */ +#define IS_VRF_LINK_ASSERT(link) \ + if ((link)->l_info_ops != &vrf_info_ops) { \ + APPBUG("Link is not a VRF link. set type \"vrf\" first."); \ + return -NLE_OPNOTSUPP; \ + } +/** @endcond */ + +/** + * @name VRF Object + * @{ + */ + +/** + * Allocate link object of type VRF + * + * @return Allocated link object or NULL. + */ +struct rtnl_link *rtnl_link_vrf_alloc(void) +{ + struct rtnl_link *link; + int err; + + if (!(link = rtnl_link_alloc())) + return NULL; + + if ((err = rtnl_link_set_type(link, "vrf")) < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Check if link is a VRF link + * @arg link Link object + * + * @return True if link is a VRF link, otherwise false is returned. + */ +int rtnl_link_is_vrf(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "vrf"); +} + +/** + * Get VRF table id + * @arg link Link object + * @arg id Pointer to store table identifier + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vrf_get_tableid(struct rtnl_link *link, uint32_t *id) +{ + struct vrf_info *vi = link->l_info; + + IS_VRF_LINK_ASSERT(link); + if(!id) + return -NLE_INVAL; + + if (vi->vi_mask & VRF_HAS_TABLE_ID) + *id = vi->table_id; + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Set VRF table id + * @arg link Link object + * @arg id Table identifier associated with VRF link + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vrf_set_tableid(struct rtnl_link *link, uint32_t id) +{ + struct vrf_info *vi = link->l_info; + + IS_VRF_LINK_ASSERT(link); + if(id > VRF_TABLE_ID_MAX) + return -NLE_INVAL; + + vi->table_id = id; + vi->vi_mask |= VRF_HAS_TABLE_ID; + + return 0; +} + +/** @} */ + +static void __init vrf_init(void) +{ + rtnl_link_register_info(&vrf_info_ops); +} + +static void __exit vrf_exit(void) +{ + rtnl_link_unregister_info(&vrf_info_ops); +} + +/** @} */ diff --git a/libnl/lib/route/link/vxlan.c b/libnl/lib/route/link/vxlan.c new file mode 100644 index 0000000..72d5509 --- /dev/null +++ b/libnl/lib/route/link/vxlan.c @@ -0,0 +1,1792 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Yasunobu Chiba + */ + +/** + * @ingroup link + * @defgroup vxlan VXLAN + * Virtual eXtensible Local Area Network link module + * + * @details + * \b Link Type Name: "vxlan" + * + * @route_doc{link_vxlan, VXLAN Documentation} + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/** @cond SKIP */ +#define VXLAN_ATTR_ID (1<<0) +#define VXLAN_ATTR_GROUP (1<<1) +#define VXLAN_ATTR_LINK (1<<2) +#define VXLAN_ATTR_LOCAL (1<<3) +#define VXLAN_ATTR_TTL (1<<4) +#define VXLAN_ATTR_TOS (1<<5) +#define VXLAN_ATTR_LEARNING (1<<6) +#define VXLAN_ATTR_AGEING (1<<7) +#define VXLAN_ATTR_LIMIT (1<<8) +#define VXLAN_ATTR_PORT_RANGE (1<<9) +#define VXLAN_ATTR_PROXY (1<<10) +#define VXLAN_ATTR_RSC (1<<11) +#define VXLAN_ATTR_L2MISS (1<<12) +#define VXLAN_ATTR_L3MISS (1<<13) +#define VXLAN_ATTR_GROUP6 (1<<14) +#define VXLAN_ATTR_LOCAL6 (1<<15) +#define VXLAN_ATTR_PORT (1<<16) +#define VXLAN_ATTR_UDP_CSUM (1<<17) +#define VXLAN_ATTR_UDP_ZERO_CSUM6_TX (1<<18) +#define VXLAN_ATTR_UDP_ZERO_CSUM6_RX (1<<19) +#define VXLAN_ATTR_REMCSUM_TX (1<<20) +#define VXLAN_ATTR_REMCSUM_RX (1<<21) +#define VXLAN_ATTR_COLLECT_METADATA (1<<22) +#define VXLAN_ATTR_LABEL (1<<23) +#define VXLAN_ATTR_FLAGS (1<<24) + +struct vxlan_info +{ + uint32_t vxi_id; + uint32_t vxi_group; + struct in6_addr vxi_group6; + uint32_t vxi_link; + uint32_t vxi_local; + struct in6_addr vxi_local6; + uint8_t vxi_ttl; + uint8_t vxi_tos; + uint8_t vxi_learning; + uint8_t vxi_flags; + uint32_t vxi_ageing; + uint32_t vxi_limit; + struct ifla_vxlan_port_range vxi_port_range; + uint8_t vxi_proxy; + uint8_t vxi_rsc; + uint8_t vxi_l2miss; + uint8_t vxi_l3miss; + uint16_t vxi_port; + uint8_t vxi_udp_csum; + uint8_t vxi_udp_zero_csum6_tx; + uint8_t vxi_udp_zero_csum6_rx; + uint8_t vxi_remcsum_tx; + uint8_t vxi_remcsum_rx; + uint8_t vxi_collect_metadata; + uint32_t vxi_label; + uint32_t ce_mask; +}; + +/** @endcond */ + +static struct nla_policy vxlan_policy[IFLA_VXLAN_MAX+1] = { + [IFLA_VXLAN_ID] = { .type = NLA_U32 }, + [IFLA_VXLAN_GROUP] = { .minlen = sizeof(uint32_t) }, + [IFLA_VXLAN_GROUP6] = { .minlen = sizeof(struct in6_addr) }, + [IFLA_VXLAN_LINK] = { .type = NLA_U32 }, + [IFLA_VXLAN_LOCAL] = { .minlen = sizeof(uint32_t) }, + [IFLA_VXLAN_LOCAL6] = { .minlen = sizeof(struct in6_addr) }, + [IFLA_VXLAN_TTL] = { .type = NLA_U8 }, + [IFLA_VXLAN_TOS] = { .type = NLA_U8 }, + [IFLA_VXLAN_LABEL] = { .type = NLA_U32 }, + [IFLA_VXLAN_LEARNING] = { .type = NLA_U8 }, + [IFLA_VXLAN_AGEING] = { .type = NLA_U32 }, + [IFLA_VXLAN_LIMIT] = { .type = NLA_U32 }, + [IFLA_VXLAN_PORT_RANGE] = { .minlen = sizeof(struct ifla_vxlan_port_range) }, + [IFLA_VXLAN_PROXY] = { .type = NLA_U8 }, + [IFLA_VXLAN_RSC] = { .type = NLA_U8 }, + [IFLA_VXLAN_L2MISS] = { .type = NLA_U8 }, + [IFLA_VXLAN_L3MISS] = { .type = NLA_U8 }, + [IFLA_VXLAN_COLLECT_METADATA] = { .type = NLA_U8 }, + [IFLA_VXLAN_PORT] = { .type = NLA_U16 }, + [IFLA_VXLAN_UDP_CSUM] = { .type = NLA_U8 }, + [IFLA_VXLAN_UDP_ZERO_CSUM6_TX] = { .type = NLA_U8 }, + [IFLA_VXLAN_UDP_ZERO_CSUM6_RX] = { .type = NLA_U8 }, + [IFLA_VXLAN_REMCSUM_TX] = { .type = NLA_U8 }, + [IFLA_VXLAN_REMCSUM_RX] = { .type = NLA_U8 }, + [IFLA_VXLAN_GBP] = { .type = NLA_FLAG, }, + [IFLA_VXLAN_GPE] = { .type = NLA_FLAG, }, + [IFLA_VXLAN_REMCSUM_NOPARTIAL] = { .type = NLA_FLAG }, +}; + +static int vxlan_alloc(struct rtnl_link *link) +{ + struct vxlan_info *vxi; + + if (link->l_info) + memset(link->l_info, 0, sizeof(*vxi)); + else { + if ((vxi = calloc(1, sizeof(*vxi))) == NULL) + return -NLE_NOMEM; + + link->l_info = vxi; + } + + return 0; +} + +static int vxlan_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_VXLAN_MAX+1]; + struct vxlan_info *vxi; + int err; + + NL_DBG(3, "Parsing VXLAN link info\n"); + + if ((err = nla_parse_nested(tb, IFLA_VXLAN_MAX, data, vxlan_policy)) < 0) + goto errout; + + if ((err = vxlan_alloc(link)) < 0) + goto errout; + + vxi = link->l_info; + + if (tb[IFLA_VXLAN_ID]) { + vxi->vxi_id = nla_get_u32(tb[IFLA_VXLAN_ID]); + vxi->ce_mask |= VXLAN_ATTR_ID; + } + + if (tb[IFLA_VXLAN_GROUP6]) { + nla_memcpy(&vxi->vxi_group6, tb[IFLA_VXLAN_GROUP6], + sizeof(vxi->vxi_group6)); + vxi->ce_mask |= VXLAN_ATTR_GROUP6; + } + + if (tb[IFLA_VXLAN_GROUP]) { + nla_memcpy(&vxi->vxi_group, tb[IFLA_VXLAN_GROUP], + sizeof(vxi->vxi_group)); + vxi->ce_mask |= VXLAN_ATTR_GROUP; + vxi->ce_mask &= ~VXLAN_ATTR_GROUP6; + } + + if (tb[IFLA_VXLAN_LINK]) { + vxi->vxi_link = nla_get_u32(tb[IFLA_VXLAN_LINK]); + vxi->ce_mask |= VXLAN_ATTR_LINK; + } + + if (tb[IFLA_VXLAN_LOCAL6]) { + nla_memcpy(&vxi->vxi_local6, tb[IFLA_VXLAN_LOCAL6], + sizeof(vxi->vxi_local6)); + vxi->ce_mask |= VXLAN_ATTR_LOCAL6; + } + + if (tb[IFLA_VXLAN_LOCAL]) { + nla_memcpy(&vxi->vxi_local, tb[IFLA_VXLAN_LOCAL], + sizeof(vxi->vxi_local)); + vxi->ce_mask |= VXLAN_ATTR_LOCAL; + vxi->ce_mask &= ~VXLAN_ATTR_LOCAL6; + } + + if (tb[IFLA_VXLAN_TTL]) { + vxi->vxi_ttl = nla_get_u8(tb[IFLA_VXLAN_TTL]); + vxi->ce_mask |= VXLAN_ATTR_TTL; + } + + if (tb[IFLA_VXLAN_TOS]) { + vxi->vxi_tos = nla_get_u8(tb[IFLA_VXLAN_TOS]); + vxi->ce_mask |= VXLAN_ATTR_TOS; + } + + if (tb[IFLA_VXLAN_LEARNING]) { + vxi->vxi_learning = nla_get_u8(tb[IFLA_VXLAN_LEARNING]); + vxi->ce_mask |= VXLAN_ATTR_LEARNING; + } + + if (tb[IFLA_VXLAN_AGEING]) { + vxi->vxi_ageing = nla_get_u32(tb[IFLA_VXLAN_AGEING]); + vxi->ce_mask |= VXLAN_ATTR_AGEING; + } + + if (tb[IFLA_VXLAN_LIMIT]) { + vxi->vxi_limit = nla_get_u32(tb[IFLA_VXLAN_LIMIT]); + vxi->ce_mask |= VXLAN_ATTR_LIMIT; + } + + if (tb[IFLA_VXLAN_PORT_RANGE]) { + nla_memcpy(&vxi->vxi_port_range, tb[IFLA_VXLAN_PORT_RANGE], + sizeof(vxi->vxi_port_range)); + vxi->ce_mask |= VXLAN_ATTR_PORT_RANGE; + } + + if (tb[IFLA_VXLAN_PROXY]) { + vxi->vxi_proxy = nla_get_u8(tb[IFLA_VXLAN_PROXY]); + vxi->ce_mask |= VXLAN_ATTR_PROXY; + } + + if (tb[IFLA_VXLAN_RSC]) { + vxi->vxi_rsc = nla_get_u8(tb[IFLA_VXLAN_RSC]); + vxi->ce_mask |= VXLAN_ATTR_RSC; + } + + if (tb[IFLA_VXLAN_L2MISS]) { + vxi->vxi_l2miss = nla_get_u8(tb[IFLA_VXLAN_L2MISS]); + vxi->ce_mask |= VXLAN_ATTR_L2MISS; + } + + if (tb[IFLA_VXLAN_L3MISS]) { + vxi->vxi_l3miss = nla_get_u8(tb[IFLA_VXLAN_L3MISS]); + vxi->ce_mask |= VXLAN_ATTR_L3MISS; + } + + if (tb[IFLA_VXLAN_PORT]) { + vxi->vxi_port = nla_get_u16(tb[IFLA_VXLAN_PORT]); + vxi->ce_mask |= VXLAN_ATTR_PORT; + } + + if (tb[IFLA_VXLAN_UDP_CSUM]) { + vxi->vxi_udp_csum = nla_get_u8(tb[IFLA_VXLAN_UDP_CSUM]); + vxi->ce_mask |= VXLAN_ATTR_UDP_CSUM; + } + + if (tb[IFLA_VXLAN_UDP_ZERO_CSUM6_TX]) { + vxi->vxi_udp_zero_csum6_tx = nla_get_u8(tb[IFLA_VXLAN_UDP_ZERO_CSUM6_TX]); + vxi->ce_mask |= VXLAN_ATTR_UDP_ZERO_CSUM6_TX; + } + + if (tb[IFLA_VXLAN_UDP_ZERO_CSUM6_RX]) { + vxi->vxi_udp_zero_csum6_rx = nla_get_u8(tb[IFLA_VXLAN_UDP_ZERO_CSUM6_RX]); + vxi->ce_mask |= VXLAN_ATTR_UDP_ZERO_CSUM6_RX; + } + + if (tb[IFLA_VXLAN_REMCSUM_TX]) { + vxi->vxi_remcsum_tx = nla_get_u8(tb[IFLA_VXLAN_REMCSUM_TX]); + vxi->ce_mask |= VXLAN_ATTR_REMCSUM_TX; + } + + if (tb[IFLA_VXLAN_REMCSUM_RX]) { + vxi->vxi_remcsum_rx = nla_get_u8(tb[IFLA_VXLAN_REMCSUM_RX]); + vxi->ce_mask |= VXLAN_ATTR_REMCSUM_RX; + } + + if (tb[IFLA_VXLAN_GBP]) + vxi->vxi_flags |= RTNL_LINK_VXLAN_F_GBP; + + if (tb[IFLA_VXLAN_REMCSUM_NOPARTIAL]) + vxi->vxi_flags |= RTNL_LINK_VXLAN_F_REMCSUM_NOPARTIAL; + + if (tb[IFLA_VXLAN_COLLECT_METADATA]) { + vxi->vxi_collect_metadata = nla_get_u8(tb[IFLA_VXLAN_COLLECT_METADATA]); + vxi->ce_mask |= VXLAN_ATTR_COLLECT_METADATA; + } + + if (tb[IFLA_VXLAN_LABEL]) { + vxi->vxi_label = nla_get_u32(tb[IFLA_VXLAN_LABEL]); + vxi->ce_mask |= VXLAN_ATTR_LABEL; + } + + if (tb[IFLA_VXLAN_GPE]) + vxi->vxi_flags |= RTNL_LINK_VXLAN_F_GPE; + + err = 0; + +errout: + return err; +} + +static void vxlan_free(struct rtnl_link *link) +{ + struct vxlan_info *vxi = link->l_info; + + free(vxi); + link->l_info = NULL; +} + +static void vxlan_dump_line(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct vxlan_info *vxi = link->l_info; + + nl_dump(p, "vxlan-id %u", vxi->vxi_id); +} + +static void vxlan_dump_details(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct vxlan_info *vxi = link->l_info; + char *name, addr[INET6_ADDRSTRLEN]; + struct rtnl_link *parent; + + nl_dump_line(p, " vxlan-id %u\n", vxi->vxi_id); + + if (vxi->ce_mask & VXLAN_ATTR_GROUP) { + nl_dump(p, " group "); + if (inet_ntop(AF_INET, &vxi->vxi_group, addr, sizeof(addr))) + nl_dump_line(p, "%s\n", addr); + else + nl_dump_line(p, "%#x\n", ntohs(vxi->vxi_group)); + } else if (vxi->ce_mask & VXLAN_ATTR_GROUP6) { + nl_dump(p, " group "); + if (inet_ntop(AF_INET6, &vxi->vxi_group6, addr, sizeof(addr))) + nl_dump_line(p, "%s\n", addr); + else + nl_dump_line(p, "%#x\n", vxi->vxi_group6); + } + + if (vxi->ce_mask & VXLAN_ATTR_LINK) { + nl_dump(p, " link "); + + name = NULL; + parent = link_lookup(link->ce_cache, vxi->vxi_link); + if (parent) + name = rtnl_link_get_name(parent); + + if (name) + nl_dump_line(p, "%s\n", name); + else + nl_dump_line(p, "%u\n", vxi->vxi_link); + } + + if (vxi->ce_mask & VXLAN_ATTR_LOCAL) { + nl_dump(p, " local "); + if (inet_ntop(AF_INET, &vxi->vxi_local, addr, sizeof(addr))) + nl_dump_line(p, "%s\n", addr); + else + nl_dump_line(p, "%#x\n", ntohs(vxi->vxi_local)); + } else if (vxi->ce_mask & VXLAN_ATTR_LOCAL6) { + nl_dump(p, " local "); + if (inet_ntop(AF_INET6, &vxi->vxi_local6, addr, sizeof(addr))) + nl_dump_line(p, "%s\n", addr); + else + nl_dump_line(p, "%#x\n", vxi->vxi_local6); + } + + + if (vxi->ce_mask & VXLAN_ATTR_TTL) { + nl_dump(p, " ttl "); + if(vxi->vxi_ttl) + nl_dump_line(p, "%u\n", vxi->vxi_ttl); + else + nl_dump_line(p, "inherit\n"); + } + + if (vxi->ce_mask & VXLAN_ATTR_TOS) { + nl_dump(p, " tos "); + if (vxi->vxi_tos == 1) + nl_dump_line(p, "inherit\n", vxi->vxi_tos); + else + nl_dump_line(p, "%#x\n", vxi->vxi_tos); + } + + if (vxi->ce_mask & VXLAN_ATTR_LEARNING) { + nl_dump(p, " learning "); + if (vxi->vxi_learning) + nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_learning); + else + nl_dump_line(p, "disabled\n"); + } + + if (vxi->ce_mask & VXLAN_ATTR_AGEING) { + nl_dump(p, " ageing "); + if (vxi->vxi_ageing) + nl_dump_line(p, "%u seconds\n", vxi->vxi_ageing); + else + nl_dump_line(p, "disabled\n"); + } + + if (vxi->ce_mask & VXLAN_ATTR_LIMIT) { + nl_dump(p, " limit "); + if (vxi->vxi_limit) + nl_dump_line(p, "%u\n", vxi->vxi_limit); + else + nl_dump_line(p, "unlimited\n"); + } + + if (vxi->ce_mask & VXLAN_ATTR_PORT_RANGE) + nl_dump_line(p, " port range %u - %u\n", + ntohs(vxi->vxi_port_range.low), + ntohs(vxi->vxi_port_range.high)); + + if (vxi->ce_mask & VXLAN_ATTR_PROXY) { + nl_dump(p, " proxy "); + if (vxi->vxi_proxy) + nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_proxy); + else + nl_dump_line(p, "disabled\n"); + } + + if (vxi->ce_mask & VXLAN_ATTR_RSC) { + nl_dump(p, " rsc "); + if (vxi->vxi_rsc) + nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_rsc); + else + nl_dump_line(p, "disabled\n"); + } + + if (vxi->ce_mask & VXLAN_ATTR_L2MISS) { + nl_dump(p, " l2miss "); + if (vxi->vxi_l2miss) + nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_l2miss); + else + nl_dump_line(p, "disabled\n"); + } + + if (vxi->ce_mask & VXLAN_ATTR_L3MISS) { + nl_dump(p, " l3miss "); + if (vxi->vxi_l3miss) + nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_l3miss); + else + nl_dump_line(p, "disabled\n"); + } + + if (vxi->ce_mask & VXLAN_ATTR_PORT) { + nl_dump(p, " port "); + nl_dump_line(p, "%u\n", ntohs(vxi->vxi_port)); + } + + if (vxi->ce_mask & VXLAN_ATTR_UDP_CSUM) { + nl_dump(p, " UDP checksums "); + if (vxi->vxi_udp_csum) + nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_udp_csum); + else + nl_dump_line(p, "disabled\n"); + } + + if (vxi->ce_mask & VXLAN_ATTR_UDP_ZERO_CSUM6_TX) { + nl_dump(p, " udp-zero-csum6-tx "); + if (vxi->vxi_udp_zero_csum6_tx) + nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_udp_zero_csum6_tx); + else + nl_dump_line(p, "disabled\n"); + } + + if (vxi->ce_mask & VXLAN_ATTR_UDP_ZERO_CSUM6_RX) { + nl_dump(p, " udp-zero-csum6-rx "); + if (vxi->vxi_udp_zero_csum6_rx) + nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_udp_zero_csum6_rx); + else + nl_dump_line(p, "disabled\n"); + } + + if (vxi->ce_mask & VXLAN_ATTR_REMCSUM_TX) { + nl_dump(p, " remcsum-tx "); + if (vxi->vxi_remcsum_tx) + nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_remcsum_tx); + else + nl_dump_line(p, "disabled\n"); + } + + if (vxi->ce_mask & VXLAN_ATTR_REMCSUM_RX) { + nl_dump(p, " remcsum-rx "); + if (vxi->vxi_remcsum_rx) + nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_remcsum_rx); + else + nl_dump_line(p, "disabled\n"); + } + + if (vxi->vxi_flags & RTNL_LINK_VXLAN_F_GBP) + nl_dump(p, " gbp\n"); + + if (vxi->vxi_flags & RTNL_LINK_VXLAN_F_REMCSUM_NOPARTIAL) + nl_dump(p, " rncsum-nopartial\n"); + + if (vxi->ce_mask & VXLAN_ATTR_COLLECT_METADATA) { + nl_dump(p, " remcsum-rx "); + if (vxi->vxi_collect_metadata) + nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_collect_metadata); + else + nl_dump_line(p, "disabled\n"); + } + + if (vxi->ce_mask & VXLAN_ATTR_LABEL) { + nl_dump(p, " label "); + nl_dump_line(p, "%u\n", ntohl(vxi->vxi_label)); + } + + if (vxi->vxi_flags & RTNL_LINK_VXLAN_F_GPE) + nl_dump(p, " gpe\n"); +} + +static int vxlan_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct vxlan_info *vdst, *vsrc = src->l_info; + int err; + + dst->l_info = NULL; + if ((err = rtnl_link_set_type(dst, "vxlan")) < 0) + return err; + vdst = dst->l_info; + + if (!vdst || !vsrc) + return -NLE_NOMEM; + + memcpy(vdst, vsrc, sizeof(struct vxlan_info)); + + return 0; +} + +static int vxlan_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct vxlan_info *vxi = link->l_info; + struct nlattr *data; + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + return -NLE_MSGSIZE; + + if (vxi->ce_mask & VXLAN_ATTR_ID) + NLA_PUT_U32(msg, IFLA_VXLAN_ID, vxi->vxi_id); + + if (vxi->ce_mask & VXLAN_ATTR_GROUP) + NLA_PUT(msg, IFLA_VXLAN_GROUP, sizeof(vxi->vxi_group), &vxi->vxi_group); + + if (vxi->ce_mask & VXLAN_ATTR_GROUP6) + NLA_PUT(msg, IFLA_VXLAN_GROUP6, sizeof(vxi->vxi_group6), &vxi->vxi_group6); + + if (vxi->ce_mask & VXLAN_ATTR_LINK) + NLA_PUT_U32(msg, IFLA_VXLAN_LINK, vxi->vxi_link); + + if (vxi->ce_mask & VXLAN_ATTR_LOCAL) + NLA_PUT(msg, IFLA_VXLAN_LOCAL, sizeof(vxi->vxi_local), &vxi->vxi_local); + + if (vxi->ce_mask & VXLAN_ATTR_LOCAL6) + NLA_PUT(msg, IFLA_VXLAN_LOCAL6, sizeof(vxi->vxi_local6), &vxi->vxi_local6); + + if (vxi->ce_mask & VXLAN_ATTR_TTL) + NLA_PUT_U8(msg, IFLA_VXLAN_TTL, vxi->vxi_ttl); + + if (vxi->ce_mask & VXLAN_ATTR_TOS) + NLA_PUT_U8(msg, IFLA_VXLAN_TOS, vxi->vxi_tos); + + if (vxi->ce_mask & VXLAN_ATTR_LEARNING) + NLA_PUT_U8(msg, IFLA_VXLAN_LEARNING, vxi->vxi_learning); + + if (vxi->ce_mask & VXLAN_ATTR_AGEING) + NLA_PUT_U32(msg, IFLA_VXLAN_AGEING, vxi->vxi_ageing); + + if (vxi->ce_mask & VXLAN_ATTR_LIMIT) + NLA_PUT_U32(msg, IFLA_VXLAN_LIMIT, vxi->vxi_limit); + + if (vxi->ce_mask & VXLAN_ATTR_PORT_RANGE) + NLA_PUT(msg, IFLA_VXLAN_PORT_RANGE, sizeof(vxi->vxi_port_range), + &vxi->vxi_port_range); + + if (vxi->ce_mask & VXLAN_ATTR_PROXY) + NLA_PUT_U8(msg, IFLA_VXLAN_PROXY, vxi->vxi_proxy); + + if (vxi->ce_mask & VXLAN_ATTR_RSC) + NLA_PUT_U8(msg, IFLA_VXLAN_RSC, vxi->vxi_rsc); + + if (vxi->ce_mask & VXLAN_ATTR_L2MISS) + NLA_PUT_U8(msg, IFLA_VXLAN_L2MISS, vxi->vxi_l2miss); + + if (vxi->ce_mask & VXLAN_ATTR_L3MISS) + NLA_PUT_U8(msg, IFLA_VXLAN_L3MISS, vxi->vxi_l3miss); + + if (vxi->ce_mask & VXLAN_ATTR_PORT) + NLA_PUT_U32(msg, IFLA_VXLAN_PORT, vxi->vxi_port); + + if (vxi->ce_mask & VXLAN_ATTR_UDP_CSUM) + NLA_PUT_U8(msg, IFLA_VXLAN_UDP_CSUM, vxi->vxi_udp_csum); + + if (vxi->ce_mask & VXLAN_ATTR_UDP_ZERO_CSUM6_TX) + NLA_PUT_U8(msg, IFLA_VXLAN_UDP_ZERO_CSUM6_TX, vxi->vxi_udp_zero_csum6_tx); + + if (vxi->ce_mask & VXLAN_ATTR_UDP_ZERO_CSUM6_RX) + NLA_PUT_U8(msg, IFLA_VXLAN_UDP_ZERO_CSUM6_RX, vxi->vxi_udp_zero_csum6_rx); + + if (vxi->ce_mask & VXLAN_ATTR_REMCSUM_TX) + NLA_PUT_U8(msg, IFLA_VXLAN_REMCSUM_TX, vxi->vxi_remcsum_tx); + + if (vxi->ce_mask & VXLAN_ATTR_REMCSUM_RX) + NLA_PUT_U8(msg, IFLA_VXLAN_REMCSUM_RX, vxi->vxi_remcsum_rx); + + if (vxi->vxi_flags & RTNL_LINK_VXLAN_F_GBP) + NLA_PUT_FLAG(msg, IFLA_VXLAN_GBP); + + if (vxi->vxi_flags & RTNL_LINK_VXLAN_F_REMCSUM_NOPARTIAL) + NLA_PUT_FLAG(msg, IFLA_VXLAN_REMCSUM_NOPARTIAL); + + if (vxi->ce_mask & VXLAN_ATTR_COLLECT_METADATA) + NLA_PUT_U8(msg, IFLA_VXLAN_COLLECT_METADATA, vxi->vxi_collect_metadata); + + if (vxi->ce_mask & VXLAN_ATTR_LABEL) + NLA_PUT_U32(msg, IFLA_VXLAN_LABEL, vxi->vxi_label); + + if (vxi->vxi_flags & RTNL_LINK_VXLAN_F_GPE) + NLA_PUT_FLAG(msg, IFLA_VXLAN_GPE); + + nla_nest_end(msg, data); + +nla_put_failure: + + return 0; +} + +static int vxlan_compare(struct rtnl_link *link_a, struct rtnl_link *link_b, + int flags) +{ + struct vxlan_info *a = link_a->l_info; + struct vxlan_info *b = link_b->l_info; + int diff = 0; + uint32_t attrs = flags & LOOSE_COMPARISON ? b->ce_mask : ~0; + +#define VXLAN_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, VXLAN_ATTR_##ATTR, a, b, EXPR) + + diff |= VXLAN_DIFF(ID, a->vxi_id != b->vxi_id); + diff |= VXLAN_DIFF(GROUP, a->vxi_group != b->vxi_group); + diff |= VXLAN_DIFF(LINK, a->vxi_link != b->vxi_link); + diff |= VXLAN_DIFF(LOCAL, a->vxi_local != b->vxi_local); + diff |= VXLAN_DIFF(TOS, a->vxi_tos != b->vxi_tos); + diff |= VXLAN_DIFF(TTL, a->vxi_ttl != b->vxi_ttl); + diff |= VXLAN_DIFF(LEARNING, a->vxi_learning != b->vxi_learning); + diff |= VXLAN_DIFF(AGEING, a->vxi_ageing != b->vxi_ageing); + diff |= VXLAN_DIFF(LIMIT, a->vxi_limit != b->vxi_limit); + diff |= VXLAN_DIFF(PORT_RANGE, + a->vxi_port_range.low != b->vxi_port_range.low); + diff |= VXLAN_DIFF(PORT_RANGE, + a->vxi_port_range.high != b->vxi_port_range.high); + diff |= VXLAN_DIFF(PROXY, a->vxi_proxy != b->vxi_proxy); + diff |= VXLAN_DIFF(RSC, a->vxi_proxy != b->vxi_proxy); + diff |= VXLAN_DIFF(L2MISS, a->vxi_proxy != b->vxi_proxy); + diff |= VXLAN_DIFF(L3MISS, a->vxi_proxy != b->vxi_proxy); + diff |= VXLAN_DIFF(PORT, a->vxi_port != b->vxi_port); + diff |= VXLAN_DIFF(GROUP6, memcmp(&a->vxi_group6, &b->vxi_group6, sizeof(a->vxi_group6)) != 0); + diff |= VXLAN_DIFF(LOCAL6, memcmp(&a->vxi_local6, &b->vxi_local6, sizeof(a->vxi_local6)) != 0); + diff |= VXLAN_DIFF(UDP_CSUM, a->vxi_proxy != b->vxi_proxy); + diff |= VXLAN_DIFF(UDP_ZERO_CSUM6_TX, a->vxi_proxy != b->vxi_proxy); + diff |= VXLAN_DIFF(UDP_ZERO_CSUM6_RX, a->vxi_proxy != b->vxi_proxy); + diff |= VXLAN_DIFF(REMCSUM_TX, a->vxi_proxy != b->vxi_proxy); + diff |= VXLAN_DIFF(REMCSUM_RX, a->vxi_proxy != b->vxi_proxy); + diff |= VXLAN_DIFF(COLLECT_METADATA, a->vxi_collect_metadata != b->vxi_collect_metadata); + diff |= VXLAN_DIFF(LABEL, a->vxi_label != b->vxi_label); + diff |= VXLAN_DIFF(FLAGS, a->vxi_flags != b->vxi_flags); +#undef VXLAN_DIFF + + return diff; +} + +static struct rtnl_link_info_ops vxlan_info_ops = { + .io_name = "vxlan", + .io_alloc = vxlan_alloc, + .io_parse = vxlan_parse, + .io_dump = { + [NL_DUMP_LINE] = vxlan_dump_line, + [NL_DUMP_DETAILS] = vxlan_dump_details, + }, + .io_clone = vxlan_clone, + .io_put_attrs = vxlan_put_attrs, + .io_free = vxlan_free, + .io_compare = vxlan_compare, +}; + +/** @cond SKIP */ +#define IS_VXLAN_LINK_ASSERT(link) \ + if ((link)->l_info_ops != &vxlan_info_ops) { \ + APPBUG("Link is not a vxlan link. set type \"vxlan\" first."); \ + return -NLE_OPNOTSUPP; \ + } +/** @endcond */ + +/** + * @name VXLAN Object + * @{ + */ + +/** + * Allocate link object of type VXLAN + * + * @return Allocated link object or NULL. + */ +struct rtnl_link *rtnl_link_vxlan_alloc(void) +{ + struct rtnl_link *link; + int err; + + if (!(link = rtnl_link_alloc())) + return NULL; + + if ((err = rtnl_link_set_type(link, "vxlan")) < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Check if link is a VXLAN link + * @arg link Link object + * + * @return True if link is a VXLAN link, otherwise false is returned. + */ +int rtnl_link_is_vxlan(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "vxlan"); +} + +/** + * Set VXLAN Network Identifier + * @arg link Link object + * @arg id VXLAN network identifier (or VXLAN segment identifier) + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_id(struct rtnl_link *link, uint32_t id) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (id > VXLAN_ID_MAX) + return -NLE_INVAL; + + vxi->vxi_id = id; + vxi->ce_mask |= VXLAN_ATTR_ID; + + return 0; +} + +/** + * Get VXLAN Network Identifier + * @arg link Link object + * @arg id Pointer to store network identifier + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_get_id(struct rtnl_link *link, uint32_t *id) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if(!id) + return -NLE_INVAL; + + if (vxi->ce_mask & VXLAN_ATTR_ID) + *id = vxi->vxi_id; + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Set VXLAN multicast IP address + * @arg link Link object + * @arg addr Multicast IP address to join + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_group(struct rtnl_link *link, struct nl_addr *addr) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if ((nl_addr_get_family(addr) == AF_INET) && + (nl_addr_get_len(addr) == sizeof(vxi->vxi_group))) { + memcpy(&vxi->vxi_group, nl_addr_get_binary_addr(addr), + sizeof(vxi->vxi_group)); + vxi->ce_mask |= VXLAN_ATTR_GROUP; + vxi->ce_mask &= ~VXLAN_ATTR_GROUP6; + } else if ((nl_addr_get_family(addr) == AF_INET6) && + (nl_addr_get_len(addr) == sizeof(vxi->vxi_group6))) { + memcpy(&vxi->vxi_group6, nl_addr_get_binary_addr(addr), + sizeof(vxi->vxi_group6)); + vxi->ce_mask |= VXLAN_ATTR_GROUP6; + vxi->ce_mask &= ~VXLAN_ATTR_GROUP; + } else + return -NLE_INVAL; + + return 0; +} + +/** + * Get VXLAN multicast IP address + * @arg link Link object + * @arg addr Pointer to store multicast IP address + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_get_group(struct rtnl_link *link, struct nl_addr **addr) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!addr) + return -NLE_INVAL; + + if (vxi->ce_mask & VXLAN_ATTR_GROUP) + *addr = nl_addr_build(AF_INET, &vxi->vxi_group, sizeof(vxi->vxi_group)); + else if (vxi->ce_mask & VXLAN_ATTR_GROUP6) + *addr = nl_addr_build(AF_INET6, &vxi->vxi_group6, sizeof(vxi->vxi_group6)); + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Set physical device to use for VXLAN + * @arg link Link object + * @arg index Interface index + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_link(struct rtnl_link *link, uint32_t index) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + vxi->vxi_link = index; + vxi->ce_mask |= VXLAN_ATTR_LINK; + + return 0; +} + +/** + * Get physical device to use for VXLAN + * @arg link Link object + * @arg index Pointer to store interface index + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_get_link(struct rtnl_link *link, uint32_t *index) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!index) + return -NLE_INVAL; + + if (!(vxi->ce_mask & VXLAN_ATTR_LINK)) + return -NLE_AGAIN; + + *index = vxi->vxi_link; + + return 0; +} + +/** + * Set source address to use for VXLAN + * @arg link Link object + * @arg addr Local address + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_local(struct rtnl_link *link, struct nl_addr *addr) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if ((nl_addr_get_family(addr) == AF_INET) && + (nl_addr_get_len(addr) == sizeof(vxi->vxi_local))) { + memcpy(&vxi->vxi_local, nl_addr_get_binary_addr(addr), + sizeof(vxi->vxi_local)); + vxi->ce_mask |= VXLAN_ATTR_LOCAL; + vxi->ce_mask &= ~VXLAN_ATTR_LOCAL6; + } else if ((nl_addr_get_family(addr) == AF_INET6) && + (nl_addr_get_len(addr) == sizeof(vxi->vxi_local6))) { + memcpy(&vxi->vxi_local6, nl_addr_get_binary_addr(addr), + sizeof(vxi->vxi_local6)); + vxi->ce_mask |= VXLAN_ATTR_LOCAL6; + vxi->ce_mask &= ~VXLAN_ATTR_LOCAL; + } else + return -NLE_INVAL; + + return 0; +} + +/** + * Get source address to use for VXLAN + * @arg link Link object + * @arg addr Pointer to store local address + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_get_local(struct rtnl_link *link, struct nl_addr **addr) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!addr) + return -NLE_INVAL; + + if (vxi->ce_mask & VXLAN_ATTR_LOCAL) + *addr = nl_addr_build(AF_INET, &vxi->vxi_local, sizeof(vxi->vxi_local)); + else if (vxi->ce_mask & VXLAN_ATTR_LOCAL6) + *addr = nl_addr_build(AF_INET6, &vxi->vxi_local6, sizeof(vxi->vxi_local6)); + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Set IP TTL value to use for VXLAN + * @arg link Link object + * @arg ttl TTL value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_ttl(struct rtnl_link *link, uint8_t ttl) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + vxi->vxi_ttl = ttl; + vxi->ce_mask |= VXLAN_ATTR_TTL; + + return 0; +} + +/** + * Get IP TTL value to use for VXLAN + * @arg link Link object + * + * @return TTL value on success or a negative error code + */ +int rtnl_link_vxlan_get_ttl(struct rtnl_link *link) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!(vxi->ce_mask & VXLAN_ATTR_TTL)) + return -NLE_AGAIN; + + return vxi->vxi_ttl; +} + +/** + * Set IP ToS value to use for VXLAN + * @arg link Link object + * @arg tos ToS value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_tos(struct rtnl_link *link, uint8_t tos) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + vxi->vxi_tos = tos; + vxi->ce_mask |= VXLAN_ATTR_TOS; + + return 0; +} + +/** + * Get IP ToS value to use for VXLAN + * @arg link Link object + * + * @return ToS value on success or a negative error code + */ +int rtnl_link_vxlan_get_tos(struct rtnl_link *link) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!(vxi->ce_mask & VXLAN_ATTR_TOS)) + return -NLE_AGAIN; + + return vxi->vxi_tos; +} + +/** + * Set VXLAN learning status + * @arg link Link object + * @arg learning Learning status value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_learning(struct rtnl_link *link, uint8_t learning) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + vxi->vxi_learning = learning; + vxi->ce_mask |= VXLAN_ATTR_LEARNING; + + return 0; +} + +/** + * Get VXLAN learning status + * @arg link Link object + * + * @return Learning status value on success or a negative error code + */ +int rtnl_link_vxlan_get_learning(struct rtnl_link *link) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!(vxi->ce_mask & VXLAN_ATTR_LEARNING)) + return -NLE_AGAIN; + + return vxi->vxi_learning; +} + +/** + * Enable VXLAN address learning + * @arg link Link object + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_enable_learning(struct rtnl_link *link) +{ + return rtnl_link_vxlan_set_learning(link, 1); +} + +/** + * Disable VXLAN address learning + * @arg link Link object + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_disable_learning(struct rtnl_link *link) +{ + return rtnl_link_vxlan_set_learning(link, 0); +} + +/** + * Set expiration timer value to use for VXLAN + * @arg link Link object + * @arg expiry Expiration timer value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_ageing(struct rtnl_link *link, uint32_t expiry) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + vxi->vxi_ageing = expiry; + vxi->ce_mask |= VXLAN_ATTR_AGEING; + + return 0; +} + +/** + * Get expiration timer value to use for VXLAN + * @arg link Link object + * @arg expiry Pointer to store expiration timer value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_get_ageing(struct rtnl_link *link, uint32_t *expiry) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!expiry) + return -NLE_INVAL; + + if (vxi->ce_mask & VXLAN_ATTR_AGEING) + *expiry = vxi->vxi_ageing; + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Set maximum number of forwarding database entries to use for VXLAN + * @arg link Link object + * @arg limit Maximum number + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_limit(struct rtnl_link *link, uint32_t limit) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + vxi->vxi_limit = limit; + vxi->ce_mask |= VXLAN_ATTR_LIMIT; + + return 0; +} + +/** + * Get maximum number of forwarding database entries to use for VXLAN + * @arg link Link object + * @arg limit Pointer to store maximum number + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_get_limit(struct rtnl_link *link, uint32_t *limit) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!limit) + return -NLE_INVAL; + + if (vxi->ce_mask & VXLAN_ATTR_LIMIT) + *limit = vxi->vxi_limit; + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Set range of UDP port numbers to use for VXLAN + * @arg link Link object + * @arg range Port number range + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_port_range(struct rtnl_link *link, + struct ifla_vxlan_port_range *range) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!range) + return -NLE_INVAL; + + memcpy(&vxi->vxi_port_range, range, sizeof(vxi->vxi_port_range)); + vxi->ce_mask |= VXLAN_ATTR_PORT_RANGE; + + return 0; +} + +/** + * Get range of UDP port numbers to use for VXLAN + * @arg link Link object + * @arg range Pointer to store port range + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_get_port_range(struct rtnl_link *link, + struct ifla_vxlan_port_range *range) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!range) + return -NLE_INVAL; + + if (vxi->ce_mask & VXLAN_ATTR_PORT_RANGE) + memcpy(range, &vxi->vxi_port_range, sizeof(*range)); + else + return -NLE_AGAIN; + + return 0; +} + +/** + * Set ARP proxy status to use for VXLAN + * @arg link Link object + * @arg proxy Status value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_proxy(struct rtnl_link *link, uint8_t proxy) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + vxi->vxi_proxy = proxy; + vxi->ce_mask |= VXLAN_ATTR_PROXY; + + return 0; +} + +/** + * Get ARP proxy status to use for VXLAN + * @arg link Link object + * + * @return Status value on success or a negative error code + */ +int rtnl_link_vxlan_get_proxy(struct rtnl_link *link) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!(vxi->ce_mask & VXLAN_ATTR_PROXY)) + return -NLE_AGAIN; + + return vxi->vxi_proxy; +} + +/** + * Enable ARP proxy + * @arg link Link object + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_enable_proxy(struct rtnl_link *link) +{ + return rtnl_link_vxlan_set_proxy(link, 1); +} + +/** + * Disable ARP proxy + * @arg link Link object + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_disable_proxy(struct rtnl_link *link) +{ + return rtnl_link_vxlan_set_proxy(link, 0); +} + +/** + * Set Route Short Circuit status to use for VXLAN + * @arg link Link object + * @arg rsc Status value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_rsc(struct rtnl_link *link, uint8_t rsc) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + vxi->vxi_rsc = rsc; + vxi->ce_mask |= VXLAN_ATTR_RSC; + + return 0; +} + +/** + * Get Route Short Circuit status to use for VXLAN + * @arg link Link object + * + * @return Status value on success or a negative error code + */ +int rtnl_link_vxlan_get_rsc(struct rtnl_link *link) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!(vxi->ce_mask & VXLAN_ATTR_RSC)) + return -NLE_AGAIN; + + return vxi->vxi_rsc; +} + +/** + * Enable Route Short Circuit + * @arg link Link object + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_enable_rsc(struct rtnl_link *link) +{ + return rtnl_link_vxlan_set_rsc(link, 1); +} + +/** + * Disable Route Short Circuit + * @arg link Link object + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_disable_rsc(struct rtnl_link *link) +{ + return rtnl_link_vxlan_set_rsc(link, 0); +} + +/** + * Set netlink LLADDR miss notification status to use for VXLAN + * @arg link Link object + * @arg miss Status value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_l2miss(struct rtnl_link *link, uint8_t miss) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + vxi->vxi_l2miss = miss; + vxi->ce_mask |= VXLAN_ATTR_L2MISS; + + return 0; +} + +/** + * Get netlink LLADDR miss notification status to use for VXLAN + * @arg link Link object + * + * @return Status value on success or a negative error code + */ +int rtnl_link_vxlan_get_l2miss(struct rtnl_link *link) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!(vxi->ce_mask & VXLAN_ATTR_L2MISS)) + return -NLE_AGAIN; + + return vxi->vxi_l2miss; +} + +/** + * Enable netlink LLADDR miss notifications + * @arg link Link object + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_enable_l2miss(struct rtnl_link *link) +{ + return rtnl_link_vxlan_set_l2miss(link, 1); +} + +/** + * Disable netlink LLADDR miss notifications + * @arg link Link object + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_disable_l2miss(struct rtnl_link *link) +{ + return rtnl_link_vxlan_set_l2miss(link, 0); +} + +/** + * Set netlink IP ADDR miss notification status to use for VXLAN + * @arg link Link object + * @arg miss Status value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_l3miss(struct rtnl_link *link, uint8_t miss) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + vxi->vxi_l3miss = miss; + vxi->ce_mask |= VXLAN_ATTR_L3MISS; + + return 0; +} + +/** + * Get netlink IP ADDR miss notification status to use for VXLAN + * @arg link Link object + * + * @return Status value on success or a negative error code + */ +int rtnl_link_vxlan_get_l3miss(struct rtnl_link *link) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!(vxi->ce_mask & VXLAN_ATTR_L3MISS)) + return -NLE_AGAIN; + + return vxi->vxi_l3miss; +} + +/** + * Enable netlink IP ADDR miss notifications + * @arg link Link object + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_enable_l3miss(struct rtnl_link *link) +{ + return rtnl_link_vxlan_set_l3miss(link, 1); +} + +/** + * Disable netlink IP ADDR miss notifications + * @arg link Link object + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_disable_l3miss(struct rtnl_link *link) +{ + return rtnl_link_vxlan_set_l3miss(link, 0); +} + +/** + * Set UDP destination port to use for VXLAN + * @arg link Link object + * @arg port Destination port + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_port(struct rtnl_link *link, uint32_t port) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + vxi->vxi_port = htons(port); + vxi->ce_mask |= VXLAN_ATTR_PORT; + + return 0; +} + +/** + * Get UDP destination port to use for VXLAN + * @arg link Link object + * @arg port Pointer to store destination port + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_get_port(struct rtnl_link *link, uint32_t *port) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!port) + return -NLE_INVAL; + + if (!(vxi->ce_mask & VXLAN_ATTR_PORT)) + return -NLE_NOATTR; + + *port = ntohs(vxi->vxi_port); + + return 0; +} + +/** + * Set UDP checksum status to use for VXLAN + * @arg link Link object + * @arg csum Status value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_udp_csum(struct rtnl_link *link, uint8_t csum) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + vxi->vxi_udp_csum = csum; + vxi->ce_mask |= VXLAN_ATTR_UDP_CSUM; + + return 0; +} + +/** + * Get UDP checksum status to use for VXLAN + * @arg link Link object + * + * @return Status value on success or a negative error code + */ +int rtnl_link_vxlan_get_udp_csum(struct rtnl_link *link) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!(vxi->ce_mask & VXLAN_ATTR_UDP_CSUM)) + return -NLE_NOATTR; + + return vxi->vxi_udp_csum; +} + +/** + * Set skip UDP checksum transmitted over IPv6 status to use for VXLAN + * @arg link Link object + * @arg csum Status value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_udp_zero_csum6_tx(struct rtnl_link *link, uint8_t csum) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + vxi->vxi_udp_zero_csum6_tx = csum; + vxi->ce_mask |= VXLAN_ATTR_UDP_ZERO_CSUM6_TX; + + return 0; +} + +/** + * Get skip UDP checksum transmitted over IPv6 status to use for VXLAN + * @arg link Link object + * + * @return Status value on success or a negative error code + */ +int rtnl_link_vxlan_get_udp_zero_csum6_tx(struct rtnl_link *link) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!(vxi->ce_mask & VXLAN_ATTR_UDP_ZERO_CSUM6_TX)) + return -NLE_NOATTR; + + return vxi->vxi_udp_zero_csum6_tx; +} + +/** + * Set skip UDP checksum received over IPv6 status to use for VXLAN + * @arg link Link object + * @arg csum Status value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_udp_zero_csum6_rx(struct rtnl_link *link, uint8_t csum) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + vxi->vxi_udp_zero_csum6_rx = csum; + vxi->ce_mask |= VXLAN_ATTR_UDP_ZERO_CSUM6_RX; + + return 0; +} + +/** + * Get skip UDP checksum received over IPv6 status to use for VXLAN + * @arg link Link object + * + * @return Status value on success or a negative error code + */ +int rtnl_link_vxlan_get_udp_zero_csum6_rx(struct rtnl_link *link) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!(vxi->ce_mask & VXLAN_ATTR_UDP_ZERO_CSUM6_RX)) + return -NLE_NOATTR; + + return vxi->vxi_udp_zero_csum6_rx; +} + +/** + * Set remote offload transmit checksum status to use for VXLAN + * @arg link Link object + * @arg csum Status value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_remcsum_tx(struct rtnl_link *link, uint8_t csum) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + vxi->vxi_remcsum_tx = csum; + vxi->ce_mask |= VXLAN_ATTR_REMCSUM_TX; + + return 0; +} + +/** + * Get remote offload transmit checksum status to use for VXLAN + * @arg link Link object + * + * @return Status value on success or a negative error code + */ +int rtnl_link_vxlan_get_remcsum_tx(struct rtnl_link *link) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!(vxi->ce_mask & VXLAN_ATTR_REMCSUM_TX)) + return -NLE_NOATTR; + + return vxi->vxi_remcsum_tx; +} + +/** + * Set remote offload receive checksum status to use for VXLAN + * @arg link Link object + * @arg csum Status value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_remcsum_rx(struct rtnl_link *link, uint8_t csum) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + vxi->vxi_remcsum_rx = csum; + vxi->ce_mask |= VXLAN_ATTR_REMCSUM_RX; + + return 0; +} + +/** + * Get remote offload receive checksum status to use for VXLAN + * @arg link Link object + * + * @return Status value on success or a negative error code + */ +int rtnl_link_vxlan_get_remcsum_rx(struct rtnl_link *link) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!(vxi->ce_mask & VXLAN_ATTR_REMCSUM_RX)) + return -NLE_NOATTR; + + return vxi->vxi_remcsum_rx; +} + +/** + * Set collect metadata status to use for VXLAN + * @arg link Link object + * @arg collect Status value + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_collect_metadata(struct rtnl_link *link, uint8_t collect) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + vxi->vxi_collect_metadata = collect; + vxi->ce_mask |= VXLAN_ATTR_COLLECT_METADATA; + + return 0; +} + +/** + * Get collect metadata status to use for VXLAN + * @arg link Link object + * + * @return Status value on success or a negative error code + */ +int rtnl_link_vxlan_get_collect_metadata(struct rtnl_link *link) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!(vxi->ce_mask & VXLAN_ATTR_COLLECT_METADATA)) + return -NLE_NOATTR; + + return vxi->vxi_collect_metadata; +} + +/** + * Set flow label to use for VXLAN + * @arg link Link object + * @arg label Destination label + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_label(struct rtnl_link *link, uint32_t label) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + vxi->vxi_label = htonl(label); + vxi->ce_mask |= VXLAN_ATTR_LABEL; + + return 0; +} + +/** + * Get flow label to use for VXLAN + * @arg link Link object + * @arg label Pointer to store destination label + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_get_label(struct rtnl_link *link, uint32_t *label) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (!label) + return -NLE_INVAL; + + if (!(vxi->ce_mask & VXLAN_ATTR_LABEL)) + return -NLE_NOATTR; + + *label = ntohl(vxi->vxi_label); + + return 0; +} + +/** + * Set VXLAN flags RTNL_LINK_VXLAN_F_* + * @arg link Link object + * @flags Which flags to set + * @arg enable Boolean enabling or disabling flag + * + * @return 0 on success or a negative error code + */ +int rtnl_link_vxlan_set_flags(struct rtnl_link *link, uint32_t flags, int enable) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + if (flags & ~(RTNL_LINK_VXLAN_F_GBP | RTNL_LINK_VXLAN_F_GPE | RTNL_LINK_VXLAN_F_REMCSUM_NOPARTIAL)) + return -NLE_INVAL; + + if (enable) + vxi->vxi_flags |= flags; + else + vxi->vxi_flags &= ~flags; + + return 0; +} + +/** + * Get VXLAN flags RTNL_LINK_VXLAN_F_* + * @arg link Link object + * @arg out_flags Output value for flags. Must be present. + * + * @return Zero on success or a negative error code + */ +int rtnl_link_vxlan_get_flags(struct rtnl_link *link, uint32_t *out_flags) +{ + struct vxlan_info *vxi = link->l_info; + + IS_VXLAN_LINK_ASSERT(link); + + *out_flags = vxi->vxi_flags; + return 0; +} + +/** @} */ + +static void __init vxlan_init(void) +{ + rtnl_link_register_info(&vxlan_info_ops); +} + +static void __exit vxlan_exit(void) +{ + rtnl_link_unregister_info(&vxlan_info_ops); +} + +/** @} */ diff --git a/libnl/lib/route/link/xfrmi.c b/libnl/lib/route/link/xfrmi.c new file mode 100644 index 0000000..2ae5fb5 --- /dev/null +++ b/libnl/lib/route/link/xfrmi.c @@ -0,0 +1,315 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2019 Eyal Birger + * + * Based on lib/route/link/ipvti.c + */ + +/** + * @ingroup link + * @defgroup xfrmi XFRMI + * xfrmi link module + * + * @details + * \b Link Type Name: "xfrmi" + * + * @route_doc{link_xfrmi, XFRMI Documentation} + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +#define XFRMI_ATTR_LINK (1 << 0) +#define XFRMI_ATTR_IF_ID (1 << 1) + +#define XFRMI_LINK_TYPE_NAME "xfrm" + +struct xfrmi_info { + uint32_t link; + uint32_t if_id; + uint32_t xfrmi_mask; +}; + +static struct nla_policy xfrmi_policy[IFLA_XFRM_MAX + 1] = { + [IFLA_XFRM_LINK] = { .type = NLA_U32 }, + [IFLA_XFRM_IF_ID] = { .type = NLA_U32 }, +}; + +static int xfrmi_alloc(struct rtnl_link *link) +{ + struct xfrmi_info *xfrmi; + + if (link->l_info) + memset(link->l_info, 0, sizeof(*xfrmi)); + else { + xfrmi = calloc(1, sizeof(*xfrmi)); + if (!xfrmi) + return -NLE_NOMEM; + + link->l_info = xfrmi; + } + + return 0; +} + +static int xfrmi_parse(struct rtnl_link *link, struct nlattr *data, + struct nlattr *xstats) +{ + struct nlattr *tb[IFLA_XFRM_MAX + 1]; + struct xfrmi_info *xfrmi; + int err; + + NL_DBG(3, "Parsing XFRMI link info\n"); + + err = nla_parse_nested(tb, IFLA_XFRM_MAX, data, xfrmi_policy); + if (err < 0) + return err; + + err = xfrmi_alloc(link); + if (err < 0) + return err; + + xfrmi = link->l_info; + + if (tb[IFLA_XFRM_LINK]) { + xfrmi->link = nla_get_u32(tb[IFLA_XFRM_LINK]); + xfrmi->xfrmi_mask |= XFRMI_ATTR_LINK; + } + + if (tb[IFLA_XFRM_IF_ID]) { + xfrmi->if_id = nla_get_u32(tb[IFLA_XFRM_IF_ID]); + xfrmi->xfrmi_mask |= XFRMI_ATTR_IF_ID; + } + + return 0; +} + +static int xfrmi_put_attrs(struct nl_msg *msg, struct rtnl_link *link) +{ + struct xfrmi_info *xfrmi = link->l_info; + struct nlattr *data; + + data = nla_nest_start(msg, IFLA_INFO_DATA); + if (!data) + return -NLE_MSGSIZE; + + if (xfrmi->xfrmi_mask & XFRMI_ATTR_LINK) + NLA_PUT_U32(msg, IFLA_XFRM_LINK, xfrmi->link); + + if (xfrmi->xfrmi_mask & XFRMI_ATTR_IF_ID) + NLA_PUT_U32(msg, IFLA_XFRM_IF_ID, xfrmi->if_id); + + nla_nest_end(msg, data); + +nla_put_failure: + return 0; +} + +static void xfrmi_free(struct rtnl_link *link) +{ + struct xfrmi_info *xfrmi = link->l_info; + + free(xfrmi); + link->l_info = NULL; +} + +static void xfrmi_dump_line(struct rtnl_link *link, struct nl_dump_params *p) +{ + nl_dump(p, "xfrmi : %s", link->l_name); +} + +static void xfrmi_dump_details(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct xfrmi_info *xfrmi = link->l_info; + + if (xfrmi->xfrmi_mask & XFRMI_ATTR_LINK) { + struct rtnl_link *parent; + char *name; + + nl_dump(p, " link "); + + name = NULL; + parent = link_lookup(link->ce_cache, xfrmi->link); + if (parent) + name = rtnl_link_get_name(parent); + + if (name) + nl_dump_line(p, "%s\n", name); + else + nl_dump_line(p, "%u\n", xfrmi->link); + } + + if (xfrmi->xfrmi_mask & XFRMI_ATTR_IF_ID) { + nl_dump(p, " if_id "); + nl_dump_line(p, "%x\n", xfrmi->if_id); + } +} + +static int xfrmi_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct xfrmi_info *xfrmi_dst, *xfrmi_src = src->l_info; + int err; + + dst->l_info = NULL; + + err = rtnl_link_set_type(dst, XFRMI_LINK_TYPE_NAME); + if (err < 0) + return err; + + xfrmi_dst = dst->l_info; + + if (!xfrmi_dst || !xfrmi_src) + BUG(); + + memcpy(xfrmi_dst, xfrmi_src, sizeof(struct xfrmi_info)); + + return 0; +} + +static struct rtnl_link_info_ops xfrmi_info_ops = { + .io_name = XFRMI_LINK_TYPE_NAME, + .io_alloc = xfrmi_alloc, + .io_parse = xfrmi_parse, + .io_dump = { + [NL_DUMP_LINE] = xfrmi_dump_line, + [NL_DUMP_DETAILS] = xfrmi_dump_details, + }, + .io_clone = xfrmi_clone, + .io_put_attrs = xfrmi_put_attrs, + .io_free = xfrmi_free, +}; + +#define IS_XFRMI_LINK_ASSERT(link) do { \ + if ((link)->l_info_ops != &xfrmi_info_ops) { \ + APPBUG("Link is not a xfrmi link. set type \"xfrmi\" first."); \ + return -NLE_OPNOTSUPP; \ + } \ + } while(0) + +struct rtnl_link *rtnl_link_xfrmi_alloc(void) +{ + struct rtnl_link *link; + int err; + + link = rtnl_link_alloc(); + if (!link) + return NULL; + + err = rtnl_link_set_type(link, XFRMI_LINK_TYPE_NAME); + if (err < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + +/** + * Check if link is a XFRMI link + * @arg link Link object + * + * @return True if link is a IXFRMI link, otherwise 0 is returned. + */ +int rtnl_link_is_xfrmi(struct rtnl_link *link) +{ + return link->l_info_ops && !strcmp(link->l_info_ops->io_name, + XFRMI_LINK_TYPE_NAME); +} + +/** + * Set XFRMI link interface index + * @arg link Link object + * @arg index interface index + * + * @return 0 on success or a negative error code + */ +int rtnl_link_xfrmi_set_link(struct rtnl_link *link, uint32_t index) +{ + struct xfrmi_info *xfrmi = link->l_info; + + IS_XFRMI_LINK_ASSERT(link); + + xfrmi->link = index; + xfrmi->xfrmi_mask |= XFRMI_ATTR_LINK; + + return 0; +} + +/** + * Get XFRMI link interface index + * @arg link Link object + * @arg out_link The output value on success + * + * @return 0 on sucess or a negative error code + */ +int rtnl_link_xfrmi_get_link(struct rtnl_link *link, uint32_t *out_link) +{ + struct xfrmi_info *xfrmi = link->l_info; + + IS_XFRMI_LINK_ASSERT(link); + + if (!(xfrmi->xfrmi_mask & XFRMI_ATTR_LINK)) + return -NLE_NOATTR; + + *out_link = xfrmi->link; + return 0; +} + +/** + * Set XFRMI if_id + * @arg link Link object + * @arg if_id xfrm if_id + * + * @return 0 on success or a negative error code + */ +int rtnl_link_xfrmi_set_if_id(struct rtnl_link *link, uint32_t if_id) +{ + struct xfrmi_info *xfrmi = link->l_info; + + IS_XFRMI_LINK_ASSERT(link); + + xfrmi->if_id = if_id; + xfrmi->xfrmi_mask |= XFRMI_ATTR_IF_ID; + + return 0; +} + +/** + * Get XFRMI if_id + * @arg link Link object + * @arg out_if_id The output value on success + * + * @return 0 on sucess or a negative error code + */ +int rtnl_link_xfrmi_get_if_id(struct rtnl_link *link, uint32_t *out_if_id) +{ + struct xfrmi_info *xfrmi = link->l_info; + + IS_XFRMI_LINK_ASSERT(link); + + if (!(xfrmi->xfrmi_mask & XFRMI_ATTR_IF_ID)) + return -NLE_NOATTR; + + *out_if_id = xfrmi->if_id; + return 0; +} + +static void __init xfrmi_init(void) +{ + rtnl_link_register_info(&xfrmi_info_ops); +} + +static void __exit xfrmi_exit(void) +{ + rtnl_link_unregister_info(&xfrmi_info_ops); +} diff --git a/libnl/lib/route/neigh.c b/libnl/lib/route/neigh.c new file mode 100644 index 0000000..69b861a --- /dev/null +++ b/libnl/lib/route/neigh.c @@ -0,0 +1,1119 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + */ + +/** + * @ingroup rtnl + * @defgroup neigh Neighbours + * @brief + * + * The neighbour table establishes bindings between protocol addresses and + * link layer addresses for hosts sharing the same physical link. This + * module allows you to access and manipulate the content of these tables. + * + * @par Neighbour States + * @code + * NUD_INCOMPLETE + * NUD_REACHABLE + * NUD_STALE + * NUD_DELAY + * NUD_PROBE + * NUD_FAILED + * NUD_NOARP + * NUD_PERMANENT + * @endcode + * + * @par Neighbour Flags + * @code + * NTF_USE + * NTF_PROXY + * NTF_ROUTER + * NTF_SELF + * @endcode + * + * @par Neighbour Identification + * A neighbour is uniquely identified by the attributes listed below, whenever + * you refer to an existing neighbour all of the attributes must be set. + * Neighbours from caches automatically have all required attributes set. + * - interface index (rtnl_neigh_set_ifindex()) + * - destination address (rtnl_neigh_set_dst()) + * + * @par Changeable Attributes + * \anchor neigh_changeable + * - state (rtnl_neigh_set_state()) + * - link layer address (rtnl_neigh_set_lladdr()) + * + * @par Required Caches for Dumping + * In order to dump neighbour attributes you must provide the following + * caches via nl_cache_provide() + * - link cache holding all links + * + * @par TODO + * - Document proxy settings + * - Document states and their influence + * + * @par 1) Retrieving information about configured neighbours + * @code + * // The first step is to retrieve a list of all available neighbour within + * // the kernel and put them into a cache. + * struct nl_cache *cache = rtnl_neigh_alloc_cache(sk); + * + * // Neighbours can then be looked up by the interface and destination + * // address: + * struct rtnl_neigh *neigh = rtnl_neigh_get(cache, ifindex, dst_addr); + * + * // After successful usage, the object must be given back to the cache + * rtnl_neigh_put(neigh); + * @endcode + * + * @par 2) Adding new neighbours + * @code + * // Allocate an empty neighbour handle to be filled out with the attributes + * // of the new neighbour. + * struct rtnl_neigh *neigh = rtnl_neigh_alloc(); + * + * // Fill out the attributes of the new neighbour + * rtnl_neigh_set_ifindex(neigh, ifindex); + * rtnl_neigh_set_dst(neigh, dst_addr); + * rtnl_neigh_set_state(neigh, rtnl_neigh_str2state("permanent")); + * + * // Build the netlink message and send it to the kernel, the operation will + * // block until the operation has been completed. Alternatively the required + * // netlink message can be built using rtnl_neigh_build_add_request() + * // to be sent out using nl_send_auto_complete(). + * rtnl_neigh_add(sk, neigh, NLM_F_CREATE); + * + * // Free the memory + * rtnl_neigh_put(neigh); + * @endcode + * + * @par 3) Deleting an existing neighbour + * @code + * // Allocate an empty neighbour object to be filled out with the attributes + * // matching the neighbour to be deleted. Alternatively a fully equipped + * // neighbour object out of a cache can be used instead. + * struct rtnl_neigh *neigh = rtnl_neigh_alloc(); + * + * // Neighbours are uniquely identified by their interface index and + * // destination address, you may fill out other attributes but they + * // will have no influence. + * rtnl_neigh_set_ifindex(neigh, ifindex); + * rtnl_neigh_set_dst(neigh, dst_addr); + * + * // Build the netlink message and send it to the kernel, the operation will + * // block until the operation has been completed. Alternatively the required + * // netlink message can be built using rtnl_neigh_build_delete_request() + * // to be sent out using nl_send_auto_complete(). + * rtnl_neigh_delete(sk, neigh, 0); + * + * // Free the memory + * rtnl_neigh_put(neigh); + * @endcode + * + * @par 4) Changing neighbour attributes + * @code + * // Allocate an empty neighbour object to be filled out with the attributes + * // matching the neighbour to be changed and the new parameters. Alternatively + * // a fully equipped modified neighbour object out of a cache can be used. + * struct rtnl_neigh *neigh = rtnl_neigh_alloc(); + * + * // Identify the neighbour to be changed by its interface index and + * // destination address + * rtnl_neigh_set_ifindex(neigh, ifindex); + * rtnl_neigh_set_dst(neigh, dst_addr); + * + * // The link layer address may be modified, if so it is wise to change + * // its state to "permanent" in order to avoid having it overwritten. + * rtnl_neigh_set_lladdr(neigh, lladdr); + * + * // Secondly the state can be modified allowing normal neighbours to be + * // converted into permanent entries or to manually confirm a neighbour. + * rtnl_neigh_set_state(neigh, state); + * + * // Build the netlink message and send it to the kernel, the operation will + * // block until the operation has been completed. Alternatively the required + * // netlink message can be built using rtnl_neigh_build_change_request() + * // to be sent out using nl_send_auto_complete(). + * rtnl_neigh_add(sk, neigh, NLM_F_REPLACE); + * + * // Free the memory + * rtnl_neigh_put(neigh); + * @endcode + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define NEIGH_ATTR_FLAGS 0x01 +#define NEIGH_ATTR_STATE 0x02 +#define NEIGH_ATTR_LLADDR 0x04 +#define NEIGH_ATTR_DST 0x08 +#define NEIGH_ATTR_CACHEINFO 0x10 +#define NEIGH_ATTR_IFINDEX 0x20 +#define NEIGH_ATTR_FAMILY 0x40 +#define NEIGH_ATTR_TYPE 0x80 +#define NEIGH_ATTR_PROBES 0x100 +#define NEIGH_ATTR_MASTER 0x200 +#define NEIGH_ATTR_VLAN 0x400 + +static struct nl_cache_ops rtnl_neigh_ops; +static struct nl_object_ops neigh_obj_ops; +/** @endcond */ + +static void neigh_free_data(struct nl_object *c) +{ + struct rtnl_neigh *neigh = nl_object_priv(c); + + if (!neigh) + return; + + nl_addr_put(neigh->n_lladdr); + nl_addr_put(neigh->n_dst); +} + +static int neigh_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct rtnl_neigh *dst = nl_object_priv(_dst); + struct rtnl_neigh *src = nl_object_priv(_src); + + if (src->n_lladdr) + if (!(dst->n_lladdr = nl_addr_clone(src->n_lladdr))) + return -NLE_NOMEM; + + if (src->n_dst) + if (!(dst->n_dst = nl_addr_clone(src->n_dst))) + return -NLE_NOMEM; + + return 0; +} + +static void neigh_keygen(struct nl_object *obj, uint32_t *hashkey, + uint32_t table_sz) +{ + struct rtnl_neigh *neigh = (struct rtnl_neigh *) obj; + unsigned int nkey_sz; + struct nl_addr *addr = NULL; + struct neigh_hash_key { + uint32_t n_family; + uint32_t n_ifindex; + uint16_t n_vlan; + char n_addr[0]; + } __attribute__((packed)) *nkey; +#ifdef NL_DEBUG + char buf[INET6_ADDRSTRLEN+5]; +#endif + + if (neigh->n_family == AF_BRIDGE) { + if (neigh->n_lladdr) + addr = neigh->n_lladdr; + } else if (neigh->n_dst) { + addr = neigh->n_dst; + } + + nkey_sz = sizeof(*nkey); + if (addr) + nkey_sz += nl_addr_get_len(addr); + + nkey = calloc(1, nkey_sz); + if (!nkey) { + *hashkey = 0; + return; + } + nkey->n_family = neigh->n_family; + if (neigh->n_family == AF_BRIDGE) { + nkey->n_vlan = neigh->n_vlan; + if (neigh->n_flags & NTF_SELF) + nkey->n_ifindex = neigh->n_ifindex; + else + nkey->n_ifindex = neigh->n_master; + } else + nkey->n_ifindex = neigh->n_ifindex; + + if (addr) + memcpy(nkey->n_addr, + nl_addr_get_binary_addr(addr), + nl_addr_get_len(addr)); + + *hashkey = nl_hash(nkey, nkey_sz, 0) % table_sz; + + NL_DBG(5, "neigh %p key (fam %d dev %d addr %s) keysz %d hash 0x%x\n", + neigh, nkey->n_family, nkey->n_ifindex, + nl_addr2str(addr, buf, sizeof(buf)), + nkey_sz, *hashkey); + + free(nkey); + + return; +} + +static uint64_t neigh_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + struct rtnl_neigh *a = (struct rtnl_neigh *) _a; + struct rtnl_neigh *b = (struct rtnl_neigh *) _b; + uint64_t diff = 0; + +#define NEIGH_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, NEIGH_ATTR_##ATTR, a, b, EXPR) + + diff |= NEIGH_DIFF(IFINDEX, a->n_ifindex != b->n_ifindex); + diff |= NEIGH_DIFF(FAMILY, a->n_family != b->n_family); + diff |= NEIGH_DIFF(TYPE, a->n_type != b->n_type); + diff |= NEIGH_DIFF(LLADDR, nl_addr_cmp(a->n_lladdr, b->n_lladdr)); + diff |= NEIGH_DIFF(DST, nl_addr_cmp(a->n_dst, b->n_dst)); + diff |= NEIGH_DIFF(MASTER, a->n_master != b->n_master); + diff |= NEIGH_DIFF(VLAN, a->n_vlan != b->n_vlan); + + if (flags & LOOSE_COMPARISON) { + diff |= NEIGH_DIFF(STATE, + (a->n_state ^ b->n_state) & b->n_state_mask); + diff |= NEIGH_DIFF(FLAGS, + (a->n_flags ^ b->n_flags) & b->n_flag_mask); + } else { + diff |= NEIGH_DIFF(STATE, a->n_state != b->n_state); + diff |= NEIGH_DIFF(FLAGS, a->n_flags != b->n_flags); + } + +#undef NEIGH_DIFF + + return diff; +} + +static const struct trans_tbl neigh_attrs[] = { + __ADD(NEIGH_ATTR_FLAGS, flags), + __ADD(NEIGH_ATTR_STATE, state), + __ADD(NEIGH_ATTR_LLADDR, lladdr), + __ADD(NEIGH_ATTR_DST, dst), + __ADD(NEIGH_ATTR_CACHEINFO, cacheinfo), + __ADD(NEIGH_ATTR_IFINDEX, ifindex), + __ADD(NEIGH_ATTR_FAMILY, family), + __ADD(NEIGH_ATTR_TYPE, type), + __ADD(NEIGH_ATTR_PROBES, probes), + __ADD(NEIGH_ATTR_MASTER, master), + __ADD(NEIGH_ATTR_VLAN, vlan), +}; + +static char *neigh_attrs2str(int attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, neigh_attrs, + ARRAY_SIZE(neigh_attrs)); +} + +static uint32_t neigh_id_attrs_get(struct nl_object *obj) +{ + struct rtnl_neigh *neigh = (struct rtnl_neigh *)obj; + + if (neigh->n_family == AF_BRIDGE) { + if (neigh->n_flags & NTF_SELF) + return (NEIGH_ATTR_LLADDR | NEIGH_ATTR_FAMILY | NEIGH_ATTR_IFINDEX | + ((neigh->ce_mask & NEIGH_ATTR_DST) ? NEIGH_ATTR_DST: 0) | + ((neigh->ce_mask & NEIGH_ATTR_VLAN) ? NEIGH_ATTR_VLAN : 0)); + else + return (NEIGH_ATTR_LLADDR | NEIGH_ATTR_FAMILY | NEIGH_ATTR_MASTER | NEIGH_ATTR_VLAN); + } else + return neigh_obj_ops.oo_id_attrs; +} + +static struct nla_policy neigh_policy[NDA_MAX+1] = { + [NDA_CACHEINFO] = { .minlen = sizeof(struct nda_cacheinfo) }, + [NDA_PROBES] = { .type = NLA_U32 }, +}; + +static int neigh_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *n, struct nl_parser_param *pp) +{ + struct rtnl_neigh *neigh; + int err; + + if ((err = rtnl_neigh_parse(n, &neigh)) < 0) + return err; + + err = pp->pp_cb((struct nl_object *) neigh, pp); + + rtnl_neigh_put(neigh); + return err; +} + + +int rtnl_neigh_parse(struct nlmsghdr *n, struct rtnl_neigh **result) +{ + struct rtnl_neigh *neigh; + struct nlattr *tb[NDA_MAX + 1]; + struct ndmsg *nm; + int err; + + neigh = rtnl_neigh_alloc(); + if (!neigh) { + err = -NLE_NOMEM; + goto errout; + } + + neigh->ce_msgtype = n->nlmsg_type; + nm = nlmsg_data(n); + + err = nlmsg_parse(n, sizeof(*nm), tb, NDA_MAX, neigh_policy); + if (err < 0) + goto errout; + + neigh->n_family = nm->ndm_family; + neigh->n_ifindex = nm->ndm_ifindex; + neigh->n_state = nm->ndm_state; + neigh->n_flags = nm->ndm_flags; + neigh->n_type = nm->ndm_type; + + neigh->ce_mask |= (NEIGH_ATTR_FAMILY | NEIGH_ATTR_IFINDEX | + NEIGH_ATTR_STATE | NEIGH_ATTR_FLAGS | + NEIGH_ATTR_TYPE); + + if (tb[NDA_LLADDR]) { + neigh->n_lladdr = nl_addr_alloc_attr(tb[NDA_LLADDR], AF_UNSPEC); + if (!neigh->n_lladdr) { + err = -NLE_NOMEM; + goto errout; + } + nl_addr_set_family(neigh->n_lladdr, + nl_addr_guess_family(neigh->n_lladdr)); + neigh->ce_mask |= NEIGH_ATTR_LLADDR; + } + + if (tb[NDA_DST]) { + neigh->n_dst = nl_addr_alloc_attr(tb[NDA_DST], AF_UNSPEC); + if (!neigh->n_dst) { + err = -NLE_NOMEM; + goto errout; + } + nl_addr_set_family(neigh->n_dst, + nl_addr_guess_family(neigh->n_dst)); + neigh->ce_mask |= NEIGH_ATTR_DST; + } + + if (tb[NDA_CACHEINFO]) { + struct nda_cacheinfo *ci = nla_data(tb[NDA_CACHEINFO]); + + neigh->n_cacheinfo.nci_confirmed = ci->ndm_confirmed; + neigh->n_cacheinfo.nci_used = ci->ndm_used; + neigh->n_cacheinfo.nci_updated = ci->ndm_updated; + neigh->n_cacheinfo.nci_refcnt = ci->ndm_refcnt; + + neigh->ce_mask |= NEIGH_ATTR_CACHEINFO; + } + + if (tb[NDA_PROBES]) { + neigh->n_probes = nla_get_u32(tb[NDA_PROBES]); + neigh->ce_mask |= NEIGH_ATTR_PROBES; + } + + if (tb[NDA_VLAN]) { + neigh->n_vlan = nla_get_u16(tb[NDA_VLAN]); + neigh->ce_mask |= NEIGH_ATTR_VLAN; + } + + /* + * Get the bridge index for AF_BRIDGE family entries + */ + if (neigh->n_family == AF_BRIDGE) { + if (tb[NDA_MASTER]) { + neigh->n_master = nla_get_u32(tb[NDA_MASTER]); + neigh->ce_mask |= NEIGH_ATTR_MASTER; + } else { + struct nl_cache *lcache = nl_cache_mngt_require_safe("route/link"); + if (lcache ) { + struct rtnl_link *link = rtnl_link_get(lcache, + neigh->n_ifindex); + if (link) { + neigh->n_master = link->l_master; + rtnl_link_put(link); + neigh->ce_mask |= NEIGH_ATTR_MASTER; + } + nl_cache_put(lcache); + } + } + } + + *result = neigh; + return 0; + +errout: + rtnl_neigh_put(neigh); + return err; +} + +static int neigh_request_update(struct nl_cache *c, struct nl_sock *h) +{ + int family = c->c_iarg1; + + if (family == AF_UNSPEC) { + return nl_rtgen_request(h, RTM_GETNEIGH, family, NLM_F_DUMP); + } else if (family == AF_BRIDGE) { + struct ifinfomsg hdr = {.ifi_family = family}; + struct nl_msg *msg; + int err; + + msg = nlmsg_alloc_simple(RTM_GETNEIGH, NLM_F_REQUEST | NLM_F_DUMP); + if (!msg) + return -NLE_NOMEM; + + err = -NLE_MSGSIZE; + if (nlmsg_append(msg, &hdr, sizeof(hdr), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + err = nl_send_auto(h, msg); + if (err > 0) + err = 0; + + nla_put_failure: + nlmsg_free(msg); + return err; + } + + return -NLE_INVAL; +} + + +static void neigh_dump_line(struct nl_object *a, struct nl_dump_params *p) +{ + char dst[INET6_ADDRSTRLEN+5], lladdr[INET6_ADDRSTRLEN+5]; + struct rtnl_neigh *n = (struct rtnl_neigh *) a; + struct nl_cache *link_cache; + char state[128], flags[64]; + char buf[128]; + + link_cache = nl_cache_mngt_require_safe("route/link"); + + if (n->n_family != AF_UNSPEC) + nl_dump_line(p, "%s ", nl_af2str(n->n_family, buf, sizeof(buf))); + + if (n->ce_mask & NEIGH_ATTR_DST) + nl_dump_line(p, "%s ", nl_addr2str(n->n_dst, dst, sizeof(dst))); + + if (link_cache) + nl_dump(p, "dev %s ", + rtnl_link_i2name(link_cache, n->n_ifindex, + state, sizeof(state))); + else + nl_dump(p, "dev %d ", n->n_ifindex); + + if (n->ce_mask & NEIGH_ATTR_LLADDR) + nl_dump(p, "lladdr %s ", + nl_addr2str(n->n_lladdr, lladdr, sizeof(lladdr))); + + if (n->ce_mask & NEIGH_ATTR_VLAN) + nl_dump(p, "vlan %d ", n->n_vlan); + + if (n->ce_mask & NEIGH_ATTR_MASTER) { + if (link_cache) + nl_dump(p, "%s ", rtnl_link_i2name(link_cache, n->n_master, + state, sizeof(state))); + else + nl_dump(p, "%d ", n->n_master); + } + + rtnl_neigh_state2str(n->n_state, state, sizeof(state)); + rtnl_neigh_flags2str(n->n_flags, flags, sizeof(flags)); + + if (state[0]) + nl_dump(p, "<%s", state); + if (flags[0]) + nl_dump(p, "%s%s", state[0] ? "," : "<", flags); + if (state[0] || flags[0]) + nl_dump(p, ">"); + nl_dump(p, "\n"); + + if (link_cache) + nl_cache_put(link_cache); +} + +static void neigh_dump_details(struct nl_object *a, struct nl_dump_params *p) +{ + char rtn_type[32]; + struct rtnl_neigh *n = (struct rtnl_neigh *) a; + int hz = nl_get_user_hz(); + + neigh_dump_line(a, p); + + nl_dump_line(p, " refcnt %u type %s confirmed %u used " + "%u updated %u\n", + n->n_cacheinfo.nci_refcnt, + nl_rtntype2str(n->n_type, rtn_type, sizeof(rtn_type)), + n->n_cacheinfo.nci_confirmed/hz, + n->n_cacheinfo.nci_used/hz, n->n_cacheinfo.nci_updated/hz); +} + +static void neigh_dump_stats(struct nl_object *a, struct nl_dump_params *p) +{ + neigh_dump_details(a, p); +} + +/** + * @name Neighbour Object Allocation/Freeage + * @{ + */ + +struct rtnl_neigh *rtnl_neigh_alloc(void) +{ + return (struct rtnl_neigh *) nl_object_alloc(&neigh_obj_ops); +} + +void rtnl_neigh_put(struct rtnl_neigh *neigh) +{ + nl_object_put((struct nl_object *) neigh); +} + +/** @} */ + +/** + * @name Neighbour Cache Managament + * @{ + */ + +/** + * Build a neighbour cache including all neighbours currently configured in the kernel. + * @arg sock Netlink socket. + * @arg result Pointer to store resulting cache. + * + * Allocates a new neighbour cache, initializes it properly and updates it + * to include all neighbours currently configured in the kernel. + * + * @return 0 on success or a negative error code. + */ +int rtnl_neigh_alloc_cache(struct nl_sock *sock, struct nl_cache **result) +{ + return nl_cache_alloc_and_fill(&rtnl_neigh_ops, sock, result); +} + +/** + * Build a neighbour cache including all neighbours currently configured in the kernel. + * @arg sock Netlink socket. + * @arg result Pointer to store resulting cache. + * @arg flags Flags to apply to cache before filling + * + * Allocates a new neighbour cache, initializes it properly and updates it + * to include all neighbours currently configured in the kernel. + * + * @return 0 on success or a negative error code. + */ +int rtnl_neigh_alloc_cache_flags(struct nl_sock *sock, struct nl_cache **result, + unsigned int flags) +{ + struct nl_cache * cache; + int err; + + cache = nl_cache_alloc(&rtnl_neigh_ops); + if (!cache) + return -NLE_NOMEM; + + nl_cache_set_flags(cache, flags); + + if (sock && (err = nl_cache_refill(sock, cache)) < 0) { + nl_cache_free(cache); + return err; + } + + *result = cache; + return 0; +} + +/** + * Look up a neighbour by interface index and destination address + * @arg cache neighbour cache + * @arg ifindex interface index the neighbour is on + * @arg dst destination address of the neighbour + * + * @return neighbour handle or NULL if no match was found. + */ +struct rtnl_neigh * rtnl_neigh_get(struct nl_cache *cache, int ifindex, + struct nl_addr *dst) +{ + struct rtnl_neigh *neigh; + + nl_list_for_each_entry(neigh, &cache->c_items, ce_list) { + if (neigh->n_ifindex == ifindex && + neigh->n_family == dst->a_family && + !nl_addr_cmp(neigh->n_dst, dst)) { + nl_object_get((struct nl_object *) neigh); + return neigh; + } + } + + return NULL; +} + +/** + * Look up a neighbour by interface index, link layer address and vlan id + * @arg cache neighbour cache + * @arg ifindex interface index the neighbour is on + * @arg lladdr link layer address of the neighbour + * @arg vlan vlan id of the neighbour + * + * @return neighbour handle or NULL if no match was found. + */ +struct rtnl_neigh * rtnl_neigh_get_by_vlan(struct nl_cache *cache, int ifindex, + struct nl_addr *lladdr, int vlan) +{ + struct rtnl_neigh *neigh; + + nl_list_for_each_entry(neigh, &cache->c_items, ce_list) { + if (neigh->n_ifindex == ifindex && + neigh->n_vlan == vlan && + neigh->n_lladdr && !nl_addr_cmp(neigh->n_lladdr, lladdr)) { + nl_object_get((struct nl_object *) neigh); + return neigh; + } + } + + return NULL; +} + +/** @} */ + +/** + * @name Neighbour Addition + * @{ + */ + +static int build_neigh_msg(struct rtnl_neigh *tmpl, int cmd, int flags, + struct nl_msg **result) +{ + struct nl_msg *msg; + struct ndmsg nhdr = { + .ndm_ifindex = tmpl->n_ifindex, + .ndm_state = NUD_PERMANENT, + }; + + if (tmpl->n_family != AF_BRIDGE) { + if (!(tmpl->ce_mask & NEIGH_ATTR_DST)) + return -NLE_MISSING_ATTR; + nhdr.ndm_family = nl_addr_get_family(tmpl->n_dst); + } + else + nhdr.ndm_family = AF_BRIDGE; + + if (tmpl->ce_mask & NEIGH_ATTR_FLAGS) + nhdr.ndm_flags = tmpl->n_flags; + + if (tmpl->ce_mask & NEIGH_ATTR_STATE) + nhdr.ndm_state = tmpl->n_state; + + msg = nlmsg_alloc_simple(cmd, flags); + if (!msg) + return -NLE_NOMEM; + + if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + if (tmpl->n_family != AF_BRIDGE) + NLA_PUT_ADDR(msg, NDA_DST, tmpl->n_dst); + + if (tmpl->ce_mask & NEIGH_ATTR_LLADDR) + NLA_PUT_ADDR(msg, NDA_LLADDR, tmpl->n_lladdr); + + if (tmpl->ce_mask & NEIGH_ATTR_VLAN) + NLA_PUT_U16(msg, NDA_VLAN, tmpl->n_vlan); + + *result = msg; + return 0; + +nla_put_failure: + nlmsg_free(msg); + return -NLE_MSGSIZE; +} + +/** + * Build netlink request message to add a new neighbour + * @arg tmpl template with data of new neighbour + * @arg flags additional netlink message flags + * @arg result Pointer to store resulting message. + * + * Builds a new netlink message requesting a addition of a new + * neighbour. The netlink message header isn't fully equipped with + * all relevant fields and must thus be sent out via nl_send_auto_complete() + * or supplemented as needed. \a tmpl must contain the attributes of the new + * neighbour set via \c rtnl_neigh_set_* functions. + * + * The following attributes must be set in the template: + * - Interface index (rtnl_neigh_set_ifindex()) + * - State (rtnl_neigh_set_state()) + * - Destination address (rtnl_neigh_set_dst()) + * - Link layer address (rtnl_neigh_set_lladdr()) + * + * @return 0 on success or a negative error code. + */ +int rtnl_neigh_build_add_request(struct rtnl_neigh *tmpl, int flags, + struct nl_msg **result) +{ + return build_neigh_msg(tmpl, RTM_NEWNEIGH, flags, result); +} + +/** + * Add a new neighbour + * @arg sk Netlink socket. + * @arg tmpl template with requested changes + * @arg flags additional netlink message flags + * + * Builds a netlink message by calling rtnl_neigh_build_add_request(), + * sends the request to the kernel and waits for the next ACK to be + * received and thus blocks until the request has been fullfilled. + * + * The following attributes must be set in the template: + * - Interface index (rtnl_neigh_set_ifindex()) + * - State (rtnl_neigh_set_state()) + * - Destination address (rtnl_neigh_set_dst()) + * - Link layer address (rtnl_neigh_set_lladdr()) + * + * @return 0 on sucess or a negative error if an error occured. + */ +int rtnl_neigh_add(struct nl_sock *sk, struct rtnl_neigh *tmpl, int flags) +{ + int err; + struct nl_msg *msg; + + if ((err = rtnl_neigh_build_add_request(tmpl, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +/** @} */ + +/** + * @name Neighbour Deletion + * @{ + */ + +/** + * Build a netlink request message to delete a neighbour + * @arg neigh neighbour to delete + * @arg flags additional netlink message flags + * @arg result Pointer to store resulting message. + * + * Builds a new netlink message requesting a deletion of a neighbour. + * The netlink message header isn't fully equipped with all relevant + * fields and must thus be sent out via nl_send_auto_complete() + * or supplemented as needed. \a neigh must point to an existing + * neighbour. + * + * @return 0 on success or a negative error code. + */ +int rtnl_neigh_build_delete_request(struct rtnl_neigh *neigh, int flags, + struct nl_msg **result) +{ + return build_neigh_msg(neigh, RTM_DELNEIGH, flags, result); +} + +/** + * Delete a neighbour + * @arg sk Netlink socket. + * @arg neigh neighbour to delete + * @arg flags additional netlink message flags + * + * Builds a netlink message by calling rtnl_neigh_build_delete_request(), + * sends the request to the kernel and waits for the next ACK to be + * received and thus blocks until the request has been fullfilled. + * + * @return 0 on sucess or a negative error if an error occured. + */ +int rtnl_neigh_delete(struct nl_sock *sk, struct rtnl_neigh *neigh, + int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_neigh_build_delete_request(neigh, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +/** @} */ + +/** + * @name Neighbour States Translations + * @{ + */ + +static const struct trans_tbl neigh_states[] = { + __ADD(NUD_INCOMPLETE, incomplete), + __ADD(NUD_REACHABLE, reachable), + __ADD(NUD_STALE, stale), + __ADD(NUD_DELAY, delay), + __ADD(NUD_PROBE, probe), + __ADD(NUD_FAILED, failed), + __ADD(NUD_NOARP, noarp), + __ADD(NUD_PERMANENT, permanent), + + /* Accept this value for backward compatibility. Originally + * there was a typo in the string value. This was fixed later, + * but we still want to successfully parse "norarp". */ + __ADD(NUD_NOARP, norarp), +}; + +char * rtnl_neigh_state2str(int state, char *buf, size_t len) +{ + return __flags2str(state, buf, len, neigh_states, + ARRAY_SIZE(neigh_states) - 1); +} + +int rtnl_neigh_str2state(const char *name) +{ + return __str2type(name, neigh_states, ARRAY_SIZE(neigh_states)); +} + +/** @} */ + +/** + * @name Neighbour Flags Translations + * @{ + */ + +static const struct trans_tbl neigh_flags[] = { + __ADD(NTF_USE, use), + __ADD(NTF_PROXY, proxy), + __ADD(NTF_ROUTER, router), + __ADD(NTF_SELF, self), + __ADD(NTF_MASTER, master), + __ADD(NTF_EXT_LEARNED, ext_learned), + __ADD(NTF_OFFLOADED, offloaded), +}; + +char * rtnl_neigh_flags2str(int flags, char *buf, size_t len) +{ + return __flags2str(flags, buf, len, neigh_flags, + ARRAY_SIZE(neigh_flags)); +} + +int rtnl_neigh_str2flag(const char *name) +{ + return __str2type(name, neigh_flags, ARRAY_SIZE(neigh_flags)); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +void rtnl_neigh_set_state(struct rtnl_neigh *neigh, int state) +{ + neigh->n_state_mask |= state; + neigh->n_state |= state; + neigh->ce_mask |= NEIGH_ATTR_STATE; +} + +int rtnl_neigh_get_state(struct rtnl_neigh *neigh) +{ + if (neigh->ce_mask & NEIGH_ATTR_STATE) + return neigh->n_state; + else + return -1; +} + +void rtnl_neigh_unset_state(struct rtnl_neigh *neigh, int state) +{ + neigh->n_state_mask |= state; + neigh->n_state &= ~state; + neigh->ce_mask |= NEIGH_ATTR_STATE; +} + +void rtnl_neigh_set_flags(struct rtnl_neigh *neigh, unsigned int flags) +{ + neigh->n_flag_mask |= flags; + neigh->n_flags |= flags; + neigh->ce_mask |= NEIGH_ATTR_FLAGS; +} + +unsigned int rtnl_neigh_get_flags(struct rtnl_neigh *neigh) +{ + return neigh->n_flags; +} + +void rtnl_neigh_unset_flags(struct rtnl_neigh *neigh, unsigned int flags) +{ + neigh->n_flag_mask |= flags; + neigh->n_flags &= ~flags; + neigh->ce_mask |= NEIGH_ATTR_FLAGS; +} + +void rtnl_neigh_set_ifindex(struct rtnl_neigh *neigh, int ifindex) +{ + neigh->n_ifindex = ifindex; + neigh->ce_mask |= NEIGH_ATTR_IFINDEX; +} + +int rtnl_neigh_get_ifindex(struct rtnl_neigh *neigh) +{ + return neigh->n_ifindex; +} + +static inline int __assign_addr(struct rtnl_neigh *neigh, struct nl_addr **pos, + struct nl_addr *new, int flag, int nocheck) +{ + if (!nocheck) { + if (neigh->ce_mask & NEIGH_ATTR_FAMILY) { + if (new->a_family != neigh->n_family) + return -NLE_AF_MISMATCH; + } else { + neigh->n_family = new->a_family; + neigh->ce_mask |= NEIGH_ATTR_FAMILY; + } + } + + if (*pos) + nl_addr_put(*pos); + + nl_addr_get(new); + *pos = new; + + neigh->ce_mask |= flag; + + return 0; +} + +void rtnl_neigh_set_lladdr(struct rtnl_neigh *neigh, struct nl_addr *addr) +{ + __assign_addr(neigh, &neigh->n_lladdr, addr, NEIGH_ATTR_LLADDR, 1); +} + +struct nl_addr *rtnl_neigh_get_lladdr(struct rtnl_neigh *neigh) +{ + if (neigh->ce_mask & NEIGH_ATTR_LLADDR) + return neigh->n_lladdr; + else + return NULL; +} + +int rtnl_neigh_set_dst(struct rtnl_neigh *neigh, struct nl_addr *addr) +{ + return __assign_addr(neigh, &neigh->n_dst, addr, + NEIGH_ATTR_DST, 0); +} + +struct nl_addr *rtnl_neigh_get_dst(struct rtnl_neigh *neigh) +{ + if (neigh->ce_mask & NEIGH_ATTR_DST) + return neigh->n_dst; + else + return NULL; +} + +void rtnl_neigh_set_family(struct rtnl_neigh *neigh, int family) +{ + neigh->n_family = family; + neigh->ce_mask |= NEIGH_ATTR_FAMILY; +} + +int rtnl_neigh_get_family(struct rtnl_neigh *neigh) +{ + return neigh->n_family; +} + +void rtnl_neigh_set_type(struct rtnl_neigh *neigh, int type) +{ + neigh->n_type = type; + neigh->ce_mask = NEIGH_ATTR_TYPE; +} + +int rtnl_neigh_get_type(struct rtnl_neigh *neigh) +{ + if (neigh->ce_mask & NEIGH_ATTR_TYPE) + return neigh->n_type; + else + return -1; +} + +void rtnl_neigh_set_vlan(struct rtnl_neigh *neigh, int vlan) +{ + neigh->n_vlan = vlan; + neigh->ce_mask |= NEIGH_ATTR_VLAN; +} + +int rtnl_neigh_get_vlan(struct rtnl_neigh *neigh) +{ + if (neigh->ce_mask & NEIGH_ATTR_VLAN) + return neigh->n_vlan; + else + return -1; +} + +void rtnl_neigh_set_master(struct rtnl_neigh *neigh, int ifindex) +{ + neigh->n_master = ifindex; + neigh->ce_mask |= NEIGH_ATTR_MASTER; +} + +int rtnl_neigh_get_master(struct rtnl_neigh *neigh) { + return neigh->n_master; +} + +/** @} */ + +static struct nl_object_ops neigh_obj_ops = { + .oo_name = "route/neigh", + .oo_size = sizeof(struct rtnl_neigh), + .oo_free_data = neigh_free_data, + .oo_clone = neigh_clone, + .oo_dump = { + [NL_DUMP_LINE] = neigh_dump_line, + [NL_DUMP_DETAILS] = neigh_dump_details, + [NL_DUMP_STATS] = neigh_dump_stats, + }, + .oo_compare = neigh_compare, + .oo_keygen = neigh_keygen, + .oo_attrs2str = neigh_attrs2str, + .oo_id_attrs = (NEIGH_ATTR_IFINDEX | NEIGH_ATTR_DST | NEIGH_ATTR_FAMILY), + .oo_id_attrs_get = neigh_id_attrs_get +}; + +static struct nl_af_group neigh_groups[] = { + { AF_UNSPEC, RTNLGRP_NEIGH }, + { AF_BRIDGE, RTNLGRP_NEIGH }, + { END_OF_GROUP_LIST }, +}; + +static struct nl_cache_ops rtnl_neigh_ops = { + .co_name = "route/neigh", + .co_hdrsize = sizeof(struct ndmsg), + .co_msgtypes = { + { RTM_NEWNEIGH, NL_ACT_NEW, "new" }, + { RTM_DELNEIGH, NL_ACT_DEL, "del" }, + { RTM_GETNEIGH, NL_ACT_GET, "get" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_ROUTE, + .co_groups = neigh_groups, + .co_request_update = neigh_request_update, + .co_msg_parser = neigh_msg_parser, + .co_obj_ops = &neigh_obj_ops, +}; + +static void __init neigh_init(void) +{ + nl_cache_mngt_register(&rtnl_neigh_ops); +} + +static void __exit neigh_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_neigh_ops); +} + +/** @} */ diff --git a/libnl/lib/route/neightbl.c b/libnl/lib/route/neightbl.c new file mode 100644 index 0000000..4264dcf --- /dev/null +++ b/libnl/lib/route/neightbl.c @@ -0,0 +1,824 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + */ + +/** + * @ingroup rtnl + * @defgroup neightbl Neighbour Tables + * @brief + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define NEIGHTBL_ATTR_FAMILY 0x001 +#define NEIGHTBL_ATTR_STATS 0x002 +#define NEIGHTBL_ATTR_NAME 0x004 +#define NEIGHTBL_ATTR_THRESH1 0x008 +#define NEIGHTBL_ATTR_THRESH2 0x010 +#define NEIGHTBL_ATTR_THRESH3 0x020 +#define NEIGHTBL_ATTR_CONFIG 0x040 +#define NEIGHTBL_ATTR_PARMS 0x080 +#define NEIGHTBL_ATTR_GC_INTERVAL 0x100 + +#define NEIGHTBLPARM_ATTR_IFINDEX 0x0001 +#define NEIGHTBLPARM_ATTR_REFCNT 0x0002 +#define NEIGHTBLPARM_ATTR_QUEUE_LEN 0x0004 +#define NEIGHTBLPARM_ATTR_APP_PROBES 0x0008 +#define NEIGHTBLPARM_ATTR_UCAST_PROBES 0x0010 +#define NEIGHTBLPARM_ATTR_MCAST_PROBES 0x0020 +#define NEIGHTBLPARM_ATTR_PROXY_QLEN 0x0040 +#define NEIGHTBLPARM_ATTR_REACHABLE_TIME 0x0080 +#define NEIGHTBLPARM_ATTR_BASE_REACHABLE_TIME 0x0100 +#define NEIGHTBLPARM_ATTR_RETRANS_TIME 0x0200 +#define NEIGHTBLPARM_ATTR_GC_STALETIME 0x0400 +#define NEIGHTBLPARM_ATTR_DELAY_PROBE_TIME 0x0800 +#define NEIGHTBLPARM_ATTR_ANYCAST_DELAY 0x1000 +#define NEIGHTBLPARM_ATTR_PROXY_DELAY 0x2000 +#define NEIGHTBLPARM_ATTR_LOCKTIME 0x4000 + +static struct nl_cache_ops rtnl_neightbl_ops; +static struct nl_object_ops neightbl_obj_ops; +/** @endcond */ + +static uint64_t neightbl_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + struct rtnl_neightbl *a = (struct rtnl_neightbl *) _a; + struct rtnl_neightbl *b = (struct rtnl_neightbl *) _b; + uint64_t diff = 0; + +#define NT_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, NEIGHTBL_ATTR_##ATTR, a, b, EXPR) + + diff |= NT_DIFF(FAMILY, a->nt_family != b->nt_family); + diff |= NT_DIFF(NAME, strcmp(a->nt_name, b->nt_name)); + diff |= NT_DIFF(THRESH1, a->nt_gc_thresh1 != b->nt_gc_thresh1); + diff |= NT_DIFF(THRESH2, a->nt_gc_thresh2 != b->nt_gc_thresh2); + diff |= NT_DIFF(THRESH3, a->nt_gc_thresh3 != b->nt_gc_thresh3); + diff |= NT_DIFF(GC_INTERVAL, a->nt_gc_interval != b->nt_gc_interval); + +#undef NT_DIFF + + if (!(a->ce_mask & NEIGHTBL_ATTR_PARMS) && + !(b->ce_mask & NEIGHTBL_ATTR_PARMS)) + return diff; + + /* XXX: FIXME: Compare parameter table */ + + +#if 0 +#define REQ(F) (fp->ntp_mask & NEIGHTBLPARM_ATTR_##F) +#define AVAIL(F) (op->ntp_mask & NEIGHTBLPARM_ATTR_##F) +#define _C(F, N) (REQ(F) && (!AVAIL(F) || (op->N != fp->N))) + if (_C(IFINDEX, ntp_ifindex) || + _C(QUEUE_LEN, ntp_queue_len) || + _C(APP_PROBES, ntp_app_probes) || + _C(UCAST_PROBES, ntp_ucast_probes) || + _C(MCAST_PROBES, ntp_mcast_probes) || + _C(PROXY_QLEN, ntp_proxy_qlen) || + _C(LOCKTIME, ntp_locktime) || + _C(RETRANS_TIME, ntp_retrans_time) || + _C(BASE_REACHABLE_TIME, ntp_base_reachable_time) || + _C(GC_STALETIME, ntp_gc_stale_time) || + _C(DELAY_PROBE_TIME, ntp_probe_delay) || + _C(ANYCAST_DELAY, ntp_anycast_delay) || + _C(PROXY_DELAY, ntp_proxy_delay)) + return 0; +#undef REQ +#undef AVAIL +#undef _C +#endif + + return diff; +} + + +static struct nla_policy neightbl_policy[NDTA_MAX+1] = { + [NDTA_NAME] = { .type = NLA_STRING, + .maxlen = NTBLNAMSIZ }, + [NDTA_THRESH1] = { .type = NLA_U32 }, + [NDTA_THRESH2] = { .type = NLA_U32 }, + [NDTA_THRESH3] = { .type = NLA_U32 }, + [NDTA_GC_INTERVAL] = { .type = NLA_U32 }, + [NDTA_CONFIG] = { .minlen = sizeof(struct ndt_config) }, + [NDTA_STATS] = { .minlen = sizeof(struct ndt_stats) }, + [NDTA_PARMS] = { .type = NLA_NESTED }, +}; + +static int neightbl_msg_parser(struct nl_cache_ops *ops, + struct sockaddr_nl *who, struct nlmsghdr *n, + struct nl_parser_param *pp) +{ + struct rtnl_neightbl *ntbl; + struct nlattr *tb[NDTA_MAX + 1]; + struct rtgenmsg *rtmsg; + int err; + + ntbl = rtnl_neightbl_alloc(); + if (!ntbl) { + err = -NLE_NOMEM; + goto errout; + } + + ntbl->ce_msgtype = n->nlmsg_type; + rtmsg = nlmsg_data(n); + + err = nlmsg_parse(n, sizeof(*rtmsg), tb, NDTA_MAX, neightbl_policy); + if (err < 0) + goto errout; + + ntbl->nt_family = rtmsg->rtgen_family; + + if (tb[NDTA_NAME] == NULL) { + err = -NLE_MISSING_ATTR; + goto errout; + } + + nla_strlcpy(ntbl->nt_name, tb[NDTA_NAME], NTBLNAMSIZ); + ntbl->ce_mask |= NEIGHTBL_ATTR_NAME; + + if (tb[NDTA_THRESH1]) { + ntbl->nt_gc_thresh1 = nla_get_u32(tb[NDTA_THRESH1]); + ntbl->ce_mask |= NEIGHTBL_ATTR_THRESH1; + } + + if (tb[NDTA_THRESH2]) { + ntbl->nt_gc_thresh2 = nla_get_u32(tb[NDTA_THRESH2]); + ntbl->ce_mask |= NEIGHTBL_ATTR_THRESH2; + } + + if (tb[NDTA_THRESH3]) { + ntbl->nt_gc_thresh3 = nla_get_u32(tb[NDTA_THRESH3]); + ntbl->ce_mask |= NEIGHTBL_ATTR_THRESH3; + } + + if (tb[NDTA_GC_INTERVAL]) { + ntbl->nt_gc_interval = nla_get_u32(tb[NDTA_GC_INTERVAL]); + ntbl->ce_mask |= NEIGHTBL_ATTR_GC_INTERVAL; + } + + if (tb[NDTA_CONFIG]) { + nla_memcpy(&ntbl->nt_config, tb[NDTA_CONFIG], + sizeof(ntbl->nt_config)); + ntbl->ce_mask |= NEIGHTBL_ATTR_CONFIG; + } + + if (tb[NDTA_STATS]) { + nla_memcpy(&ntbl->nt_stats, tb[NDTA_STATS], + sizeof(ntbl->nt_stats)); + ntbl->ce_mask |= NEIGHTBL_ATTR_STATS; + } + + if (tb[NDTA_PARMS]) { + struct nlattr *tbp[NDTPA_MAX + 1]; + struct rtnl_neightbl_parms *p = &ntbl->nt_parms; + + err = nla_parse_nested(tbp, NDTPA_MAX, tb[NDTA_PARMS], NULL); + if (err < 0) + goto errout; + +#define COPY_ENTRY(name, var) \ + if (tbp[NDTPA_ ##name]) { \ + p->ntp_ ##var = nla_get_u32(tbp[NDTPA_ ##name]); \ + p->ntp_mask |= NEIGHTBLPARM_ATTR_ ##name; \ + } + + COPY_ENTRY(IFINDEX, ifindex); + COPY_ENTRY(REFCNT, refcnt); + COPY_ENTRY(QUEUE_LEN, queue_len); + COPY_ENTRY(APP_PROBES, app_probes); + COPY_ENTRY(UCAST_PROBES, ucast_probes); + COPY_ENTRY(MCAST_PROBES, mcast_probes); + COPY_ENTRY(PROXY_QLEN, proxy_qlen); + COPY_ENTRY(PROXY_DELAY, proxy_delay); + COPY_ENTRY(ANYCAST_DELAY, anycast_delay); + COPY_ENTRY(LOCKTIME, locktime); + COPY_ENTRY(REACHABLE_TIME, reachable_time); + COPY_ENTRY(BASE_REACHABLE_TIME, base_reachable_time); + COPY_ENTRY(RETRANS_TIME, retrans_time); + COPY_ENTRY(GC_STALETIME, gc_stale_time); + COPY_ENTRY(DELAY_PROBE_TIME, probe_delay); +#undef COPY_ENTRY + + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; + } + + err = pp->pp_cb((struct nl_object *) ntbl, pp); +errout: + rtnl_neightbl_put(ntbl); + return err; +} + +static int neightbl_request_update(struct nl_cache *c, struct nl_sock *h) +{ + return nl_rtgen_request(h, RTM_GETNEIGHTBL, AF_UNSPEC, NLM_F_DUMP); +} + + +static void neightbl_dump_line(struct nl_object *arg, struct nl_dump_params *p) +{ + struct rtnl_neightbl *ntbl = (struct rtnl_neightbl *) arg; + + nl_dump_line(p, "%s", ntbl->nt_name); + + if (ntbl->nt_parms.ntp_mask & NEIGHTBLPARM_ATTR_IFINDEX) { + struct nl_cache *link_cache; + + link_cache = nl_cache_mngt_require_safe("route/link"); + + if (link_cache) { + char buf[32]; + nl_dump(p, "<%s> ", + rtnl_link_i2name(link_cache, + ntbl->nt_parms.ntp_ifindex, + buf, sizeof(buf))); + nl_cache_put(link_cache); + } else + nl_dump(p, "<%u> ", ntbl->nt_parms.ntp_ifindex); + } else + nl_dump(p, " "); + + if (ntbl->ce_mask & NEIGHTBL_ATTR_CONFIG) + nl_dump(p, "entries %u ", ntbl->nt_config.ndtc_entries); + + if (ntbl->ce_mask & NEIGHTBL_ATTR_PARMS) { + char rt[32], rt2[32]; + struct rtnl_neightbl_parms *pa = &ntbl->nt_parms; + + nl_dump(p, "reachable-time %s retransmit-time %s", + nl_msec2str(pa->ntp_reachable_time, rt, sizeof(rt)), + nl_msec2str(pa->ntp_retrans_time, rt2, sizeof(rt2))); + } + + nl_dump(p, "\n"); +} + +static void neightbl_dump_details(struct nl_object *arg, struct nl_dump_params *p) +{ + char x[32], y[32], z[32]; + struct rtnl_neightbl *ntbl = (struct rtnl_neightbl *) arg; + + neightbl_dump_line(arg, p); + + if (ntbl->ce_mask & NEIGHTBL_ATTR_CONFIG) { + nl_dump_line(p, " key-len %u entry-size %u last-flush %s\n", + ntbl->nt_config.ndtc_key_len, + ntbl->nt_config.ndtc_entry_size, + nl_msec2str(ntbl->nt_config.ndtc_last_flush, + x, sizeof(x))); + + nl_dump_line(p, " gc threshold %u/%u/%u interval %s " \ + "chain-position %u\n", + ntbl->nt_gc_thresh1, ntbl->nt_gc_thresh2, + ntbl->nt_gc_thresh3, + nl_msec2str(ntbl->nt_gc_interval, x, sizeof(x)), + ntbl->nt_config.ndtc_hash_chain_gc); + + nl_dump_line(p, " hash-rand 0x%08X/0x%08X last-rand %s\n", + ntbl->nt_config.ndtc_hash_rnd, + ntbl->nt_config.ndtc_hash_mask, + nl_msec2str(ntbl->nt_config.ndtc_last_rand, + x, sizeof(x))); + } + + if (ntbl->ce_mask & NEIGHTBL_ATTR_PARMS) { + struct rtnl_neightbl_parms *pa = &ntbl->nt_parms; + + nl_dump_line(p, " refcnt %u pending-queue-limit %u " \ + "proxy-delayed-queue-limit %u\n", + pa->ntp_refcnt, + pa->ntp_queue_len, + pa->ntp_proxy_qlen); + + nl_dump_line(p, " num-userspace-probes %u num-unicast-probes " \ + "%u num-multicast-probes %u\n", + pa->ntp_app_probes, + pa->ntp_ucast_probes, + pa->ntp_mcast_probes); + + nl_dump_line(p, " min-age %s base-reachable-time %s " \ + "stale-check-interval %s\n", + nl_msec2str(pa->ntp_locktime, x, sizeof(x)), + nl_msec2str(pa->ntp_base_reachable_time, + y, sizeof(y)), + nl_msec2str(pa->ntp_gc_stale_time, z, sizeof(z))); + + nl_dump_line(p, " initial-probe-delay %s answer-delay %s " \ + "proxy-answer-delay %s\n", + nl_msec2str(pa->ntp_probe_delay, x, sizeof(x)), + nl_msec2str(pa->ntp_anycast_delay, y, sizeof(y)), + nl_msec2str(pa->ntp_proxy_delay, z, sizeof(z))); + } +} + +static void neightbl_dump_stats(struct nl_object *arg, struct nl_dump_params *p) +{ + struct rtnl_neightbl *ntbl = (struct rtnl_neightbl *) arg; + + neightbl_dump_details(arg, p); + + if (!(ntbl->ce_mask & NEIGHTBL_ATTR_STATS)) + return; + + nl_dump_line(p, " " \ + " lookups %" PRIu64 \ + " hits %" PRIu64 \ + " failed %" PRIu64 \ + " allocations %" PRIu64 \ + " destroys %" PRIu64 \ + "\n", + ntbl->nt_stats.ndts_lookups, + ntbl->nt_stats.ndts_hits, + ntbl->nt_stats.ndts_res_failed, + ntbl->nt_stats.ndts_allocs, + ntbl->nt_stats.ndts_destroys); + + nl_dump_line(p, " " \ + " hash-grows %" PRIu64 \ + " forced-gc-runs %" PRIu64 \ + " periodic-gc-runs %" PRIu64 \ + "\n", + ntbl->nt_stats.ndts_hash_grows, + ntbl->nt_stats.ndts_forced_gc_runs, + ntbl->nt_stats.ndts_periodic_gc_runs); + + nl_dump_line(p, " " \ + " rcv-unicast-probes %" PRIu64 \ + " rcv-multicast-probes %" PRIu64 \ + "\n", + ntbl->nt_stats.ndts_rcv_probes_ucast, + ntbl->nt_stats.ndts_rcv_probes_mcast); +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct rtnl_neightbl *rtnl_neightbl_alloc(void) +{ + return (struct rtnl_neightbl *) nl_object_alloc(&neightbl_obj_ops); +} + +void rtnl_neightbl_put(struct rtnl_neightbl *neightbl) +{ + nl_object_put((struct nl_object *) neightbl); +} + +/** @} */ + +/** + * @name Neighbour Table Cache Management + * @{ + */ + +/** + * Build a neighbour table cache including all neighbour tables currently configured in the kernel. + * @arg sk Netlink socket. + * @arg result Pointer to store resulting cache. + * + * Allocates a new neighbour table cache, initializes it properly and + * updates it to include all neighbour tables currently configured in + * the kernel. + * + * @return 0 on success or a negative error code. + */ +int rtnl_neightbl_alloc_cache(struct nl_sock *sk, struct nl_cache **result) +{ + return nl_cache_alloc_and_fill(&rtnl_neightbl_ops, sk, result); +} + +/** + * Lookup neighbour table by name and optional interface index + * @arg cache neighbour table cache + * @arg name name of table + * @arg ifindex optional interface index + * + * Looks up the neighbour table matching the specified name and + * optionally the specified ifindex to retrieve device specific + * parameter sets. + * + * @return ptr to neighbour table inside the cache or NULL if no + * match was found. + */ +struct rtnl_neightbl *rtnl_neightbl_get(struct nl_cache *cache, + const char *name, int ifindex) +{ + struct rtnl_neightbl *nt; + + if (cache->c_ops != &rtnl_neightbl_ops) + return NULL; + + nl_list_for_each_entry(nt, &cache->c_items, ce_list) { + if (!strcasecmp(nt->nt_name, name) && + ((!ifindex && !nt->nt_parms.ntp_ifindex) || + (ifindex && ifindex == nt->nt_parms.ntp_ifindex))) { + nl_object_get((struct nl_object *) nt); + return nt; + } + } + + return NULL; +} + +/** @} */ + +/** + * @name Neighbour Table Modifications + * @{ + */ + +/** + * Builds a netlink change request message to change neighbour table attributes + * @arg old neighbour table to change + * @arg tmpl template with requested changes + * @arg result Pointer to store resulting message. + * + * Builds a new netlink message requesting a change of neighbour table + * attributes. The netlink message header isn't fully equipped with all + * relevant fields and must be sent out via nl_send_auto_complete() or + * supplemented as needed. + * \a old must point to a neighbour table currently configured in the + * kernel and \a tmpl must contain the attributes to be changed set via + * \c rtnl_neightbl_set_* functions. + * + * @return 0 on success or a negative error code. + */ +int rtnl_neightbl_build_change_request(struct rtnl_neightbl *old, + struct rtnl_neightbl *tmpl, + struct nl_msg **result) +{ + struct nl_msg *m, *parms = NULL; + struct ndtmsg ndt = { + .ndtm_family = old->nt_family, + }; + + m = nlmsg_alloc_simple(RTM_SETNEIGHTBL, 0); + if (!m) + return -NLE_NOMEM; + + if (nlmsg_append(m, &ndt, sizeof(ndt), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + NLA_PUT_STRING(m, NDTA_NAME, old->nt_name); + + if (tmpl->ce_mask & NEIGHTBL_ATTR_THRESH1) + NLA_PUT_U32(m, NDTA_THRESH1, tmpl->nt_gc_thresh1); + + if (tmpl->ce_mask & NEIGHTBL_ATTR_THRESH2) + NLA_PUT_U32(m, NDTA_THRESH2, tmpl->nt_gc_thresh2); + + if (tmpl->ce_mask & NEIGHTBL_ATTR_THRESH2) + NLA_PUT_U32(m, NDTA_THRESH2, tmpl->nt_gc_thresh2); + + if (tmpl->ce_mask & NEIGHTBL_ATTR_GC_INTERVAL) + NLA_PUT_U64(m, NDTA_GC_INTERVAL, + tmpl->nt_gc_interval); + + if (tmpl->ce_mask & NEIGHTBL_ATTR_PARMS) { + struct rtnl_neightbl_parms *p = &tmpl->nt_parms; + + parms = nlmsg_alloc(); + if (!parms) + goto nla_put_failure; + + if (old->nt_parms.ntp_mask & NEIGHTBLPARM_ATTR_IFINDEX) + NLA_PUT_U32(parms, NDTPA_IFINDEX, + old->nt_parms.ntp_ifindex); + + + if (p->ntp_mask & NEIGHTBLPARM_ATTR_QUEUE_LEN) + NLA_PUT_U32(parms, NDTPA_QUEUE_LEN, p->ntp_queue_len); + + if (p->ntp_mask & NEIGHTBLPARM_ATTR_APP_PROBES) + NLA_PUT_U32(parms, NDTPA_APP_PROBES, p->ntp_app_probes); + + if (p->ntp_mask & NEIGHTBLPARM_ATTR_UCAST_PROBES) + NLA_PUT_U32(parms, NDTPA_UCAST_PROBES, + p->ntp_ucast_probes); + + if (p->ntp_mask & NEIGHTBLPARM_ATTR_MCAST_PROBES) + NLA_PUT_U32(parms, NDTPA_MCAST_PROBES, + p->ntp_mcast_probes); + + if (p->ntp_mask & NEIGHTBLPARM_ATTR_PROXY_QLEN) + NLA_PUT_U32(parms, NDTPA_PROXY_QLEN, + p->ntp_proxy_qlen); + + if (p->ntp_mask & NEIGHTBLPARM_ATTR_BASE_REACHABLE_TIME) + NLA_PUT_U64(parms, NDTPA_BASE_REACHABLE_TIME, + p->ntp_base_reachable_time); + + if (p->ntp_mask & NEIGHTBLPARM_ATTR_RETRANS_TIME) + NLA_PUT_U64(parms, NDTPA_RETRANS_TIME, + p->ntp_retrans_time); + + if (p->ntp_mask & NEIGHTBLPARM_ATTR_GC_STALETIME) + NLA_PUT_U64(parms, NDTPA_GC_STALETIME, + p->ntp_gc_stale_time); + + if (p->ntp_mask & NEIGHTBLPARM_ATTR_DELAY_PROBE_TIME) + NLA_PUT_U64(parms, NDTPA_DELAY_PROBE_TIME, + p->ntp_proxy_delay); + + if (p->ntp_mask & NEIGHTBLPARM_ATTR_ANYCAST_DELAY) + NLA_PUT_U64(parms, NDTPA_ANYCAST_DELAY, + p->ntp_anycast_delay); + + if (p->ntp_mask & NEIGHTBLPARM_ATTR_PROXY_DELAY) + NLA_PUT_U64(parms, NDTPA_PROXY_DELAY, + p->ntp_proxy_delay); + + if (p->ntp_mask & NEIGHTBLPARM_ATTR_LOCKTIME) + NLA_PUT_U64(parms, NDTPA_LOCKTIME, p->ntp_locktime); + + if (nla_put_nested(m, NDTA_PARMS, parms) < 0) + goto nla_put_failure; + + nlmsg_free(parms); + } + + *result = m; + return 0; + +nla_put_failure: + if (parms) + nlmsg_free(parms); + nlmsg_free(m); + return -NLE_MSGSIZE; +} + +/** + * Change neighbour table attributes + * @arg sk Netlink socket. + * @arg old neighbour table to be changed + * @arg tmpl template with requested changes + * + * Builds a new netlink message by calling + * rtnl_neightbl_build_change_request(), sends the request to the + * kernel and waits for the next ACK to be received, i.e. blocks + * until the request has been processed. + * + * @return 0 on success or a negative error code + */ +int rtnl_neightbl_change(struct nl_sock *sk, struct rtnl_neightbl *old, + struct rtnl_neightbl *tmpl) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_neightbl_build_change_request(old, tmpl, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +/** @} */ + +/** + * @name Attribute Modification + * @{ + */ + +void rtnl_neightbl_set_family(struct rtnl_neightbl *ntbl, int family) +{ + ntbl->nt_family = family; + ntbl->ce_mask |= NEIGHTBL_ATTR_FAMILY; +} + +void rtnl_neightbl_set_gc_interval(struct rtnl_neightbl *ntbl, uint64_t ms) +{ + ntbl->nt_gc_interval = ms; + ntbl->ce_mask |= NEIGHTBL_ATTR_GC_INTERVAL; +} + +void rtnl_neightbl_set_gc_tresh1(struct rtnl_neightbl *ntbl, int thresh) +{ + ntbl->nt_gc_thresh1 = thresh; + ntbl->ce_mask |= NEIGHTBL_ATTR_THRESH1; +} + +void rtnl_neightbl_set_gc_tresh2(struct rtnl_neightbl *ntbl, int thresh) +{ + ntbl->nt_gc_thresh2 = thresh; + ntbl->ce_mask |= NEIGHTBL_ATTR_THRESH2; +} + +void rtnl_neightbl_set_gc_tresh3(struct rtnl_neightbl *ntbl, int thresh) +{ + ntbl->nt_gc_thresh3 = thresh; + ntbl->ce_mask |= NEIGHTBL_ATTR_THRESH3; +} + +void rtnl_neightbl_set_name(struct rtnl_neightbl *ntbl, const char *name) +{ + _nl_strncpy_trunc(ntbl->nt_name, name, sizeof(ntbl->nt_name)); + ntbl->ce_mask |= NEIGHTBL_ATTR_NAME; +} + +void rtnl_neightbl_set_dev(struct rtnl_neightbl *ntbl, int ifindex) +{ + ntbl->nt_parms.ntp_ifindex = ifindex; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_IFINDEX; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** + * Set the queue length for pending requests of a neighbour table to the specified value + * @arg ntbl neighbour table to change + * @arg len new queue len + */ +void rtnl_neightbl_set_queue_len(struct rtnl_neightbl *ntbl, int len) +{ + ntbl->nt_parms.ntp_queue_len = len; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_QUEUE_LEN; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** + * Set the queue length for delay proxy arp requests of a neighbour table to the specified value + * @arg ntbl neighbour table to change + * @arg len new queue len + */ +void rtnl_neightbl_set_proxy_queue_len(struct rtnl_neightbl *ntbl, int len) +{ + ntbl->nt_parms.ntp_proxy_qlen = len; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_PROXY_QLEN; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** + * Set the number of application probes of a neighbour table to the specified value + * @arg ntbl neighbour table to change + * @arg probes new probes value + */ +void rtnl_neightbl_set_app_probes(struct rtnl_neightbl *ntbl, int probes) +{ + ntbl->nt_parms.ntp_app_probes = probes; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_APP_PROBES; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** + * Set the number of unicast probes of a neighbour table to the specified value + * @arg ntbl neighbour table to change + * @arg probes new probes value + */ +void rtnl_neightbl_set_ucast_probes(struct rtnl_neightbl *ntbl, int probes) +{ + ntbl->nt_parms.ntp_ucast_probes = probes; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_UCAST_PROBES; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** + * Set the number of multicast probes of a neighbour table to the specified value + * @arg ntbl neighbour table to change + * @arg probes new probes value + */ +void rtnl_neightbl_set_mcast_probes(struct rtnl_neightbl *ntbl, int probes) +{ + ntbl->nt_parms.ntp_mcast_probes = probes; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_MCAST_PROBES; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** + * Set the base reachable time of a neighbour table to the specified value + * @arg ntbl neighbour table to change + * @arg ms new base reachable time in milliseconds + */ +void rtnl_neightbl_set_base_reachable_time(struct rtnl_neightbl *ntbl, + uint64_t ms) +{ + ntbl->nt_parms.ntp_base_reachable_time = ms; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_BASE_REACHABLE_TIME; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** + * Set the retransmit time of a neighbour table to the specified value + * @arg ntbl neighbour table to change + * @arg ms new retransmit time + */ +void rtnl_neightbl_set_retrans_time(struct rtnl_neightbl *ntbl, uint64_t ms) +{ + ntbl->nt_parms.ntp_retrans_time = ms; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_RETRANS_TIME; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** + * Set the gc stale time of a neighbour table to the specified value + * @arg ntbl neighbour table to change + * @arg ms new gc stale time in milliseconds + */ +void rtnl_neightbl_set_gc_stale_time(struct rtnl_neightbl *ntbl, uint64_t ms) +{ + ntbl->nt_parms.ntp_gc_stale_time = ms; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_GC_STALETIME; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** + * Set the first probe delay time of a neighbour table to the specified value + * @arg ntbl neighbour table to change + * @arg ms new first probe delay time in milliseconds + */ +void rtnl_neightbl_set_delay_probe_time(struct rtnl_neightbl *ntbl, uint64_t ms) +{ + ntbl->nt_parms.ntp_probe_delay = ms; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_DELAY_PROBE_TIME; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** + * Set the anycast delay of a neighbour table to the specified value + * @arg ntbl neighbour table to change + * @arg ms new anycast delay in milliseconds + */ +void rtnl_neightbl_set_anycast_delay(struct rtnl_neightbl *ntbl, uint64_t ms) +{ + ntbl->nt_parms.ntp_anycast_delay = ms; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_ANYCAST_DELAY; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** + * Set the proxy delay of a neighbour table to the specified value + * @arg ntbl neighbour table to change + * @arg ms new proxy delay in milliseconds + */ +void rtnl_neightbl_set_proxy_delay(struct rtnl_neightbl *ntbl, uint64_t ms) +{ + ntbl->nt_parms.ntp_proxy_delay = ms; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_PROXY_DELAY; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** + * Set the locktime of a neighbour table to the specified value + * @arg ntbl neighbour table to change + * @arg ms new locktime in milliseconds + */ +void rtnl_neightbl_set_locktime(struct rtnl_neightbl *ntbl, uint64_t ms) +{ + ntbl->nt_parms.ntp_locktime = ms; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_LOCKTIME; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** @} */ + +static struct nl_object_ops neightbl_obj_ops = { + .oo_name = "route/neightbl", + .oo_size = sizeof(struct rtnl_neightbl), + .oo_dump = { + [NL_DUMP_LINE] = neightbl_dump_line, + [NL_DUMP_DETAILS] = neightbl_dump_details, + [NL_DUMP_STATS] = neightbl_dump_stats, + }, + .oo_compare = neightbl_compare, +}; + +static struct nl_cache_ops rtnl_neightbl_ops = { + .co_name = "route/neightbl", + .co_hdrsize = sizeof(struct rtgenmsg), + .co_msgtypes = { + { RTM_NEWNEIGHTBL, NL_ACT_NEW, "new" }, + { RTM_SETNEIGHTBL, NL_ACT_SET, "set" }, + { RTM_GETNEIGHTBL, NL_ACT_GET, "get" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_ROUTE, + .co_request_update = neightbl_request_update, + .co_msg_parser = neightbl_msg_parser, + .co_obj_ops = &neightbl_obj_ops, +}; + +static void __init neightbl_init(void) +{ + nl_cache_mngt_register(&rtnl_neightbl_ops); +} + +static void __exit neightbl_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_neightbl_ops); +} + +/** @} */ diff --git a/libnl/lib/route/netconf.c b/libnl/lib/route/netconf.c new file mode 100644 index 0000000..1b93b9c --- /dev/null +++ b/libnl/lib/route/netconf.c @@ -0,0 +1,579 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2017 David Ahern + */ + +/** + * @ingroup rtnl + * @defgroup netconf Netconf + * @brief + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define NETCONF_ATTR_FAMILY 0x0001 +#define NETCONF_ATTR_IFINDEX 0x0002 +#define NETCONF_ATTR_RP_FILTER 0x0004 +#define NETCONF_ATTR_FWDING 0x0008 +#define NETCONF_ATTR_MC_FWDING 0x0010 +#define NETCONF_ATTR_PROXY_NEIGH 0x0020 +#define NETCONF_ATTR_IGNORE_RT_LINKDWN 0x0040 +#define NETCONF_ATTR_INPUT 0x0080 + +struct rtnl_netconf +{ + NLHDR_COMMON + + int family; + int ifindex; + int rp_filter; + int forwarding; + int mc_forwarding; + int proxy_neigh; + int ignore_routes_linkdown; + int input; +}; + +static struct nl_cache_ops rtnl_netconf_ops; +static struct nl_object_ops netconf_obj_ops; +/** @endcond */ + +static struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = { + [NETCONFA_IFINDEX] = { .type = NLA_S32 }, + [NETCONFA_FORWARDING] = { .type = NLA_S32 }, + [NETCONFA_MC_FORWARDING] = { .type = NLA_S32 }, + [NETCONFA_RP_FILTER] = { .type = NLA_S32 }, + [NETCONFA_PROXY_NEIGH] = { .type = NLA_S32 }, + [NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN] = { .type = NLA_S32 }, +}; + +static struct nla_policy devconf_ipv6_policy[NETCONFA_MAX+1] = { + [NETCONFA_IFINDEX] = { .type = NLA_S32 }, + [NETCONFA_FORWARDING] = { .type = NLA_S32 }, + [NETCONFA_MC_FORWARDING] = { .type = NLA_S32 }, + [NETCONFA_PROXY_NEIGH] = { .type = NLA_S32 }, + [NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN] = { .type = NLA_S32 }, +}; + +static struct nla_policy devconf_mpls_policy[NETCONFA_MAX+1] = { + [NETCONFA_IFINDEX] = { .type = NLA_S32 }, + [NETCONFA_INPUT] = { .type = NLA_S32 }, +}; + +static struct rtnl_netconf *rtnl_netconf_alloc(void) +{ + return (struct rtnl_netconf *) nl_object_alloc(&netconf_obj_ops); +} + +static int netconf_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct rtnl_netconf *dst = nl_object_priv(_dst); + struct rtnl_netconf *src = nl_object_priv(_src); + + *dst = *src; + + return 0; +} + +static int netconf_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, struct nl_parser_param *pp) +{ + struct nlattr *tb[NETCONFA_MAX+1], *attr; + struct rtnl_netconf *nc; + struct netconfmsg *ncm; + int err; + + ncm = nlmsg_data(nlh); + switch (ncm->ncm_family) { + case AF_INET: + err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX, + devconf_ipv4_policy); + if (err < 0) + return err; + break; + case AF_INET6: + err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX, + devconf_ipv6_policy); + if (err < 0) + return err; + break; + case AF_MPLS: + err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX, + devconf_mpls_policy); + if (err < 0) + return err; + break; + default: + printf("unexpected netconf family: %d\n", ncm->ncm_family); + return -1; + } + + if (!tb[NETCONFA_IFINDEX]) + return -1; + + nc = rtnl_netconf_alloc(); + if (!nc) + return -NLE_NOMEM; + + nc->ce_msgtype = nlh->nlmsg_type; + nc->family = ncm->ncm_family; + nc->ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]); + + nc->ce_mask = NETCONF_ATTR_FAMILY | NETCONF_ATTR_IFINDEX; + + + if (tb[NETCONFA_RP_FILTER]) { + attr = tb[NETCONFA_RP_FILTER]; + nc->rp_filter = nla_get_s32(attr); + nc->ce_mask |= NETCONF_ATTR_RP_FILTER; + } + + if (tb[NETCONFA_FORWARDING]) { + attr = tb[NETCONFA_FORWARDING]; + nc->forwarding = nla_get_s32(attr); + nc->ce_mask |= NETCONF_ATTR_FWDING; + } + + if (tb[NETCONFA_MC_FORWARDING]) { + attr = tb[NETCONFA_MC_FORWARDING]; + nc->mc_forwarding = nla_get_s32(attr); + nc->ce_mask |= NETCONF_ATTR_MC_FWDING; + } + + if (tb[NETCONFA_PROXY_NEIGH]) { + attr = tb[NETCONFA_PROXY_NEIGH]; + nc->proxy_neigh = nla_get_s32(attr); + nc->ce_mask |= NETCONF_ATTR_PROXY_NEIGH; + } + + if (tb[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]) { + attr = tb[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]; + nc->ignore_routes_linkdown = nla_get_s32(attr); + nc->ce_mask |= NETCONF_ATTR_IGNORE_RT_LINKDWN; + } + + if (tb[NETCONFA_INPUT]) { + attr = tb[NETCONFA_INPUT]; + nc->input = nla_get_s32(attr); + nc->ce_mask |= NETCONF_ATTR_INPUT; + } + + err = pp->pp_cb((struct nl_object *) nc, pp); + + rtnl_netconf_put(nc); + return err; +} + +static int netconf_request_update(struct nl_cache *cache, struct nl_sock *sk) +{ + struct netconfmsg nc = { + .ncm_family = cache->c_iarg1, + }; + + return nl_send_simple(sk, RTM_GETNETCONF, NLM_F_DUMP, &nc, sizeof(nc)); +} + +static void netconf_dump_line(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_netconf *nc = (struct rtnl_netconf *) obj; + struct nl_cache *link_cache; + char buf[64]; + + switch(nc->family) { + case AF_INET: + nl_dump(p, "ipv4 "); + break; + case AF_INET6: + nl_dump(p, "ipv6 "); + break; + case AF_MPLS: + nl_dump(p, "mpls "); + break; + default: + return; + } + + switch(nc->ifindex) { + case NETCONFA_IFINDEX_ALL: + nl_dump(p, "all "); + break; + case NETCONFA_IFINDEX_DEFAULT: + nl_dump(p, "default "); + break; + default: + link_cache = nl_cache_mngt_require_safe("route/link"); + if (link_cache) { + nl_dump(p, "dev %s ", + rtnl_link_i2name(link_cache, nc->ifindex, + buf, sizeof(buf))); + nl_cache_put(link_cache); + } else + nl_dump(p, "dev %d ", nc->ifindex); + } + + if (nc->ce_mask & NETCONF_ATTR_FWDING) { + nl_dump(p, "forwarding %s ", + nc->forwarding ? "on" : "off"); + } + + if (nc->ce_mask & NETCONF_ATTR_RP_FILTER) { + if (nc->rp_filter == 0) + nl_dump(p, "rp_filter off "); + else if (nc->rp_filter == 1) + nl_dump(p, "rp_filter strict "); + else if (nc->rp_filter == 2) + nl_dump(p, "rp_filter loose "); + else + nl_dump(p, "rp_filter unknown-mode "); + } + + if (nc->ce_mask & NETCONF_ATTR_MC_FWDING) { + nl_dump(p, "mc_forwarding %s ", + nc->mc_forwarding ? "on" : "off"); + } + + if (nc->ce_mask & NETCONF_ATTR_PROXY_NEIGH) + nl_dump(p, "proxy_neigh %d ", nc->proxy_neigh); + + if (nc->ce_mask & NETCONF_ATTR_IGNORE_RT_LINKDWN) { + nl_dump(p, "ignore_routes_with_linkdown %s ", + nc->ignore_routes_linkdown ? "on" : "off"); + } + + if (nc->ce_mask & NETCONF_ATTR_INPUT) + nl_dump(p, "input %s ", nc->input ? "on" : "off"); + + nl_dump(p, "\n"); +} + +static const struct trans_tbl netconf_attrs[] = { + __ADD(NETCONF_ATTR_FAMILY, family), + __ADD(NETCONF_ATTR_IFINDEX, ifindex), + __ADD(NETCONF_ATTR_RP_FILTER, rp_filter), + __ADD(NETCONF_ATTR_FWDING, forwarding), + __ADD(NETCONF_ATTR_MC_FWDING, mc_forwarding), + __ADD(NETCONF_ATTR_PROXY_NEIGH, proxy_neigh), + __ADD(NETCONF_ATTR_IGNORE_RT_LINKDWN, ignore_routes_with_linkdown), + __ADD(NETCONF_ATTR_INPUT, input), +}; + +static char *netconf_attrs2str(int attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, netconf_attrs, + ARRAY_SIZE(netconf_attrs)); +} + +static void netconf_keygen(struct nl_object *obj, uint32_t *hashkey, + uint32_t table_sz) +{ + struct rtnl_netconf *nc = (struct rtnl_netconf *) obj; + unsigned int nckey_sz; + struct nc_hash_key { + int nc_family; + int nc_index; + } __attribute__((packed)) nckey; + + nckey_sz = sizeof(nckey); + nckey.nc_family = nc->family; + nckey.nc_index = nc->ifindex; + + *hashkey = nl_hash(&nckey, nckey_sz, 0) % table_sz; + + NL_DBG(5, "netconf %p key (dev %d fam %d) keysz %d, hash 0x%x\n", + nc, nckey.nc_index, nckey.nc_family, nckey_sz, *hashkey); +} + +static uint64_t netconf_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + struct rtnl_netconf *a = (struct rtnl_netconf *) _a; + struct rtnl_netconf *b = (struct rtnl_netconf *) _b; + uint64_t diff = 0; + +#define NETCONF_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, NETCONF_ATTR_##ATTR, a, b, EXPR) + + diff |= NETCONF_DIFF(FAMILY, a->family != b->family); + diff |= NETCONF_DIFF(IFINDEX, a->ifindex != b->ifindex); + diff |= NETCONF_DIFF(RP_FILTER, a->rp_filter != b->rp_filter); + diff |= NETCONF_DIFF(FWDING, a->forwarding != b->forwarding); + diff |= NETCONF_DIFF(MC_FWDING, a->mc_forwarding != b->mc_forwarding); + diff |= NETCONF_DIFF(PROXY_NEIGH, a->proxy_neigh != b->proxy_neigh); + diff |= NETCONF_DIFF(IGNORE_RT_LINKDWN, + a->ignore_routes_linkdown != b->ignore_routes_linkdown); + diff |= NETCONF_DIFF(INPUT, a->input != b->input); + +#undef NETCONF_DIFF + + return diff; +} + +static int netconf_update(struct nl_object *old_obj, struct nl_object *new_obj) +{ + struct rtnl_netconf *new_nc = (struct rtnl_netconf *) new_obj; + struct rtnl_netconf *old_nc = (struct rtnl_netconf *) old_obj; + int action = new_obj->ce_msgtype; + + switch(action) { + case RTM_NEWNETCONF: + if (new_nc->family != old_nc->family || + new_nc->ifindex != old_nc->ifindex) + return -NLE_OPNOTSUPP; + + if (new_nc->ce_mask & NETCONF_ATTR_RP_FILTER) + old_nc->rp_filter = new_nc->rp_filter; + if (new_nc->ce_mask & NETCONF_ATTR_FWDING) + old_nc->forwarding = new_nc->forwarding; + if (new_nc->ce_mask & NETCONF_ATTR_MC_FWDING) + old_nc->mc_forwarding = new_nc->mc_forwarding; + if (new_nc->ce_mask & NETCONF_ATTR_PROXY_NEIGH) + old_nc->proxy_neigh = new_nc->proxy_neigh; + if (new_nc->ce_mask & NETCONF_ATTR_IGNORE_RT_LINKDWN) + old_nc->ignore_routes_linkdown = new_nc->ignore_routes_linkdown; + + break; + default: + return -NLE_OPNOTSUPP; + } + + return NLE_SUCCESS; +} + +/** + * @name Cache Management + * @{ + */ + +int rtnl_netconf_alloc_cache(struct nl_sock *sk, struct nl_cache **result) +{ + return nl_cache_alloc_and_fill(&rtnl_netconf_ops, sk, result); +} + +/** + * Search netconf in cache + * @arg cache netconf cache + * @arg family Address family of interest + * @arg ifindex Interface index of interest + * + * Searches netconf cache previously allocated with rtnl_netconf_alloc_cache() + * for given index and family + * + * The reference counter is incremented before returning the netconf entry, + * therefore the reference must be given back with rtnl_netconf_put() after + * usage. + * + * @return netconf object or NULL if no match was found. + */ +struct rtnl_netconf *rtnl_netconf_get_by_idx(struct nl_cache *cache, int family, + int ifindex) +{ + struct rtnl_netconf *nc; + + if (!ifindex || !family || cache->c_ops != &rtnl_netconf_ops) + return NULL; + + nl_list_for_each_entry(nc, &cache->c_items, ce_list) { + if (nc->ifindex == ifindex && + nc->family == family) { + nl_object_get((struct nl_object *) nc); + return nc; + } + } + + return NULL; +} + +void rtnl_netconf_put(struct rtnl_netconf *nc) +{ + nl_object_put((struct nl_object *) nc); +} + +/** + * Search netconf in cache + * @arg cache netconf cache + * @arg family Address family of interest + * + * Searches netconf cache previously allocated with rtnl_netconf_alloc_cache() + * for "all" netconf settings for given family + * + * The reference counter is incremented before returning the netconf entry, + * therefore the reference must be given back with rtnl_netconf_put() after + * usage. + * + * @return netconf object or NULL if no match was found. + */ +struct rtnl_netconf *rtnl_netconf_get_all(struct nl_cache *cache, int family) +{ + return rtnl_netconf_get_by_idx(cache, family, NETCONFA_IFINDEX_ALL); +} + +/** + * Search netconf in cache + * @arg cache netconf cache + * @arg family Address family of interest + * + * Searches netconf cache previously allocated with rtnl_netconf_alloc_cache() + * for "default" netconf settings for given family + * + * The reference counter is incremented before returning the netconf entry, + * therefore the reference must be given back with rtnl_netconf_put() after + * usage. + * + * @return netconf object or NULL if no match was found. + */ +struct rtnl_netconf *rtnl_netconf_get_default(struct nl_cache *cache, int family) +{ + return rtnl_netconf_get_by_idx(cache, family, NETCONFA_IFINDEX_DEFAULT); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +int rtnl_netconf_get_family(struct rtnl_netconf *nc, int *val) +{ + if (!nc) + return -NLE_INVAL; + if (!(nc->ce_mask & NETCONF_ATTR_FAMILY)) + return -NLE_MISSING_ATTR; + if (val) + *val = nc->family; + return 0; +} +int rtnl_netconf_get_ifindex(struct rtnl_netconf *nc, int *val) +{ + if (!nc) + return -NLE_INVAL; + if (!(nc->ce_mask & NETCONF_ATTR_IFINDEX)) + return -NLE_MISSING_ATTR; + if (val) + *val = nc->ifindex; + return 0; +} +int rtnl_netconf_get_forwarding(struct rtnl_netconf *nc, int *val) +{ + if (!nc) + return -NLE_INVAL; + if (!(nc->ce_mask & NETCONF_ATTR_FWDING)) + return -NLE_MISSING_ATTR; + if (val) + *val = nc->forwarding; + return 0; +} +int rtnl_netconf_get_mc_forwarding(struct rtnl_netconf *nc, int *val) +{ + if (!nc) + return -NLE_INVAL; + if (!(nc->ce_mask & NETCONF_ATTR_MC_FWDING)) + return -NLE_MISSING_ATTR; + if (val) + *val = nc->mc_forwarding; + return 0; +} +int rtnl_netconf_get_rp_filter(struct rtnl_netconf *nc, int *val) +{ + if (!nc) + return -NLE_INVAL; + if (!(nc->ce_mask & NETCONF_ATTR_RP_FILTER)) + return -NLE_MISSING_ATTR; + if (val) + *val = nc->rp_filter; + return 0; +} +int rtnl_netconf_get_proxy_neigh(struct rtnl_netconf *nc, int *val) +{ + if (!nc) + return -NLE_INVAL; + if (!(nc->ce_mask & NETCONF_ATTR_PROXY_NEIGH)) + return -NLE_MISSING_ATTR; + if (val) + *val = nc->proxy_neigh; + return 0; +} +int rtnl_netconf_get_ignore_routes_linkdown(struct rtnl_netconf *nc, int *val) +{ + if (!nc) + return -NLE_INVAL; + if (!(nc->ce_mask & NETCONF_ATTR_IGNORE_RT_LINKDWN)) + return -NLE_MISSING_ATTR; + if (val) + *val = nc->ignore_routes_linkdown; + return 0; +} +int rtnl_netconf_get_input(struct rtnl_netconf *nc, int *val) +{ + if (!nc) + return -NLE_INVAL; + if (!(nc->ce_mask & NETCONF_ATTR_INPUT)) + return -NLE_MISSING_ATTR; + if (val) + *val = nc->input; + return 0; +} + + +/** @} */ + +static struct nl_object_ops netconf_obj_ops = { + .oo_name = "route/netconf", + .oo_size = sizeof(struct rtnl_netconf), + .oo_clone = netconf_clone, + .oo_dump = { + [NL_DUMP_LINE] = netconf_dump_line, + [NL_DUMP_DETAILS] = netconf_dump_line, + }, + .oo_compare = netconf_compare, + .oo_keygen = netconf_keygen, + .oo_update = netconf_update, + .oo_attrs2str = netconf_attrs2str, + .oo_id_attrs = (NETCONF_ATTR_FAMILY | + NETCONF_ATTR_IFINDEX) +}; + +static struct nl_af_group netconf_groups[] = { + { AF_INET, RTNLGRP_IPV4_NETCONF }, + { AF_INET6, RTNLGRP_IPV6_NETCONF }, + { AF_MPLS, RTNLGRP_MPLS_NETCONF }, + { END_OF_GROUP_LIST }, +}; + +static struct nl_cache_ops rtnl_netconf_ops = { + .co_name = "route/netconf", + .co_hdrsize = sizeof(struct netconfmsg), + .co_msgtypes = { + { RTM_NEWNETCONF, NL_ACT_NEW, "new" }, + { RTM_DELNETCONF, NL_ACT_DEL, "del" }, + { RTM_GETNETCONF, NL_ACT_GET, "get" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_ROUTE, + .co_groups = netconf_groups, + .co_request_update = netconf_request_update, + .co_msg_parser = netconf_msg_parser, + .co_obj_ops = &netconf_obj_ops, +}; + +static void __init netconf_init(void) +{ + nl_cache_mngt_register(&rtnl_netconf_ops); +} + +static void __exit netconf_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_netconf_ops); +} + +/** @} */ diff --git a/libnl/lib/route/nexthop.c b/libnl/lib/route/nexthop.c new file mode 100644 index 0000000..794db6d --- /dev/null +++ b/libnl/lib/route/nexthop.c @@ -0,0 +1,424 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + */ + +/** + * @ingroup route_obj + * @defgroup nexthop Nexthop + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define NH_ATTR_FLAGS 0x000001 +#define NH_ATTR_WEIGHT 0x000002 +#define NH_ATTR_IFINDEX 0x000004 +#define NH_ATTR_GATEWAY 0x000008 +#define NH_ATTR_REALMS 0x000010 +#define NH_ATTR_NEWDST 0x000020 +#define NH_ATTR_VIA 0x000040 +#define NH_ATTR_ENCAP 0x000080 +/** @endcond */ + +/** + * @name Allocation/Freeing + * @{ + */ + +struct rtnl_nexthop *rtnl_route_nh_alloc(void) +{ + struct rtnl_nexthop *nh; + + nh = calloc(1, sizeof(*nh)); + if (!nh) + return NULL; + + nl_init_list_head(&nh->rtnh_list); + + return nh; +} + +struct rtnl_nexthop *rtnl_route_nh_clone(struct rtnl_nexthop *src) +{ + struct rtnl_nexthop *nh; + + nh = rtnl_route_nh_alloc(); + if (!nh) + return NULL; + + nh->rtnh_flags = src->rtnh_flags; + nh->rtnh_flag_mask = src->rtnh_flag_mask; + nh->rtnh_weight = src->rtnh_weight; + nh->rtnh_ifindex = src->rtnh_ifindex; + nh->ce_mask = src->ce_mask; + + if (src->rtnh_gateway) { + nh->rtnh_gateway = nl_addr_clone(src->rtnh_gateway); + if (!nh->rtnh_gateway) { + free(nh); + return NULL; + } + } + + if (src->rtnh_newdst) { + nh->rtnh_newdst = nl_addr_clone(src->rtnh_newdst); + if (!nh->rtnh_newdst) { + nl_addr_put(nh->rtnh_gateway); + free(nh); + return NULL; + } + } + + if (src->rtnh_via) { + nh->rtnh_via = nl_addr_clone(src->rtnh_via); + if (!nh->rtnh_via) { + nl_addr_put(nh->rtnh_gateway); + nl_addr_put(nh->rtnh_newdst); + free(nh); + return NULL; + } + } + + return nh; +} + +void rtnl_route_nh_free(struct rtnl_nexthop *nh) +{ + nl_addr_put(nh->rtnh_gateway); + nl_addr_put(nh->rtnh_newdst); + nl_addr_put(nh->rtnh_via); + if (nh->rtnh_encap) { + if (nh->rtnh_encap->ops && nh->rtnh_encap->ops->destructor) + nh->rtnh_encap->ops->destructor(nh->rtnh_encap->priv); + free(nh->rtnh_encap->priv); + free(nh->rtnh_encap); + } + free(nh); +} + +/** @} */ + +int rtnl_route_nh_compare(struct rtnl_nexthop *a, struct rtnl_nexthop *b, + uint32_t attrs, int loose) +{ + int diff = 0; + +#define NH_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, NH_ATTR_##ATTR, a, b, EXPR) + + diff |= NH_DIFF(IFINDEX, a->rtnh_ifindex != b->rtnh_ifindex); + diff |= NH_DIFF(WEIGHT, a->rtnh_weight != b->rtnh_weight); + diff |= NH_DIFF(REALMS, a->rtnh_realms != b->rtnh_realms); + diff |= NH_DIFF(GATEWAY, nl_addr_cmp(a->rtnh_gateway, + b->rtnh_gateway)); + diff |= NH_DIFF(NEWDST, nl_addr_cmp(a->rtnh_newdst, + b->rtnh_newdst)); + diff |= NH_DIFF(VIA, nl_addr_cmp(a->rtnh_via, + b->rtnh_via)); + diff |= NH_DIFF(ENCAP, nh_encap_compare(a->rtnh_encap, + b->rtnh_encap)); + + if (loose) + diff |= NH_DIFF(FLAGS, + (a->rtnh_flags ^ b->rtnh_flags) & b->rtnh_flag_mask); + else + diff |= NH_DIFF(FLAGS, a->rtnh_flags != b->rtnh_flags); + +#undef NH_DIFF + + return diff; +} + +static void nh_dump_line(struct rtnl_nexthop *nh, struct nl_dump_params *dp) +{ + struct nl_cache *link_cache; + char buf[128]; + + link_cache = nl_cache_mngt_require_safe("route/link"); + + if (nh->ce_mask & NH_ATTR_ENCAP) + nh_encap_dump(nh->rtnh_encap, dp); + + if (nh->ce_mask & NH_ATTR_NEWDST) + nl_dump(dp, "as to %s ", + nl_addr2str(nh->rtnh_newdst, buf, sizeof(buf))); + + nl_dump(dp, "via"); + + if (nh->ce_mask & NH_ATTR_VIA) + nl_dump(dp, " %s", + nl_addr2str(nh->rtnh_via, buf, sizeof(buf))); + + if (nh->ce_mask & NH_ATTR_GATEWAY) + nl_dump(dp, " %s", nl_addr2str(nh->rtnh_gateway, + buf, sizeof(buf))); + + if(nh->ce_mask & NH_ATTR_IFINDEX) { + if (link_cache) { + nl_dump(dp, " dev %s", + rtnl_link_i2name(link_cache, + nh->rtnh_ifindex, + buf, sizeof(buf))); + } else + nl_dump(dp, " dev %d", nh->rtnh_ifindex); + } + + nl_dump(dp, " "); + + if (link_cache) + nl_cache_put(link_cache); +} + +static void nh_dump_details(struct rtnl_nexthop *nh, struct nl_dump_params *dp) +{ + struct nl_cache *link_cache; + char buf[128]; + + link_cache = nl_cache_mngt_require_safe("route/link"); + + nl_dump(dp, "nexthop"); + + if (nh->ce_mask & NH_ATTR_ENCAP) + nh_encap_dump(nh->rtnh_encap, dp); + + if (nh->ce_mask & NH_ATTR_NEWDST) + nl_dump(dp, " as to %s", + nl_addr2str(nh->rtnh_newdst, buf, sizeof(buf))); + + if (nh->ce_mask & NH_ATTR_VIA) + nl_dump(dp, " via %s", + nl_addr2str(nh->rtnh_via, buf, sizeof(buf))); + + if (nh->ce_mask & NH_ATTR_GATEWAY) + nl_dump(dp, " via %s", nl_addr2str(nh->rtnh_gateway, + buf, sizeof(buf))); + + if(nh->ce_mask & NH_ATTR_IFINDEX) { + if (link_cache) { + nl_dump(dp, " dev %s", + rtnl_link_i2name(link_cache, + nh->rtnh_ifindex, + buf, sizeof(buf))); + } else + nl_dump(dp, " dev %d", nh->rtnh_ifindex); + } + + if (nh->ce_mask & NH_ATTR_WEIGHT) + nl_dump(dp, " weight %u", nh->rtnh_weight); + + if (nh->ce_mask & NH_ATTR_REALMS) + nl_dump(dp, " realm %04x:%04x", + RTNL_REALM_FROM(nh->rtnh_realms), + RTNL_REALM_TO(nh->rtnh_realms)); + + if (nh->ce_mask & NH_ATTR_FLAGS) + nl_dump(dp, " <%s>", rtnl_route_nh_flags2str(nh->rtnh_flags, + buf, sizeof(buf))); + + if (link_cache) + nl_cache_put(link_cache); +} + +void rtnl_route_nh_dump(struct rtnl_nexthop *nh, struct nl_dump_params *dp) +{ + switch (dp->dp_type) { + case NL_DUMP_LINE: + nh_dump_line(nh, dp); + break; + + case NL_DUMP_DETAILS: + case NL_DUMP_STATS: + if (dp->dp_ivar == NH_DUMP_FROM_DETAILS) + nh_dump_details(nh, dp); + break; + + default: + break; + } +} + +void nh_set_encap(struct rtnl_nexthop *nh, struct rtnl_nh_encap *rtnh_encap) +{ + if (nh->rtnh_encap) { + if (nh->rtnh_encap->ops && nh->rtnh_encap->ops->destructor) + nh->rtnh_encap->ops->destructor(nh->rtnh_encap->priv); + free(nh->rtnh_encap->priv); + free(nh->rtnh_encap); + } + + if (rtnh_encap) { + nh->rtnh_encap = rtnh_encap; + nh->ce_mask |= NH_ATTR_ENCAP; + } else { + nh->rtnh_encap = NULL; + nh->ce_mask &= ~NH_ATTR_ENCAP; + } +} + +/** + * @name Attributes + * @{ + */ + +void rtnl_route_nh_set_weight(struct rtnl_nexthop *nh, uint8_t weight) +{ + nh->rtnh_weight = weight; + nh->ce_mask |= NH_ATTR_WEIGHT; +} + +uint8_t rtnl_route_nh_get_weight(struct rtnl_nexthop *nh) +{ + return nh->rtnh_weight; +} + +void rtnl_route_nh_set_ifindex(struct rtnl_nexthop *nh, int ifindex) +{ + nh->rtnh_ifindex = ifindex; + nh->ce_mask |= NH_ATTR_IFINDEX; +} + +int rtnl_route_nh_get_ifindex(struct rtnl_nexthop *nh) +{ + return nh->rtnh_ifindex; +} + +/* FIXME: Convert to return an int */ +void rtnl_route_nh_set_gateway(struct rtnl_nexthop *nh, struct nl_addr *addr) +{ + struct nl_addr *old = nh->rtnh_gateway; + + if (addr) { + nh->rtnh_gateway = nl_addr_get(addr); + nh->ce_mask |= NH_ATTR_GATEWAY; + } else { + nh->ce_mask &= ~NH_ATTR_GATEWAY; + nh->rtnh_gateway = NULL; + } + + if (old) + nl_addr_put(old); +} + +struct nl_addr *rtnl_route_nh_get_gateway(struct rtnl_nexthop *nh) +{ + return nh->rtnh_gateway; +} + +void rtnl_route_nh_set_flags(struct rtnl_nexthop *nh, unsigned int flags) +{ + nh->rtnh_flag_mask |= flags; + nh->rtnh_flags |= flags; + nh->ce_mask |= NH_ATTR_FLAGS; +} + +void rtnl_route_nh_unset_flags(struct rtnl_nexthop *nh, unsigned int flags) +{ + nh->rtnh_flag_mask |= flags; + nh->rtnh_flags &= ~flags; + nh->ce_mask |= NH_ATTR_FLAGS; +} + +unsigned int rtnl_route_nh_get_flags(struct rtnl_nexthop *nh) +{ + return nh->rtnh_flags; +} + +void rtnl_route_nh_set_realms(struct rtnl_nexthop *nh, uint32_t realms) +{ + nh->rtnh_realms = realms; + nh->ce_mask |= NH_ATTR_REALMS; +} + +uint32_t rtnl_route_nh_get_realms(struct rtnl_nexthop *nh) +{ + return nh->rtnh_realms; +} + +int rtnl_route_nh_set_newdst(struct rtnl_nexthop *nh, struct nl_addr *addr) +{ + struct nl_addr *old = nh->rtnh_newdst; + + if (!nl_addr_valid(nl_addr_get_binary_addr(addr), + nl_addr_get_len(addr))) + return -NLE_INVAL; + + if (addr) { + nh->rtnh_newdst = nl_addr_get(addr); + nh->ce_mask |= NH_ATTR_NEWDST; + } else { + nh->ce_mask &= ~NH_ATTR_NEWDST; + nh->rtnh_newdst = NULL; + } + + if (old) + nl_addr_put(old); + + return 0; +} + +struct nl_addr *rtnl_route_nh_get_newdst(struct rtnl_nexthop *nh) +{ + return nh->rtnh_newdst; +} + +int rtnl_route_nh_set_via(struct rtnl_nexthop *nh, struct nl_addr *addr) +{ + struct nl_addr *old = nh->rtnh_via; + + if (!nl_addr_valid(nl_addr_get_binary_addr(addr), + nl_addr_get_len(addr))) + return -NLE_INVAL; + + if (addr) { + nh->rtnh_via = nl_addr_get(addr); + nh->ce_mask |= NH_ATTR_VIA; + } else { + nh->ce_mask &= ~NH_ATTR_VIA; + nh->rtnh_via= NULL; + } + + if (old) + nl_addr_put(old); + + return 0; +} + +struct nl_addr *rtnl_route_nh_get_via(struct rtnl_nexthop *nh) +{ + return nh->rtnh_via; +} + +/** @} */ + +/** + * @name Nexthop Flags Translations + * @{ + */ + +static const struct trans_tbl nh_flags[] = { + __ADD(RTNH_F_DEAD, dead), + __ADD(RTNH_F_PERVASIVE, pervasive), + __ADD(RTNH_F_ONLINK, onlink), +}; + +char *rtnl_route_nh_flags2str(int flags, char *buf, size_t len) +{ + return __flags2str(flags, buf, len, nh_flags, ARRAY_SIZE(nh_flags)); +} + +int rtnl_route_nh_str2flags(const char *name) +{ + return __str2flags(name, nh_flags, ARRAY_SIZE(nh_flags)); +} + +/** @} */ + +/** @} */ diff --git a/libnl/lib/route/nexthop_encap.c b/libnl/lib/route/nexthop_encap.c new file mode 100644 index 0000000..c95c17b --- /dev/null +++ b/libnl/lib/route/nexthop_encap.c @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#define _GNU_SOURCE + +#include +#include +#include +#include + +static struct lwtunnel_encap_type { + const char *name; + struct nh_encap_ops *ops; +} lwtunnel_encap_types[__LWTUNNEL_ENCAP_MAX] = { + [LWTUNNEL_ENCAP_NONE] = { .name = "none" }, + [LWTUNNEL_ENCAP_MPLS] = { .name = "mpls", .ops = &mpls_encap_ops }, + [LWTUNNEL_ENCAP_IP] = { .name = "ip" }, + [LWTUNNEL_ENCAP_IP6] = { .name = "ip6" }, + [LWTUNNEL_ENCAP_ILA] = { .name = "ila" }, + [LWTUNNEL_ENCAP_BPF] = { .name = "bpf" }, +}; + +static const char *nh_encap_type2str(unsigned int type) +{ + const char *name; + + if (type > LWTUNNEL_ENCAP_MAX) + return "unknown"; + + name = lwtunnel_encap_types[type].name; + + return name ? name : "unknown"; +} + +void nh_encap_dump(struct rtnl_nh_encap *rtnh_encap, struct nl_dump_params *dp) +{ + nl_dump(dp, " encap %s ", + nh_encap_type2str(rtnh_encap->ops->encap_type)); + + if (rtnh_encap->ops && rtnh_encap->ops->dump) + rtnh_encap->ops->dump(rtnh_encap->priv, dp); +} + +int nh_encap_build_msg(struct nl_msg *msg, struct rtnl_nh_encap *rtnh_encap) +{ + struct nlattr *encap; + int err; + + if (!rtnh_encap->ops || !rtnh_encap->ops->build_msg) { + NL_DBG(2, "Nexthop encap type not implemented\n"); + return -NLE_INVAL; + } + + NLA_PUT_U16(msg, RTA_ENCAP_TYPE, rtnh_encap->ops->encap_type); + + encap = nla_nest_start(msg, RTA_ENCAP); + if (!encap) + goto nla_put_failure; + + err = rtnh_encap->ops->build_msg(msg, rtnh_encap->priv); + if (err < 0) + return err; + + nla_nest_end(msg, encap); + + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +int nh_encap_parse_msg(struct nlattr *encap, struct nlattr *encap_type, + struct rtnl_nexthop *rtnh) +{ + uint16_t e_type = nla_get_u16(encap_type); + + if (e_type == LWTUNNEL_ENCAP_NONE) { + NL_DBG(2, "RTA_ENCAP_TYPE should not be LWTUNNEL_ENCAP_NONE\n"); + return -NLE_INVAL; + } + if (e_type > LWTUNNEL_ENCAP_MAX) { + NL_DBG(2, "Unknown RTA_ENCAP_TYPE: %d\n", e_type); + return -NLE_INVAL; + } + + if (!lwtunnel_encap_types[e_type].ops) { + NL_DBG(2, "RTA_ENCAP_TYPE %s is not implemented\n", + lwtunnel_encap_types[e_type].name); + return -NLE_MSGTYPE_NOSUPPORT; + } + + return lwtunnel_encap_types[e_type].ops->parse_msg(encap, rtnh); +} + +int nh_encap_compare(struct rtnl_nh_encap *a, struct rtnl_nh_encap *b) +{ + if (!a && !b) + return 0; + + if ((a && !b) || (!a && b) || (a->ops != b->ops)) + return 1; + + if (!a->ops || !a->ops->compare) + return 0; + + return a->ops->compare(a->priv, b->priv); +} diff --git a/libnl/lib/route/nh_encap_mpls.c b/libnl/lib/route/nh_encap_mpls.c new file mode 100644 index 0000000..50ae18c --- /dev/null +++ b/libnl/lib/route/nh_encap_mpls.c @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + +struct mpls_iptunnel_encap { + struct nl_addr *dst; + uint8_t ttl; +}; + +static void mpls_encap_dump(void *priv, struct nl_dump_params *dp) +{ + struct mpls_iptunnel_encap *encap_info = priv; + char buf[256]; + + nl_dump(dp, "%s ", nl_addr2str(encap_info->dst, buf, sizeof(buf))); + + if (encap_info->ttl) + nl_dump(dp, "ttl %u ", encap_info->ttl); +} + +static int mpls_encap_build_msg(struct nl_msg *msg, void *priv) +{ + struct mpls_iptunnel_encap *encap_info = priv; + + NLA_PUT_ADDR(msg, MPLS_IPTUNNEL_DST, encap_info->dst); + if (encap_info->ttl) + NLA_PUT_U8(msg, MPLS_IPTUNNEL_TTL, encap_info->ttl); + + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +static void mpls_encap_destructor(void *priv) +{ + struct mpls_iptunnel_encap *encap_info = priv; + + nl_addr_put(encap_info->dst); +} + +static struct nla_policy mpls_encap_policy[MPLS_IPTUNNEL_MAX + 1] = { + [MPLS_IPTUNNEL_DST] = { .type = NLA_U32 }, + [MPLS_IPTUNNEL_TTL] = { .type = NLA_U8 }, +}; + +static int mpls_encap_parse_msg(struct nlattr *nla, struct rtnl_nexthop *nh) +{ + struct nlattr *tb[MPLS_IPTUNNEL_MAX + 1]; + struct nl_addr *labels; + uint8_t ttl = 0; + int err; + + err = nla_parse_nested(tb, MPLS_IPTUNNEL_MAX, nla, mpls_encap_policy); + if (err < 0) + return err; + + if (!tb[MPLS_IPTUNNEL_DST]) + return -NLE_INVAL; + + labels = nl_addr_alloc_attr(tb[MPLS_IPTUNNEL_DST], AF_MPLS); + if (!labels) + return -NLE_NOMEM; + + if (tb[MPLS_IPTUNNEL_TTL]) + ttl = nla_get_u8(tb[MPLS_IPTUNNEL_TTL]); + + err = rtnl_route_nh_encap_mpls(nh, labels, ttl); + + nl_addr_put(labels); + + return err; +} + +static int mpls_encap_compare(void *_a, void *_b) +{ + struct mpls_iptunnel_encap *a = _a; + struct mpls_iptunnel_encap *b = _b; + int diff = 0; + + diff |= (a->ttl != b->ttl); + diff |= nl_addr_cmp(a->dst, b->dst); + + return diff; +} + +struct nh_encap_ops mpls_encap_ops = { + .encap_type = LWTUNNEL_ENCAP_MPLS, + .build_msg = mpls_encap_build_msg, + .parse_msg = mpls_encap_parse_msg, + .compare = mpls_encap_compare, + .dump = mpls_encap_dump, + .destructor = mpls_encap_destructor, +}; + +int rtnl_route_nh_encap_mpls(struct rtnl_nexthop *nh, + struct nl_addr *addr, + uint8_t ttl) +{ + struct mpls_iptunnel_encap *mpls_encap; + struct rtnl_nh_encap *rtnh_encap; + + if (!addr) + return -NLE_INVAL; + + if (!nl_addr_valid(nl_addr_get_binary_addr(addr), + nl_addr_get_len(addr))) + return -NLE_INVAL; + + rtnh_encap = calloc(1, sizeof(*rtnh_encap)); + if (!rtnh_encap) + return -NLE_NOMEM; + + mpls_encap = calloc(1, sizeof(*mpls_encap)); + if (!mpls_encap) { + free(rtnh_encap); + return -NLE_NOMEM; + } + + mpls_encap->dst = nl_addr_get(addr); + mpls_encap->ttl = ttl; + + rtnh_encap->priv = mpls_encap; + rtnh_encap->ops = &mpls_encap_ops; + + nh_set_encap(nh, rtnh_encap); + + return 0; +} diff --git a/libnl/lib/route/pktloc.c b/libnl/lib/route/pktloc.c new file mode 100644 index 0000000..d56d341 --- /dev/null +++ b/libnl/lib/route/pktloc.c @@ -0,0 +1,257 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2008-2013 Thomas Graf + */ + +/** + * @ingroup tc + * @defgroup pktloc Packet Location Aliasing + * Packet Location Aliasing + * + * The packet location aliasing interface eases the use of offset definitions + * inside packets by allowing them to be referenced by name. Known positions + * of protocol fields are stored in a configuration file and associated with + * a name for later reference. The configuration file is distributed with the + * library and provides a well defined set of definitions for most common + * protocol fields. + * + * @section pktloc_examples Examples + * @par Example 1.1 Looking up a packet location + * @code + * struct rtnl_pktloc *loc; + * + * rtnl_pktloc_lookup("ip.src", &loc); + * @endcode + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include + +#include "pktloc_syntax.h" +#include "pktloc_grammar.h" + +/** @cond SKIP */ +#define PKTLOC_NAME_HT_SIZ 256 + +static struct nl_list_head pktloc_name_ht[PKTLOC_NAME_HT_SIZ]; + +/* djb2 */ +static unsigned int pktloc_hash(const char *str) +{ + unsigned long hash = 5381; + int c; + + while ((c = *str++)) + hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ + + return hash % PKTLOC_NAME_HT_SIZ; +} + +static int __pktloc_lookup(const char *name, struct rtnl_pktloc **result) +{ + struct rtnl_pktloc *loc; + int hash; + + hash = pktloc_hash(name); + nl_list_for_each_entry(loc, &pktloc_name_ht[hash], list) { + if (!strcasecmp(loc->name, name)) { + loc->refcnt++; + *result = loc; + return 0; + } + } + + return -NLE_OBJ_NOTFOUND; +} + +extern int pktloc_parse(void *scanner); + +static void rtnl_pktloc_free(struct rtnl_pktloc *loc) +{ + if (!loc) + return; + + free(loc->name); + free(loc); +} + +static int read_pktlocs(void) +{ + YY_BUFFER_STATE buf = NULL; + yyscan_t scanner = NULL; + static time_t last_read; + struct stat st; + char *path; + int i, err; + FILE *fd; + + if (build_sysconf_path(&path, "pktloc") < 0) + return -NLE_NOMEM; + + /* if stat fails, just try to read the file */ + if (stat(path, &st) == 0) { + /* Don't re-read file if file is unchanged */ + if (last_read == st.st_mtime) { + err = 0; + goto errout; + } + } + + NL_DBG(2, "Reading packet location file \"%s\"\n", path); + + if (!(fd = fopen(path, "re"))) { + err = -NLE_PKTLOC_FILE; + goto errout; + } + + for (i = 0; i < PKTLOC_NAME_HT_SIZ; i++) { + struct rtnl_pktloc *loc, *n; + + nl_list_for_each_entry_safe(loc, n, &pktloc_name_ht[i], list) + rtnl_pktloc_put(loc); + + nl_init_list_head(&pktloc_name_ht[i]); + } + + if ((err = pktloc_lex_init(&scanner)) < 0) { + err = -NLE_FAILURE; + goto errout_close; + } + + buf = pktloc__create_buffer(fd, YY_BUF_SIZE, scanner); + pktloc__switch_to_buffer(buf, scanner); + + if ((err = pktloc_parse(scanner)) != 0) { + pktloc__delete_buffer(buf, scanner); + err = -NLE_PARSE_ERR; + goto errout_scanner; + } + + last_read = st.st_mtime; + +errout_scanner: + pktloc_lex_destroy(scanner); +errout_close: + fclose(fd); +errout: + free(path); + + return err; +} + +/** @endcond */ + +/** + * Lookup packet location alias + * @arg name Name of packet location. + * @arg result Result pointer + * + * Tries to find a matching packet location alias for the supplied + * packet location name. + * + * The file containing the packet location definitions is automatically + * re-read if its modification time has changed since the last call. + * + * The returned packet location has to be returned after use by calling + * rtnl_pktloc_put() in order to allow freeing its memory after the last + * user has abandoned it. + * + * @return 0 on success or a negative error code. + * @retval NLE_PKTLOC_FILE Unable to open packet location file. + * @retval NLE_OBJ_NOTFOUND No matching packet location alias found. + */ +int rtnl_pktloc_lookup(const char *name, struct rtnl_pktloc **result) +{ + int err; + + if ((err = read_pktlocs()) < 0) + return err; + + return __pktloc_lookup(name, result); +} + +/** + * Allocate packet location object + */ +struct rtnl_pktloc *rtnl_pktloc_alloc(void) +{ + struct rtnl_pktloc *loc; + + if (!(loc = calloc(1, sizeof(*loc)))) + return NULL; + + loc->refcnt = 1; + nl_init_list_head(&loc->list); + + return loc; +} + +/** + * Return reference of a packet location + * @arg loc packet location object. + */ +void rtnl_pktloc_put(struct rtnl_pktloc *loc) +{ + if (!loc) + return; + + loc->refcnt--; + if (loc->refcnt <= 0) + rtnl_pktloc_free(loc); +} + +/** + * Add a packet location to the hash table + * @arg loc packet location object + * + * @return 0 on success or a negative error code. + */ +int rtnl_pktloc_add(struct rtnl_pktloc *loc) +{ + struct rtnl_pktloc *l; + + if (__pktloc_lookup(loc->name, &l) == 0) { + rtnl_pktloc_put(l); + return -NLE_EXIST; + } + + NL_DBG(2, "New packet location entry \"%s\" align=%u layer=%u " + "offset=%u mask=%#x shift=%u refnt=%u\n", + loc->name, loc->align, loc->layer, loc->offset, + loc->mask, loc->shift, loc->refcnt); + + nl_list_add_tail(&loc->list, &pktloc_name_ht[pktloc_hash(loc->name)]); + + return 0; +} + +void rtnl_pktloc_foreach(void (*cb)(struct rtnl_pktloc *, void *), void *arg) +{ + struct rtnl_pktloc *loc; + int i; + + /* ignore errors */ + read_pktlocs(); + + for (i = 0; i < PKTLOC_NAME_HT_SIZ; i++) + nl_list_for_each_entry(loc, &pktloc_name_ht[i], list) + cb(loc, arg); +} + +static int __init pktloc_init(void) +{ + int i; + + for (i = 0; i < PKTLOC_NAME_HT_SIZ; i++) + nl_init_list_head(&pktloc_name_ht[i]); + + return 0; +} + +/** @} */ diff --git a/libnl/lib/route/pktloc_grammar.c b/libnl/lib/route/pktloc_grammar.c new file mode 100644 index 0000000..48707f9 --- /dev/null +++ b/libnl/lib/route/pktloc_grammar.c @@ -0,0 +1,2278 @@ +#line 2 "pktloc_grammar.c" + +#line 4 "pktloc_grammar.c" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yy_create_buffer +#define pktloc__create_buffer_ALREADY_DEFINED +#else +#define yy_create_buffer pktloc__create_buffer +#endif + +#ifdef yy_delete_buffer +#define pktloc__delete_buffer_ALREADY_DEFINED +#else +#define yy_delete_buffer pktloc__delete_buffer +#endif + +#ifdef yy_scan_buffer +#define pktloc__scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer pktloc__scan_buffer +#endif + +#ifdef yy_scan_string +#define pktloc__scan_string_ALREADY_DEFINED +#else +#define yy_scan_string pktloc__scan_string +#endif + +#ifdef yy_scan_bytes +#define pktloc__scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes pktloc__scan_bytes +#endif + +#ifdef yy_init_buffer +#define pktloc__init_buffer_ALREADY_DEFINED +#else +#define yy_init_buffer pktloc__init_buffer +#endif + +#ifdef yy_flush_buffer +#define pktloc__flush_buffer_ALREADY_DEFINED +#else +#define yy_flush_buffer pktloc__flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define pktloc__load_buffer_state_ALREADY_DEFINED +#else +#define yy_load_buffer_state pktloc__load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define pktloc__switch_to_buffer_ALREADY_DEFINED +#else +#define yy_switch_to_buffer pktloc__switch_to_buffer +#endif + +#ifdef yypush_buffer_state +#define pktloc_push_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state pktloc_push_buffer_state +#endif + +#ifdef yypop_buffer_state +#define pktloc_pop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state pktloc_pop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define pktloc_ensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack pktloc_ensure_buffer_stack +#endif + +#ifdef yylex +#define pktloc_lex_ALREADY_DEFINED +#else +#define yylex pktloc_lex +#endif + +#ifdef yyrestart +#define pktloc_restart_ALREADY_DEFINED +#else +#define yyrestart pktloc_restart +#endif + +#ifdef yylex_init +#define pktloc_lex_init_ALREADY_DEFINED +#else +#define yylex_init pktloc_lex_init +#endif + +#ifdef yylex_init_extra +#define pktloc_lex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra pktloc_lex_init_extra +#endif + +#ifdef yylex_destroy +#define pktloc_lex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy pktloc_lex_destroy +#endif + +#ifdef yyget_debug +#define pktloc_get_debug_ALREADY_DEFINED +#else +#define yyget_debug pktloc_get_debug +#endif + +#ifdef yyset_debug +#define pktloc_set_debug_ALREADY_DEFINED +#else +#define yyset_debug pktloc_set_debug +#endif + +#ifdef yyget_extra +#define pktloc_get_extra_ALREADY_DEFINED +#else +#define yyget_extra pktloc_get_extra +#endif + +#ifdef yyset_extra +#define pktloc_set_extra_ALREADY_DEFINED +#else +#define yyset_extra pktloc_set_extra +#endif + +#ifdef yyget_in +#define pktloc_get_in_ALREADY_DEFINED +#else +#define yyget_in pktloc_get_in +#endif + +#ifdef yyset_in +#define pktloc_set_in_ALREADY_DEFINED +#else +#define yyset_in pktloc_set_in +#endif + +#ifdef yyget_out +#define pktloc_get_out_ALREADY_DEFINED +#else +#define yyget_out pktloc_get_out +#endif + +#ifdef yyset_out +#define pktloc_set_out_ALREADY_DEFINED +#else +#define yyset_out pktloc_set_out +#endif + +#ifdef yyget_leng +#define pktloc_get_leng_ALREADY_DEFINED +#else +#define yyget_leng pktloc_get_leng +#endif + +#ifdef yyget_text +#define pktloc_get_text_ALREADY_DEFINED +#else +#define yyget_text pktloc_get_text +#endif + +#ifdef yyget_lineno +#define pktloc_get_lineno_ALREADY_DEFINED +#else +#define yyget_lineno pktloc_get_lineno +#endif + +#ifdef yyset_lineno +#define pktloc_set_lineno_ALREADY_DEFINED +#else +#define yyset_lineno pktloc_set_lineno +#endif + +#ifdef yyget_column +#define pktloc_get_column_ALREADY_DEFINED +#else +#define yyget_column pktloc_get_column +#endif + +#ifdef yyset_column +#define pktloc_set_column_ALREADY_DEFINED +#else +#define yyset_column pktloc_set_column +#endif + +#ifdef yywrap +#define pktloc_wrap_ALREADY_DEFINED +#else +#define yywrap pktloc_wrap +#endif + +#ifdef yyget_lval +#define pktloc_get_lval_ALREADY_DEFINED +#else +#define yyget_lval pktloc_get_lval +#endif + +#ifdef yyset_lval +#define pktloc_set_lval_ALREADY_DEFINED +#else +#define yyset_lval pktloc_set_lval +#endif + +#ifdef yyget_lloc +#define pktloc_get_lloc_ALREADY_DEFINED +#else +#define yyget_lloc pktloc_get_lloc +#endif + +#ifdef yyset_lloc +#define pktloc_set_lloc_ALREADY_DEFINED +#else +#define yyset_lloc pktloc_set_lloc +#endif + +#ifdef yyalloc +#define pktloc_alloc_ALREADY_DEFINED +#else +#define yyalloc pktloc_alloc +#endif + +#ifdef yyrealloc +#define pktloc_realloc_ALREADY_DEFINED +#else +#define yyrealloc pktloc_realloc +#endif + +#ifdef yyfree +#define pktloc_free_ALREADY_DEFINED +#else +#define yyfree pktloc_free +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an + * integer in range [0..255] for use as an array index. + */ +#define YY_SC_TO_UI(c) ((YY_CHAR) (c)) + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN yyg->yy_start = 1 + 2 * +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START ((yyg->yy_start - 1) / 2) +#define YYSTATE YY_START +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart( yyin , yyscanner ) +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + #define YY_LINENO_REWIND_TO(ptr) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = yyg->yy_hold_char; \ + YY_RESTORE_YY_MORE_OFFSET \ + yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) +#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ + ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ + : NULL) +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] + +void yyrestart ( FILE *input_file , yyscan_t yyscanner ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); +void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +void yypop_buffer_state ( yyscan_t yyscanner ); + +static void yyensure_buffer_stack ( yyscan_t yyscanner ); +static void yy_load_buffer_state ( yyscan_t yyscanner ); +static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file , yyscan_t yyscanner ); +#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER , yyscanner) + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); + +void *yyalloc ( yy_size_t , yyscan_t yyscanner ); +void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); +void yyfree ( void * , yyscan_t yyscanner ); + +#define yy_new_buffer yy_create_buffer +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +#define pktloc_wrap(yyscanner) (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP +typedef flex_uint8_t YY_CHAR; + +typedef int yy_state_type; + +#define yytext_ptr yytext_r + +static yy_state_type yy_get_previous_state ( yyscan_t yyscanner ); +static yy_state_type yy_try_NUL_trans ( yy_state_type current_state , yyscan_t yyscanner); +static int yy_get_next_buffer ( yyscan_t yyscanner ); +static void yynoreturn yy_fatal_error ( const char* msg , yyscan_t yyscanner ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + yyg->yytext_ptr = yy_bp; \ + yyleng = (int) (yy_cp - yy_bp); \ + yyg->yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yyg->yy_c_buf_p = yy_cp; +#define YY_NUM_RULES 16 +#define YY_END_OF_BUFFER 17 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static const flex_int16_t yy_accept[47] = + { 0, + 0, 0, 17, 15, 1, 2, 5, 3, 3, 15, + 15, 15, 15, 15, 15, 15, 1, 2, 2, 3, + 15, 15, 12, 15, 15, 15, 15, 15, 15, 6, + 4, 10, 15, 11, 14, 15, 7, 8, 9, 15, + 15, 15, 15, 15, 13, 0 + } ; + +static const YY_CHAR yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 1, 4, 1, 1, 1, 1, 1, + 1, 1, 5, 1, 1, 1, 1, 6, 7, 8, + 9, 10, 10, 11, 10, 12, 10, 1, 1, 1, + 1, 1, 1, 1, 13, 14, 15, 14, 16, 14, + 1, 17, 18, 1, 19, 20, 1, 21, 22, 23, + 1, 24, 25, 26, 27, 1, 1, 28, 1, 1, + 1, 1, 1, 1, 1, 1, 13, 14, 15, 14, + + 16, 14, 1, 17, 18, 1, 19, 20, 1, 21, + 22, 23, 1, 24, 25, 26, 27, 1, 1, 28, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static const YY_CHAR yy_meta[29] = + { 0, + 1, 2, 3, 1, 2, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1 + } ; + +static const flex_int16_t yy_base[50] = + { 0, + 0, 0, 86, 0, 27, 29, 87, 29, 57, 58, + 60, 64, 65, 18, 36, 0, 44, 47, 0, 52, + 52, 62, 0, 57, 51, 53, 62, 63, 65, 0, + 0, 0, 37, 0, 0, 34, 0, 0, 0, 29, + 30, 29, 26, 18, 0, 87, 31, 68, 70 + } ; + +static const flex_int16_t yy_def[50] = + { 0, + 46, 1, 46, 47, 46, 48, 46, 47, 8, 47, + 47, 47, 47, 47, 47, 47, 46, 48, 49, 8, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 21, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 0, 46, 46, 46 + } ; + +static const flex_int16_t yy_nxt[116] = + { 0, + 4, 5, 5, 6, 7, 8, 9, 9, 9, 9, + 9, 9, 4, 4, 4, 10, 4, 11, 4, 12, + 13, 4, 4, 4, 4, 14, 15, 4, 17, 17, + 19, 16, 26, 19, 20, 20, 20, 20, 20, 20, + 20, 27, 28, 45, 29, 17, 17, 30, 19, 44, + 43, 19, 42, 41, 40, 39, 21, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 18, 18, + 19, 19, 38, 37, 36, 35, 34, 33, 32, 16, + 25, 24, 23, 22, 16, 46, 3, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46 + } ; + +static const flex_int16_t yy_chk[116] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, + 6, 47, 14, 6, 8, 8, 8, 8, 8, 8, + 8, 14, 15, 44, 15, 17, 17, 15, 18, 43, + 42, 18, 41, 40, 36, 33, 8, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 48, 48, + 49, 49, 29, 28, 27, 26, 25, 24, 22, 20, + 13, 12, 11, 10, 9, 3, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46 + } ; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +#line 1 "pktloc_grammar.l" +#line 2 "pktloc_grammar.l" + #include + #include + #include + #include + #include + #include + #include "pktloc_syntax.h" + + int pktloc_get_column(yyscan_t); + void pktloc_set_column(int, yyscan_t); +#line 703 "pktloc_grammar.c" +#define YY_NO_INPUT 1 +#line 705 "pktloc_grammar.c" + +#define INITIAL 0 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +/* Holds the entire state of the reentrant scanner. */ +struct yyguts_t + { + + /* User-defined. Not touched by flex. */ + YY_EXTRA_TYPE yyextra_r; + + /* The rest are the same as the globals declared in the non-reentrant scanner. */ + FILE *yyin_r, *yyout_r; + size_t yy_buffer_stack_top; /**< index of top of stack. */ + size_t yy_buffer_stack_max; /**< capacity of stack. */ + YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ + char yy_hold_char; + int yy_n_chars; + int yyleng_r; + char *yy_c_buf_p; + int yy_init; + int yy_start; + int yy_did_buffer_switch_on_eof; + int yy_start_stack_ptr; + int yy_start_stack_depth; + int *yy_start_stack; + yy_state_type yy_last_accepting_state; + char* yy_last_accepting_cpos; + + int yylineno_r; + int yy_flex_debug_r; + + char *yytext_r; + int yy_more_flag; + int yy_more_len; + + YYSTYPE * yylval_r; + + YYLTYPE * yylloc_r; + + }; /* end struct yyguts_t */ + +static int yy_init_globals ( yyscan_t yyscanner ); + + /* This must go here because YYSTYPE and YYLTYPE are included + * from bison output in section 1.*/ + # define yylval yyg->yylval_r + + # define yylloc yyg->yylloc_r + +int yylex_init (yyscan_t* scanner); + +int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy ( yyscan_t yyscanner ); + +int yyget_debug ( yyscan_t yyscanner ); + +void yyset_debug ( int debug_flag , yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); + +FILE *yyget_in ( yyscan_t yyscanner ); + +void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); + +FILE *yyget_out ( yyscan_t yyscanner ); + +void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); + + int yyget_leng ( yyscan_t yyscanner ); + +char *yyget_text ( yyscan_t yyscanner ); + +int yyget_lineno ( yyscan_t yyscanner ); + +void yyset_lineno ( int _line_number , yyscan_t yyscanner ); + +int yyget_column ( yyscan_t yyscanner ); + +void yyset_column ( int _column_no , yyscan_t yyscanner ); + +YYSTYPE * yyget_lval ( yyscan_t yyscanner ); + +void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); + + YYLTYPE *yyget_lloc ( yyscan_t yyscanner ); + + void yyset_lloc ( YYLTYPE * yylloc_param , yyscan_t yyscanner ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap ( yyscan_t yyscanner ); +#else +extern int yywrap ( yyscan_t yyscanner ); +#endif +#endif + +#ifndef YY_NO_UNPUT + +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const char * , yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus +static int yyinput ( yyscan_t yyscanner ); +#else +static int input ( yyscan_t yyscanner ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + int n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner); + +#define YY_DECL int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK /*LINTED*/break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + yy_state_type yy_current_state; + char *yy_cp, *yy_bp; + int yy_act; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yylval = yylval_param; + + yylloc = yylloc_param; + + if ( !yyg->yy_init ) + { + yyg->yy_init = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! yyg->yy_start ) + yyg->yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_load_buffer_state( yyscanner ); + } + + { +#line 24 "pktloc_grammar.l" + + +#line 990 "pktloc_grammar.c" + + while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ + { + yy_cp = yyg->yy_c_buf_p; + + /* Support of yytext. */ + *yy_cp = yyg->yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = yyg->yy_start; +yy_match: + do + { + YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 47 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 87 ); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + if ( yy_act == 0 ) + { /* have to back up */ + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + yy_act = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yyg->yy_hold_char; + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + goto yy_find_action; + +case 1: +/* rule 1 can match eol */ +YY_RULE_SETUP +#line 26 "pktloc_grammar.l" + + YY_BREAK +case 2: +YY_RULE_SETUP +#line 28 "pktloc_grammar.l" + + YY_BREAK +case 3: +#line 31 "pktloc_grammar.l" +case 4: +YY_RULE_SETUP +#line 31 "pktloc_grammar.l" +{ + yylval->i = strtoul(yytext, NULL, 0); + return NUMBER; + } + YY_BREAK +case 5: +YY_RULE_SETUP +#line 36 "pktloc_grammar.l" +{ return yylval->i = yytext[0]; } + YY_BREAK +case 6: +YY_RULE_SETUP +#line 38 "pktloc_grammar.l" +{ yylval->i = TCF_EM_ALIGN_U8; return ALIGN; } + YY_BREAK +case 7: +YY_RULE_SETUP +#line 39 "pktloc_grammar.l" +{ yylval->i = TCF_EM_ALIGN_U16; return ALIGN; } + YY_BREAK +case 8: +YY_RULE_SETUP +#line 40 "pktloc_grammar.l" +{ yylval->i = TCF_EM_ALIGN_U32; return ALIGN; } + YY_BREAK +case 9: +#line 43 "pktloc_grammar.l" +case 10: +YY_RULE_SETUP +#line 43 "pktloc_grammar.l" +{ yylval->i = TCF_LAYER_LINK; return LAYER; } + YY_BREAK +case 11: +#line 45 "pktloc_grammar.l" +case 12: +YY_RULE_SETUP +#line 45 "pktloc_grammar.l" +{ yylval->i = TCF_LAYER_NETWORK; return LAYER; } + YY_BREAK +case 13: +#line 47 "pktloc_grammar.l" +case 14: +YY_RULE_SETUP +#line 47 "pktloc_grammar.l" +{ yylval->i = TCF_LAYER_TRANSPORT; return LAYER; } + YY_BREAK +case 15: +YY_RULE_SETUP +#line 50 "pktloc_grammar.l" +{ + yylval->s = strdup(yytext); + if (yylval->s == NULL) + return ERROR; + return NAME; + } + YY_BREAK +case 16: +YY_RULE_SETUP +#line 56 "pktloc_grammar.l" +ECHO; + YY_BREAK +#line 1124 "pktloc_grammar.c" +case YY_STATE_EOF(INITIAL): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = yyg->yy_hold_char; + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); + + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++yyg->yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = yyg->yy_c_buf_p; + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_END_OF_FILE: + { + yyg->yy_did_buffer_switch_on_eof = 0; + + if ( yywrap( yyscanner ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = + yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yyg->yy_c_buf_p = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of user's declarations */ +} /* end of yylex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + char *source = yyg->yytext_ptr; + int number_to_move, i; + int ret_val; + + if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr - 1); + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; + + int yy_c_buf_p_offset = + (int) (yyg->yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yyrealloc( (void *) b->yy_ch_buf, + (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = NULL; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + yyg->yy_n_chars, num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + if ( yyg->yy_n_chars == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart( yyin , yyscanner); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if ((yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( + (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size , yyscanner ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + /* "- 2" to take care of EOB's */ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); + } + + yyg->yy_n_chars += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (yyscan_t yyscanner) +{ + yy_state_type yy_current_state; + char *yy_cp; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_current_state = yyg->yy_start; + + for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) + { + YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 47 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) +{ + int yy_is_jam; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ + char *yy_cp = yyg->yy_c_buf_p; + + YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 47 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + yy_is_jam = (yy_current_state == 46); + + (void)yyg; + return yy_is_jam ? 0 : yy_current_state; +} + +#ifndef YY_NO_UNPUT + +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (yyscan_t yyscanner) +#else + static int input (yyscan_t yyscanner) +#endif + +{ + int c; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + *yyg->yy_c_buf_p = yyg->yy_hold_char; + + if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + /* This was really a NUL. */ + *yyg->yy_c_buf_p = '\0'; + + else + { /* need more input */ + int offset = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr); + ++yyg->yy_c_buf_p; + + switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart( yyin , yyscanner); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap( yyscanner ) ) + return 0; + + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(yyscanner); +#else + return input(yyscanner); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = yyg->yytext_ptr + offset; + break; + } + } + } + + c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ + *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ + yyg->yy_hold_char = *++yyg->yy_c_buf_p; + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * @param yyscanner The scanner object. + * @note This function does not reset the start condition to @c INITIAL . + */ + void yyrestart (FILE * input_file , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_init_buffer( YY_CURRENT_BUFFER, input_file , yyscanner); + yy_load_buffer_state( yyscanner ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * @param yyscanner The scanner object. + */ + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* TODO. We should be able to replace this entire function body + * with + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + yyensure_buffer_stack (yyscanner); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + yy_load_buffer_state( yyscanner ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yyg->yy_did_buffer_switch_on_eof = 1; +} + +static void yy_load_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + yyg->yy_hold_char = *yyg->yy_c_buf_p; +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * @param yyscanner The scanner object. + * @return the allocated buffer state. + */ + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file , yyscanner); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * @param yyscanner The scanner object. + */ + void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yyfree( (void *) b->yy_ch_buf , yyscanner ); + + yyfree( (void *) b , yyscanner ); +} + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) + +{ + int oerrno = errno; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_flush_buffer( b , yyscanner); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * @param yyscanner The scanner object. + */ + void yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + yy_load_buffer_state( yyscanner ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * @param yyscanner The scanner object. + */ +void yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(yyscanner); + + /* This block is copied from yy_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + yyg->yy_buffer_stack_top++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state( yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * @param yyscanner The scanner object. + */ +void yypop_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (!YY_CURRENT_BUFFER) + return; + + yy_delete_buffer(YY_CURRENT_BUFFER , yyscanner); + YY_CURRENT_BUFFER_LVALUE = NULL; + if (yyg->yy_buffer_stack_top > 0) + --yyg->yy_buffer_stack_top; + + if (YY_CURRENT_BUFFER) { + yy_load_buffer_state( yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void yyensure_buffer_stack (yyscan_t yyscanner) +{ + yy_size_t num_to_alloc; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (!yyg->yy_buffer_stack) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + yyg->yy_buffer_stack_max = num_to_alloc; + yyg->yy_buffer_stack_top = 0; + return; + } + + if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + yy_size_t grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = yyg->yy_buffer_stack_max + grow_size; + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc + (yyg->yy_buffer_stack, + num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); + yyg->yy_buffer_stack_max = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return NULL; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = NULL; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer( b , yyscanner ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to yylex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner) +{ + + return yy_scan_bytes( yystr, (int) strlen(yystr) , yyscanner); +} + +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param yybytes the byte buffer to scan + * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = (yy_size_t) (_yybytes_len + 2); + buf = (char *) yyalloc( n , yyscanner ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer( buf, n , yyscanner); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yynoreturn yy_fatal_error (const char* msg , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + yytext[yyleng] = yyg->yy_hold_char; \ + yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ + yyg->yy_hold_char = *yyg->yy_c_buf_p; \ + *yyg->yy_c_buf_p = '\0'; \ + yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the user-defined data for this scanner. + * @param yyscanner The scanner object. + */ +YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyextra; +} + +/** Get the current line number. + * @param yyscanner The scanner object. + */ +int yyget_lineno (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yylineno; +} + +/** Get the current column number. + * @param yyscanner The scanner object. + */ +int yyget_column (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yycolumn; +} + +/** Get the input stream. + * @param yyscanner The scanner object. + */ +FILE *yyget_in (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyin; +} + +/** Get the output stream. + * @param yyscanner The scanner object. + */ +FILE *yyget_out (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyout; +} + +/** Get the length of the current token. + * @param yyscanner The scanner object. + */ +int yyget_leng (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyleng; +} + +/** Get the current token. + * @param yyscanner The scanner object. + */ + +char *yyget_text (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yytext; +} + +/** Set the user-defined data. This data is never touched by the scanner. + * @param user_defined The data to be associated with this scanner. + * @param yyscanner The scanner object. + */ +void yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyextra = user_defined ; +} + +/** Set the current line number. + * @param _line_number line number + * @param yyscanner The scanner object. + */ +void yyset_lineno (int _line_number , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* lineno is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_lineno called with no buffer" ); + + yylineno = _line_number; +} + +/** Set the current column. + * @param _column_no column number + * @param yyscanner The scanner object. + */ +void yyset_column (int _column_no , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* column is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_column called with no buffer" ); + + yycolumn = _column_no; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param _in_str A readable stream. + * @param yyscanner The scanner object. + * @see yy_switch_to_buffer + */ +void yyset_in (FILE * _in_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyin = _in_str ; +} + +void yyset_out (FILE * _out_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyout = _out_str ; +} + +int yyget_debug (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yy_flex_debug; +} + +void yyset_debug (int _bdebug , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yy_flex_debug = _bdebug ; +} + +/* Accessor methods for yylval and yylloc */ + +YYSTYPE * yyget_lval (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylval; +} + +void yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylval = yylval_param; +} + +YYLTYPE *yyget_lloc (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylloc; +} + +void yyset_lloc (YYLTYPE * yylloc_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylloc = yylloc_param; +} + +/* User-visible API */ + +/* yylex_init is special because it creates the scanner itself, so it is + * the ONLY reentrant function that doesn't take the scanner as the last argument. + * That's why we explicitly handle the declaration, instead of using our macros. + */ +int yylex_init(yyscan_t* ptr_yy_globals) +{ + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), NULL ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + return yy_init_globals ( *ptr_yy_globals ); +} + +/* yylex_init_extra has the same functionality as yylex_init, but follows the + * convention of taking the scanner as the last argument. Note however, that + * this is a *pointer* to a scanner, as it will be allocated by this call (and + * is the reason, too, why this function also must handle its own declaration). + * The user defined value in the first argument will be available to yyalloc in + * the yyextra field. + */ +int yylex_init_extra( YY_EXTRA_TYPE yy_user_defined, yyscan_t* ptr_yy_globals ) +{ + struct yyguts_t dummy_yyguts; + + yyset_extra (yy_user_defined, &dummy_yyguts); + + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in + yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + yyset_extra (yy_user_defined, *ptr_yy_globals); + + return yy_init_globals ( *ptr_yy_globals ); +} + +static int yy_init_globals (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from yylex_destroy(), so don't allocate here. + */ + + yyg->yy_buffer_stack = NULL; + yyg->yy_buffer_stack_top = 0; + yyg->yy_buffer_stack_max = 0; + yyg->yy_c_buf_p = NULL; + yyg->yy_init = 0; + yyg->yy_start = 0; + + yyg->yy_start_stack_ptr = 0; + yyg->yy_start_stack_depth = 0; + yyg->yy_start_stack = NULL; + +/* Defined in main.c */ +#ifdef YY_STDINIT + yyin = stdin; + yyout = stdout; +#else + yyin = NULL; + yyout = NULL; +#endif + + /* For future reference: Set errno on error, since we are called by + * yylex_init() + */ + return 0; +} + +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer( YY_CURRENT_BUFFER , yyscanner ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(yyscanner); + } + + /* Destroy the stack itself. */ + yyfree(yyg->yy_buffer_stack , yyscanner); + yyg->yy_buffer_stack = NULL; + + /* Destroy the start condition stack. */ + yyfree( yyg->yy_start_stack , yyscanner ); + yyg->yy_start_stack = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * yylex() is called, initialization will occur. */ + yy_init_globals( yyscanner); + + /* Destroy the main struct (reentrant only). */ + yyfree ( yyscanner , yyscanner ); + yyscanner = NULL; + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (const char * s , yyscan_t yyscanner) +{ + int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *yyalloc (yy_size_t size , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + return malloc(size); +} + +void *yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return realloc(ptr, size); +} + +void yyfree (void * ptr , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 56 "pktloc_grammar.l" diff --git a/libnl/lib/route/pktloc_grammar.h b/libnl/lib/route/pktloc_grammar.h new file mode 100644 index 0000000..4aadd06 --- /dev/null +++ b/libnl/lib/route/pktloc_grammar.h @@ -0,0 +1,733 @@ +#ifndef pktloc_HEADER_H +#define pktloc_HEADER_H 1 +#define pktloc_IN_HEADER 1 + +#line 6 "pktloc_grammar.h" + +#line 8 "pktloc_grammar.h" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yy_create_buffer +#define pktloc__create_buffer_ALREADY_DEFINED +#else +#define yy_create_buffer pktloc__create_buffer +#endif + +#ifdef yy_delete_buffer +#define pktloc__delete_buffer_ALREADY_DEFINED +#else +#define yy_delete_buffer pktloc__delete_buffer +#endif + +#ifdef yy_scan_buffer +#define pktloc__scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer pktloc__scan_buffer +#endif + +#ifdef yy_scan_string +#define pktloc__scan_string_ALREADY_DEFINED +#else +#define yy_scan_string pktloc__scan_string +#endif + +#ifdef yy_scan_bytes +#define pktloc__scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes pktloc__scan_bytes +#endif + +#ifdef yy_init_buffer +#define pktloc__init_buffer_ALREADY_DEFINED +#else +#define yy_init_buffer pktloc__init_buffer +#endif + +#ifdef yy_flush_buffer +#define pktloc__flush_buffer_ALREADY_DEFINED +#else +#define yy_flush_buffer pktloc__flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define pktloc__load_buffer_state_ALREADY_DEFINED +#else +#define yy_load_buffer_state pktloc__load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define pktloc__switch_to_buffer_ALREADY_DEFINED +#else +#define yy_switch_to_buffer pktloc__switch_to_buffer +#endif + +#ifdef yypush_buffer_state +#define pktloc_push_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state pktloc_push_buffer_state +#endif + +#ifdef yypop_buffer_state +#define pktloc_pop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state pktloc_pop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define pktloc_ensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack pktloc_ensure_buffer_stack +#endif + +#ifdef yylex +#define pktloc_lex_ALREADY_DEFINED +#else +#define yylex pktloc_lex +#endif + +#ifdef yyrestart +#define pktloc_restart_ALREADY_DEFINED +#else +#define yyrestart pktloc_restart +#endif + +#ifdef yylex_init +#define pktloc_lex_init_ALREADY_DEFINED +#else +#define yylex_init pktloc_lex_init +#endif + +#ifdef yylex_init_extra +#define pktloc_lex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra pktloc_lex_init_extra +#endif + +#ifdef yylex_destroy +#define pktloc_lex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy pktloc_lex_destroy +#endif + +#ifdef yyget_debug +#define pktloc_get_debug_ALREADY_DEFINED +#else +#define yyget_debug pktloc_get_debug +#endif + +#ifdef yyset_debug +#define pktloc_set_debug_ALREADY_DEFINED +#else +#define yyset_debug pktloc_set_debug +#endif + +#ifdef yyget_extra +#define pktloc_get_extra_ALREADY_DEFINED +#else +#define yyget_extra pktloc_get_extra +#endif + +#ifdef yyset_extra +#define pktloc_set_extra_ALREADY_DEFINED +#else +#define yyset_extra pktloc_set_extra +#endif + +#ifdef yyget_in +#define pktloc_get_in_ALREADY_DEFINED +#else +#define yyget_in pktloc_get_in +#endif + +#ifdef yyset_in +#define pktloc_set_in_ALREADY_DEFINED +#else +#define yyset_in pktloc_set_in +#endif + +#ifdef yyget_out +#define pktloc_get_out_ALREADY_DEFINED +#else +#define yyget_out pktloc_get_out +#endif + +#ifdef yyset_out +#define pktloc_set_out_ALREADY_DEFINED +#else +#define yyset_out pktloc_set_out +#endif + +#ifdef yyget_leng +#define pktloc_get_leng_ALREADY_DEFINED +#else +#define yyget_leng pktloc_get_leng +#endif + +#ifdef yyget_text +#define pktloc_get_text_ALREADY_DEFINED +#else +#define yyget_text pktloc_get_text +#endif + +#ifdef yyget_lineno +#define pktloc_get_lineno_ALREADY_DEFINED +#else +#define yyget_lineno pktloc_get_lineno +#endif + +#ifdef yyset_lineno +#define pktloc_set_lineno_ALREADY_DEFINED +#else +#define yyset_lineno pktloc_set_lineno +#endif + +#ifdef yyget_column +#define pktloc_get_column_ALREADY_DEFINED +#else +#define yyget_column pktloc_get_column +#endif + +#ifdef yyset_column +#define pktloc_set_column_ALREADY_DEFINED +#else +#define yyset_column pktloc_set_column +#endif + +#ifdef yywrap +#define pktloc_wrap_ALREADY_DEFINED +#else +#define yywrap pktloc_wrap +#endif + +#ifdef yyget_lval +#define pktloc_get_lval_ALREADY_DEFINED +#else +#define yyget_lval pktloc_get_lval +#endif + +#ifdef yyset_lval +#define pktloc_set_lval_ALREADY_DEFINED +#else +#define yyset_lval pktloc_set_lval +#endif + +#ifdef yyget_lloc +#define pktloc_get_lloc_ALREADY_DEFINED +#else +#define yyget_lloc pktloc_get_lloc +#endif + +#ifdef yyset_lloc +#define pktloc_set_lloc_ALREADY_DEFINED +#else +#define yyset_lloc pktloc_set_lloc +#endif + +#ifdef yyalloc +#define pktloc_alloc_ALREADY_DEFINED +#else +#define yyalloc pktloc_alloc +#endif + +#ifdef yyrealloc +#define pktloc_realloc_ALREADY_DEFINED +#else +#define yyrealloc pktloc_realloc +#endif + +#ifdef yyfree +#define pktloc_free_ALREADY_DEFINED +#else +#define yyfree pktloc_free +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +void yyrestart ( FILE *input_file , yyscan_t yyscanner ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); +void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +void yypop_buffer_state ( yyscan_t yyscanner ); + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); + +void *yyalloc ( yy_size_t , yyscan_t yyscanner ); +void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); +void yyfree ( void * , yyscan_t yyscanner ); + +#define pktloc_wrap(yyscanner) (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP + +#define yytext_ptr yytext_r + +#ifdef YY_HEADER_EXPORT_START_CONDITIONS +#define INITIAL 0 + +#endif + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +int yylex_init (yyscan_t* scanner); + +int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy ( yyscan_t yyscanner ); + +int yyget_debug ( yyscan_t yyscanner ); + +void yyset_debug ( int debug_flag , yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); + +FILE *yyget_in ( yyscan_t yyscanner ); + +void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); + +FILE *yyget_out ( yyscan_t yyscanner ); + +void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); + + int yyget_leng ( yyscan_t yyscanner ); + +char *yyget_text ( yyscan_t yyscanner ); + +int yyget_lineno ( yyscan_t yyscanner ); + +void yyset_lineno ( int _line_number , yyscan_t yyscanner ); + +int yyget_column ( yyscan_t yyscanner ); + +void yyset_column ( int _column_no , yyscan_t yyscanner ); + +YYSTYPE * yyget_lval ( yyscan_t yyscanner ); + +void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); + + YYLTYPE *yyget_lloc ( yyscan_t yyscanner ); + + void yyset_lloc ( YYLTYPE * yylloc_param , yyscan_t yyscanner ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap ( yyscan_t yyscanner ); +#else +extern int yywrap ( yyscan_t yyscanner ); +#endif +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const char * , yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner); + +#define YY_DECL int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + +#undef YY_NEW_FILE +#undef YY_FLUSH_BUFFER +#undef yy_set_bol +#undef yy_new_buffer +#undef yy_set_interactive +#undef YY_DO_BEFORE_ACTION + +#ifdef YY_DECL_IS_OURS +#undef YY_DECL_IS_OURS +#undef YY_DECL +#endif + +#ifndef pktloc__create_buffer_ALREADY_DEFINED +#undef yy_create_buffer +#endif +#ifndef pktloc__delete_buffer_ALREADY_DEFINED +#undef yy_delete_buffer +#endif +#ifndef pktloc__scan_buffer_ALREADY_DEFINED +#undef yy_scan_buffer +#endif +#ifndef pktloc__scan_string_ALREADY_DEFINED +#undef yy_scan_string +#endif +#ifndef pktloc__scan_bytes_ALREADY_DEFINED +#undef yy_scan_bytes +#endif +#ifndef pktloc__init_buffer_ALREADY_DEFINED +#undef yy_init_buffer +#endif +#ifndef pktloc__flush_buffer_ALREADY_DEFINED +#undef yy_flush_buffer +#endif +#ifndef pktloc__load_buffer_state_ALREADY_DEFINED +#undef yy_load_buffer_state +#endif +#ifndef pktloc__switch_to_buffer_ALREADY_DEFINED +#undef yy_switch_to_buffer +#endif +#ifndef pktloc_push_buffer_state_ALREADY_DEFINED +#undef yypush_buffer_state +#endif +#ifndef pktloc_pop_buffer_state_ALREADY_DEFINED +#undef yypop_buffer_state +#endif +#ifndef pktloc_ensure_buffer_stack_ALREADY_DEFINED +#undef yyensure_buffer_stack +#endif +#ifndef pktloc_lex_ALREADY_DEFINED +#undef yylex +#endif +#ifndef pktloc_restart_ALREADY_DEFINED +#undef yyrestart +#endif +#ifndef pktloc_lex_init_ALREADY_DEFINED +#undef yylex_init +#endif +#ifndef pktloc_lex_init_extra_ALREADY_DEFINED +#undef yylex_init_extra +#endif +#ifndef pktloc_lex_destroy_ALREADY_DEFINED +#undef yylex_destroy +#endif +#ifndef pktloc_get_debug_ALREADY_DEFINED +#undef yyget_debug +#endif +#ifndef pktloc_set_debug_ALREADY_DEFINED +#undef yyset_debug +#endif +#ifndef pktloc_get_extra_ALREADY_DEFINED +#undef yyget_extra +#endif +#ifndef pktloc_set_extra_ALREADY_DEFINED +#undef yyset_extra +#endif +#ifndef pktloc_get_in_ALREADY_DEFINED +#undef yyget_in +#endif +#ifndef pktloc_set_in_ALREADY_DEFINED +#undef yyset_in +#endif +#ifndef pktloc_get_out_ALREADY_DEFINED +#undef yyget_out +#endif +#ifndef pktloc_set_out_ALREADY_DEFINED +#undef yyset_out +#endif +#ifndef pktloc_get_leng_ALREADY_DEFINED +#undef yyget_leng +#endif +#ifndef pktloc_get_text_ALREADY_DEFINED +#undef yyget_text +#endif +#ifndef pktloc_get_lineno_ALREADY_DEFINED +#undef yyget_lineno +#endif +#ifndef pktloc_set_lineno_ALREADY_DEFINED +#undef yyset_lineno +#endif +#ifndef pktloc_get_column_ALREADY_DEFINED +#undef yyget_column +#endif +#ifndef pktloc_set_column_ALREADY_DEFINED +#undef yyset_column +#endif +#ifndef pktloc_wrap_ALREADY_DEFINED +#undef yywrap +#endif +#ifndef pktloc_get_lval_ALREADY_DEFINED +#undef yyget_lval +#endif +#ifndef pktloc_set_lval_ALREADY_DEFINED +#undef yyset_lval +#endif +#ifndef pktloc_get_lloc_ALREADY_DEFINED +#undef yyget_lloc +#endif +#ifndef pktloc_set_lloc_ALREADY_DEFINED +#undef yyset_lloc +#endif +#ifndef pktloc_alloc_ALREADY_DEFINED +#undef yyalloc +#endif +#ifndef pktloc_realloc_ALREADY_DEFINED +#undef yyrealloc +#endif +#ifndef pktloc_free_ALREADY_DEFINED +#undef yyfree +#endif +#ifndef pktloc_text_ALREADY_DEFINED +#undef yytext +#endif +#ifndef pktloc_leng_ALREADY_DEFINED +#undef yyleng +#endif +#ifndef pktloc_in_ALREADY_DEFINED +#undef yyin +#endif +#ifndef pktloc_out_ALREADY_DEFINED +#undef yyout +#endif +#ifndef pktloc__flex_debug_ALREADY_DEFINED +#undef yy_flex_debug +#endif +#ifndef pktloc_lineno_ALREADY_DEFINED +#undef yylineno +#endif +#ifndef pktloc_tables_fload_ALREADY_DEFINED +#undef yytables_fload +#endif +#ifndef pktloc_tables_destroy_ALREADY_DEFINED +#undef yytables_destroy +#endif +#ifndef pktloc_TABLES_NAME_ALREADY_DEFINED +#undef yyTABLES_NAME +#endif + +#line 56 "pktloc_grammar.l" + +#line 732 "pktloc_grammar.h" +#undef pktloc_IN_HEADER +#endif /* pktloc_HEADER_H */ diff --git a/libnl/lib/route/pktloc_grammar.l b/libnl/lib/route/pktloc_grammar.l new file mode 100644 index 0000000..ab592d1 --- /dev/null +++ b/libnl/lib/route/pktloc_grammar.l @@ -0,0 +1,55 @@ +%{ + #include + #include + #include + #include + #include + #include + #include "pktloc_syntax.h" + + int pktloc_get_column(yyscan_t); + void pktloc_set_column(int, yyscan_t); +%} + +%option 8bit +%option reentrant +%option warn +%option noyywrap +%option noinput +%option nounput +%option bison-bridge +%option bison-locations +%option prefix="pktloc_" + +%% + +[ \t\r\n]+ + +"#".* + +[[:digit:]]+ | +0[xX][[:xdigit:]]+ { + yylval->i = strtoul(yytext, NULL, 0); + return NUMBER; + } + +"+" { return yylval->i = yytext[0]; } + +[uU]8 { yylval->i = TCF_EM_ALIGN_U8; return ALIGN; } +[uU]16 { yylval->i = TCF_EM_ALIGN_U16; return ALIGN; } +[uU]32 { yylval->i = TCF_EM_ALIGN_U32; return ALIGN; } + +[lL][iI][nN][kK] | +[eE][tT][hH] { yylval->i = TCF_LAYER_LINK; return LAYER; } +[nN][eE][tT] | +[iI][pP] { yylval->i = TCF_LAYER_NETWORK; return LAYER; } +[tT][rR][aA][nN][sS][pP][oO][rR][tT] | +[tT][cC][pP] { yylval->i = TCF_LAYER_TRANSPORT; return LAYER; } + + +[^ \t\r\n+]+ { + yylval->s = strdup(yytext); + if (yylval->s == NULL) + return ERROR; + return NAME; + } diff --git a/libnl/lib/route/pktloc_syntax.c b/libnl/lib/route/pktloc_syntax.c new file mode 100644 index 0000000..d7e630b --- /dev/null +++ b/libnl/lib/route/pktloc_syntax.c @@ -0,0 +1,1777 @@ +/* A Bison parser, made by GNU Bison 3.5.1. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Undocumented macros, especially those whose name start with YY_, + are private implementation details. Do not rely on them. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "3.5.1" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + +/* Substitute the variable and function names. */ +#define yyparse pktloc_parse +#define yylex pktloc_lex +#define yyerror pktloc_error +#define yydebug pktloc_debug +#define yynerrs pktloc_nerrs + +/* First part of user prologue. */ +#line 1 "pktloc_syntax.y" + +#include +#include +#include +#include +#include + +#line 83 "pktloc_syntax.c" + +# ifndef YY_CAST +# ifdef __cplusplus +# define YY_CAST(Type, Val) static_cast (Val) +# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast (Val) +# else +# define YY_CAST(Type, Val) ((Type) (Val)) +# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) +# endif +# endif +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 1 +#endif + +/* Use api.header.include to #include this header + instead of duplicating it here. */ +#ifndef YY_PKTLOC_PKTLOC_SYNTAX_H_INCLUDED +# define YY_PKTLOC_PKTLOC_SYNTAX_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int pktloc_debug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + ERROR = 258, + NUMBER = 259, + LAYER = 260, + ALIGN = 261, + NAME = 262 + }; +#endif +/* Tokens. */ +#define ERROR 258 +#define NUMBER 259 +#define LAYER 260 +#define ALIGN 261 +#define NAME 262 + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 18 "pktloc_syntax.y" + + struct rtnl_pktloc *l; + uint32_t i; + char *s; + +#line 155 "pktloc_syntax.c" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + +/* Location type. */ +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE YYLTYPE; +struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +}; +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + + +int pktloc_parse (void *scanner); + +#endif /* !YY_PKTLOC_PKTLOC_SYNTAX_H_INCLUDED */ + +/* Second part of user prologue. */ +#line 24 "pktloc_syntax.y" + +extern int pktloc_lex(YYSTYPE *, YYLTYPE *, void *); + +static void yyerror(YYLTYPE *locp, void *scanner, const char *msg) +{ + NL_DBG(1, "Error while parsing packet location file: %s\n", msg); +} + +#line 193 "pktloc_syntax.c" + + +#ifdef short +# undef short +#endif + +/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure + and (if available) are included + so that the code can choose integer types of a good width. */ + +#ifndef __PTRDIFF_MAX__ +# include /* INFRINGES ON USER NAME SPACE */ +# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_STDINT_H +# endif +#endif + +/* Narrow types that promote to a signed type and that can represent a + signed or unsigned integer of at least N bits. In tables they can + save space and decrease cache pressure. Promoting to a signed type + helps avoid bugs in integer arithmetic. */ + +#ifdef __INT_LEAST8_MAX__ +typedef __INT_LEAST8_TYPE__ yytype_int8; +#elif defined YY_STDINT_H +typedef int_least8_t yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef __INT_LEAST16_MAX__ +typedef __INT_LEAST16_TYPE__ yytype_int16; +#elif defined YY_STDINT_H +typedef int_least16_t yytype_int16; +#else +typedef short yytype_int16; +#endif + +#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST8_TYPE__ yytype_uint8; +#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST8_MAX <= INT_MAX) +typedef uint_least8_t yytype_uint8; +#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX +typedef unsigned char yytype_uint8; +#else +typedef short yytype_uint8; +#endif + +#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST16_TYPE__ yytype_uint16; +#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST16_MAX <= INT_MAX) +typedef uint_least16_t yytype_uint16; +#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX +typedef unsigned short yytype_uint16; +#else +typedef int yytype_uint16; +#endif + +#ifndef YYPTRDIFF_T +# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ +# define YYPTRDIFF_T __PTRDIFF_TYPE__ +# define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ +# elif defined PTRDIFF_MAX +# ifndef ptrdiff_t +# include /* INFRINGES ON USER NAME SPACE */ +# endif +# define YYPTRDIFF_T ptrdiff_t +# define YYPTRDIFF_MAXIMUM PTRDIFF_MAX +# else +# define YYPTRDIFF_T long +# define YYPTRDIFF_MAXIMUM LONG_MAX +# endif +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned +# endif +#endif + +#define YYSIZE_MAXIMUM \ + YY_CAST (YYPTRDIFF_T, \ + (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ + ? YYPTRDIFF_MAXIMUM \ + : YY_CAST (YYSIZE_T, -1))) + +#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) + +/* Stored state numbers (used for stacks). */ +typedef yytype_int8 yy_state_t; + +/* State numbers in computations. */ +typedef int yy_state_fast_t; + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + +#ifndef YY_ATTRIBUTE_PURE +# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# else +# define YY_ATTRIBUTE_PURE +# endif +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# else +# define YY_ATTRIBUTE_UNUSED +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(E) ((void) (E)) +#else +# define YYUSE(E) /* empty */ +#endif + +#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ +# define YY_IGNORE_USELESS_CAST_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") +# define YY_IGNORE_USELESS_CAST_END \ + _Pragma ("GCC diagnostic pop") +#endif +#ifndef YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_END +#endif + + +#define YY_ASSERT(E) ((void) (0 && (E))) + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ + && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yy_state_t yyss_alloc; + YYSTYPE yyvs_alloc; + YYLTYPE yyls_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE) \ + + YYSIZEOF (YYLTYPE)) \ + + 2 * YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYPTRDIFF_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / YYSIZEOF (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYPTRDIFF_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 7 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 10 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 9 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 7 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 12 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 17 + +#define YYUNDEFTOK 2 +#define YYMAXUTOK 262 + + +/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, with out-of-bounds checking. */ +#define YYTRANSLATE(YYX) \ + (0 <= (YYX) && (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex. */ +static const yytype_int8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 8, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7 +}; + +#if YYDEBUG + /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_int8 yyrline[] = +{ + 0, 45, 45, 47, 51, 78, 80, 86, 87, 93, + 94, 100, 101 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || 1 +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "ERROR", "NUMBER", "LAYER", "ALIGN", + "NAME", "'+'", "$accept", "input", "location", "align", "layer", "mask", + "shift", YY_NULLPTR +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[NUM] -- (External) token number corresponding to the + (internal) symbol number NUM (which must be that of a token). */ +static const yytype_int16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 43 +}; +# endif + +#define YYPACT_NINF (-7) + +#define yypact_value_is_default(Yyn) \ + ((Yyn) == YYPACT_NINF) + +#define YYTABLE_NINF (-1) + +#define yytable_value_is_error(Yyn) \ + 0 + + /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int8 yypact[] = +{ + -6, -4, 3, -6, -7, -7, -1, -7, -7, -3, + 2, -7, 4, -7, 5, -7, -7 +}; + + /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_int8 yydefact[] = +{ + 2, 0, 0, 2, 6, 5, 7, 1, 3, 0, + 0, 8, 9, 10, 11, 12, 4 +}; + + /* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -7, 7, -7, -7, -7, -7, -7 +}; + + /* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + -1, 2, 3, 6, 10, 14, 16 +}; + + /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_int8 yytable[] = +{ + 4, 1, 5, 7, 9, 11, 12, 0, 13, 15, + 8 +}; + +static const yytype_int8 yycheck[] = +{ + 4, 7, 6, 0, 5, 8, 4, -1, 4, 4, + 3 +}; + + /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_int8 yystos[] = +{ + 0, 7, 10, 11, 4, 6, 12, 0, 10, 5, + 13, 8, 4, 4, 14, 4, 15 +}; + + /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_int8 yyr1[] = +{ + 0, 9, 10, 10, 11, 12, 12, 13, 13, 14, + 14, 15, 15 +}; + + /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ +static const yytype_int8 yyr2[] = +{ + 0, 2, 0, 2, 6, 1, 1, 0, 2, 0, + 1, 0, 1 +}; + + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ + do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (&yylloc, scanner, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ + while (0) + +/* Error token number */ +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (N) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (0) +#endif + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + + +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ + +#ifndef YY_LOCATION_PRINT +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + +/* Print *YYLOCP on YYO. Private, do not rely on its existence. */ + +YY_ATTRIBUTE_UNUSED +static int +yy_location_print_ (FILE *yyo, YYLTYPE const * const yylocp) +{ + int res = 0; + int end_col = 0 != yylocp->last_column ? yylocp->last_column - 1 : 0; + if (0 <= yylocp->first_line) + { + res += YYFPRINTF (yyo, "%d", yylocp->first_line); + if (0 <= yylocp->first_column) + res += YYFPRINTF (yyo, ".%d", yylocp->first_column); + } + if (0 <= yylocp->last_line) + { + if (yylocp->first_line < yylocp->last_line) + { + res += YYFPRINTF (yyo, "-%d", yylocp->last_line); + if (0 <= end_col) + res += YYFPRINTF (yyo, ".%d", end_col); + } + else if (0 <= end_col && yylocp->first_column < end_col) + res += YYFPRINTF (yyo, "-%d", end_col); + } + return res; + } + +# define YY_LOCATION_PRINT(File, Loc) \ + yy_location_print_ (File, &(Loc)) + +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value, Location, scanner); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*-----------------------------------. +| Print this symbol's value on YYO. | +`-----------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, void *scanner) +{ + FILE *yyoutput = yyo; + YYUSE (yyoutput); + YYUSE (yylocationp); + YYUSE (scanner); + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyo, yytoknum[yytype], *yyvaluep); +# endif + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yytype); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/*---------------------------. +| Print this symbol on YYO. | +`---------------------------*/ + +static void +yy_symbol_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, void *scanner) +{ + YYFPRINTF (yyo, "%s %s (", + yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); + + YY_LOCATION_PRINT (yyo, *yylocationp); + YYFPRINTF (yyo, ": "); + yy_symbol_value_print (yyo, yytype, yyvaluep, yylocationp, scanner); + YYFPRINTF (yyo, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, void *scanner) +{ + int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + yystos[+yyssp[yyi + 1 - yynrhs]], + &yyvsp[(yyi + 1) - (yynrhs)] + , &(yylsp[(yyi + 1) - (yynrhs)]) , scanner); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, yylsp, Rule, scanner); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen(S) (YY_CAST (YYPTRDIFF_T, strlen (S))) +# else +/* Return the length of YYSTR. */ +static YYPTRDIFF_T +yystrlen (const char *yystr) +{ + YYPTRDIFF_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +yystpcpy (char *yydest, const char *yysrc) +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYPTRDIFF_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYPTRDIFF_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + else + goto append; + + append: + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (yyres) + return yystpcpy (yyres, yystr) - yyres; + else + return yystrlen (yystr); +} +# endif + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYPTRDIFF_T *yymsg_alloc, char **yymsg, + yy_state_t *yyssp, int yytoken) +{ + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULLPTR; + /* Arguments of yyformat: reported tokens (one for the "unexpected", + one per "expected"). */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Actual size of YYARG. */ + int yycount = 0; + /* Cumulated lengths of YYARG. */ + YYPTRDIFF_T yysize = 0; + + /* There are many possibilities here to consider: + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[+*yyssp]; + YYPTRDIFF_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]); + yysize = yysize0; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + { + YYPTRDIFF_T yysize1 + = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]); + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return 2; + } + } + } + } + + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + default: /* Avoid compiler warnings. */ + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } + + { + /* Don't count the "%s"s in the final size, but reserve room for + the terminator. */ + YYPTRDIFF_T yysize1 = yysize + (yystrlen (yyformat) - 2 * yycount) + 1; + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return 2; + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; + } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + ++yyp; + ++yyformat; + } + } + return 0; +} +#endif /* YYERROR_VERBOSE */ + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, void *scanner) +{ + YYUSE (yyvaluep); + YYUSE (yylocationp); + YYUSE (scanner); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + switch (yytype) + { + case 7: /* NAME */ +#line 39 "pktloc_syntax.y" + { free(((*yyvaluep).s)); } +#line 1165 "pktloc_syntax.c" + break; + + default: + break; + } + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (void *scanner) +{ +/* The lookahead symbol. */ +int yychar; + + +/* The semantic value of the lookahead symbol. */ +/* Default value used for initialization, for pacifying older GCCs + or non-GCC compilers. */ +YY_INITIAL_VALUE (static YYSTYPE yyval_default;) +YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); + +/* Location data for the lookahead symbol. */ +static YYLTYPE yyloc_default +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + = { 1, 1, 1, 1 } +# endif +; +YYLTYPE yylloc = yyloc_default; + + /* Number of syntax errors so far. */ + int yynerrs; + + yy_state_fast_t yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + 'yyss': related to states. + 'yyvs': related to semantic values. + 'yyls': related to locations. + + Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yy_state_t yyssa[YYINITDEPTH]; + yy_state_t *yyss; + yy_state_t *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + /* The location stack. */ + YYLTYPE yylsa[YYINITDEPTH]; + YYLTYPE *yyls; + YYLTYPE *yylsp; + + /* The locations where the error started and ended. */ + YYLTYPE yyerror_range[3]; + + YYPTRDIFF_T yystacksize; + + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken = 0; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + YYLTYPE yyloc; + +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYPTRDIFF_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + yyssp = yyss = yyssa; + yyvsp = yyvs = yyvsa; + yylsp = yyls = yylsa; + yystacksize = YYINITDEPTH; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + yylsp[0] = yylloc; + goto yysetstate; + + +/*------------------------------------------------------------. +| yynewstate -- push a new state, which is found in yystate. | +`------------------------------------------------------------*/ +yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + +/*--------------------------------------------------------------------. +| yysetstate -- set current state (the top of the stack) to yystate. | +`--------------------------------------------------------------------*/ +yysetstate: + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + YY_ASSERT (0 <= yystate && yystate < YYNSTATES); + YY_IGNORE_USELESS_CAST_BEGIN + *yyssp = YY_CAST (yy_state_t, yystate); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) +#if !defined yyoverflow && !defined YYSTACK_RELOCATE + goto yyexhaustedlab; +#else + { + /* Get the current used size of the three stacks, in elements. */ + YYPTRDIFF_T yysize = yyssp - yyss + 1; + +# if defined yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + yy_state_t *yyss1 = yyss; + YYSTYPE *yyvs1 = yyvs; + YYLTYPE *yyls1 = yyls; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * YYSIZEOF (*yyssp), + &yyvs1, yysize * YYSIZEOF (*yyvsp), + &yyls1, yysize * YYSIZEOF (*yylsp), + &yystacksize); + yyss = yyss1; + yyvs = yyvs1; + yyls = yyls1; + } +# else /* defined YYSTACK_RELOCATE */ + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yy_state_t *yyss1 = yyss; + union yyalloc *yyptr = + YY_CAST (union yyalloc *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); + YYSTACK_RELOCATE (yyls_alloc, yyls); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + yylsp = yyls + yysize - 1; + + YY_IGNORE_USELESS_CAST_BEGIN + YYDPRINTF ((stderr, "Stack size increased to %ld\n", + YY_CAST (long, yystacksize))); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } +#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = yylex (&yylval, &yylloc, scanner); + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + *++yylsp = yylloc; + + /* Discard the shifted token. */ + yychar = YYEMPTY; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + '$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + /* Default location. */ + YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); + yyerror_range[1] = yyloc; + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 4: +#line 52 "pktloc_syntax.y" + { + struct rtnl_pktloc *loc; + + if (!(loc = rtnl_pktloc_alloc())) { + NL_DBG(1, "Allocating a packet location " + "object failed.\n"); + YYABORT; + } + + loc->name = (yyvsp[-5].s); + loc->align = (yyvsp[-4].i); + loc->layer = (yyvsp[-3].i); + loc->offset = (yyvsp[-2].i); + loc->mask = (yyvsp[-1].i); + loc->shift = (yyvsp[0].i); + + if (rtnl_pktloc_add(loc) < 0) { + NL_DBG(1, "Duplicate packet location entry " + "\"%s\"\n", (yyvsp[-5].s)); + } + + (yyval.l) = loc; + } +#line 1489 "pktloc_syntax.c" + break; + + case 5: +#line 79 "pktloc_syntax.y" + { (yyval.i) = (yyvsp[0].i); } +#line 1495 "pktloc_syntax.c" + break; + + case 6: +#line 81 "pktloc_syntax.y" + { (yyval.i) = (yyvsp[0].i); } +#line 1501 "pktloc_syntax.c" + break; + + case 7: +#line 86 "pktloc_syntax.y" + { (yyval.i) = TCF_LAYER_NETWORK; } +#line 1507 "pktloc_syntax.c" + break; + + case 8: +#line 88 "pktloc_syntax.y" + { (yyval.i) = (yyvsp[-1].i); } +#line 1513 "pktloc_syntax.c" + break; + + case 9: +#line 93 "pktloc_syntax.y" + { (yyval.i) = 0; } +#line 1519 "pktloc_syntax.c" + break; + + case 10: +#line 95 "pktloc_syntax.y" + { (yyval.i) = (yyvsp[0].i); } +#line 1525 "pktloc_syntax.c" + break; + + case 11: +#line 100 "pktloc_syntax.y" + { (yyval.i) = 0; } +#line 1531 "pktloc_syntax.c" + break; + + case 12: +#line 102 "pktloc_syntax.y" + { (yyval.i) = (yyvsp[0].i); } +#line 1537 "pktloc_syntax.c" + break; + + +#line 1541 "pktloc_syntax.c" + + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + *++yylsp = yyloc; + + /* Now 'shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + { + const int yylhs = yyr1[yyn] - YYNTOKENS; + const int yyi = yypgoto[yylhs] + *yyssp; + yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp + ? yytable[yyi] + : yydefgoto[yylhs]); + } + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (&yylloc, scanner, YY_("syntax error")); +#else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) + { + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = YY_CAST (char *, YYSTACK_ALLOC (YY_CAST (YYSIZE_T, yymsg_alloc))); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (&yylloc, scanner, yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; + } +# undef YYSYNTAX_ERROR +#endif + } + + yyerror_range[1] = yylloc; + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, &yylloc, scanner); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + /* Pacify compilers when the user code never invokes YYERROR and the + label yyerrorlab therefore never appears in user code. */ + if (0) + YYERROR; + + /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + yyerror_range[1] = *yylsp; + yydestruct ("Error: popping", + yystos[yystate], yyvsp, yylsp, scanner); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + yyerror_range[2] = yylloc; + /* Using YYLLOC is tempting, but would change the location of + the lookahead. YYLOC is available though. */ + YYLLOC_DEFAULT (yyloc, yyerror_range, 2); + *++yylsp = yyloc; + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + + +#if !defined yyoverflow || YYERROR_VERBOSE +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (&yylloc, scanner, YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + + +/*-----------------------------------------------------. +| yyreturn -- parsing is finished, return the result. | +`-----------------------------------------------------*/ +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, &yylloc, scanner); + } + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[+*yyssp], yyvsp, yylsp, scanner); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + return yyresult; +} diff --git a/libnl/lib/route/pktloc_syntax.h b/libnl/lib/route/pktloc_syntax.h new file mode 100644 index 0000000..08584e1 --- /dev/null +++ b/libnl/lib/route/pktloc_syntax.h @@ -0,0 +1,102 @@ +/* A Bison parser, made by GNU Bison 3.5.1. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* Undocumented macros, especially those whose name start with YY_, + are private implementation details. Do not rely on them. */ + +#ifndef YY_PKTLOC_PKTLOC_SYNTAX_H_INCLUDED +# define YY_PKTLOC_PKTLOC_SYNTAX_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int pktloc_debug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + ERROR = 258, + NUMBER = 259, + LAYER = 260, + ALIGN = 261, + NAME = 262 + }; +#endif +/* Tokens. */ +#define ERROR 258 +#define NUMBER 259 +#define LAYER 260 +#define ALIGN 261 +#define NAME 262 + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 18 "pktloc_syntax.y" + + struct rtnl_pktloc *l; + uint32_t i; + char *s; + +#line 77 "pktloc_syntax.h" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + +/* Location type. */ +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE YYLTYPE; +struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +}; +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + + +int pktloc_parse (void *scanner); + +#endif /* !YY_PKTLOC_PKTLOC_SYNTAX_H_INCLUDED */ diff --git a/libnl/lib/route/pktloc_syntax.y b/libnl/lib/route/pktloc_syntax.y new file mode 100644 index 0000000..25d8710 --- /dev/null +++ b/libnl/lib/route/pktloc_syntax.y @@ -0,0 +1,103 @@ +%{ +#include +#include +#include +#include +#include +%} + +%locations +%error-verbose +%define api.pure +%name-prefix "pktloc_" + +%parse-param {void *scanner} +%lex-param {void *scanner} +%expect 1 + +%union { + struct rtnl_pktloc *l; + uint32_t i; + char *s; +} + +%{ +extern int pktloc_lex(YYSTYPE *, YYLTYPE *, void *); + +static void yyerror(YYLTYPE *locp, void *scanner, const char *msg) +{ + NL_DBG(1, "Error while parsing packet location file: %s\n", msg); +} +%} + +%token ERROR NUMBER LAYER ALIGN +%token NAME + +%type mask layer align shift +%type location + +%destructor { free($$); } NAME + +%start input + +%% + +input: + /* empty */ + | location input + ; + +location: + NAME align layer NUMBER mask shift + { + struct rtnl_pktloc *loc; + + if (!(loc = rtnl_pktloc_alloc())) { + NL_DBG(1, "Allocating a packet location " + "object failed.\n"); + YYABORT; + } + + loc->name = $1; + loc->align = $2; + loc->layer = $3; + loc->offset = $4; + loc->mask = $5; + loc->shift = $6; + + if (rtnl_pktloc_add(loc) < 0) { + NL_DBG(1, "Duplicate packet location entry " + "\"%s\"\n", $1); + } + + $$ = loc; + } + ; + +align: + ALIGN + { $$ = $1; } + | NUMBER + { $$ = $1; } + ; + +layer: + /* empty */ + { $$ = TCF_LAYER_NETWORK; } + | LAYER '+' + { $$ = $1; } + ; + +mask: + /* empty */ + { $$ = 0; } + | NUMBER + { $$ = $1; } + ; + +shift: + /* empty */ + { $$ = 0; } + | NUMBER + { $$ = $1; } + ; diff --git a/libnl/lib/route/qdisc.c b/libnl/lib/route/qdisc.c new file mode 100644 index 0000000..8cdce54 --- /dev/null +++ b/libnl/lib/route/qdisc.c @@ -0,0 +1,572 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +/** + * @ingroup tc + * @defgroup qdisc Queueing Disciplines + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct nl_cache_ops rtnl_qdisc_ops; +static struct nl_object_ops qdisc_obj_ops; + +static int qdisc_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *n, struct nl_parser_param *pp) +{ + struct rtnl_qdisc *qdisc; + int err; + + if (!(qdisc = rtnl_qdisc_alloc())) + return -NLE_NOMEM; + + if ((err = rtnl_tc_msg_parse(n, TC_CAST(qdisc))) < 0) + goto errout; + + err = pp->pp_cb(OBJ_CAST(qdisc), pp); +errout: + rtnl_qdisc_put(qdisc); + + return err; +} + +static int qdisc_request_update(struct nl_cache *c, struct nl_sock *sk) +{ + struct tcmsg tchdr = { + .tcm_family = AF_UNSPEC, + .tcm_ifindex = c->c_iarg1, + }; + + return nl_send_simple(sk, RTM_GETQDISC, NLM_F_DUMP, &tchdr, + sizeof(tchdr)); +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct rtnl_qdisc *rtnl_qdisc_alloc(void) +{ + struct rtnl_tc *tc; + + tc = TC_CAST(nl_object_alloc(&qdisc_obj_ops)); + if (tc) + tc->tc_type = RTNL_TC_TYPE_QDISC; + + return (struct rtnl_qdisc *) tc; +} + +void rtnl_qdisc_put(struct rtnl_qdisc *qdisc) +{ + nl_object_put((struct nl_object *) qdisc); +} + +/** @} */ + +/** + * @name Addition / Modification / Deletion + * @{ + */ + +static int build_qdisc_msg(struct rtnl_qdisc *qdisc, int type, int flags, + struct nl_msg **result) +{ + if (!(qdisc->ce_mask & TCA_ATTR_IFINDEX)) { + APPBUG("ifindex must be specified"); + return -NLE_MISSING_ATTR; + } + + return rtnl_tc_msg_build(TC_CAST(qdisc), type, flags, result); +} + +/** + * Build a netlink message requesting the addition of a qdisc + * @arg qdisc Qdisc to add + * @arg flags Additional netlink message flags + * @arg result Pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_qdisc_add() with + * the exception that it will not send the message but return it int the + * provided return pointer instead. + * + * @see rtnl_qdisc_add() + * + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc, int flags, + struct nl_msg **result) +{ + if (!(qdisc->ce_mask & (TCA_ATTR_HANDLE | TCA_ATTR_PARENT))) { + APPBUG("handle or parent must be specified"); + return -NLE_MISSING_ATTR; + } + + return build_qdisc_msg(qdisc, RTM_NEWQDISC, flags, result); +} + +/** + * Add qdisc + * @arg sk Netlink socket + * @arg qdisc Qdisc to add + * @arg flags Additional netlink message flags + * + * Builds a \c RTM_NEWQDISC netlink message requesting the addition + * of a new qdisc and sends the message to the kernel. The configuration + * of the qdisc is derived from the attributes of the specified qdisc. + * + * The following flags may be specified: + * - \c NLM_F_CREATE: Create qdisc if it does not exist, otherwise + * -NLE_OBJ_NOTFOUND is returned. + * - \c NLM_F_REPLACE: If another qdisc is already attached to the + * parent, replace it even if the handles mismatch. + * - \c NLM_F_EXCL: Return -NLE_EXISTS if a qdisc with matching + * handle exists already. + * + * Existing qdiscs with matching handles will be updated, unless the + * flag \c NLM_F_EXCL is specified. If their handles do not match, the + * error -NLE_EXISTS is returned unless the flag \c NLM_F_REPLACE is + * specified in which case the existing qdisc is replaced with the new + * one. If no matching qdisc exists, it will be created if the flag + * \c NLM_F_CREATE is set, otherwise the error -NLE_OBJ_NOTFOUND is + * returned. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause + * this function to return immediately after sending. In this case, + * it is the responsibility of the caller to handle any error + * messages returned. + * + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_add(struct nl_sock *sk, struct rtnl_qdisc *qdisc, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_qdisc_build_add_request(qdisc, flags, &msg)) < 0) + return err; + + return nl_send_sync(sk, msg); +} + +/** + * Build netlink message requesting the update of a qdisc + * @arg qdisc Qdisc to update + * @arg new Qdisc with updated attributes + * @arg flags Additional netlink message flags + * @arg result Pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_qdisc_update() with + * the exception that it will not send the message but return it in the + * provided return pointer instead. + * + * @see rtnl_qdisc_update() + * + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_build_update_request(struct rtnl_qdisc *qdisc, + struct rtnl_qdisc *new, int flags, + struct nl_msg **result) +{ + if (flags & (NLM_F_CREATE | NLM_F_EXCL)) { + APPBUG("NLM_F_CREATE and NLM_F_EXCL may not be used here, " + "use rtnl_qdisc_add()"); + return -NLE_INVAL; + } + + if (!(qdisc->ce_mask & TCA_ATTR_IFINDEX)) { + APPBUG("ifindex must be specified"); + return -NLE_MISSING_ATTR; + } + + if (!(qdisc->ce_mask & (TCA_ATTR_HANDLE | TCA_ATTR_PARENT))) { + APPBUG("handle or parent must be specified"); + return -NLE_MISSING_ATTR; + } + + rtnl_tc_set_ifindex(TC_CAST(new), qdisc->q_ifindex); + + if (qdisc->ce_mask & TCA_ATTR_HANDLE) + rtnl_tc_set_handle(TC_CAST(new), qdisc->q_handle); + + if (qdisc->ce_mask & TCA_ATTR_PARENT) + rtnl_tc_set_parent(TC_CAST(new), qdisc->q_parent); + + return build_qdisc_msg(new, RTM_NEWQDISC, flags, result); +} + +/** + * Update qdisc + * @arg sk Netlink socket + * @arg qdisc Qdisc to update + * @arg new Qdisc with updated attributes + * @arg flags Additional netlink message flags + * + * Builds a \c RTM_NEWQDISC netlink message requesting the update + * of an existing qdisc and sends the message to the kernel. + * + * This function is a varation of rtnl_qdisc_add() to update qdiscs + * if the qdisc to be updated is available as qdisc object. The + * behaviour is identical to the one of rtnl_qdisc_add except that + * before constructing the message, it copies the \c ifindex, + * \c handle, and \c parent from the original \p qdisc to the \p new + * qdisc. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause + * this function to return immediately after sending. In this case, + * it is the responsibility of the caller to handle any error + * messages returned. + * + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_update(struct nl_sock *sk, struct rtnl_qdisc *qdisc, + struct rtnl_qdisc *new, int flags) +{ + struct nl_msg *msg; + int err; + + err = rtnl_qdisc_build_update_request(qdisc, new, flags, &msg); + if (err < 0) + return err; + + return nl_send_sync(sk, msg); +} + +/** + * Build netlink message requesting the deletion of a qdisc + * @arg qdisc Qdisc to delete + * @arg result Pointer to store resulting netlink message + * + * The behaviour of this function is identical to rtnl_qdisc_delete() with + * the exception that it will not send the message but return it in the + * provided return pointer instead. + * + * @see rtnl_qdisc_delete() + * + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc, + struct nl_msg **result) +{ + struct nl_msg *msg; + struct tcmsg tchdr; + uint32_t required = TCA_ATTR_IFINDEX | TCA_ATTR_PARENT; + + if ((qdisc->ce_mask & required) != required) { + APPBUG("ifindex and parent must be specified"); + return -NLE_MISSING_ATTR; + } + + if (!(msg = nlmsg_alloc_simple(RTM_DELQDISC, 0))) + return -NLE_NOMEM; + + memset(&tchdr, 0, sizeof(tchdr)); + + tchdr.tcm_family = AF_UNSPEC; + tchdr.tcm_ifindex = qdisc->q_ifindex; + tchdr.tcm_parent = qdisc->q_parent; + + if (qdisc->ce_mask & TCA_ATTR_HANDLE) + tchdr.tcm_handle = qdisc->q_handle; + + if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + if (qdisc->ce_mask & TCA_ATTR_KIND) + NLA_PUT_STRING(msg, TCA_KIND, qdisc->q_kind); + + *result = msg; + return 0; + +nla_put_failure: + nlmsg_free(msg); + return -NLE_MSGSIZE; +} + +/** + * Delete qdisc + * @arg sk Netlink socket + * @arg qdisc Qdisc to add + * + * Builds a \c RTM_NEWQDISC netlink message requesting the deletion + * of a qdisc and sends the message to the kernel. + * + * The message is constructed out of the following attributes: + * - \c ifindex and \c parent + * - \c handle (optional, must match if provided) + * - \c kind (optional, must match if provided) + * + * All other qdisc attributes including all qdisc type specific + * attributes are ignored. + * + * After sending, the function will wait for the ACK or an eventual + * error message to be received and will therefore block until the + * operation has been completed. + * + * @note It is not possible to delete default qdiscs. + * + * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause + * this function to return immediately after sending. In this case, + * it is the responsibility of the caller to handle any error + * messages returned. + * + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_delete(struct nl_sock *sk, struct rtnl_qdisc *qdisc) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_qdisc_build_delete_request(qdisc, &msg)) < 0) + return err; + + return nl_send_sync(sk, msg); +} + +/** @} */ + +/** + * @name Cache Related Functions + * @{ + */ + +/** + * Allocate a cache and fill it with all configured qdiscs + * @arg sk Netlink socket + * @arg result Pointer to store the created cache + * + * Allocates a new qdisc cache and fills it with a list of all configured + * qdiscs on all network devices. Release the cache with nl_cache_free(). + * + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_alloc_cache(struct nl_sock *sk, struct nl_cache **result) +{ + return nl_cache_alloc_and_fill(&rtnl_qdisc_ops, sk, result); +} + +/** + * Search qdisc by interface index and parent + * @arg cache Qdisc cache + * @arg ifindex Interface index + * @arg parent Handle of parent qdisc + * + * Searches a qdisc cache previously allocated with rtnl_qdisc_alloc_cache() + * and searches for a qdisc matching the interface index and parent qdisc. + * + * The reference counter is incremented before returning the qdisc, therefore + * the reference must be given back with rtnl_qdisc_put() after usage. + * + * @return pointer to qdisc inside the cache or NULL if no match was found. + */ +struct rtnl_qdisc *rtnl_qdisc_get_by_parent(struct nl_cache *cache, + int ifindex, uint32_t parent) +{ + struct rtnl_qdisc *q; + + if (cache->c_ops != &rtnl_qdisc_ops) + return NULL; + + nl_list_for_each_entry(q, &cache->c_items, ce_list) { + if (q->q_parent == parent && q->q_ifindex == ifindex) { + nl_object_get((struct nl_object *) q); + return q; + } + } + + return NULL; +} + +/** + * Search qdisc by interface index and handle + * @arg cache Qdisc cache + * @arg ifindex Interface index + * @arg handle Handle + * + * Searches a qdisc cache previously allocated with rtnl_qdisc_alloc_cache() + * and searches for a qdisc matching the interface index and handle. + * + * The reference counter is incremented before returning the qdisc, therefore + * the reference must be given back with rtnl_qdisc_put() after usage. + * + * @return Qdisc or NULL if no match was found. + */ +struct rtnl_qdisc *rtnl_qdisc_get(struct nl_cache *cache, int ifindex, + uint32_t handle) +{ + struct rtnl_qdisc *q; + + if (cache->c_ops != &rtnl_qdisc_ops) + return NULL; + + nl_list_for_each_entry(q, &cache->c_items, ce_list) { + if (q->q_handle == handle && q->q_ifindex == ifindex) { + nl_object_get((struct nl_object *) q); + return q; + } + } + + return NULL; +} + +/** @} */ + +/** + * @name Deprecated Functions + * @{ + */ + +/** + * Call a callback for each child class of a qdisc (deprecated) + * + * @deprecated Use of this function is deprecated, it does not allow + * to handle the out of memory situation that can occur. + */ +void rtnl_qdisc_foreach_child(struct rtnl_qdisc *qdisc, struct nl_cache *cache, + void (*cb)(struct nl_object *, void *), void *arg) +{ + struct rtnl_class *filter; + + filter = rtnl_class_alloc(); + if (!filter) + return; + + rtnl_tc_set_parent(TC_CAST(filter), qdisc->q_handle); + rtnl_tc_set_ifindex(TC_CAST(filter), qdisc->q_ifindex); + rtnl_tc_set_kind(TC_CAST(filter), qdisc->q_kind); + + nl_cache_foreach_filter(cache, OBJ_CAST(filter), cb, arg); + + rtnl_class_put(filter); +} + +/** + * Call a callback for each filter attached to the qdisc (deprecated) + * + * @deprecated Use of this function is deprecated, it does not allow + * to handle the out of memory situation that can occur. + */ +void rtnl_qdisc_foreach_cls(struct rtnl_qdisc *qdisc, struct nl_cache *cache, + void (*cb)(struct nl_object *, void *), void *arg) +{ + struct rtnl_cls *filter; + + if (!(filter = rtnl_cls_alloc())) + return; + + rtnl_tc_set_ifindex(TC_CAST(filter), qdisc->q_ifindex); + rtnl_tc_set_parent(TC_CAST(filter), qdisc->q_parent); + + nl_cache_foreach_filter(cache, OBJ_CAST(filter), cb, arg); + rtnl_cls_put(filter); +} + +/** + * Build a netlink message requesting the update of a qdisc + * + * @deprecated Use of this function is deprecated in favour of + * rtnl_qdisc_build_update_request() due to the missing + * possibility of specifying additional flags. + */ +int rtnl_qdisc_build_change_request(struct rtnl_qdisc *qdisc, + struct rtnl_qdisc *new, + struct nl_msg **result) +{ + return rtnl_qdisc_build_update_request(qdisc, new, NLM_F_REPLACE, + result); +} + +/** + * Change attributes of a qdisc + * + * @deprecated Use of this function is deprecated in favour of + * rtnl_qdisc_update() due to the missing possibility of + * specifying additional flags. + */ +int rtnl_qdisc_change(struct nl_sock *sk, struct rtnl_qdisc *qdisc, + struct rtnl_qdisc *new) +{ + return rtnl_qdisc_update(sk, qdisc, new, NLM_F_REPLACE); +} + +/** @} */ + +static void qdisc_dump_details(struct rtnl_tc *tc, struct nl_dump_params *p) +{ + struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) tc; + + nl_dump(p, "refcnt %u", qdisc->q_info); +} + +static struct rtnl_tc_type_ops qdisc_ops = { + .tt_type = RTNL_TC_TYPE_QDISC, + .tt_dump_prefix = "qdisc", + .tt_dump = { + [NL_DUMP_DETAILS] = qdisc_dump_details, + }, +}; + +static struct nl_cache_ops rtnl_qdisc_ops = { + .co_name = "route/qdisc", + .co_hdrsize = sizeof(struct tcmsg), + .co_msgtypes = { + { RTM_NEWQDISC, NL_ACT_NEW, "new" }, + { RTM_DELQDISC, NL_ACT_DEL, "del" }, + { RTM_GETQDISC, NL_ACT_GET, "get" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_ROUTE, + .co_groups = tc_groups, + .co_request_update = qdisc_request_update, + .co_msg_parser = qdisc_msg_parser, + .co_obj_ops = &qdisc_obj_ops, +}; + +static struct nl_object_ops qdisc_obj_ops = { + .oo_name = "route/qdisc", + .oo_size = sizeof(struct rtnl_qdisc), + .oo_free_data = rtnl_tc_free_data, + .oo_clone = rtnl_tc_clone, + .oo_dump = { + [NL_DUMP_LINE] = rtnl_tc_dump_line, + [NL_DUMP_DETAILS] = rtnl_tc_dump_details, + [NL_DUMP_STATS] = rtnl_tc_dump_stats, + }, + .oo_compare = rtnl_tc_compare, + .oo_id_attrs = (TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE), +}; + +static void __init qdisc_init(void) +{ + rtnl_tc_type_register(&qdisc_ops); + nl_cache_mngt_register(&rtnl_qdisc_ops); +} + +static void __exit qdisc_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_qdisc_ops); + rtnl_tc_type_unregister(&qdisc_ops); +} + +/** @} */ diff --git a/libnl/lib/route/qdisc/blackhole.c b/libnl/lib/route/qdisc/blackhole.c new file mode 100644 index 0000000..3613f6b --- /dev/null +++ b/libnl/lib/route/qdisc/blackhole.c @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +/** + * @ingroup qdisc + * @defgroup qdisc_blackhole Blackhole + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include + +static struct rtnl_tc_ops blackhole_ops = { + .to_kind = "blackhole", + .to_type = RTNL_TC_TYPE_QDISC, +}; + +static void __init blackhole_init(void) +{ + rtnl_tc_register(&blackhole_ops); +} + +static void __exit blackhole_exit(void) +{ + rtnl_tc_unregister(&blackhole_ops); +} + +/** @} */ diff --git a/libnl/lib/route/qdisc/cbq.c b/libnl/lib/route/qdisc/cbq.c new file mode 100644 index 0000000..44682e7 --- /dev/null +++ b/libnl/lib/route/qdisc/cbq.c @@ -0,0 +1,200 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @ingroup qdisc + * @ingroup class + * @defgroup qdisc_cbq Class Based Queueing (CBQ) + * @{ + */ + +static const struct trans_tbl ovl_strategies[] = { + __ADD(TC_CBQ_OVL_CLASSIC,classic), + __ADD(TC_CBQ_OVL_DELAY,delay), + __ADD(TC_CBQ_OVL_LOWPRIO,lowprio), + __ADD(TC_CBQ_OVL_DROP,drop), + __ADD(TC_CBQ_OVL_RCLASSIC,rclassic), +}; + +/** + * Convert a CBQ OVL strategy to a character string + * @arg type CBQ OVL strategy + * @arg buf destination buffer + * @arg len length of destination buffer + * + * Converts a CBQ OVL strategy to a character string and stores in the + * provided buffer. Returns the destination buffer or the type + * encoded in hex if no match was found. + */ +char *nl_ovl_strategy2str(int type, char *buf, size_t len) +{ + return __type2str(type, buf, len, ovl_strategies, + ARRAY_SIZE(ovl_strategies)); +} + +/** + * Convert a string to a CBQ OVL strategy + * @arg name CBQ OVL stragegy name + * + * Converts a CBQ OVL stragegy name to it's corresponding CBQ OVL strategy + * type. Returns the type or -1 if none was found. + */ +int nl_str2ovl_strategy(const char *name) +{ + return __str2type(name, ovl_strategies, ARRAY_SIZE(ovl_strategies)); +} + +static struct nla_policy cbq_policy[TCA_CBQ_MAX+1] = { + [TCA_CBQ_LSSOPT] = { .minlen = sizeof(struct tc_cbq_lssopt) }, + [TCA_CBQ_RATE] = { .minlen = sizeof(struct tc_ratespec) }, + [TCA_CBQ_WRROPT] = { .minlen = sizeof(struct tc_cbq_wrropt) }, + [TCA_CBQ_OVL_STRATEGY] = { .minlen = sizeof(struct tc_cbq_ovl) }, + [TCA_CBQ_FOPT] = { .minlen = sizeof(struct tc_cbq_fopt) }, + [TCA_CBQ_POLICE] = { .minlen = sizeof(struct tc_cbq_police) }, +}; + +static int cbq_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct nlattr *tb[TCA_CBQ_MAX + 1]; + struct rtnl_cbq *cbq = data; + int err; + + err = tca_parse(tb, TCA_CBQ_MAX, tc, cbq_policy); + if (err < 0) + return err; + + nla_memcpy(&cbq->cbq_lss, tb[TCA_CBQ_LSSOPT], sizeof(cbq->cbq_lss)); + nla_memcpy(&cbq->cbq_rate, tb[TCA_CBQ_RATE], sizeof(cbq->cbq_rate)); + nla_memcpy(&cbq->cbq_wrr, tb[TCA_CBQ_WRROPT], sizeof(cbq->cbq_wrr)); + nla_memcpy(&cbq->cbq_fopt, tb[TCA_CBQ_FOPT], sizeof(cbq->cbq_fopt)); + nla_memcpy(&cbq->cbq_ovl, tb[TCA_CBQ_OVL_STRATEGY], + sizeof(cbq->cbq_ovl)); + nla_memcpy(&cbq->cbq_police, tb[TCA_CBQ_POLICE], + sizeof(cbq->cbq_police)); + + return 0; +} + +static void cbq_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_cbq *cbq = data; + double r, rbit; + char *ru, *rubit; + + if (!cbq) + return; + + r = nl_cancel_down_bytes(cbq->cbq_rate.rate, &ru); + rbit = nl_cancel_down_bits(cbq->cbq_rate.rate * 8, &rubit); + + nl_dump(p, " rate %.2f%s/s (%.0f%s) prio %u", + r, ru, rbit, rubit, cbq->cbq_wrr.priority); +} + +static void cbq_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_cbq *cbq = data; + char *unit, buf[32]; + double w; + uint32_t el; + + if (!cbq) + return; + + w = nl_cancel_down_bits(cbq->cbq_wrr.weight * 8, &unit); + + nl_dump(p, "avgpkt %u mpu %u cell %u allot %u weight %.0f%s\n", + cbq->cbq_lss.avpkt, + cbq->cbq_rate.mpu, + 1 << cbq->cbq_rate.cell_log, + cbq->cbq_wrr.allot, w, unit); + + el = cbq->cbq_lss.ewma_log; + nl_dump_line(p, " minidle %uus maxidle %uus offtime " + "%uus level %u ewma_log %u\n", + nl_ticks2us(cbq->cbq_lss.minidle >> el), + nl_ticks2us(cbq->cbq_lss.maxidle >> el), + nl_ticks2us(cbq->cbq_lss.offtime >> el), + cbq->cbq_lss.level, + cbq->cbq_lss.ewma_log); + + nl_dump_line(p, " penalty %uus strategy %s ", + nl_ticks2us(cbq->cbq_ovl.penalty), + nl_ovl_strategy2str(cbq->cbq_ovl.strategy, buf, sizeof(buf))); + + nl_dump(p, "split %s defmap 0x%08x ", + rtnl_tc_handle2str(cbq->cbq_fopt.split, buf, sizeof(buf)), + cbq->cbq_fopt.defmap); + + nl_dump(p, "police %s", + nl_police2str(cbq->cbq_police.police, buf, sizeof(buf))); +} + +static void cbq_dump_stats(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct tc_cbq_xstats *x; + + if (!(x = tca_xstats(tc))) + return; + + nl_dump_line(p, " borrows overact " + " avgidle undertime\n"); + nl_dump_line(p, " %10u %10u %10u %10u\n", + x->borrows, x->overactions, x->avgidle, x->undertime); +} + +static struct rtnl_tc_ops cbq_qdisc_ops = { + .to_kind = "cbq", + .to_type = RTNL_TC_TYPE_QDISC, + .to_size = sizeof(struct rtnl_cbq), + .to_msg_parser = cbq_msg_parser, + .to_dump = { + [NL_DUMP_LINE] = cbq_dump_line, + [NL_DUMP_DETAILS] = cbq_dump_details, + [NL_DUMP_STATS] = cbq_dump_stats, + }, +}; + +static struct rtnl_tc_ops cbq_class_ops = { + .to_kind = "cbq", + .to_type = RTNL_TC_TYPE_CLASS, + .to_size = sizeof(struct rtnl_cbq), + .to_msg_parser = cbq_msg_parser, + .to_dump = { + [NL_DUMP_LINE] = cbq_dump_line, + [NL_DUMP_DETAILS] = cbq_dump_details, + [NL_DUMP_STATS] = cbq_dump_stats, + }, +}; + +static void __init cbq_init(void) +{ + rtnl_tc_register(&cbq_qdisc_ops); + rtnl_tc_register(&cbq_class_ops); +} + +static void __exit cbq_exit(void) +{ + rtnl_tc_unregister(&cbq_qdisc_ops); + rtnl_tc_unregister(&cbq_class_ops); +} + +/** @} */ diff --git a/libnl/lib/route/qdisc/dsmark.c b/libnl/lib/route/qdisc/dsmark.c new file mode 100644 index 0000000..1fa446e --- /dev/null +++ b/libnl/lib/route/qdisc/dsmark.c @@ -0,0 +1,409 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +/** + * @ingroup qdisc + * @ingroup class + * @defgroup qdisc_dsmark Differentiated Services Marker (DSMARK) + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define SCH_DSMARK_ATTR_INDICES 0x1 +#define SCH_DSMARK_ATTR_DEFAULT_INDEX 0x2 +#define SCH_DSMARK_ATTR_SET_TC_INDEX 0x4 + +#define SCH_DSMARK_ATTR_MASK 0x1 +#define SCH_DSMARK_ATTR_VALUE 0x2 +/** @endcond */ + +static struct nla_policy dsmark_policy[TCA_DSMARK_MAX+1] = { + [TCA_DSMARK_INDICES] = { .type = NLA_U16 }, + [TCA_DSMARK_DEFAULT_INDEX] = { .type = NLA_U16 }, + [TCA_DSMARK_SET_TC_INDEX] = { .type = NLA_FLAG }, + [TCA_DSMARK_VALUE] = { .type = NLA_U8 }, + [TCA_DSMARK_MASK] = { .type = NLA_U8 }, +}; + +static int dsmark_qdisc_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct rtnl_dsmark_qdisc *dsmark = data; + struct nlattr *tb[TCA_DSMARK_MAX + 1]; + int err; + + err = tca_parse(tb, TCA_DSMARK_MAX, tc, dsmark_policy); + if (err < 0) + return err; + + if (tb[TCA_DSMARK_INDICES]) { + dsmark->qdm_indices = nla_get_u16(tb[TCA_DSMARK_INDICES]); + dsmark->qdm_mask |= SCH_DSMARK_ATTR_INDICES; + } + + if (tb[TCA_DSMARK_DEFAULT_INDEX]) { + dsmark->qdm_default_index = + nla_get_u16(tb[TCA_DSMARK_DEFAULT_INDEX]); + dsmark->qdm_mask |= SCH_DSMARK_ATTR_DEFAULT_INDEX; + } + + if (tb[TCA_DSMARK_SET_TC_INDEX]) { + dsmark->qdm_set_tc_index = 1; + dsmark->qdm_mask |= SCH_DSMARK_ATTR_SET_TC_INDEX; + } + + return 0; +} + +static int dsmark_class_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct rtnl_dsmark_class *dsmark = data; + struct nlattr *tb[TCA_DSMARK_MAX + 1]; + int err; + + err = tca_parse(tb, TCA_DSMARK_MAX, tc, dsmark_policy); + if (err < 0) + return err; + + if (tb[TCA_DSMARK_MASK]) { + dsmark->cdm_bmask = nla_get_u8(tb[TCA_DSMARK_MASK]); + dsmark->cdm_mask |= SCH_DSMARK_ATTR_MASK; + } + + if (tb[TCA_DSMARK_VALUE]) { + dsmark->cdm_value = nla_get_u8(tb[TCA_DSMARK_VALUE]); + dsmark->cdm_mask |= SCH_DSMARK_ATTR_VALUE; + } + + return 0; +} + +static void dsmark_qdisc_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_dsmark_qdisc *dsmark = data; + + if (dsmark && (dsmark->qdm_mask & SCH_DSMARK_ATTR_INDICES)) + nl_dump(p, " indices 0x%04x", dsmark->qdm_indices); +} + +static void dsmark_qdisc_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_dsmark_qdisc *dsmark = data; + + if (!dsmark) + return; + + if (dsmark->qdm_mask & SCH_DSMARK_ATTR_DEFAULT_INDEX) + nl_dump(p, " default index 0x%04x", dsmark->qdm_default_index); + + if (dsmark->qdm_mask & SCH_DSMARK_ATTR_SET_TC_INDEX) + nl_dump(p, " set-tc-index"); +} + +static void dsmark_class_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_dsmark_class *dsmark = data; + + if (!dsmark) + return; + + if (dsmark->cdm_mask & SCH_DSMARK_ATTR_VALUE) + nl_dump(p, " value 0x%02x", dsmark->cdm_value); + + if (dsmark->cdm_mask & SCH_DSMARK_ATTR_MASK) + nl_dump(p, " mask 0x%02x", dsmark->cdm_bmask); +} + +static int dsmark_qdisc_msg_fill(struct rtnl_tc *tc, void *data, + struct nl_msg *msg) +{ + struct rtnl_dsmark_qdisc *dsmark = data; + + if (!dsmark) + return 0; + + if (dsmark->qdm_mask & SCH_DSMARK_ATTR_INDICES) + NLA_PUT_U16(msg, TCA_DSMARK_INDICES, dsmark->qdm_indices); + + if (dsmark->qdm_mask & SCH_DSMARK_ATTR_DEFAULT_INDEX) + NLA_PUT_U16(msg, TCA_DSMARK_DEFAULT_INDEX, + dsmark->qdm_default_index); + + if (dsmark->qdm_mask & SCH_DSMARK_ATTR_SET_TC_INDEX) + NLA_PUT_FLAG(msg, TCA_DSMARK_SET_TC_INDEX); + + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +static int dsmark_class_msg_fill(struct rtnl_tc *tc, void *data, + struct nl_msg *msg) +{ + struct rtnl_dsmark_class *dsmark = data; + + if (!dsmark) + return 0; + + if (dsmark->cdm_mask & SCH_DSMARK_ATTR_MASK) + NLA_PUT_U8(msg, TCA_DSMARK_MASK, dsmark->cdm_bmask); + + if (dsmark->cdm_mask & SCH_DSMARK_ATTR_VALUE) + NLA_PUT_U8(msg, TCA_DSMARK_VALUE, dsmark->cdm_value); + + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +/** + * @name Class Attribute Access + * @{ + */ + +/** + * Set bitmask of DSMARK class. + * @arg class DSMARK class to be modified. + * @arg mask New bitmask. + * @return 0 on success or a negative error code. + */ +int rtnl_class_dsmark_set_bitmask(struct rtnl_class *class, uint8_t mask) +{ + struct rtnl_dsmark_class *dsmark; + + if (!(dsmark = rtnl_tc_data(TC_CAST(class)))) + return -NLE_NOMEM; + + dsmark->cdm_bmask = mask; + dsmark->cdm_mask |= SCH_DSMARK_ATTR_MASK; + + return 0; +} + +/** + * Get bitmask of DSMARK class. + * @arg class DSMARK class. + * @return Bitmask or a negative error code. + */ +int rtnl_class_dsmark_get_bitmask(struct rtnl_class *class) +{ + struct rtnl_dsmark_class *dsmark; + + if (!(dsmark = rtnl_tc_data(TC_CAST(class)))) + return -NLE_NOMEM; + + if (dsmark->cdm_mask & SCH_DSMARK_ATTR_MASK) + return dsmark->cdm_bmask; + else + return -NLE_NOATTR; +} + +/** + * Set value of DSMARK class. + * @arg class DSMARK class to be modified. + * @arg value New value. + * @return 0 on success or a negative errror code. + */ +int rtnl_class_dsmark_set_value(struct rtnl_class *class, uint8_t value) +{ + struct rtnl_dsmark_class *dsmark; + + if (!(dsmark = rtnl_tc_data(TC_CAST(class)))) + return -NLE_NOMEM; + + dsmark->cdm_value = value; + dsmark->cdm_mask |= SCH_DSMARK_ATTR_VALUE; + + return 0; +} + +/** + * Get value of DSMARK class. + * @arg class DSMARK class. + * @return Value or a negative error code. + */ +int rtnl_class_dsmark_get_value(struct rtnl_class *class) +{ + struct rtnl_dsmark_class *dsmark; + + if (!(dsmark = rtnl_tc_data(TC_CAST(class)))) + return -NLE_NOMEM; + + if (dsmark->cdm_mask & SCH_DSMARK_ATTR_VALUE) + return dsmark->cdm_value; + else + return -NLE_NOATTR; +} + +/** @} */ + +/** + * @name Qdisc Attribute Access + * @{ + */ + +/** + * Set indices of DSMARK qdisc. + * @arg qdisc DSMARK qdisc to be modified. + * @arg indices New indices. + */ +int rtnl_qdisc_dsmark_set_indices(struct rtnl_qdisc *qdisc, uint16_t indices) +{ + struct rtnl_dsmark_qdisc *dsmark; + + if (!(dsmark = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + dsmark->qdm_indices = indices; + dsmark->qdm_mask |= SCH_DSMARK_ATTR_INDICES; + + return 0; +} + +/** + * Get indices of DSMARK qdisc. + * @arg qdisc DSMARK qdisc. + * @return Indices or a negative error code. + */ +int rtnl_qdisc_dsmark_get_indices(struct rtnl_qdisc *qdisc) +{ + struct rtnl_dsmark_qdisc *dsmark; + + if (!(dsmark = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + if (dsmark->qdm_mask & SCH_DSMARK_ATTR_INDICES) + return dsmark->qdm_indices; + else + return -NLE_NOATTR; +} + +/** + * Set default index of DSMARK qdisc. + * @arg qdisc DSMARK qdisc to be modified. + * @arg default_index New default index. + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_dsmark_set_default_index(struct rtnl_qdisc *qdisc, + uint16_t default_index) +{ + struct rtnl_dsmark_qdisc *dsmark; + + if (!(dsmark = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + dsmark->qdm_default_index = default_index; + dsmark->qdm_mask |= SCH_DSMARK_ATTR_DEFAULT_INDEX; + + return 0; +} + +/** + * Get default index of DSMARK qdisc. + * @arg qdisc DSMARK qdisc. + * @return Default index or a negative error code. + */ +int rtnl_qdisc_dsmark_get_default_index(struct rtnl_qdisc *qdisc) +{ + struct rtnl_dsmark_qdisc *dsmark; + + if (!(dsmark = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + if (dsmark->qdm_mask & SCH_DSMARK_ATTR_DEFAULT_INDEX) + return dsmark->qdm_default_index; + else + return -NLE_NOATTR; +} + +/** + * Set set-tc-index flag of DSMARK qdisc. + * @arg qdisc DSMARK qdisc to be modified. + * @arg flag Flag indicating whether to enable or disable. + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_dsmark_set_set_tc_index(struct rtnl_qdisc *qdisc, int flag) +{ + struct rtnl_dsmark_qdisc *dsmark; + + if (!(dsmark = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + dsmark->qdm_set_tc_index = !!flag; + dsmark->qdm_mask |= SCH_DSMARK_ATTR_SET_TC_INDEX; + + return 0; +} + +/** + * Get set-tc-index flag of DSMARK qdisc. + * @arg qdisc DSMARK qdisc to be modified. + * @return 1 or 0 to indicate wehther the flag is enabled or a negative + * error code. + */ +int rtnl_qdisc_dsmark_get_set_tc_index(struct rtnl_qdisc *qdisc) +{ + struct rtnl_dsmark_qdisc *dsmark; + + if (!(dsmark = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + if (dsmark->qdm_mask & SCH_DSMARK_ATTR_SET_TC_INDEX) + return dsmark->qdm_set_tc_index; + else + return -NLE_NOATTR; +} + +/** @} */ + +static struct rtnl_tc_ops dsmark_qdisc_ops = { + .to_kind = "dsmark", + .to_type = RTNL_TC_TYPE_QDISC, + .to_size = sizeof(struct rtnl_dsmark_qdisc), + .to_msg_parser = dsmark_qdisc_msg_parser, + .to_dump = { + [NL_DUMP_LINE] = dsmark_qdisc_dump_line, + [NL_DUMP_DETAILS] = dsmark_qdisc_dump_details, + }, + .to_msg_fill = dsmark_qdisc_msg_fill, +}; + +static struct rtnl_tc_ops dsmark_class_ops = { + .to_kind = "dsmark", + .to_type = RTNL_TC_TYPE_CLASS, + .to_size = sizeof(struct rtnl_dsmark_class), + .to_msg_parser = dsmark_class_msg_parser, + .to_dump[NL_DUMP_LINE] = dsmark_class_dump_line, + .to_msg_fill = dsmark_class_msg_fill, +}; + +static void __init dsmark_init(void) +{ + rtnl_tc_register(&dsmark_qdisc_ops); + rtnl_tc_register(&dsmark_class_ops); +} + +static void __exit dsmark_exit(void) +{ + rtnl_tc_unregister(&dsmark_qdisc_ops); + rtnl_tc_unregister(&dsmark_class_ops); +} + +/** @} */ diff --git a/libnl/lib/route/qdisc/fifo.c b/libnl/lib/route/qdisc/fifo.c new file mode 100644 index 0000000..a9b3a0e --- /dev/null +++ b/libnl/lib/route/qdisc/fifo.c @@ -0,0 +1,165 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +/** + * @ingroup qdisc + * @defgroup qdisc_fifo Packet/Bytes FIFO (pfifo/bfifo) + * @brief + * + * The FIFO qdisc comes in two flavours: + * @par bfifo (Byte FIFO) + * Allows enqueuing until the currently queued volume in bytes exceeds + * the configured limit.backlog contains currently enqueued volume in bytes. + * + * @par pfifo (Packet FIFO) + * Allows enquueing until the currently queued number of packets + * exceeds the configured limit. + * + * The configuration is exactly the same, the decision which of + * the two variations is going to be used is made based on the + * kind of the qdisc (rtnl_tc_set_kind()). + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define SCH_FIFO_ATTR_LIMIT 1 +/** @endcond */ + +static int fifo_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct rtnl_fifo *fifo = data; + struct tc_fifo_qopt *opt; + + if (tc->tc_opts->d_size < sizeof(struct tc_fifo_qopt)) + return -NLE_INVAL; + + opt = (struct tc_fifo_qopt *) tc->tc_opts->d_data; + fifo->qf_limit = opt->limit; + fifo->qf_mask = SCH_FIFO_ATTR_LIMIT; + + return 0; +} + +static void pfifo_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_fifo *fifo = data; + + if (fifo) + nl_dump(p, " limit %u packets", fifo->qf_limit); +} + +static void bfifo_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_fifo *fifo = data; + char *unit; + double r; + + if (!fifo) + return; + + r = nl_cancel_down_bytes(fifo->qf_limit, &unit); + nl_dump(p, " limit %.1f%s", r, unit); +} + +static int fifo_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + struct rtnl_fifo *fifo = data; + struct tc_fifo_qopt opts = {0}; + + if (!fifo || !(fifo->qf_mask & SCH_FIFO_ATTR_LIMIT)) + return -NLE_INVAL; + + opts.limit = fifo->qf_limit; + + return nlmsg_append(msg, &opts, sizeof(opts), NL_DONTPAD); +} + +/** + * @name Attribute Modification + * @{ + */ + +/** + * Set limit of FIFO qdisc. + * @arg qdisc FIFO qdisc to be modified. + * @arg limit New limit. + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_fifo_set_limit(struct rtnl_qdisc *qdisc, int limit) +{ + struct rtnl_fifo *fifo; + + if (!(fifo = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + fifo->qf_limit = limit; + fifo->qf_mask |= SCH_FIFO_ATTR_LIMIT; + + return 0; +} + +/** + * Get limit of a FIFO qdisc. + * @arg qdisc FIFO qdisc. + * @return Numeric limit or a negative error code. + */ +int rtnl_qdisc_fifo_get_limit(struct rtnl_qdisc *qdisc) +{ + struct rtnl_fifo *fifo; + + if (!(fifo = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + if (fifo->qf_mask & SCH_FIFO_ATTR_LIMIT) + return fifo->qf_limit; + else + return -NLE_NOATTR; +} + +/** @} */ + +static struct rtnl_tc_ops pfifo_ops = { + .to_kind = "pfifo", + .to_type = RTNL_TC_TYPE_QDISC, + .to_size = sizeof(struct rtnl_fifo), + .to_msg_parser = fifo_msg_parser, + .to_dump[NL_DUMP_LINE] = pfifo_dump_line, + .to_msg_fill = fifo_msg_fill, +}; + +static struct rtnl_tc_ops bfifo_ops = { + .to_kind = "bfifo", + .to_type = RTNL_TC_TYPE_QDISC, + .to_size = sizeof(struct rtnl_fifo), + .to_msg_parser = fifo_msg_parser, + .to_dump[NL_DUMP_LINE] = bfifo_dump_line, + .to_msg_fill = fifo_msg_fill, +}; + +static void __init fifo_init(void) +{ + rtnl_tc_register(&pfifo_ops); + rtnl_tc_register(&bfifo_ops); +} + +static void __exit fifo_exit(void) +{ + rtnl_tc_unregister(&pfifo_ops); + rtnl_tc_unregister(&bfifo_ops); +} + +/** @} */ diff --git a/libnl/lib/route/qdisc/fq_codel.c b/libnl/lib/route/qdisc/fq_codel.c new file mode 100644 index 0000000..dfbc454 --- /dev/null +++ b/libnl/lib/route/qdisc/fq_codel.c @@ -0,0 +1,373 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Cong Wang + */ + +/** + * @ingroup qdisc + * @defgroup qdisc_fq_codel Fair Queue CoDel + * @brief + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define SCH_FQ_CODEL_ATTR_TARGET 0x1 +#define SCH_FQ_CODEL_ATTR_LIMIT 0x2 +#define SCH_FQ_CODEL_ATTR_INTERVAL 0x4 +#define SCH_FQ_CODEL_ATTR_FLOWS 0x8 +#define SCH_FQ_CODEL_ATTR_QUANTUM 0x10 +#define SCH_FQ_CODEL_ATTR_ECN 0x20 +/** @endcond */ + +static struct nla_policy fq_codel_policy[TCA_FQ_CODEL_MAX + 1] = { + [TCA_FQ_CODEL_TARGET] = { .type = NLA_U32 }, + [TCA_FQ_CODEL_LIMIT] = { .type = NLA_U32 }, + [TCA_FQ_CODEL_INTERVAL] = { .type = NLA_U32 }, + [TCA_FQ_CODEL_ECN] = { .type = NLA_U32 }, + [TCA_FQ_CODEL_FLOWS] = { .type = NLA_U32 }, + [TCA_FQ_CODEL_QUANTUM] = { .type = NLA_U32 }, +}; + +static int fq_codel_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct rtnl_fq_codel *fq_codel = data; + struct nlattr *tb[TCA_FQ_CODEL_MAX + 1]; + int err; + + err = tca_parse(tb, TCA_FQ_CODEL_MAX, tc, fq_codel_policy); + if (err < 0) + return err; + + if (tb[TCA_FQ_CODEL_TARGET]) { + fq_codel->fq_target = nla_get_u32(tb[TCA_FQ_CODEL_TARGET]); + fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_TARGET; + } + + if (tb[TCA_FQ_CODEL_INTERVAL]) { + fq_codel->fq_interval = nla_get_u32(tb[TCA_FQ_CODEL_INTERVAL]); + fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_INTERVAL; + } + + if (tb[TCA_FQ_CODEL_LIMIT]) { + fq_codel->fq_limit = nla_get_u32(tb[TCA_FQ_CODEL_LIMIT]); + fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_LIMIT; + } + + if (tb[TCA_FQ_CODEL_QUANTUM]) { + fq_codel->fq_quantum = nla_get_u32(tb[TCA_FQ_CODEL_QUANTUM]); + fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_QUANTUM; + } + + if (tb[TCA_FQ_CODEL_FLOWS]) { + fq_codel->fq_flows = nla_get_u32(tb[TCA_FQ_CODEL_FLOWS]); + fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_FLOWS; + } + + if (tb[TCA_FQ_CODEL_ECN]) { + fq_codel->fq_ecn = nla_get_u32(tb[TCA_FQ_CODEL_ECN]); + fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_ECN; + } + + return 0; +} + +static void fq_codel_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_fq_codel *fq_codel = data; + + if (!fq_codel) + return; + + if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_LIMIT) + nl_dump(p, " limit %u packets", fq_codel->fq_limit); + if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_TARGET) + nl_dump(p, " target %u", fq_codel->fq_target); + if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_INTERVAL) + nl_dump(p, " interval %u", fq_codel->fq_interval); + if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_ECN) + nl_dump(p, " ecn %u", fq_codel->fq_ecn); + if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_FLOWS) + nl_dump(p, " flows %u", fq_codel->fq_flows); + if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_QUANTUM) + nl_dump(p, " quantum %u", fq_codel->fq_quantum); +} + +static int fq_codel_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + struct rtnl_fq_codel *fq_codel = data; + + if (!fq_codel) + return -NLE_INVAL; + + if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_LIMIT) + NLA_PUT_U32(msg, TCA_FQ_CODEL_LIMIT, fq_codel->fq_limit); + if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_INTERVAL) + NLA_PUT_U32(msg, TCA_FQ_CODEL_INTERVAL, fq_codel->fq_interval); + if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_TARGET) + NLA_PUT_U32(msg, TCA_FQ_CODEL_TARGET, fq_codel->fq_target); + if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_QUANTUM) + NLA_PUT_U32(msg, TCA_FQ_CODEL_QUANTUM, fq_codel->fq_quantum); + if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_FLOWS) + NLA_PUT_U32(msg, TCA_FQ_CODEL_FLOWS, fq_codel->fq_flows); + if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_ECN) + NLA_PUT_U32(msg, TCA_FQ_CODEL_ECN, fq_codel->fq_ecn); + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; + +} + +/** + * @name Attribute Modification + * @{ + */ + +/** + * Set limit of fq_codel qdisc. + * @arg qdisc fq_codel qdisc to be modified. + * @arg limit New limit. + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_fq_codel_set_limit(struct rtnl_qdisc *qdisc, int limit) +{ + struct rtnl_fq_codel *fq_codel; + + if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + fq_codel->fq_limit = limit; + fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_LIMIT; + + return 0; +} + +/** + * Get limit of a fq_codel qdisc. + * @arg qdisc fq_codel qdisc. + * @return Numeric limit or a negative error code. + */ +int rtnl_qdisc_fq_codel_get_limit(struct rtnl_qdisc *qdisc) +{ + struct rtnl_fq_codel *fq_codel; + + if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_LIMIT) + return fq_codel->fq_limit; + else + return -NLE_NOATTR; +} + +/** + * Set target of fq_codel qdisc. + * @arg qdisc fq_codel qdisc to be modified. + * @arg target New target. + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_fq_codel_set_target(struct rtnl_qdisc *qdisc, uint32_t target) +{ + struct rtnl_fq_codel *fq_codel; + + if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + fq_codel->fq_target = target; + fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_TARGET; + + return 0; +} + +/** + * Get target of a fq_codel qdisc. + * @arg qdisc fq_codel qdisc. + * @return Numeric target or zero. + */ +uint32_t rtnl_qdisc_fq_codel_get_target(struct rtnl_qdisc *qdisc) +{ + struct rtnl_fq_codel *fq_codel; + + if ((fq_codel = rtnl_tc_data(TC_CAST(qdisc))) && + fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_TARGET) + return fq_codel->fq_target; + else + return 0; +} + +/** + * Set interval of fq_codel qdisc. + * @arg qdisc fq_codel qdisc to be modified. + * @arg interval New interval. + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_fq_codel_set_interval(struct rtnl_qdisc *qdisc, uint32_t interval) +{ + struct rtnl_fq_codel *fq_codel; + + if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + fq_codel->fq_interval = interval; + fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_INTERVAL; + + return 0; +} + +/** + * Get target of a fq_codel qdisc. + * @arg qdisc fq_codel qdisc. + * @return Numeric interval or zero. + */ +uint32_t rtnl_qdisc_fq_codel_get_interval(struct rtnl_qdisc *qdisc) +{ + struct rtnl_fq_codel *fq_codel; + + if ((fq_codel = rtnl_tc_data(TC_CAST(qdisc))) && + fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_INTERVAL) + return fq_codel->fq_interval; + else + return 0; +} + +/** + * Set quantum of fq_codel qdisc. + * @arg qdisc fq_codel qdisc to be modified. + * @arg quantum New quantum. + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_fq_codel_set_quantum(struct rtnl_qdisc *qdisc, uint32_t quantum) +{ + struct rtnl_fq_codel *fq_codel; + + if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + fq_codel->fq_quantum = quantum; + fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_QUANTUM; + + return 0; +} + +/** + * Get quantum of a fq_codel qdisc. + * @arg qdisc fq_codel qdisc. + * @return Numeric quantum or zero. + */ +uint32_t rtnl_qdisc_fq_codel_get_quantum(struct rtnl_qdisc *qdisc) +{ + struct rtnl_fq_codel *fq_codel; + + if ((fq_codel = rtnl_tc_data(TC_CAST(qdisc))) && + (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_QUANTUM)) + return fq_codel->fq_quantum; + else + return 0; +} + +/** + * Set flows of fq_codel qdisc. + * @arg qdisc fq_codel qdisc to be modified. + * @arg flows New flows value. + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_fq_codel_set_flows(struct rtnl_qdisc *qdisc, int flows) +{ + struct rtnl_fq_codel *fq_codel; + + if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + fq_codel->fq_flows = flows; + fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_FLOWS; + + return 0; +} + +/** + * Get flows of a fq_codel qdisc. + * @arg qdisc fq_codel qdisc. + * @return Numeric flows or a negative error code. + */ +int rtnl_qdisc_fq_codel_get_flows(struct rtnl_qdisc *qdisc) +{ + struct rtnl_fq_codel *fq_codel; + + if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_FLOWS) + return fq_codel->fq_flows; + else + return -NLE_NOATTR; +} +/** + * Set ecn of fq_codel qdisc. + * @arg qdisc fq_codel qdisc to be modified. + * @arg ecn New ecn value. + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_fq_codel_set_ecn(struct rtnl_qdisc *qdisc, int ecn) +{ + struct rtnl_fq_codel *fq_codel; + + if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + fq_codel->fq_ecn = ecn; + fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_ECN; + + return 0; +} + +/** + * Get ecn of a fq_codel qdisc. + * @arg qdisc fq_codel qdisc. + * @return Numeric ecn or a negative error code. + */ +int rtnl_qdisc_fq_codel_get_ecn(struct rtnl_qdisc *qdisc) +{ + struct rtnl_fq_codel *fq_codel; + + if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_ECN) + return fq_codel->fq_ecn; + else + return -NLE_NOATTR; +} +/** @} */ + +static struct rtnl_tc_ops fq_codel_ops = { + .to_kind = "fq_codel", + .to_type = RTNL_TC_TYPE_QDISC, + .to_size = sizeof(struct rtnl_fq_codel), + .to_msg_parser = fq_codel_msg_parser, + .to_dump[NL_DUMP_LINE] = fq_codel_dump_line, + .to_msg_fill = fq_codel_msg_fill, +}; + +static void __init fq_codel_init(void) +{ + rtnl_tc_register(&fq_codel_ops); +} + +static void __exit fq_codel_exit(void) +{ + rtnl_tc_unregister(&fq_codel_ops); +} + +/** @} */ diff --git a/libnl/lib/route/qdisc/hfsc.c b/libnl/lib/route/qdisc/hfsc.c new file mode 100644 index 0000000..4d39dc1 --- /dev/null +++ b/libnl/lib/route/qdisc/hfsc.c @@ -0,0 +1,347 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2014 Cong Wang + */ + +/** + * @ingroup qdisc + * @ingroup class + * @defgroup qdisc_hfsc Hierarchical Fair Service Curve (HFSC) + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define SCH_HFSC_CLS_HAS_RSC 0x001 +#define SCH_HFSC_CLS_HAS_FSC 0x002 +#define SCH_HFSC_CLS_HAS_USC 0x004 + +#define SCH_HFSC_QD_HAS_DEFCLS 0x01 +/** @endcond */ + +static struct nla_policy hfsc_policy[TCA_HFSC_MAX + 1] = { + [TCA_HFSC_RSC] = { .minlen = sizeof(struct tc_service_curve) }, + [TCA_HFSC_FSC] = { .minlen = sizeof(struct tc_service_curve) }, + [TCA_HFSC_USC] = { .minlen = sizeof(struct tc_service_curve) }, +}; + +static int hfsc_qdisc_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct rtnl_hfsc_qdisc *hfsc = data; + struct tc_hfsc_qopt *opts; + + opts = (struct tc_hfsc_qopt *) tc->tc_opts->d_data; + hfsc->qh_defcls = opts->defcls; + hfsc->qh_mask |= SCH_HFSC_QD_HAS_DEFCLS; + return 0; +} + +static int hfsc_class_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct nlattr *tb[TCA_HFSC_MAX + 1]; + struct rtnl_hfsc_class *hfsc = data; + int err; + + if ((err = tca_parse(tb, TCA_HFSC_MAX, tc, hfsc_policy)) < 0) + return err; + + if (tb[TCA_HFSC_RSC]) { + struct tc_service_curve tsc; + + nla_memcpy(&tsc, tb[TCA_HFSC_RSC], sizeof(tsc)); + hfsc->ch_rsc = tsc; + hfsc->ch_mask |= SCH_HFSC_CLS_HAS_RSC; + } + + if (tb[TCA_HFSC_FSC]) { + struct tc_service_curve tsc; + + nla_memcpy(&tsc, tb[TCA_HFSC_FSC], sizeof(tsc)); + hfsc->ch_fsc = tsc; + hfsc->ch_mask |= SCH_HFSC_CLS_HAS_FSC; + } + + if (tb[TCA_HFSC_USC]) { + struct tc_service_curve tsc; + + nla_memcpy(&tsc, tb[TCA_HFSC_USC], sizeof(tsc)); + hfsc->ch_usc = tsc; + hfsc->ch_mask |= SCH_HFSC_CLS_HAS_USC; + } + + return 0; +} + +static void hfsc_qdisc_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_hfsc_qdisc *hfsc = data; + + if (!hfsc) + return; + + if (hfsc->qh_mask & SCH_HFSC_QD_HAS_DEFCLS) { + char buf[64]; + nl_dump(p, " default-class %s", + rtnl_tc_handle2str(hfsc->qh_defcls, buf, sizeof(buf))); + } +} + +static void hfsc_dump_tsc(struct nl_dump_params *p, struct tc_service_curve *tsc) +{ + nl_dump(p, " m1 %u d %u m2 %u\n", tsc->m1, tsc->d, tsc->m2); +} + +static void hfsc_class_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_hfsc_class *hfsc = data; + + if (!hfsc) + return; + if (hfsc->ch_mask & SCH_HFSC_CLS_HAS_RSC) + hfsc_dump_tsc(p, &hfsc->ch_rsc); + if (hfsc->ch_mask & SCH_HFSC_CLS_HAS_FSC) + hfsc_dump_tsc(p, &hfsc->ch_fsc); + if (hfsc->ch_mask & SCH_HFSC_CLS_HAS_USC) + hfsc_dump_tsc(p, &hfsc->ch_usc); +} + +static void hfsc_class_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + return; +} + +static int hfsc_qdisc_msg_fill(struct rtnl_tc *tc, void *data, + struct nl_msg *msg) +{ + struct rtnl_hfsc_qdisc *hfsc = data; + struct tc_hfsc_qopt opts = {0}; + + if (!hfsc) + BUG(); + + opts.defcls = hfsc->qh_defcls; + return nlmsg_append(msg, &opts, sizeof(opts), NL_DONTPAD); +} + +static int hfsc_class_msg_fill(struct rtnl_tc *tc, void *data, + struct nl_msg *msg) +{ + struct rtnl_hfsc_class *hfsc = data; + struct tc_service_curve tsc; + + if (!hfsc) + BUG(); + + if (hfsc->ch_mask & SCH_HFSC_CLS_HAS_RSC) { + tsc = hfsc->ch_rsc; + NLA_PUT(msg, TCA_HFSC_RSC, sizeof(tsc), &tsc); + } + + if (hfsc->ch_mask & SCH_HFSC_CLS_HAS_FSC) { + tsc = hfsc->ch_fsc; + NLA_PUT(msg, TCA_HFSC_FSC, sizeof(tsc), &tsc); + } + + if (hfsc->ch_mask & SCH_HFSC_CLS_HAS_USC) { + tsc = hfsc->ch_usc; + NLA_PUT(msg, TCA_HFSC_USC, sizeof(tsc), &tsc); + } + + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +static struct rtnl_tc_ops hfsc_qdisc_ops; +static struct rtnl_tc_ops hfsc_class_ops; + +static struct rtnl_hfsc_qdisc *hfsc_qdisc_data(const struct rtnl_qdisc *qdisc, int *err) +{ + return rtnl_tc_data_check(TC_CAST(qdisc), &hfsc_qdisc_ops, err); +} + +static struct rtnl_hfsc_class *hfsc_class_data(const struct rtnl_class *class, int *err) +{ + return rtnl_tc_data_check(TC_CAST(class), &hfsc_class_ops, err); +} + +/** + * @name Attribute Modifications + * @{ + */ + +/** + * Return default class of HFSC qdisc + * @arg qdisc hfsc qdisc object + * + * Returns the classid of the class where all unclassified traffic + * goes to. + * + * @return classid or TC_H_UNSPEC if unspecified. + */ +uint32_t rtnl_qdisc_hfsc_get_defcls(const struct rtnl_qdisc *qdisc) +{ + struct rtnl_hfsc_qdisc *hfsc; + + if ((hfsc = hfsc_qdisc_data(qdisc, NULL)) && + (hfsc->qh_mask & SCH_HFSC_QD_HAS_DEFCLS)) + return hfsc->qh_defcls; + + return TC_H_UNSPEC; +} + +/** + * Set default class of the hfsc qdisc to the specified value + * @arg qdisc qdisc to change + * @arg defcls new default class + */ +int rtnl_qdisc_hfsc_set_defcls(struct rtnl_qdisc *qdisc, uint32_t defcls) +{ + struct rtnl_hfsc_qdisc *hfsc; + int err; + + if (!(hfsc = hfsc_qdisc_data(qdisc, &err))) + return err; + + hfsc->qh_defcls = defcls; + hfsc->qh_mask |= SCH_HFSC_QD_HAS_DEFCLS; + + return 0; +} + +int rtnl_class_hfsc_get_rsc(const struct rtnl_class *class, struct tc_service_curve *tsc) +{ + struct rtnl_hfsc_class *hfsc; + int err = -NLE_OPNOTSUPP; + + if ((hfsc = hfsc_class_data(class, &err)) && + (hfsc->ch_mask & SCH_HFSC_CLS_HAS_RSC)) { + *tsc = hfsc->ch_rsc; + return 0; + } + + return err; +} + +int rtnl_class_hfsc_set_rsc(struct rtnl_class *class, const struct tc_service_curve *tsc) +{ + struct rtnl_hfsc_class *hfsc; + int err; + + if (!(hfsc = hfsc_class_data(class, &err))) + return err; + + hfsc->ch_rsc = *tsc; + hfsc->ch_mask |= SCH_HFSC_CLS_HAS_RSC; + + return 0; +} + +int rtnl_class_hfsc_get_fsc(const struct rtnl_class *class, struct tc_service_curve *tsc) +{ + struct rtnl_hfsc_class *hfsc; + int err = -NLE_OPNOTSUPP; + + if ((hfsc = hfsc_class_data(class, &err)) && + (hfsc->ch_mask & SCH_HFSC_CLS_HAS_FSC)) { + *tsc = hfsc->ch_fsc; + return 0; + } + + return err; +} + +int rtnl_class_hfsc_set_fsc(struct rtnl_class *class, const struct tc_service_curve *tsc) +{ + struct rtnl_hfsc_class *hfsc; + int err; + + if (!(hfsc = hfsc_class_data(class, &err))) + return err; + + hfsc->ch_fsc = *tsc; + hfsc->ch_mask |= SCH_HFSC_CLS_HAS_FSC; + + return 0; +} + +int rtnl_class_hfsc_get_usc(const struct rtnl_class *class, struct tc_service_curve *tsc) +{ + struct rtnl_hfsc_class *hfsc; + int err = -NLE_OPNOTSUPP; + + if ((hfsc = hfsc_class_data(class, &err)) && + (hfsc->ch_mask & SCH_HFSC_CLS_HAS_USC)) { + *tsc = hfsc->ch_usc; + return 0; + } + + return err; +} + +int rtnl_class_hfsc_set_usc(struct rtnl_class *class, const struct tc_service_curve *tsc) +{ + struct rtnl_hfsc_class *hfsc; + int err; + + if (!(hfsc = hfsc_class_data(class, &err))) + return err; + + hfsc->ch_usc = *tsc; + hfsc->ch_mask |= SCH_HFSC_CLS_HAS_USC; + + return 0; +} + +/** @} */ + +static struct rtnl_tc_ops hfsc_qdisc_ops = { + .to_kind = "hfsc", + .to_type = RTNL_TC_TYPE_QDISC, + .to_size = sizeof(struct rtnl_hfsc_qdisc), + .to_msg_parser = hfsc_qdisc_msg_parser, + .to_dump[NL_DUMP_LINE] = hfsc_qdisc_dump_line, + .to_msg_fill = hfsc_qdisc_msg_fill, +}; + +static struct rtnl_tc_ops hfsc_class_ops = { + .to_kind = "hfsc", + .to_type = RTNL_TC_TYPE_CLASS, + .to_size = sizeof(struct rtnl_hfsc_class), + .to_msg_parser = hfsc_class_msg_parser, + .to_dump = { + [NL_DUMP_LINE] = hfsc_class_dump_line, + [NL_DUMP_DETAILS] = hfsc_class_dump_details, + }, + .to_msg_fill = hfsc_class_msg_fill, +}; + +static void __init hfsc_init(void) +{ + rtnl_tc_register(&hfsc_qdisc_ops); + rtnl_tc_register(&hfsc_class_ops); +} + +static void __exit hfsc_exit(void) +{ + rtnl_tc_unregister(&hfsc_qdisc_ops); + rtnl_tc_unregister(&hfsc_class_ops); +} + +/** @} */ diff --git a/libnl/lib/route/qdisc/htb.c b/libnl/lib/route/qdisc/htb.c new file mode 100644 index 0000000..40b8447 --- /dev/null +++ b/libnl/lib/route/qdisc/htb.c @@ -0,0 +1,744 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + * Copyright (c) 2005-2006 Petr Gotthard + * Copyright (c) 2005-2006 Siemens AG Oesterreich + */ + +/** + * @ingroup qdisc + * @ingroup class + * @defgroup qdisc_htb Hierachical Token Bucket (HTB) + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define SCH_HTB_HAS_RATE2QUANTUM 0x01 +#define SCH_HTB_HAS_DEFCLS 0x02 + +#define SCH_HTB_HAS_PRIO 0x001 +#define SCH_HTB_HAS_RATE 0x002 +#define SCH_HTB_HAS_CEIL 0x004 +#define SCH_HTB_HAS_RBUFFER 0x008 +#define SCH_HTB_HAS_CBUFFER 0x010 +#define SCH_HTB_HAS_QUANTUM 0x020 +#define SCH_HTB_HAS_LEVEL 0x040 +/** @endcond */ + +static struct nla_policy htb_policy[TCA_HTB_MAX+1] = { + [TCA_HTB_INIT] = { .minlen = sizeof(struct tc_htb_glob) }, + [TCA_HTB_PARMS] = { .minlen = sizeof(struct tc_htb_opt) }, + [TCA_HTB_RATE64] = { .minlen = sizeof(uint64_t) }, + [TCA_HTB_CEIL64] = { .minlen = sizeof(uint64_t) }, +}; + +static int htb_qdisc_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct nlattr *tb[TCA_HTB_MAX + 1]; + struct rtnl_htb_qdisc *htb = data; + int err; + + if ((err = tca_parse(tb, TCA_HTB_MAX, tc, htb_policy)) < 0) + return err; + + if (tb[TCA_HTB_INIT]) { + struct tc_htb_glob opts; + + nla_memcpy(&opts, tb[TCA_HTB_INIT], sizeof(opts)); + htb->qh_rate2quantum = opts.rate2quantum; + htb->qh_defcls = opts.defcls; + htb->qh_direct_pkts = opts.direct_pkts; + + htb->qh_mask = (SCH_HTB_HAS_RATE2QUANTUM | SCH_HTB_HAS_DEFCLS); + } + + return 0; +} + +static int htb_class_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct nlattr *tb[TCA_HTB_MAX + 1]; + struct rtnl_htb_class *htb = data; + int err; + + if ((err = tca_parse(tb, TCA_HTB_MAX, tc, htb_policy)) < 0) + return err; + + if (tb[TCA_HTB_PARMS]) { + struct tc_htb_opt opts; + + nla_memcpy(&opts, tb[TCA_HTB_PARMS], sizeof(opts)); + htb->ch_prio = opts.prio; + rtnl_copy_ratespec(&htb->ch_rate, &opts.rate); + rtnl_copy_ratespec(&htb->ch_ceil, &opts.ceil); + + if (tb[TCA_HTB_RATE64]) + nla_memcpy(&htb->ch_rate.rs_rate64, tb[TCA_HTB_RATE64], sizeof(uint64_t)); + if (tb[TCA_HTB_CEIL64]) + nla_memcpy(&htb->ch_ceil.rs_rate64, tb[TCA_HTB_CEIL64], sizeof(uint64_t)); + + htb->ch_rbuffer = rtnl_tc_calc_bufsize64(nl_ticks2us(opts.buffer), + htb->ch_rate.rs_rate64); + htb->ch_cbuffer = rtnl_tc_calc_bufsize64(nl_ticks2us(opts.cbuffer), + htb->ch_ceil.rs_rate64); + htb->ch_quantum = opts.quantum; + htb->ch_level = opts.level; + + rtnl_tc_set_mpu(tc, htb->ch_rate.rs_mpu); + rtnl_tc_set_overhead(tc, htb->ch_rate.rs_overhead); + + htb->ch_mask = (SCH_HTB_HAS_PRIO | SCH_HTB_HAS_RATE | + SCH_HTB_HAS_CEIL | SCH_HTB_HAS_RBUFFER | + SCH_HTB_HAS_CBUFFER | SCH_HTB_HAS_QUANTUM | + SCH_HTB_HAS_LEVEL); + } + + return 0; +} + +static void htb_qdisc_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_htb_qdisc *htb = data; + + if (!htb) + return; + + if (htb->qh_mask & SCH_HTB_HAS_RATE2QUANTUM) + nl_dump(p, " r2q %u", htb->qh_rate2quantum); + + if (htb->qh_mask & SCH_HTB_HAS_DEFCLS) { + char buf[64]; + nl_dump(p, " default-class %s", + rtnl_tc_handle2str(htb->qh_defcls, buf, sizeof(buf))); + } +} + +static void htb_class_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_htb_class *htb = data; + + if (!htb) + return; + + if (htb->ch_mask & SCH_HTB_HAS_RATE) { + double r, rbit; + char *ru, *rubit; + + r = nl_cancel_down_bytes(htb->ch_rate.rs_rate64, &ru); + rbit = nl_cancel_down_bits(htb->ch_rate.rs_rate64*8, &rubit); + + nl_dump(p, " rate %.2f%s/s (%.0f%s) log %u", + r, ru, rbit, rubit, 1<ch_rate.rs_cell_log); + } +} + +static void htb_class_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_htb_class *htb = data; + + if (!htb) + return; + + /* line 1 */ + if (htb->ch_mask & SCH_HTB_HAS_CEIL) { + double r, rbit; + char *ru, *rubit; + + r = nl_cancel_down_bytes(htb->ch_ceil.rs_rate64, &ru); + rbit = nl_cancel_down_bits(htb->ch_ceil.rs_rate64*8, &rubit); + + nl_dump(p, " ceil %.2f%s/s (%.0f%s) log %u", + r, ru, rbit, rubit, 1<ch_ceil.rs_cell_log); + } + + if (htb->ch_mask & SCH_HTB_HAS_PRIO) + nl_dump(p, " prio %u", htb->ch_prio); + + if (htb->ch_mask & SCH_HTB_HAS_RBUFFER) { + double b; + char *bu; + + b = nl_cancel_down_bytes(htb->ch_rbuffer, &bu); + nl_dump(p, " rbuffer %.2f%s", b, bu); + } + + if (htb->ch_mask & SCH_HTB_HAS_CBUFFER) { + double b; + char *bu; + + b = nl_cancel_down_bytes(htb->ch_cbuffer, &bu); + nl_dump(p, " cbuffer %.2f%s", b, bu); + } + + if (htb->ch_mask & SCH_HTB_HAS_QUANTUM) + nl_dump(p, " quantum %u", htb->ch_quantum); +} + +static int htb_qdisc_msg_fill(struct rtnl_tc *tc, void *data, + struct nl_msg *msg) +{ + struct rtnl_htb_qdisc *htb = data; + struct tc_htb_glob opts = { + .version = TC_HTB_PROTOVER, + .rate2quantum = 10, + }; + + if (htb) { + if (htb->qh_mask & SCH_HTB_HAS_RATE2QUANTUM) + opts.rate2quantum = htb->qh_rate2quantum; + + if (htb->qh_mask & SCH_HTB_HAS_DEFCLS) + opts.defcls = htb->qh_defcls; + } + + return nla_put(msg, TCA_HTB_INIT, sizeof(opts), &opts); +} + +static int htb_class_msg_fill(struct rtnl_tc *tc, void *data, + struct nl_msg *msg) +{ + struct rtnl_htb_class *htb = data; + uint32_t mtu, rtable[RTNL_TC_RTABLE_SIZE], ctable[RTNL_TC_RTABLE_SIZE]; + struct tc_htb_opt opts; + int buffer, cbuffer; + uint64_t rate64; + uint64_t ceil64; + + if (!htb || !(htb->ch_mask & SCH_HTB_HAS_RATE)) + BUG(); + + memset(&opts, 0, sizeof(opts)); + + /* if not set, zero (0) is used as priority */ + if (htb->ch_mask & SCH_HTB_HAS_PRIO) + opts.prio = htb->ch_prio; + + mtu = rtnl_tc_get_mtu(tc); + + rtnl_tc_build_rate_table(tc, &htb->ch_rate, rtable); + rtnl_rcopy_ratespec(&opts.rate, &htb->ch_rate); + rate64 = htb->ch_rate.rs_rate64; + + if (htb->ch_mask & SCH_HTB_HAS_CEIL) { + rtnl_tc_build_rate_table(tc, &htb->ch_ceil, ctable); + rtnl_rcopy_ratespec(&opts.ceil, &htb->ch_ceil); + ceil64 = htb->ch_ceil.rs_rate64; + } else { + /* + * If not set, configured rate is used as ceil, which implies + * no borrowing. + */ + memcpy(&opts.ceil, &opts.rate, sizeof(struct tc_ratespec)); + ceil64 = rate64; + } + + if (htb->ch_mask & SCH_HTB_HAS_RBUFFER) + buffer = htb->ch_rbuffer; + else + buffer = rate64 / nl_get_psched_hz() + mtu; /* XXX */ + + opts.buffer = nl_us2ticks(rtnl_tc_calc_txtime64(buffer, rate64)); + + if (htb->ch_mask & SCH_HTB_HAS_CBUFFER) + cbuffer = htb->ch_cbuffer; + else + cbuffer = ceil64 / nl_get_psched_hz() + mtu; /* XXX */ + + opts.cbuffer = nl_us2ticks(rtnl_tc_calc_txtime64(cbuffer, ceil64)); + + if (htb->ch_mask & SCH_HTB_HAS_QUANTUM) + opts.quantum = htb->ch_quantum; + + NLA_PUT(msg, TCA_HTB_PARMS, sizeof(opts), &opts); + if (rate64 > 0xFFFFFFFFull) + NLA_PUT(msg, TCA_HTB_RATE64, sizeof(uint64_t), &rate64); + if (ceil64 > 0xFFFFFFFFull) + NLA_PUT(msg, TCA_HTB_CEIL64, sizeof(uint64_t), &ceil64); + NLA_PUT(msg, TCA_HTB_RTAB, sizeof(rtable), &rtable); + NLA_PUT(msg, TCA_HTB_CTAB, sizeof(ctable), &ctable); + + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +static struct rtnl_tc_ops htb_qdisc_ops; +static struct rtnl_tc_ops htb_class_ops; + +static struct rtnl_htb_qdisc *htb_qdisc_data(struct rtnl_qdisc *qdisc, int *err) +{ + return rtnl_tc_data_check(TC_CAST(qdisc), &htb_qdisc_ops, err); +} + +static struct rtnl_htb_class *htb_class_data(struct rtnl_class *class, int *err) +{ + return rtnl_tc_data_check(TC_CAST(class), &htb_class_ops, err); +} + +/** + * @name Attribute Modifications + * @{ + */ + +/** + * Return rate/quantum ratio of HTB qdisc + * @arg qdisc htb qdisc object + * + * @return rate/quantum ratio or 0 if unspecified + */ +uint32_t rtnl_htb_get_rate2quantum(struct rtnl_qdisc *qdisc) +{ + struct rtnl_htb_qdisc *htb; + + if ((htb = htb_qdisc_data(qdisc, NULL)) && + (htb->qh_mask & SCH_HTB_HAS_RATE2QUANTUM)) + return htb->qh_rate2quantum; + + return 0; +} + +int rtnl_htb_set_rate2quantum(struct rtnl_qdisc *qdisc, uint32_t rate2quantum) +{ + struct rtnl_htb_qdisc *htb; + int err; + + if (!(htb = htb_qdisc_data(qdisc, &err))) + return err; + + htb->qh_rate2quantum = rate2quantum; + htb->qh_mask |= SCH_HTB_HAS_RATE2QUANTUM; + + return 0; +} + +/** + * Return default class of HTB qdisc + * @arg qdisc htb qdisc object + * + * Returns the classid of the class where all unclassified traffic + * goes to. + * + * @return classid or TC_H_UNSPEC if unspecified. + */ +uint32_t rtnl_htb_get_defcls(struct rtnl_qdisc *qdisc) +{ + struct rtnl_htb_qdisc *htb; + + if ((htb = htb_qdisc_data(qdisc, NULL)) && + htb->qh_mask & SCH_HTB_HAS_DEFCLS) + return htb->qh_defcls; + + return TC_H_UNSPEC; +} + +/** + * Set default class of the htb qdisc to the specified value + * @arg qdisc qdisc to change + * @arg defcls new default class + */ +int rtnl_htb_set_defcls(struct rtnl_qdisc *qdisc, uint32_t defcls) +{ + struct rtnl_htb_qdisc *htb; + int err; + + if (!(htb = htb_qdisc_data(qdisc, &err))) + return err; + + htb->qh_defcls = defcls; + htb->qh_mask |= SCH_HTB_HAS_DEFCLS; + + return 0; +} + +uint32_t rtnl_htb_get_prio(struct rtnl_class *class) +{ + struct rtnl_htb_class *htb; + + if ((htb = htb_class_data(class, NULL)) && + (htb->ch_mask & SCH_HTB_HAS_PRIO)) + return htb->ch_prio; + + return 0; +} + +int rtnl_htb_set_prio(struct rtnl_class *class, uint32_t prio) +{ + struct rtnl_htb_class *htb; + int err; + + if (!(htb = htb_class_data(class, &err))) + return err; + + htb->ch_prio = prio; + htb->ch_mask |= SCH_HTB_HAS_PRIO; + + return 0; +} + +/** + * Return rate of HTB class + * @arg class htb class object + * + * @return Rate in bytes/s or 0 if unspecified. If the value + * cannot be represented as 32 bit integer, (1<<32) is returned. + * Use rtnl_htb_get_rate64() instead. + */ +uint32_t rtnl_htb_get_rate(struct rtnl_class *class) +{ + struct rtnl_htb_class *htb; + + if ( !(htb = htb_class_data(class, NULL)) + || !(htb->ch_mask & SCH_HTB_HAS_RATE)) + return 0; + + if (htb->ch_rate.rs_rate64 > 0xFFFFFFFFull) + return 0xFFFFFFFFull; + + return htb->ch_rate.rs_rate64; +} + +/** + * Return rate of HTB class + * @arg class htb class object + * @arg out_rate64 on success, the set rate. + * + * @return 0 on success or a negative error code. + */ +int rtnl_htb_get_rate64(struct rtnl_class *class, uint64_t *out_rate64) +{ + struct rtnl_htb_class *htb; + + if (!(htb = htb_class_data(class, NULL))) + return -NLE_INVAL; + if (!(htb->ch_mask & SCH_HTB_HAS_RATE)) + return -NLE_NOATTR; + + *out_rate64 = htb->ch_rate.rs_rate64; + return 0; +} + +/** + * Set rate of HTB class + * @arg class htb class object + * @arg rate new rate in bytes per second + * + * @return 0 on success or a negative error code. + */ +int rtnl_htb_set_rate(struct rtnl_class *class, uint32_t rate) +{ + return rtnl_htb_set_rate64(class, rate); +} + +/** + * Set rate of HTB class + * @arg class htb class object + * @arg rate new rate in bytes per second + * + * @return 0 on success or a negative error code. + */ +int rtnl_htb_set_rate64(struct rtnl_class *class, uint64_t rate) +{ + struct rtnl_htb_class *htb; + int err; + + if (!(htb = htb_class_data(class, &err))) + return err; + + htb->ch_rate.rs_cell_log = UINT8_MAX; /* use default value */ + htb->ch_rate.rs_rate64 = rate; + htb->ch_mask |= SCH_HTB_HAS_RATE; + + return 0; +} + +/** + * Return ceil rate of HTB class + * @arg class htb class object + * + * @return Ceil rate in bytes/s or 0 if unspecified. If the value + * cannot be represented as 32 bit integer, (1<<32) is returned. + * Use rtnl_htb_get_ceil64() instead. + */ +uint32_t rtnl_htb_get_ceil(struct rtnl_class *class) +{ + struct rtnl_htb_class *htb; + + if ( !(htb = htb_class_data(class, NULL)) + || !(htb->ch_mask & SCH_HTB_HAS_CEIL)) + return 0; + + if (htb->ch_ceil.rs_rate64 > 0xFFFFFFFFull) + return 0xFFFFFFFFull; + + return htb->ch_ceil.rs_rate64; +} + +/** + * Return ceil rate of HTB class + * @arg class htb class object + * @arg out_ceil64 on success, the set ceil value. + * + * @return 0 on success or a negative error code. + */ +int rtnl_htb_get_ceil64(struct rtnl_class *class, uint64_t *out_ceil64) +{ + struct rtnl_htb_class *htb; + + if (!(htb = htb_class_data(class, NULL))) + return -NLE_INVAL; + if (!(htb->ch_mask & SCH_HTB_HAS_CEIL)) + return -NLE_NOATTR; + + *out_ceil64 = htb->ch_ceil.rs_rate64; + return 0; +} + +/** + * Set ceil rate of HTB class + * @arg class htb class object + * @arg ceil new ceil rate number of bytes per second + * + * @return 0 on success or a negative error code. + */ +int rtnl_htb_set_ceil(struct rtnl_class *class, uint32_t ceil) +{ + return rtnl_htb_set_ceil64(class, ceil); +} + +/** + * Set ceil rate of HTB class + * @arg class htb class object + * @arg ceil64 new ceil rate number of bytes per second + * + * @return 0 on success or a negative error code. + */ +int rtnl_htb_set_ceil64(struct rtnl_class *class, uint64_t ceil64) +{ + struct rtnl_htb_class *htb; + int err; + + if (!(htb = htb_class_data(class, &err))) + return err; + + htb->ch_ceil.rs_cell_log = UINT8_MAX; /* use default value */ + htb->ch_ceil.rs_rate64 = ceil64; + htb->ch_mask |= SCH_HTB_HAS_CEIL; + + return 0; +} + +/** + * Return burst buffer size of HTB class + * @arg class htb class object + * + * @return Burst buffer size or 0 if unspecified + */ +uint32_t rtnl_htb_get_rbuffer(struct rtnl_class *class) +{ + struct rtnl_htb_class *htb; + + if ((htb = htb_class_data(class, NULL)) && + htb->ch_mask & SCH_HTB_HAS_RBUFFER) + return htb->ch_rbuffer; + + return 0; +} + +/** + * Set size of the rate bucket of HTB class. + * @arg class HTB class to be modified. + * @arg rbuffer New size in bytes. + */ +int rtnl_htb_set_rbuffer(struct rtnl_class *class, uint32_t rbuffer) +{ + struct rtnl_htb_class *htb; + int err; + + if (!(htb = htb_class_data(class, &err))) + return err; + + htb->ch_rbuffer = rbuffer; + htb->ch_mask |= SCH_HTB_HAS_RBUFFER; + + return 0; +} + +/** + * Return ceil burst buffer size of HTB class + * @arg class htb class object + * + * @return Ceil burst buffer size or 0 if unspecified + */ +uint32_t rtnl_htb_get_cbuffer(struct rtnl_class *class) +{ + struct rtnl_htb_class *htb; + + if ((htb = htb_class_data(class, NULL)) && + htb->ch_mask & SCH_HTB_HAS_CBUFFER) + return htb->ch_cbuffer; + + return 0; +} + +/** + * Set size of the ceil bucket of HTB class. + * @arg class HTB class to be modified. + * @arg cbuffer New size in bytes. + */ +int rtnl_htb_set_cbuffer(struct rtnl_class *class, uint32_t cbuffer) +{ + struct rtnl_htb_class *htb; + int err; + + if (!(htb = htb_class_data(class, &err))) + return err; + + htb->ch_cbuffer = cbuffer; + htb->ch_mask |= SCH_HTB_HAS_CBUFFER; + + return 0; +} + +/** + * Return quantum of HTB class + * @arg class htb class object + * + * See XXX[quantum def] + * + * @return Quantum or 0 if unspecified. + */ +uint32_t rtnl_htb_get_quantum(struct rtnl_class *class) +{ + struct rtnl_htb_class *htb; + + if ((htb = htb_class_data(class, NULL)) && + htb->ch_mask & SCH_HTB_HAS_QUANTUM) + return htb->ch_quantum; + + return 0; +} + +/** + * Set quantum of HTB class (overwrites value calculated based on r2q) + * @arg class htb class object + * @arg quantum new quantum in number of bytes + * + * See XXX[quantum def] + * + * @return 0 on success or a negative error code. + */ +int rtnl_htb_set_quantum(struct rtnl_class *class, uint32_t quantum) +{ + struct rtnl_htb_class *htb; + int err; + + if (!(htb = htb_class_data(class, &err))) + return err; + + htb->ch_quantum = quantum; + htb->ch_mask |= SCH_HTB_HAS_QUANTUM; + + return 0; +} + +/** + * Return level of HTB class + * @arg class htb class object + * + * Returns the level of the HTB class. Leaf classes are assigned level + * 0, root classes have level (TC_HTB_MAXDEPTH - 1). Interior classes + * have a level of one less than their parent. + * + * @return Level or a negative error code. + */ +int rtnl_htb_get_level(struct rtnl_class *class) +{ + struct rtnl_htb_class *htb; + int err = -NLE_OPNOTSUPP; + + if ((htb = htb_class_data(class, &err)) && + (htb->ch_mask & SCH_HTB_HAS_LEVEL)) + return htb->ch_level; + + return err; +} + +/** + * Set level of HTB class + * @arg class htb class object + * @arg level new level of HTB class + * + * Sets the level of a HTB class. Note that changing the level of a HTB + * class does not change the level of its in kernel counterpart. This + * function is provided only to create HTB objects which can be compared + * against or filtered upon. + * + * @return 0 on success or a negative error code. + */ +int rtnl_htb_set_level(struct rtnl_class *class, int level) +{ + struct rtnl_htb_class *htb; + int err; + + if (!(htb = htb_class_data(class, &err))) + return err; + + htb->ch_level = level; + htb->ch_mask |= SCH_HTB_HAS_LEVEL; + + return 0; +} + +/** @} */ + +static struct rtnl_tc_ops htb_qdisc_ops = { + .to_kind = "htb", + .to_type = RTNL_TC_TYPE_QDISC, + .to_size = sizeof(struct rtnl_htb_qdisc), + .to_msg_parser = htb_qdisc_msg_parser, + .to_dump[NL_DUMP_LINE] = htb_qdisc_dump_line, + .to_msg_fill = htb_qdisc_msg_fill, +}; + +static struct rtnl_tc_ops htb_class_ops = { + .to_kind = "htb", + .to_type = RTNL_TC_TYPE_CLASS, + .to_size = sizeof(struct rtnl_htb_class), + .to_msg_parser = htb_class_msg_parser, + .to_dump = { + [NL_DUMP_LINE] = htb_class_dump_line, + [NL_DUMP_DETAILS] = htb_class_dump_details, + }, + .to_msg_fill = htb_class_msg_fill, +}; + +static void __init htb_init(void) +{ + rtnl_tc_register(&htb_qdisc_ops); + rtnl_tc_register(&htb_class_ops); +} + +static void __exit htb_exit(void) +{ + rtnl_tc_unregister(&htb_qdisc_ops); + rtnl_tc_unregister(&htb_class_ops); +} + +/** @} */ diff --git a/libnl/lib/route/qdisc/ingress.c b/libnl/lib/route/qdisc/ingress.c new file mode 100644 index 0000000..254dd86 --- /dev/null +++ b/libnl/lib/route/qdisc/ingress.c @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2013 Cong Wang + */ + +/** + * @ingroup qdisc + * @defgroup qdisc_ingress Ingress qdisc + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + +struct dumb { + uint32_t foo; +}; + +static int dumb_msg_parser(struct rtnl_tc *tc, void *data) +{ + return 0; +} + +static void dumb_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ +} + +static int dumb_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + return 0; +} + +static struct rtnl_tc_ops ingress_ops = { + .to_kind = "ingress", + .to_type = RTNL_TC_TYPE_QDISC, + .to_size = sizeof(struct dumb), + .to_msg_parser = dumb_msg_parser, + .to_dump[NL_DUMP_LINE] = dumb_dump_line, + .to_msg_fill = dumb_msg_fill, +}; + +static void __init ingress_init(void) +{ + rtnl_tc_register(&ingress_ops); +} + +static void __exit ingress_exit(void) +{ + rtnl_tc_unregister(&ingress_ops); +} + +/** @} */ diff --git a/libnl/lib/route/qdisc/mqprio.c b/libnl/lib/route/qdisc/mqprio.c new file mode 100644 index 0000000..62343e7 --- /dev/null +++ b/libnl/lib/route/qdisc/mqprio.c @@ -0,0 +1,601 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2018 Volodymyr Bendiuga + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define SCH_MQPRIO_ATTR_NUMTC (1 << 0) +#define SCH_MQPRIO_ATTR_PRIOMAP (1 << 1) +#define SCH_MQPRIO_ATTR_HW (1 << 2) +#define SCH_MQPRIO_ATTR_QUEUE (1 << 3) +#define SCH_MQPRIO_ATTR_MODE (1 << 4) +#define SCH_MQPRIO_ATTR_SHAPER (1 << 5) +#define SCH_MQPRIO_ATTR_MIN_RATE (1 << 6) +#define SCH_MQPRIO_ATTR_MAX_RATE (1 << 7) +/** @endcond */ + +static struct nla_policy mqprio_policy[TCA_MQPRIO_MAX + 1] = { + [TCA_MQPRIO_MODE] = { .minlen = sizeof(uint16_t) }, + [TCA_MQPRIO_SHAPER] = { .minlen = sizeof(uint16_t) }, + [TCA_MQPRIO_MIN_RATE64] = { .type = NLA_NESTED }, + [TCA_MQPRIO_MAX_RATE64] = { .type = NLA_NESTED }, +}; + +static int mqprio_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct rtnl_mqprio *mqprio = data; + struct tc_mqprio_qopt *qopt; + struct nlattr *attr; + int len, rem, i, err; + + if (tc->tc_opts->d_size < sizeof(*qopt)) + return -NLE_INVAL; + + qopt = (struct tc_mqprio_qopt *) tc->tc_opts->d_data; + mqprio->qm_num_tc = qopt->num_tc; + mqprio->qm_hw = qopt->hw; + memcpy(mqprio->qm_prio_map, qopt->prio_tc_map, + TC_QOPT_MAX_QUEUE * sizeof(uint8_t)); + memcpy(mqprio->qm_count, qopt->count, + TC_QOPT_MAX_QUEUE * sizeof(uint16_t)); + memcpy(mqprio->qm_offset, qopt->offset, + TC_QOPT_MAX_QUEUE * sizeof(uint16_t)); + mqprio->qm_mask = (SCH_MQPRIO_ATTR_NUMTC | SCH_MQPRIO_ATTR_PRIOMAP | + SCH_MQPRIO_ATTR_QUEUE | SCH_MQPRIO_ATTR_HW); + + len = tc->tc_opts->d_size - NLA_ALIGN(sizeof(*qopt)); + + if (len > 0) { + struct nlattr *tb[TCA_MQPRIO_MAX + 1]; + + err = nla_parse(tb, TCA_MQPRIO_MAX, (struct nlattr *) + ((char *) tc->tc_opts->d_data + NLA_ALIGN(sizeof(*qopt))), + len, mqprio_policy); + if (err < 0) + return err; + + if (tb[TCA_MQPRIO_MODE]) { + mqprio->qm_mode = nla_get_u16(tb[TCA_MQPRIO_MODE]); + mqprio->qm_mask |= SCH_MQPRIO_ATTR_MODE; + } + + if (tb[TCA_MQPRIO_SHAPER]) { + mqprio->qm_shaper = nla_get_u16(tb[TCA_MQPRIO_SHAPER]); + mqprio->qm_mask |= SCH_MQPRIO_ATTR_SHAPER; + } + + if (tb[TCA_MQPRIO_MIN_RATE64]) { + i = 0; + nla_for_each_nested(attr, tb[TCA_MQPRIO_MIN_RATE64], rem) { + if (nla_type(attr) != TCA_MQPRIO_MIN_RATE64) + return -EINVAL; + + if (i >= mqprio->qm_num_tc) + break; + + mqprio->qm_min_rate[i] = nla_get_u64(attr); + } + + mqprio->qm_mask |= SCH_MQPRIO_ATTR_MIN_RATE; + } + + if (tb[TCA_MQPRIO_MAX_RATE64]) { + i = 0; + nla_for_each_nested(attr, tb[TCA_MQPRIO_MAX_RATE64], rem) { + if (nla_type(attr) != TCA_MQPRIO_MAX_RATE64) + return -EINVAL; + + if (i >= mqprio->qm_num_tc) + break; + + mqprio->qm_max_rate[i] = nla_get_u64(attr); + } + + mqprio->qm_mask |= SCH_MQPRIO_ATTR_MAX_RATE; + } + } + + return 0; +} + +static int mqprio_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + struct rtnl_mqprio *mqprio = data; + struct tc_mqprio_qopt qopt = { 0 }; + struct nlattr *nest = NULL; + int i; + + if (!mqprio || + !(mqprio->qm_mask & SCH_MQPRIO_ATTR_NUMTC) || + !(mqprio->qm_mask & SCH_MQPRIO_ATTR_PRIOMAP) || + !(mqprio->qm_mask & SCH_MQPRIO_ATTR_QUEUE)) + return -NLE_INVAL; + + if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_HW)) + qopt.hw = 0; + else + qopt.hw = mqprio->qm_hw; + + qopt.num_tc = mqprio->qm_num_tc; + memcpy(qopt.count, mqprio->qm_count, TC_QOPT_MAX_QUEUE * sizeof(uint16_t)); + memcpy(qopt.offset, mqprio->qm_offset, TC_QOPT_MAX_QUEUE * sizeof(uint16_t)); + memcpy(qopt.prio_tc_map, mqprio->qm_prio_map, TC_QOPT_MAX_QUEUE * sizeof(uint8_t)); + + nlmsg_append(msg, &qopt, sizeof(qopt), NL_DONTPAD); + + if (mqprio->qm_hw) { + if (mqprio->qm_mask & SCH_MQPRIO_ATTR_MODE) + NLA_PUT_U16(msg, TCA_MQPRIO_MODE, mqprio->qm_mode); + + if (mqprio->qm_mask & SCH_MQPRIO_ATTR_SHAPER) + NLA_PUT_U16(msg, TCA_MQPRIO_SHAPER, mqprio->qm_shaper); + + if (mqprio->qm_mask & SCH_MQPRIO_ATTR_MIN_RATE) { + nest = nla_nest_start(msg, TCA_MQPRIO_MIN_RATE64); + if (!nest) + goto nla_put_failure; + + for (i = 0; i < mqprio->qm_num_tc; i++) { + if (nla_put(msg, TCA_MQPRIO_MIN_RATE64, + sizeof(mqprio->qm_min_rate[i]), + &mqprio->qm_min_rate[i]) < 0) + goto nla_nest_cancel; + } + nla_nest_end(msg, nest); + } + + if (mqprio->qm_mask & SCH_MQPRIO_ATTR_MAX_RATE) { + nest = nla_nest_start(msg, TCA_MQPRIO_MAX_RATE64); + if (!nest) + goto nla_put_failure; + + for (i = 0; i < mqprio->qm_num_tc; i++) { + if (nla_put(msg, TCA_MQPRIO_MAX_RATE64, + sizeof(mqprio->qm_max_rate[i]), + &mqprio->qm_max_rate[i]) < 0) + goto nla_nest_cancel; + } + nla_nest_end(msg, nest); + } + } + + return 0; + +nla_nest_cancel: + nla_nest_cancel(msg, nest); + return -NLE_MSGSIZE; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +static void mqprio_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_mqprio *mqprio = data; + + if (mqprio) + nl_dump(p, " num_tc %u", mqprio->qm_num_tc); +} + +static void mqprio_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_mqprio *mqprio = data; + int i; + + if (!mqprio) + return; + + nl_dump(p, "map ["); + + for (i = 0; i <= TC_QOPT_BITMASK; i++) + nl_dump(p, "%u%s", mqprio->qm_prio_map[i], + i < TC_QOPT_BITMASK ? " " : ""); + + nl_dump(p, "]\n"); + nl_new_line(p); +} + +/** + * @name Attribute Modification + * @{ + */ + +/** + * Set number of traffic classes. + * @arg qdisc MQPRIO qdisc to be modified. + * @arg num_tc Number of traffic classes to create. + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_mqprio_set_num_tc(struct rtnl_qdisc *qdisc, int num_tc) +{ + struct rtnl_mqprio *mqprio; + + if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + mqprio->qm_num_tc = num_tc; + mqprio->qm_mask |= SCH_MQPRIO_ATTR_NUMTC; + return 0; +} + +/** + * Get number of traffic classes of MQPRIO qdisc. + * @arg qdisc MQPRIO qdisc. + * @return Number of traffic classes or a negative error code. + */ +int rtnl_qdisc_mqprio_get_num_tc(struct rtnl_qdisc *qdisc) +{ + struct rtnl_mqprio *mqprio; + + if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc)))) + return -NLE_INVAL; + + if (mqprio->qm_mask & SCH_MQPRIO_ATTR_NUMTC) + return mqprio->qm_num_tc; + else + return -NLE_MISSING_ATTR; +} + +/** + * Set priomap of the MQPRIO qdisc. + * @arg qdisc MQPRIO qdisc to be modified. + * @arg priomap New priority mapping. + * @arg len Length of priomap (# of elements). + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_mqprio_set_priomap(struct rtnl_qdisc *qdisc, uint8_t priomap[], + int len) +{ + struct rtnl_mqprio *mqprio; + int i; + + if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_NUMTC)) + return -NLE_MISSING_ATTR; + + if ((len / sizeof(uint8_t)) > (TC_QOPT_BITMASK+1)) + return -NLE_RANGE; + + for (i = 0; i <= TC_QOPT_BITMASK; i++) { + if (priomap[i] > mqprio->qm_num_tc) + return -NLE_RANGE; + } + + memcpy(mqprio->qm_prio_map, priomap, len * sizeof(uint8_t)); + mqprio->qm_mask |= SCH_MQPRIO_ATTR_PRIOMAP; + + return 0; +} + +/** + * Get priomap of MQPRIO qdisc. + * @arg qdisc MQPRIO qdisc. + * @return Priority mapping as array of size TC_QOPT_BANDS+1 + * or NULL if an error occured. + */ +uint8_t *rtnl_qdisc_mqprio_get_priomap(struct rtnl_qdisc *qdisc) +{ + struct rtnl_mqprio *mqprio; + + if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc)))) + return NULL; + + if (mqprio->qm_mask & SCH_MQPRIO_ATTR_PRIOMAP) + return mqprio->qm_prio_map; + else + return NULL; +} + +/** + * Offload to HW or run in SW (default). + * @arg qdisc MQPRIO qdisc to be modified. + * @arg offload 1 - offload to HW, 0 - run in SW only (default). + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_mqprio_hw_offload(struct rtnl_qdisc *qdisc, int offload) +{ + struct rtnl_mqprio *mqprio; + + if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + switch (offload) { + case 0: + case 1: + mqprio->qm_hw = offload; + break; + default: + return -NLE_INVAL; + } + + mqprio->qm_mask |= SCH_MQPRIO_ATTR_HW; + return 0; +} + +/** + * Check whether running in HW or SW. + * @arg qdisc MQPRIO qdisc to be modified. + * @return 0 if running in SW, otherwise 1 (HW) + */ +int rtnl_qdisc_mqprio_get_hw_offload(struct rtnl_qdisc *qdisc) +{ + struct rtnl_mqprio *mqprio; + + if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc)))) + return -NLE_INVAL; + + if (mqprio->qm_mask & SCH_MQPRIO_ATTR_HW) + return mqprio->qm_hw; + + return 0; +} + +/** + * Set tc queue of the MQPRIO qdisc. + * @arg qdisc MQPRIO qdisc to be modified. + * @arg count count of queue range for each traffic class + * @arg offset offset of queue range for each traffic class + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_mqprio_set_queue(struct rtnl_qdisc *qdisc, uint16_t count[], + uint16_t offset[], int len) +{ + struct rtnl_mqprio *mqprio; + + if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_NUMTC)) + return -NLE_MISSING_ATTR; + + if ((len / sizeof(uint16_t)) > TC_QOPT_MAX_QUEUE) + return -NLE_RANGE; + + memcpy(mqprio->qm_count, count, len * sizeof(uint16_t)); + memcpy(mqprio->qm_offset, offset, len * sizeof(uint16_t)); + mqprio->qm_mask |= SCH_MQPRIO_ATTR_QUEUE; + + return 0; +} + +/** + * Get tc queue of the MQPRIO qdisc. + * @arg qdisc MQPRIO qdisc to be modified. + * @arg count count of queue range for each traffic class + * @arg offset offset of queue range for each traffic class + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_mqprio_get_queue(struct rtnl_qdisc *qdisc, uint16_t *count, + uint16_t *offset) +{ + struct rtnl_mqprio *mqprio; + + if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc)))) + return -NLE_INVAL; + + if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_QUEUE)) + return -NLE_MISSING_ATTR; + + memcpy(count, mqprio->qm_count, TC_QOPT_MAX_QUEUE * sizeof(uint16_t)); + memcpy(offset, mqprio->qm_offset, TC_QOPT_MAX_QUEUE * sizeof(uint16_t)); + + return 0; +} + +/** + * Set mode of mqprio Qdisc + * @arg qdisc MQPRIO qdisc to be modified. + * @arg mode one of: TC_MQPRIO_MODE_DCB, TC_MQPRIO_MODE_CHANNEL + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_mqprio_set_mode(struct rtnl_qdisc *qdisc, uint16_t mode) +{ + struct rtnl_mqprio *mqprio; + + if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_HW)) + return -NLE_MISSING_ATTR; + + mqprio->qm_mode = mode; + mqprio->qm_mask |= SCH_MQPRIO_ATTR_MODE; + + return 0; +} + +/** + * Get mode of mqprio Qdisc + * @arg qdisc MQPRIO qdisc. + * @return mode on success or negative error code. + */ +int rtnl_qdisc_mqprio_get_mode(struct rtnl_qdisc *qdisc) +{ + struct rtnl_mqprio *mqprio; + + if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc)))) + return -NLE_INVAL; + + if (mqprio->qm_mask & SCH_MQPRIO_ATTR_MODE) + return mqprio->qm_mode; + else + return -NLE_MISSING_ATTR; +} + +/** + * Set shaper of mqprio Qdisc + * @arg qdisc MQPRIO qdisc to be modified. + * @arg shaper one of: TC_MQPRIO_SHAPER_DCB, TC_MQPRIO_SHAPER_BW_RATE + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_mqprio_set_shaper(struct rtnl_qdisc *qdisc, uint16_t shaper) +{ + struct rtnl_mqprio *mqprio; + + if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_HW)) + return -NLE_MISSING_ATTR; + + mqprio->qm_shaper = shaper; + mqprio->qm_mask |= SCH_MQPRIO_ATTR_SHAPER; + + return 0; +} + +/** + * Get shaper of mqprio Qdisc + * @arg qdisc MQPRIO qdisc. + * @return shaper on success or negative error code. + */ +int rtnl_qdisc_mqprio_get_shaper(struct rtnl_qdisc *qdisc) +{ + struct rtnl_mqprio *mqprio; + + if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc)))) + return -NLE_INVAL; + + if (mqprio->qm_mask & SCH_MQPRIO_ATTR_SHAPER) + return mqprio->qm_shaper; + else + return -NLE_MISSING_ATTR; +} + +/** + * Set minimum value of bandwidth rate limit for each traffic class + * @arg qdisc MQPRIO qdisc. + * @arg min minimum rate for each traffic class + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_mqprio_set_min_rate(struct rtnl_qdisc *qdisc, uint64_t min[], int len) +{ + struct rtnl_mqprio *mqprio; + + if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_SHAPER)) + return -NLE_MISSING_ATTR; + + if (mqprio->qm_shaper != TC_MQPRIO_SHAPER_BW_RATE) + return -NLE_INVAL; + + if ((len / sizeof(uint64_t)) > TC_QOPT_MAX_QUEUE) + return -NLE_RANGE; + + memcpy(mqprio->qm_min_rate, min, len * sizeof(uint64_t)); + mqprio->qm_mask |= SCH_MQPRIO_ATTR_MIN_RATE; + + return 0; +} + +/** + * Get minimum value of bandwidth rate limit for each traffic class + * @arg qdisc MQPRIO qdisc. + * @arg min minimum rate for each traffic class + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_mqprio_get_min_rate(struct rtnl_qdisc *qdisc, uint64_t *min) +{ + struct rtnl_mqprio *mqprio; + + if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc)))) + return -NLE_INVAL; + + if (mqprio->qm_mask & SCH_MQPRIO_ATTR_MIN_RATE) { + memcpy(min, mqprio->qm_min_rate, TC_QOPT_MAX_QUEUE * sizeof(uint64_t)); + return 0; + } + + return -NLE_MISSING_ATTR; +} + +/** + * Set maximum value of bandwidth rate limit for each traffic class + * @arg qdisc MQPRIO qdisc. + * @arg max maximum rate for each traffic class + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_mqprio_set_max_rate(struct rtnl_qdisc *qdisc, uint64_t max[], int len) +{ + struct rtnl_mqprio *mqprio; + + if (!(mqprio = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + if (!(mqprio->qm_mask & SCH_MQPRIO_ATTR_SHAPER)) + return -NLE_MISSING_ATTR; + + if (mqprio->qm_shaper != TC_MQPRIO_SHAPER_BW_RATE) + return -NLE_INVAL; + + if ((len / sizeof(uint64_t)) > TC_QOPT_MAX_QUEUE) + return -NLE_RANGE; + + memcpy(mqprio->qm_max_rate, max, len * sizeof(uint64_t)); + mqprio->qm_mask |= SCH_MQPRIO_ATTR_MAX_RATE; + + return 0; +} + +/** + * Get maximum value of bandwidth rate limit for each traffic class + * @arg qdisc MQPRIO qdisc. + * @arg min maximum rate for each traffic class + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_mqprio_get_max_rate(struct rtnl_qdisc *qdisc, uint64_t *max) +{ + struct rtnl_mqprio *mqprio; + + if (!(mqprio = rtnl_tc_data_peek(TC_CAST(qdisc)))) + return -NLE_INVAL; + + if (mqprio->qm_mask & SCH_MQPRIO_ATTR_MAX_RATE) { + memcpy(max, mqprio->qm_max_rate, TC_QOPT_MAX_QUEUE * sizeof(uint64_t)); + return 0; + } + + return -NLE_MISSING_ATTR; +} + +/** @} */ + +static struct rtnl_tc_ops mqprio_ops = { + .to_kind = "mqprio", + .to_type = RTNL_TC_TYPE_QDISC, + .to_size = sizeof(struct rtnl_mqprio), + .to_msg_parser = mqprio_msg_parser, + .to_dump = { + [NL_DUMP_LINE] = mqprio_dump_line, + [NL_DUMP_DETAILS] = mqprio_dump_details, + }, + .to_msg_fill = mqprio_msg_fill, +}; + +static void __init mqprio_init(void) +{ + rtnl_tc_register(&mqprio_ops); +} + +static void __exit mqprio_exit(void) +{ + rtnl_tc_unregister(&mqprio_ops); +} + +/** @} */ diff --git a/libnl/lib/route/qdisc/netem.c b/libnl/lib/route/qdisc/netem.c new file mode 100644 index 0000000..9ee6b1a --- /dev/null +++ b/libnl/lib/route/qdisc/netem.c @@ -0,0 +1,994 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +/** + * @ingroup qdisc + * @defgroup qdisc_netem Network Emulator + * @brief + * + * For further documentation see http://linux-net.osdl.org/index.php/Netem + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +#include "netlink-private/utils.h" + +/** @cond SKIP */ +#define SCH_NETEM_ATTR_LATENCY 0x0001 +#define SCH_NETEM_ATTR_LIMIT 0x0002 +#define SCH_NETEM_ATTR_LOSS 0x0004 +#define SCH_NETEM_ATTR_GAP 0x0008 +#define SCH_NETEM_ATTR_DUPLICATE 0x0010 +#define SCH_NETEM_ATTR_JITTER 0x0020 +#define SCH_NETEM_ATTR_DELAY_CORR 0x0040 +#define SCH_NETEM_ATTR_LOSS_CORR 0x0080 +#define SCH_NETEM_ATTR_DUP_CORR 0x0100 +#define SCH_NETEM_ATTR_RO_PROB 0x0200 +#define SCH_NETEM_ATTR_RO_CORR 0x0400 +#define SCH_NETEM_ATTR_CORRUPT_PROB 0x0800 +#define SCH_NETEM_ATTR_CORRUPT_CORR 0x1000 +#define SCH_NETEM_ATTR_DIST 0x2000 +/** @endcond */ + +static struct nla_policy netem_policy[TCA_NETEM_MAX+1] = { + [TCA_NETEM_CORR] = { .minlen = sizeof(struct tc_netem_corr) }, + [TCA_NETEM_REORDER] = { .minlen = sizeof(struct tc_netem_reorder) }, + [TCA_NETEM_CORRUPT] = { .minlen = sizeof(struct tc_netem_corrupt) }, +}; + +static int netem_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct rtnl_netem *netem = data; + struct tc_netem_qopt *opts; + int len, err = 0; + + if (tc->tc_opts->d_size < sizeof(*opts)) + return -NLE_INVAL; + + opts = (struct tc_netem_qopt *) tc->tc_opts->d_data; + netem->qnm_latency = opts->latency; + netem->qnm_limit = opts->limit; + netem->qnm_loss = opts->loss; + netem->qnm_gap = opts->gap; + netem->qnm_duplicate = opts->duplicate; + netem->qnm_jitter = opts->jitter; + + netem->qnm_mask = (SCH_NETEM_ATTR_LATENCY | SCH_NETEM_ATTR_LIMIT | + SCH_NETEM_ATTR_LOSS | SCH_NETEM_ATTR_GAP | + SCH_NETEM_ATTR_DUPLICATE | SCH_NETEM_ATTR_JITTER); + + len = tc->tc_opts->d_size - sizeof(*opts); + + if (len > 0) { + struct nlattr *tb[TCA_NETEM_MAX+1]; + + err = nla_parse(tb, TCA_NETEM_MAX, (struct nlattr *) + ((char *) tc->tc_opts->d_data + sizeof(*opts)), + len, netem_policy); + if (err < 0) { + free(netem); + return err; + } + + if (tb[TCA_NETEM_CORR]) { + struct tc_netem_corr cor; + + nla_memcpy(&cor, tb[TCA_NETEM_CORR], sizeof(cor)); + netem->qnm_corr.nmc_delay = cor.delay_corr; + netem->qnm_corr.nmc_loss = cor.loss_corr; + netem->qnm_corr.nmc_duplicate = cor.dup_corr; + + netem->qnm_mask |= (SCH_NETEM_ATTR_DELAY_CORR | + SCH_NETEM_ATTR_LOSS_CORR | + SCH_NETEM_ATTR_DUP_CORR); + } + + if (tb[TCA_NETEM_REORDER]) { + struct tc_netem_reorder ro; + + nla_memcpy(&ro, tb[TCA_NETEM_REORDER], sizeof(ro)); + netem->qnm_ro.nmro_probability = ro.probability; + netem->qnm_ro.nmro_correlation = ro.correlation; + + netem->qnm_mask |= (SCH_NETEM_ATTR_RO_PROB | + SCH_NETEM_ATTR_RO_CORR); + } + + if (tb[TCA_NETEM_CORRUPT]) { + struct tc_netem_corrupt corrupt; + + nla_memcpy(&corrupt, tb[TCA_NETEM_CORRUPT], sizeof(corrupt)); + netem->qnm_crpt.nmcr_probability = corrupt.probability; + netem->qnm_crpt.nmcr_correlation = corrupt.correlation; + + netem->qnm_mask |= (SCH_NETEM_ATTR_CORRUPT_PROB | + SCH_NETEM_ATTR_CORRUPT_CORR); + } + + /* sch_netem does not currently dump TCA_NETEM_DELAY_DIST */ + netem->qnm_dist.dist_data = NULL; + netem->qnm_dist.dist_size = 0; + } + + return 0; +} + +static void netem_free_data(struct rtnl_tc *tc, void *data) +{ + struct rtnl_netem *netem = data; + + if (!netem) + return; + + free(netem->qnm_dist.dist_data); +} + +static void netem_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_netem *netem = data; + + if (netem) { + if (netem->qnm_mask & SCH_NETEM_ATTR_LIMIT && netem->qnm_limit > 0) + nl_dump(p, " limit %dpkts", netem->qnm_limit); + else + nl_dump(p, " no limit"); + } +} + +static void netem_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_netem *netem = data; + char buf[32]; + + if (netem) { + if (netem->qnm_mask & SCH_NETEM_ATTR_LATENCY && netem->qnm_latency > 0) { + nl_msec2str(nl_ticks2us(netem->qnm_latency) / 1000, buf, sizeof(buf)); + nl_dump(p, " latency %s", buf); + + if (netem->qnm_mask & SCH_NETEM_ATTR_JITTER && netem->qnm_jitter > 0) { + nl_msec2str(nl_ticks2us(netem->qnm_jitter) / 1000, buf, sizeof(buf)); + nl_dump(p, " jitter %s", buf); + + if (netem->qnm_mask & SCH_NETEM_ATTR_DELAY_CORR && netem->qnm_corr.nmc_delay > 0) + nl_dump(p, " %d%", netem->qnm_corr.nmc_delay); + } + } + + if (netem->qnm_mask & SCH_NETEM_ATTR_LOSS && netem->qnm_loss > 0) { + nl_dump(p, " loss %d%", netem->qnm_loss); + + if (netem->qnm_mask & SCH_NETEM_ATTR_LOSS_CORR && netem->qnm_corr.nmc_loss > 0) + nl_dump(p, " %d%", netem->qnm_corr.nmc_loss); + } + + if (netem->qnm_mask & SCH_NETEM_ATTR_DUPLICATE && netem->qnm_duplicate > 0) { + nl_dump(p, " duplicate %d%", netem->qnm_duplicate); + + if (netem->qnm_mask & SCH_NETEM_ATTR_DUP_CORR && netem->qnm_corr.nmc_duplicate > 0) + nl_dump(p, " %d%", netem->qnm_corr.nmc_duplicate); + } + + if (netem->qnm_mask & SCH_NETEM_ATTR_RO_PROB && netem->qnm_ro.nmro_probability > 0) { + nl_dump(p, " reorder %d%", netem->qnm_ro.nmro_probability); + + if (netem->qnm_mask & SCH_NETEM_ATTR_RO_CORR && netem->qnm_ro.nmro_correlation > 0) + nl_dump(p, " %d%", netem->qnm_ro.nmro_correlation); + + if (netem->qnm_mask & SCH_NETEM_ATTR_GAP && netem->qnm_gap > 0) + nl_dump(p, " gap %d", netem->qnm_gap); + } + + if (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_PROB && netem->qnm_crpt.nmcr_probability > 0) { + nl_dump(p, " reorder %d%", netem->qnm_crpt.nmcr_probability); + + if (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_CORR && netem->qnm_crpt.nmcr_correlation > 0) + nl_dump(p, " %d%", netem->qnm_crpt.nmcr_correlation); + } + } +} + +static int netem_msg_fill_raw(struct rtnl_tc *tc, void *data, + struct nl_msg *msg) +{ + int err = 0; + struct tc_netem_qopt opts; + struct tc_netem_corr cor; + struct tc_netem_reorder reorder; + struct tc_netem_corrupt corrupt; + struct rtnl_netem *netem = data; + + unsigned char set_correlation = 0, set_reorder = 0; + unsigned char set_corrupt = 0, set_dist = 0; + + struct nlattr* head; + struct nlattr* tail; + int old_len; + + if (!netem) + BUG(); + + memset(&opts, 0, sizeof(opts)); + memset(&cor, 0, sizeof(cor)); + memset(&reorder, 0, sizeof(reorder)); + memset(&corrupt, 0, sizeof(corrupt)); + + msg->nm_nlh->nlmsg_flags |= NLM_F_REQUEST; + + if (netem->qnm_ro.nmro_probability != 0) { + if (netem->qnm_latency == 0) + return -NLE_MISSING_ATTR; + if (netem->qnm_gap == 0) + netem->qnm_gap = 1; + } else if (netem->qnm_gap) + return -NLE_MISSING_ATTR; + + if (netem->qnm_corr.nmc_delay != 0) { + if (netem->qnm_latency == 0 || netem->qnm_jitter == 0) + return -NLE_MISSING_ATTR; + set_correlation = 1; + } + + if (netem->qnm_corr.nmc_loss != 0) { + if (netem->qnm_loss == 0) + return -NLE_MISSING_ATTR; + set_correlation = 1; + } + + if (netem->qnm_corr.nmc_duplicate != 0) { + if (netem->qnm_duplicate == 0) + return -NLE_MISSING_ATTR; + set_correlation = 1; + } + + if (netem->qnm_ro.nmro_probability != 0) + set_reorder = 1; + else if (netem->qnm_ro.nmro_correlation != 0) + return -NLE_MISSING_ATTR; + + if (netem->qnm_crpt.nmcr_probability != 0) + set_corrupt = 1; + else if (netem->qnm_crpt.nmcr_correlation != 0) + return -NLE_MISSING_ATTR; + + if (netem->qnm_dist.dist_data && netem->qnm_dist.dist_size) { + if (netem->qnm_latency == 0 || netem->qnm_jitter == 0) + return -NLE_MISSING_ATTR; + else { + /* Resize to accomodate the large distribution table */ + int new_msg_len = msg->nm_size + netem->qnm_dist.dist_size * + sizeof(netem->qnm_dist.dist_data[0]); + struct nlmsghdr *new_nlh = realloc(msg->nm_nlh, new_msg_len); + + if (new_nlh == NULL) + return -NLE_NOMEM; + msg->nm_nlh = new_nlh; + msg->nm_size = new_msg_len; + set_dist = 1; + } + } + + opts.latency = netem->qnm_latency; + opts.limit = netem->qnm_limit ? netem->qnm_limit : 1000; + opts.loss = netem->qnm_loss; + opts.gap = netem->qnm_gap; + opts.duplicate = netem->qnm_duplicate; + opts.jitter = netem->qnm_jitter; + + NLA_PUT(msg, TCA_OPTIONS, sizeof(opts), &opts); + + if (set_correlation) { + cor.delay_corr = netem->qnm_corr.nmc_delay; + cor.loss_corr = netem->qnm_corr.nmc_loss; + cor.dup_corr = netem->qnm_corr.nmc_duplicate; + + NLA_PUT(msg, TCA_NETEM_CORR, sizeof(cor), &cor); + } + + if (set_reorder) { + reorder.probability = netem->qnm_ro.nmro_probability; + reorder.correlation = netem->qnm_ro.nmro_correlation; + + NLA_PUT(msg, TCA_NETEM_REORDER, sizeof(reorder), &reorder); + } + + if (set_corrupt) { + corrupt.probability = netem->qnm_crpt.nmcr_probability; + corrupt.correlation = netem->qnm_crpt.nmcr_correlation; + + NLA_PUT(msg, TCA_NETEM_CORRUPT, sizeof(corrupt), &corrupt); + } + + if (set_dist) { + NLA_PUT(msg, TCA_NETEM_DELAY_DIST, + netem->qnm_dist.dist_size * sizeof(netem->qnm_dist.dist_data[0]), + netem->qnm_dist.dist_data); + } + + /* Length specified in the TCA_OPTIONS section must span the entire + * remainder of the message. That's just the way that sch_netem expects it. + * Maybe there's a more succinct way to do this at a higher level. + */ + head = (struct nlattr *)(((char *) NLMSG_DATA(msg->nm_nlh)) + + NLMSG_LENGTH(sizeof(struct tcmsg)) - NLMSG_ALIGNTO); + + tail = (struct nlattr *)(((char *) (msg->nm_nlh)) + + NLMSG_ALIGN(msg->nm_nlh->nlmsg_len)); + + old_len = head->nla_len; + head->nla_len = (char *)tail - (char *)head; + msg->nm_nlh->nlmsg_len += (head->nla_len - old_len); + + return err; +nla_put_failure: + return -NLE_MSGSIZE; +} + +/** + * @name Queue Limit + * @{ + */ + +/** + * Set limit of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg limit New limit in bytes. + * @return 0 on success or a negative error code. + */ +void rtnl_netem_set_limit(struct rtnl_qdisc *qdisc, int limit) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + netem->qnm_limit = limit; + netem->qnm_mask |= SCH_NETEM_ATTR_LIMIT; +} + +/** + * Get limit of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Limit in bytes or a negative error code. + */ +int rtnl_netem_get_limit(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + if (netem->qnm_mask & SCH_NETEM_ATTR_LIMIT) + return netem->qnm_limit; + else + return -NLE_NOATTR; +} + +/** @} */ + +/** + * @name Packet Re-ordering + * @{ + */ + +/** + * Set re-ordering gap of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg gap New gap in number of packets. + * @return 0 on success or a negative error code. + */ +void rtnl_netem_set_gap(struct rtnl_qdisc *qdisc, int gap) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + netem->qnm_gap = gap; + netem->qnm_mask |= SCH_NETEM_ATTR_GAP; +} + +/** + * Get re-ordering gap of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Re-ordering gap in packets or a negative error code. + */ +int rtnl_netem_get_gap(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + if (netem->qnm_mask & SCH_NETEM_ATTR_GAP) + return netem->qnm_gap; + else + return -NLE_NOATTR; +} + +/** + * Set re-ordering probability of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg prob New re-ordering probability. + * @return 0 on success or a negative error code. + */ +void rtnl_netem_set_reorder_probability(struct rtnl_qdisc *qdisc, int prob) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + netem->qnm_ro.nmro_probability = prob; + netem->qnm_mask |= SCH_NETEM_ATTR_RO_PROB; +} + +/** + * Get re-ordering probability of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Re-ordering probability or a negative error code. + */ +int rtnl_netem_get_reorder_probability(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + if (netem->qnm_mask & SCH_NETEM_ATTR_RO_PROB) + return netem->qnm_ro.nmro_probability; + else + return -NLE_NOATTR; +} + +/** + * Set re-order correlation probability of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg prob New re-ordering correlation probability. + * @return 0 on success or a negative error code. + */ +void rtnl_netem_set_reorder_correlation(struct rtnl_qdisc *qdisc, int prob) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + netem->qnm_ro.nmro_correlation = prob; + netem->qnm_mask |= SCH_NETEM_ATTR_RO_CORR; +} + +/** + * Get re-ordering correlation probability of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Re-ordering correlation probability or a negative error code. + */ +int rtnl_netem_get_reorder_correlation(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + if (netem->qnm_mask & SCH_NETEM_ATTR_RO_CORR) + return netem->qnm_ro.nmro_correlation; + else + return -NLE_NOATTR; +} + +/** @} */ + +/** + * @name Corruption + * @{ + */ + +/** + * Set corruption probability of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg prob New corruption probability. + * @return 0 on success or a negative error code. + */ +void rtnl_netem_set_corruption_probability(struct rtnl_qdisc *qdisc, int prob) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + netem->qnm_crpt.nmcr_probability = prob; + netem->qnm_mask |= SCH_NETEM_ATTR_CORRUPT_PROB; +} + +/** + * Get corruption probability of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Corruption probability or a negative error code. + */ +int rtnl_netem_get_corruption_probability(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_PROB) + return netem->qnm_crpt.nmcr_probability; + else + return -NLE_NOATTR; +} + +/** + * Set corruption correlation probability of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg prob New corruption correlation probability. + * @return 0 on success or a negative error code. + */ +void rtnl_netem_set_corruption_correlation(struct rtnl_qdisc *qdisc, int prob) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + netem->qnm_crpt.nmcr_correlation = prob; + netem->qnm_mask |= SCH_NETEM_ATTR_CORRUPT_CORR; +} + +/** + * Get corruption correlation probability of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Corruption correlation probability or a negative error code. + */ +int rtnl_netem_get_corruption_correlation(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_CORR) + return netem->qnm_crpt.nmcr_correlation; + else + return -NLE_NOATTR; +} + +/** @} */ + +/** + * @name Packet Loss + * @{ + */ + +/** + * Set packet loss probability of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg prob New packet loss probability. + * @return 0 on success or a negative error code. + */ +void rtnl_netem_set_loss(struct rtnl_qdisc *qdisc, int prob) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + netem->qnm_loss = prob; + netem->qnm_mask |= SCH_NETEM_ATTR_LOSS; +} + +/** + * Get packet loss probability of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Packet loss probability or a negative error code. + */ +int rtnl_netem_get_loss(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (netem->qnm_mask & SCH_NETEM_ATTR_LOSS) + return netem->qnm_loss; + else + return -NLE_NOATTR; +} + +/** + * Set packet loss correlation probability of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg prob New packet loss correlation. + * @return 0 on success or a negative error code. + */ +void rtnl_netem_set_loss_correlation(struct rtnl_qdisc *qdisc, int prob) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + netem->qnm_corr.nmc_loss = prob; + netem->qnm_mask |= SCH_NETEM_ATTR_LOSS_CORR; +} + +/** + * Get packet loss correlation probability of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Packet loss correlation probability or a negative error code. + */ +int rtnl_netem_get_loss_correlation(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (netem->qnm_mask & SCH_NETEM_ATTR_LOSS_CORR) + return netem->qnm_corr.nmc_loss; + else + return -NLE_NOATTR; +} + +/** @} */ + +/** + * @name Packet Duplication + * @{ + */ + +/** + * Set packet duplication probability of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg prob New packet duplication probability. + * @return 0 on success or a negative error code. + */ +void rtnl_netem_set_duplicate(struct rtnl_qdisc *qdisc, int prob) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + netem->qnm_duplicate = prob; + netem->qnm_mask |= SCH_NETEM_ATTR_DUPLICATE; +} + +/** + * Get packet duplication probability of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Packet duplication probability or a negative error code. + */ +int rtnl_netem_get_duplicate(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (netem->qnm_mask & SCH_NETEM_ATTR_DUPLICATE) + return netem->qnm_duplicate; + else + return -NLE_NOATTR; +} + +/** + * Set packet duplication correlation probability of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg prob New packet duplication correlation probability. + * @return 0 on sucess or a negative error code. + */ +void rtnl_netem_set_duplicate_correlation(struct rtnl_qdisc *qdisc, int prob) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + netem->qnm_corr.nmc_duplicate = prob; + netem->qnm_mask |= SCH_NETEM_ATTR_DUP_CORR; +} + +/** + * Get packet duplication correlation probability of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Packet duplication correlation probability or a negative error code. + */ +int rtnl_netem_get_duplicate_correlation(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (netem->qnm_mask & SCH_NETEM_ATTR_DUP_CORR) + return netem->qnm_corr.nmc_duplicate; + else + return -NLE_NOATTR; +} + +/** @} */ + +/** + * @name Packet Delay + * @{ + */ + +/** + * Set packet delay of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg delay New packet delay in micro seconds. + * @return 0 on success or a negative error code. + */ +void rtnl_netem_set_delay(struct rtnl_qdisc *qdisc, int delay) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + netem->qnm_latency = nl_us2ticks(delay); + netem->qnm_mask |= SCH_NETEM_ATTR_LATENCY; +} + +/** + * Get packet delay of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Packet delay in micro seconds or a negative error code. + */ +int rtnl_netem_get_delay(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (netem->qnm_mask & SCH_NETEM_ATTR_LATENCY) + return nl_ticks2us(netem->qnm_latency); + else + return -NLE_NOATTR; +} + +/** + * Set packet delay jitter of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg jitter New packet delay jitter in micro seconds. + * @return 0 on success or a negative error code. + */ +void rtnl_netem_set_jitter(struct rtnl_qdisc *qdisc, int jitter) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + netem->qnm_jitter = nl_us2ticks(jitter); + netem->qnm_mask |= SCH_NETEM_ATTR_JITTER; +} + +/** + * Get packet delay jitter of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Packet delay jitter in micro seconds or a negative error code. + */ +int rtnl_netem_get_jitter(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (netem->qnm_mask & SCH_NETEM_ATTR_JITTER) + return nl_ticks2us(netem->qnm_jitter); + else + return -NLE_NOATTR; +} + +/** + * Set packet delay correlation probability of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg prob New packet delay correlation probability. + */ +void rtnl_netem_set_delay_correlation(struct rtnl_qdisc *qdisc, int prob) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + netem->qnm_corr.nmc_delay = prob; + netem->qnm_mask |= SCH_NETEM_ATTR_DELAY_CORR; +} + +/** + * Get packet delay correlation probability of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Packet delay correlation probability or a negative error code. + */ +int rtnl_netem_get_delay_correlation(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (netem->qnm_mask & SCH_NETEM_ATTR_DELAY_CORR) + return netem->qnm_corr.nmc_delay; + else + return -NLE_NOATTR; +} + +/** + * Get the size of the distribution table. + * @arg qdisc Netem qdisc. + * @return Distribution table size or a negative error code. + */ +int rtnl_netem_get_delay_distribution_size(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (netem->qnm_mask & SCH_NETEM_ATTR_DIST) + return netem->qnm_dist.dist_size; + else + return -NLE_NOATTR; +} + +/** + * Get a pointer to the distribution table. + * @arg qdisc Netem qdisc. + * @arg dist_ptr The pointer to set. + * @return Negative error code on failure or 0 on success. + */ +int rtnl_netem_get_delay_distribution(struct rtnl_qdisc *qdisc, int16_t **dist_ptr) +{ + struct rtnl_netem *netem; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (netem->qnm_mask & SCH_NETEM_ATTR_DIST) { + *dist_ptr = netem->qnm_dist.dist_data; + return 0; + } else + return -NLE_NOATTR; +} + +/** + * Set the delay distribution data. Latency/jitter must be set before applying. + * @arg qdisc Netem qdisc. + * @return 0 on success, error code on failure. + */ +int rtnl_netem_set_delay_distribution_data(struct rtnl_qdisc *qdisc, const int16_t *data, size_t len) { + struct rtnl_netem *netem; + int16_t *new_data; + + if (!(netem = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (len > MAXDIST) + return -NLE_INVAL; + + new_data = (int16_t *) calloc(len, sizeof(int16_t)); + if (!new_data) + return -NLE_NOMEM; + + free (netem->qnm_dist.dist_data); + netem->qnm_dist.dist_data = new_data; + + memcpy(netem->qnm_dist.dist_data, data, len * sizeof(int16_t)); + + netem->qnm_dist.dist_size = len; + netem->qnm_mask |= SCH_NETEM_ATTR_DIST; + + return 0; +} + +/** + * Load the delay distribution from a file. Latency/jitter must be set before applying. + * @arg qdisc Netem qdisc. + * @arg dist_type The name of the distribution (type, file, path/file). + * @return 0 on success, error code on failure. + */ +int rtnl_netem_set_delay_distribution(struct rtnl_qdisc *qdisc, const char *dist_type) { + FILE *f; + int n = 0; + size_t i; + size_t len = 2048; + _nl_auto_free char *line = NULL; + char name[NAME_MAX]; + char dist_suffix[] = ".dist"; + _nl_auto_free int16_t *data = NULL; + char *test_suffix; + + /* Check several locations for the dist file */ + char *test_path[] = { + "", + "./", + "/usr/lib/tc/", + "/usr/lib64/tc/", + "/usr/local/lib/tc/", + }; + + /* If the given filename already ends in .dist, don't append it later */ + test_suffix = strstr(dist_type, dist_suffix); + if (test_suffix != NULL && strlen(test_suffix) == 5) + strcpy(dist_suffix, ""); + + for (i = 0; i < ARRAY_SIZE(test_path); i++) { + snprintf(name, NAME_MAX, "%s%s%s", test_path[i], dist_type, dist_suffix); + if ((f = fopen(name, "re"))) + break; + } + + if (f == NULL) + return -nl_syserr2nlerr(errno); + + data = (int16_t *) calloc(MAXDIST, sizeof(int16_t)); + line = (char *) calloc(sizeof(char), len + 1); + if (!data || !line) { + fclose(f); + return -NLE_NOMEM; + } + + while (getline(&line, &len, f) != -1) { + char *p, *endp; + + if (*line == '\n' || *line == '#') + continue; + + for (p = line; ; p = endp) { + long x = strtol(p, &endp, 0); + if (endp == p) break; + + if (n >= MAXDIST) { + fclose(f); + return -NLE_INVAL; + } + data[n++] = x; + } + } + + fclose(f); + i = rtnl_netem_set_delay_distribution_data(qdisc, data, n); + return i; +} + +/** @} */ + +static struct rtnl_tc_ops netem_ops = { + .to_kind = "netem", + .to_type = RTNL_TC_TYPE_QDISC, + .to_size = sizeof(struct rtnl_netem), + .to_msg_parser = netem_msg_parser, + .to_free_data = netem_free_data, + .to_dump[NL_DUMP_LINE] = netem_dump_line, + .to_dump[NL_DUMP_DETAILS] = netem_dump_details, + .to_msg_fill_raw = netem_msg_fill_raw, +}; + +static void __init netem_init(void) +{ + rtnl_tc_register(&netem_ops); +} + +static void __exit netem_exit(void) +{ + rtnl_tc_unregister(&netem_ops); +} + +/** @} */ diff --git a/libnl/lib/route/qdisc/plug.c b/libnl/lib/route/qdisc/plug.c new file mode 100644 index 0000000..1124cd4 --- /dev/null +++ b/libnl/lib/route/qdisc/plug.c @@ -0,0 +1,173 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2012 Shriram Rajagopalan + */ + +/** + * @ingroup qdisc + * @defgroup qdisc_plug Plug/Unplug Traffic (PLUG) + * @brief + * + * Queue traffic until an explicit release command. + * + * There are two ways to use this qdisc: + * 1. A simple "instantaneous" plug/unplug operation, by issuing an alternating + * sequence of TCQ_PLUG_BUFFER & TCQ_PLUG_RELEASE_INDEFINITE commands. + * + * 2. For network output buffering (a.k.a output commit) functionality. + * Output commit property is commonly used by applications using checkpoint + * based fault-tolerance to ensure that the checkpoint from which a system + * is being restored is consistent w.r.t outside world. + * + * Consider for e.g. Remus - a Virtual Machine checkpointing system, + * wherein a VM is checkpointed, say every 50ms. The checkpoint is replicated + * asynchronously to the backup host, while the VM continues executing the + * next epoch speculatively. + * + * The following is a typical sequence of output buffer operations: + * 1.At epoch i, start_buffer(i) + * 2. At end of epoch i (i.e. after 50ms): + * 2.1 Stop VM and take checkpoint(i). + * 2.2 start_buffer(i+1) and Resume VM + * 3. While speculatively executing epoch(i+1), asynchronously replicate + * checkpoint(i) to backup host. + * 4. When checkpoint_ack(i) is received from backup, release_buffer(i) + * Thus, this Qdisc would receive the following sequence of commands: + * TCQ_PLUG_BUFFER (epoch i) + * .. TCQ_PLUG_BUFFER (epoch i+1) + * ....TCQ_PLUG_RELEASE_ONE (epoch i) + * ......TCQ_PLUG_BUFFER (epoch i+2) + * ........ + * + * + * State of the queue, when used for network output buffering: + * + * plug(i+1) plug(i) head + * ------------------+--------------------+----------------> + * | | + * | | + * pkts_current_epoch| pkts_last_epoch |pkts_to_release + * ----------------->|<--------+--------->|+---------------> + * v v + * + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + +static int plug_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + struct rtnl_plug *plug = data; + struct tc_plug_qopt opts; + + if (!plug) + return -NLE_INVAL; + + opts.action = plug->action; + opts.limit = plug->limit; + + return nlmsg_append(msg, &opts, sizeof(opts), NL_DONTPAD); +} + +/** + * @name Attribute Modification + * @{ + */ + +/** + * Insert a plug into the qdisc and buffer any incoming + * network traffic. + * @arg qdisc PLUG qdisc to be modified. + */ +int rtnl_qdisc_plug_buffer(struct rtnl_qdisc *qdisc) +{ + struct rtnl_plug *plug; + + if (!(plug = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + plug->action = TCQ_PLUG_BUFFER; + return 0; +} + +/** + * Unplug the qdisc, releasing packets from queue head + * to the last complete buffer, while new traffic + * continues to be buffered. + * @arg qdisc PLUG qdisc to be modified. + */ +int rtnl_qdisc_plug_release_one(struct rtnl_qdisc *qdisc) +{ + struct rtnl_plug *plug; + + if (!(plug = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + plug->action = TCQ_PLUG_RELEASE_ONE; + return 0; +} + +/** + * Indefinitely unplug the qdisc, releasing all packets. + * Network traffic will not be buffered until the next + * buffer command is issued. + * @arg qdisc PLUG qdisc to be modified. + */ +int rtnl_qdisc_plug_release_indefinite(struct rtnl_qdisc *qdisc) +{ + struct rtnl_plug *plug; + + if (!(plug = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + plug->action = TCQ_PLUG_RELEASE_INDEFINITE; + return 0; +} + +/** + * Set limit of PLUG qdisc. + * @arg qdisc PLUG qdisc to be modified. + * @arg limit New limit. + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_plug_set_limit(struct rtnl_qdisc *qdisc, int limit) +{ + struct rtnl_plug *plug; + + if (!(plug = rtnl_tc_data(TC_CAST(qdisc)))) + return -NLE_NOMEM; + + plug->action = TCQ_PLUG_LIMIT; + plug->limit = limit; + + return 0; +} + +/** @} */ + +static struct rtnl_tc_ops plug_ops = { + .to_kind = "plug", + .to_type = RTNL_TC_TYPE_QDISC, + .to_size = sizeof(struct rtnl_plug), + .to_msg_fill = plug_msg_fill, +}; + +static void __init plug_init(void) +{ + rtnl_tc_register(&plug_ops); +} + +static void __exit plug_exit(void) +{ + rtnl_tc_unregister(&plug_ops); +} + +/** @} */ diff --git a/libnl/lib/route/qdisc/prio.c b/libnl/lib/route/qdisc/prio.c new file mode 100644 index 0000000..5507fd1 --- /dev/null +++ b/libnl/lib/route/qdisc/prio.c @@ -0,0 +1,290 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +/** + * @ingroup qdisc + * @defgroup qdisc_prio (Fast) Prio + * @brief + * + * @par 1) Typical PRIO configuration + * @code + * // Specify the maximal number of bands to be used for this PRIO qdisc. + * rtnl_qdisc_prio_set_bands(qdisc, QDISC_PRIO_DEFAULT_BANDS); + * + * // Provide a map assigning each priority to a band number. + * uint8_t map[] = QDISC_PRIO_DEFAULT_PRIOMAP; + * rtnl_qdisc_prio_set_priomap(qdisc, map, sizeof(map)); + * @endcode + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define SCH_PRIO_ATTR_BANDS 1 +#define SCH_PRIO_ATTR_PRIOMAP 2 +/** @endcond */ + +static int prio_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct rtnl_prio *prio = data; + struct tc_prio_qopt *opt; + + if (tc->tc_opts->d_size < sizeof(*opt)) + return -NLE_INVAL; + + opt = (struct tc_prio_qopt *) tc->tc_opts->d_data; + prio->qp_bands = opt->bands; + memcpy(prio->qp_priomap, opt->priomap, sizeof(prio->qp_priomap)); + prio->qp_mask = (SCH_PRIO_ATTR_BANDS | SCH_PRIO_ATTR_PRIOMAP); + + return 0; +} + +static void prio_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_prio *prio = data; + + if (prio) + nl_dump(p, " bands %u", prio->qp_bands); +} + +static void prio_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_prio *prio = data; + int i, hp; + + if (!prio) + return; + + nl_dump(p, "priomap ["); + + for (i = 0; i <= TC_PRIO_MAX; i++) + nl_dump(p, "%u%s", prio->qp_priomap[i], + i < TC_PRIO_MAX ? " " : ""); + + nl_dump(p, "]\n"); + nl_new_line(p); + + hp = (((TC_PRIO_MAX/2) + 1) & ~1); + + for (i = 0; i < hp; i++) { + char a[32]; + nl_dump(p, " %18s => %u", + rtnl_prio2str(i, a, sizeof(a)), + prio->qp_priomap[i]); + if (hp+i <= TC_PRIO_MAX) { + nl_dump(p, " %18s => %u", + rtnl_prio2str(hp+i, a, sizeof(a)), + prio->qp_priomap[hp+i]); + if (i < (hp - 1)) { + nl_dump(p, "\n"); + nl_new_line(p); + } + } + } +} + +static int prio_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + struct rtnl_prio *prio = data; + struct tc_prio_qopt opts; + + if (!prio || !(prio->qp_mask & SCH_PRIO_ATTR_PRIOMAP)) + BUG(); + + opts.bands = prio->qp_bands; + memcpy(opts.priomap, prio->qp_priomap, sizeof(opts.priomap)); + + return nlmsg_append(msg, &opts, sizeof(opts), NL_DONTPAD); +} + +/** + * @name Attribute Modification + * @{ + */ + +/** + * Set number of bands of PRIO qdisc. + * @arg qdisc PRIO qdisc to be modified. + * @arg bands New number of bands. + * @return 0 on success or a negative error code. + */ +void rtnl_qdisc_prio_set_bands(struct rtnl_qdisc *qdisc, int bands) +{ + struct rtnl_prio *prio; + + if (!(prio = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + prio->qp_bands = bands; + prio->qp_mask |= SCH_PRIO_ATTR_BANDS; +} + +/** + * Get number of bands of PRIO qdisc. + * @arg qdisc PRIO qdisc. + * @return Number of bands or a negative error code. + */ +int rtnl_qdisc_prio_get_bands(struct rtnl_qdisc *qdisc) +{ + struct rtnl_prio *prio; + + if (!(prio = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (prio->qp_mask & SCH_PRIO_ATTR_BANDS) + return prio->qp_bands; + else + return -NLE_NOMEM; +} + +/** + * Set priomap of the PRIO qdisc. + * @arg qdisc PRIO qdisc to be modified. + * @arg priomap New priority mapping. + * @arg len Length of priomap (# of elements). + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_prio_set_priomap(struct rtnl_qdisc *qdisc, uint8_t priomap[], + int len) +{ + struct rtnl_prio *prio; + int i; + + if (!(prio = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (!(prio->qp_mask & SCH_PRIO_ATTR_BANDS)) + return -NLE_MISSING_ATTR; + + if ((len / sizeof(uint8_t)) > (TC_PRIO_MAX+1)) + return -NLE_RANGE; + + for (i = 0; i <= TC_PRIO_MAX; i++) { + if (priomap[i] > prio->qp_bands) + return -NLE_RANGE; + } + + memcpy(prio->qp_priomap, priomap, len); + prio->qp_mask |= SCH_PRIO_ATTR_PRIOMAP; + + return 0; +} + +/** + * Get priomap of a PRIO qdisc. + * @arg qdisc PRIO qdisc. + * @return Priority mapping as array of size TC_PRIO_MAX+1 + * or NULL if an error occured. + */ +uint8_t *rtnl_qdisc_prio_get_priomap(struct rtnl_qdisc *qdisc) +{ + struct rtnl_prio *prio; + + if (!(prio = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (prio->qp_mask & SCH_PRIO_ATTR_PRIOMAP) + return prio->qp_priomap; + else + return NULL; +} + +/** @} */ + +/** + * @name Priority Band Translations + * @{ + */ + +static const struct trans_tbl prios[] = { + __ADD(TC_PRIO_BESTEFFORT,besteffort), + __ADD(TC_PRIO_FILLER,filler), + __ADD(TC_PRIO_BULK,bulk), + __ADD(TC_PRIO_INTERACTIVE_BULK,interactive_bulk), + __ADD(TC_PRIO_INTERACTIVE,interactive), + __ADD(TC_PRIO_CONTROL,control), +}; + +/** + * Convert priority to character string. + * @arg prio Priority. + * @arg buf Destination buffer + * @arg size Size of destination buffer. + * + * Converts a priority to a character string and stores the result in + * the specified destination buffer. + * + * @return Name of priority as character string. + */ +char * rtnl_prio2str(int prio, char *buf, size_t size) +{ + return __type2str(prio, buf, size, prios, ARRAY_SIZE(prios)); +} + +/** + * Convert character string to priority. + * @arg name Name of priority. + * + * Converts the provided character string specifying a priority + * to the corresponding numeric value. + * + * @return Numeric priority or a negative value if no match was found. + */ +int rtnl_str2prio(const char *name) +{ + return __str2type(name, prios, ARRAY_SIZE(prios)); +} + +/** @} */ + +static struct rtnl_tc_ops prio_ops = { + .to_kind = "prio", + .to_type = RTNL_TC_TYPE_QDISC, + .to_size = sizeof(struct rtnl_prio), + .to_msg_parser = prio_msg_parser, + .to_dump = { + [NL_DUMP_LINE] = prio_dump_line, + [NL_DUMP_DETAILS] = prio_dump_details, + }, + .to_msg_fill = prio_msg_fill, +}; + +static struct rtnl_tc_ops pfifo_fast_ops = { + .to_kind = "pfifo_fast", + .to_type = RTNL_TC_TYPE_QDISC, + .to_size = sizeof(struct rtnl_prio), + .to_msg_parser = prio_msg_parser, + .to_dump = { + [NL_DUMP_LINE] = prio_dump_line, + [NL_DUMP_DETAILS] = prio_dump_details, + }, + .to_msg_fill = prio_msg_fill, +}; + +static void __init prio_init(void) +{ + rtnl_tc_register(&prio_ops); + rtnl_tc_register(&pfifo_fast_ops); +} + +static void __exit prio_exit(void) +{ + rtnl_tc_unregister(&prio_ops); + rtnl_tc_unregister(&pfifo_fast_ops); +} + +/** @} */ diff --git a/libnl/lib/route/qdisc/red.c b/libnl/lib/route/qdisc/red.c new file mode 100644 index 0000000..86ceb4e --- /dev/null +++ b/libnl/lib/route/qdisc/red.c @@ -0,0 +1,186 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +/** + * @ingroup qdisc + * @defgroup qdisc_red Random Early Detection (RED) + * @brief + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define RED_ATTR_LIMIT 0x01 +#define RED_ATTR_QTH_MIN 0x02 +#define RED_ATTR_QTH_MAX 0x04 +#define RED_ATTR_FLAGS 0x08 +#define RED_ATTR_WLOG 0x10 +#define RED_ATTR_PLOG 0x20 +#define RED_ATTR_SCELL_LOG 0x40 +/** @endcond */ + +static struct nla_policy red_policy[TCA_RED_MAX+1] = { + [TCA_RED_PARMS] = { .minlen = sizeof(struct tc_red_qopt) }, +}; + +static int red_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct nlattr *tb[TCA_RED_MAX+1]; + struct rtnl_red *red = data; + struct tc_red_qopt *opts; + int err; + + if (!(tc->ce_mask & TCA_ATTR_OPTS)) + return 0; + + err = tca_parse(tb, TCA_RED_MAX, tc, red_policy); + if (err < 0) + return err; + + if (!tb[TCA_RED_PARMS]) + return -NLE_MISSING_ATTR; + + opts = nla_data(tb[TCA_RED_PARMS]); + + red->qr_limit = opts->limit; + red->qr_qth_min = opts->qth_min; + red->qr_qth_max = opts->qth_max; + red->qr_flags = opts->flags; + red->qr_wlog = opts->Wlog; + red->qr_plog = opts->Plog; + red->qr_scell_log = opts->Scell_log; + + red->qr_mask = (RED_ATTR_LIMIT | RED_ATTR_QTH_MIN | RED_ATTR_QTH_MAX | + RED_ATTR_FLAGS | RED_ATTR_WLOG | RED_ATTR_PLOG | + RED_ATTR_SCELL_LOG); + + return 0; +} + +static void red_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_red *red = data; + + if (red) { + /* XXX: limit, min, max, flags */ + } +} + +static void red_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_red *red = data; + + if (red) { + /* XXX: wlog, plog, scell_log */ + } +} + +static void red_dump_stats(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_red *red = data; + + if (red) { + /* XXX: xstats */ + } +} + +static int red_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + struct rtnl_red *red = data; + + if (!red) + BUG(); + +#if 0 + memset(&opts, 0, sizeof(opts)); + opts.quantum = sfq->qs_quantum; + opts.perturb_period = sfq->qs_perturb; + opts.limit = sfq->qs_limit; + + if (nlmsg_append(msg, &opts, sizeof(opts), NL_DONTPAD) < 0) + goto errout; +#endif + + return -NLE_OPNOTSUPP; +} + +/** + * @name Attribute Access + * @{ + */ + +/** + * Set limit of RED qdisc. + * @arg qdisc RED qdisc to be modified. + * @arg limit New limit in number of packets. + * @return 0 on success or a negative error code. + */ +void rtnl_red_set_limit(struct rtnl_qdisc *qdisc, int limit) +{ + struct rtnl_red *red; + + if (!(red = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + red->qr_limit = limit; + red->qr_mask |= RED_ATTR_LIMIT; +} + +/** + * Get limit of RED qdisc. + * @arg qdisc RED qdisc. + * @return Limit or a negative error code. + */ +int rtnl_red_get_limit(struct rtnl_qdisc *qdisc) +{ + struct rtnl_red *red; + + if (!(red = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (red->qr_mask & RED_ATTR_LIMIT) + return red->qr_limit; + else + return -NLE_NOATTR; +} + +/** @} */ + +static struct rtnl_tc_ops red_ops = { + .to_kind = "red", + .to_type = RTNL_TC_TYPE_QDISC, + .to_size = sizeof(struct rtnl_red), + .to_msg_parser = red_msg_parser, + .to_dump = { + [NL_DUMP_LINE] = red_dump_line, + [NL_DUMP_DETAILS] = red_dump_details, + [NL_DUMP_STATS] = red_dump_stats, + }, + .to_msg_fill = red_msg_fill, +}; + +static void __init red_init(void) +{ + rtnl_tc_register(&red_ops); +} + +static void __exit red_exit(void) +{ + rtnl_tc_unregister(&red_ops); +} + +/** @} */ diff --git a/libnl/lib/route/qdisc/sfq.c b/libnl/lib/route/qdisc/sfq.c new file mode 100644 index 0000000..2e88e1c --- /dev/null +++ b/libnl/lib/route/qdisc/sfq.c @@ -0,0 +1,252 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +/** + * @ingroup qdisc + * @defgroup qdisc_sfq Stochastic Fairness Queueing (SFQ) + * @brief + * + * @par Parameter Description + * - \b Quantum: Number of bytes to send out per slot and round. + * - \b Perturbation: Timer period between changing the hash function. + * - \b Limit: Upper limit of queue in number of packets before SFQ starts + * dropping packets. + * - \b Divisor: Hash table divisor, i.e. size of hash table. + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define SCH_SFQ_ATTR_QUANTUM 0x01 +#define SCH_SFQ_ATTR_PERTURB 0x02 +#define SCH_SFQ_ATTR_LIMIT 0x04 +#define SCH_SFQ_ATTR_DIVISOR 0x08 +#define SCH_SFQ_ATTR_FLOWS 0x10 +/** @endcond */ + +static int sfq_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct rtnl_sfq *sfq = data; + struct tc_sfq_qopt *opts; + + if (!(tc->ce_mask & TCA_ATTR_OPTS)) + return 0; + + if (tc->tc_opts->d_size < sizeof(*opts)) + return -NLE_INVAL; + + opts = (struct tc_sfq_qopt *) tc->tc_opts->d_data; + + sfq->qs_quantum = opts->quantum; + sfq->qs_perturb = opts->perturb_period; + sfq->qs_limit = opts->limit; + sfq->qs_divisor = opts->divisor; + sfq->qs_flows = opts->flows; + + sfq->qs_mask = (SCH_SFQ_ATTR_QUANTUM | SCH_SFQ_ATTR_PERTURB | + SCH_SFQ_ATTR_LIMIT | SCH_SFQ_ATTR_DIVISOR | + SCH_SFQ_ATTR_FLOWS); + + return 0; +} + +static void sfq_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_sfq *sfq = data; + + if (sfq) + nl_dump(p, " quantum %u perturb %us", sfq->qs_quantum, + sfq->qs_perturb); +} + +static void sfq_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_sfq *sfq = data; + + if (sfq) + nl_dump(p, "limit %u divisor %u", + sfq->qs_limit, sfq->qs_divisor); +} + +static int sfq_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + struct rtnl_sfq *sfq = data; + struct tc_sfq_qopt opts = {0}; + + if (!sfq) + BUG(); + + opts.quantum = sfq->qs_quantum; + opts.perturb_period = sfq->qs_perturb; + opts.limit = sfq->qs_limit; + + return nlmsg_append(msg, &opts, sizeof(opts), NL_DONTPAD); +} + +/** + * @name Attribute Access + * @{ + */ + +/** + * Set quantum of SFQ qdisc. + * @arg qdisc SFQ qdisc to be modified. + * @arg quantum New quantum in bytes. + * @return 0 on success or a negative error code. + */ +void rtnl_sfq_set_quantum(struct rtnl_qdisc *qdisc, int quantum) +{ + struct rtnl_sfq *sfq; + + if (!(sfq = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + sfq->qs_quantum = quantum; + sfq->qs_mask |= SCH_SFQ_ATTR_QUANTUM; +} + +/** + * Get quantum of SFQ qdisc. + * @arg qdisc SFQ qdisc. + * @return Quantum in bytes or a negative error code. + */ +int rtnl_sfq_get_quantum(struct rtnl_qdisc *qdisc) +{ + struct rtnl_sfq *sfq; + + if (!(sfq = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (sfq->qs_mask & SCH_SFQ_ATTR_QUANTUM) + return sfq->qs_quantum; + else + return -NLE_NOATTR; +} + +/** + * Set limit of SFQ qdisc. + * @arg qdisc SFQ qdisc to be modified. + * @arg limit New limit in number of packets. + * @return 0 on success or a negative error code. + */ +void rtnl_sfq_set_limit(struct rtnl_qdisc *qdisc, int limit) +{ + struct rtnl_sfq *sfq; + + if (!(sfq = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + sfq->qs_limit = limit; + sfq->qs_mask |= SCH_SFQ_ATTR_LIMIT; +} + +/** + * Get limit of SFQ qdisc. + * @arg qdisc SFQ qdisc. + * @return Limit or a negative error code. + */ +int rtnl_sfq_get_limit(struct rtnl_qdisc *qdisc) +{ + struct rtnl_sfq *sfq; + + if (!(sfq = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (sfq->qs_mask & SCH_SFQ_ATTR_LIMIT) + return sfq->qs_limit; + else + return -NLE_NOATTR; +} + +/** + * Set perturbation interval of SFQ qdisc. + * @arg qdisc SFQ qdisc to be modified. + * @arg perturb New perturbation interval in seconds. + * @note A value of 0 disables perturbation altogether. + * @return 0 on success or a negative error code. + */ +void rtnl_sfq_set_perturb(struct rtnl_qdisc *qdisc, int perturb) +{ + struct rtnl_sfq *sfq; + + if (!(sfq = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + sfq->qs_perturb = perturb; + sfq->qs_mask |= SCH_SFQ_ATTR_PERTURB; +} + +/** + * Get perturbation interval of SFQ qdisc. + * @arg qdisc SFQ qdisc. + * @return Perturbation interval in seconds or a negative error code. + */ +int rtnl_sfq_get_perturb(struct rtnl_qdisc *qdisc) +{ + struct rtnl_sfq *sfq; + + if (!(sfq = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (sfq->qs_mask & SCH_SFQ_ATTR_PERTURB) + return sfq->qs_perturb; + else + return -NLE_NOATTR; +} + +/** + * Get divisor of SFQ qdisc. + * @arg qdisc SFQ qdisc. + * @return Divisor in number of entries or a negative error code. + */ +int rtnl_sfq_get_divisor(struct rtnl_qdisc *qdisc) +{ + struct rtnl_sfq *sfq; + + if (!(sfq = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (sfq->qs_mask & SCH_SFQ_ATTR_DIVISOR) + return sfq->qs_divisor; + else + return -NLE_NOATTR; +} + +/** @} */ + +static struct rtnl_tc_ops sfq_ops = { + .to_kind = "sfq", + .to_type = RTNL_TC_TYPE_QDISC, + .to_size = sizeof(struct rtnl_sfq), + .to_msg_parser = sfq_msg_parser, + .to_dump = { + [NL_DUMP_LINE] = sfq_dump_line, + [NL_DUMP_DETAILS] = sfq_dump_details, + }, + .to_msg_fill = sfq_msg_fill, +}; + +static void __init sfq_init(void) +{ + rtnl_tc_register(&sfq_ops); +} + +static void __exit sfq_exit(void) +{ + rtnl_tc_unregister(&sfq_ops); +} + +/** @} */ diff --git a/libnl/lib/route/qdisc/tbf.c b/libnl/lib/route/qdisc/tbf.c new file mode 100644 index 0000000..6d39661 --- /dev/null +++ b/libnl/lib/route/qdisc/tbf.c @@ -0,0 +1,456 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +/** + * @ingroup qdisc + * @defgroup qdisc_tbf Token Bucket Filter (TBF) + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define TBF_ATTR_LIMIT 0x01 +#define TBF_ATTR_RATE 0x02 +#define TBF_ATTR_PEAKRATE 0x10 +/** @endcond */ + +static struct nla_policy tbf_policy[TCA_TBF_MAX+1] = { + [TCA_TBF_PARMS] = { .minlen = sizeof(struct tc_tbf_qopt) }, +}; + +static int tbf_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct nlattr *tb[TCA_TBF_MAX + 1]; + struct rtnl_tbf *tbf = data; + int err; + + if ((err = tca_parse(tb, TCA_TBF_MAX, tc, tbf_policy)) < 0) + return err; + + if (tb[TCA_TBF_PARMS]) { + struct tc_tbf_qopt opts; + int bufsize; + + nla_memcpy(&opts, tb[TCA_TBF_PARMS], sizeof(opts)); + tbf->qt_limit = opts.limit; + + rtnl_copy_ratespec(&tbf->qt_rate, &opts.rate); + tbf->qt_rate_txtime = opts.buffer; + bufsize = rtnl_tc_calc_bufsize64(nl_ticks2us(opts.buffer), + tbf->qt_rate.rs_rate64); + tbf->qt_rate_bucket = bufsize; + + rtnl_copy_ratespec(&tbf->qt_peakrate, &opts.peakrate); + tbf->qt_peakrate_txtime = opts.mtu; + bufsize = rtnl_tc_calc_bufsize64(nl_ticks2us(opts.mtu), + tbf->qt_peakrate.rs_rate64); + tbf->qt_peakrate_bucket = bufsize; + + rtnl_tc_set_mpu(tc, tbf->qt_rate.rs_mpu); + rtnl_tc_set_overhead(tc, tbf->qt_rate.rs_overhead); + + tbf->qt_mask = (TBF_ATTR_LIMIT | TBF_ATTR_RATE | TBF_ATTR_PEAKRATE); + } + + return 0; +} + +static void tbf_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + double r, rbit, lim; + char *ru, *rubit, *limu; + struct rtnl_tbf *tbf = data; + + if (!tbf) + return; + + r = nl_cancel_down_bytes(tbf->qt_rate.rs_rate64, &ru); + rbit = nl_cancel_down_bits(tbf->qt_rate.rs_rate64*8, &rubit); + lim = nl_cancel_down_bytes(tbf->qt_limit, &limu); + + nl_dump(p, " rate %.2f%s/s (%.0f%s) limit %.2f%s", + r, ru, rbit, rubit, lim, limu); +} + +static void tbf_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_tbf *tbf = data; + + if (!tbf) + return; + + if (1) { + char *bu, *cu; + double bs = nl_cancel_down_bytes(tbf->qt_rate_bucket, &bu); + double cl = nl_cancel_down_bytes(1 << tbf->qt_rate.rs_cell_log, + &cu); + + nl_dump(p, "rate-bucket-size %1.f%s " + "rate-cell-size %.1f%s\n", + bs, bu, cl, cu); + + } + + if (tbf->qt_mask & TBF_ATTR_PEAKRATE) { + char *pru, *prbu, *bsu, *clu; + double pr, prb, bs, cl; + + pr = nl_cancel_down_bytes(tbf->qt_peakrate.rs_rate64, &pru); + prb = nl_cancel_down_bits(tbf->qt_peakrate.rs_rate64 * 8, &prbu); + bs = nl_cancel_down_bits(tbf->qt_peakrate_bucket, &bsu); + cl = nl_cancel_down_bits(1 << tbf->qt_peakrate.rs_cell_log, + &clu); + + nl_dump_line(p, " peak-rate %.2f%s/s (%.0f%s) " + "bucket-size %.1f%s cell-size %.1f%s" + "latency %.1f%s", + pr, pru, prb, prbu, bs, bsu, cl, clu); + } +} + +static int tbf_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + uint32_t rtab[RTNL_TC_RTABLE_SIZE], ptab[RTNL_TC_RTABLE_SIZE]; + struct tc_tbf_qopt opts; + struct rtnl_tbf *tbf = data; + int required = TBF_ATTR_RATE | TBF_ATTR_LIMIT; + + if ((tbf->qt_mask & required) != required) + return -NLE_MISSING_ATTR; + + memset(&opts, 0, sizeof(opts)); + opts.limit = tbf->qt_limit; + opts.buffer = tbf->qt_rate_txtime; + + rtnl_tc_build_rate_table(tc, &tbf->qt_rate, rtab); + rtnl_rcopy_ratespec(&opts.rate, &tbf->qt_rate); + + if (tbf->qt_mask & TBF_ATTR_PEAKRATE) { + opts.mtu = tbf->qt_peakrate_txtime; + rtnl_tc_build_rate_table(tc, &tbf->qt_peakrate, ptab); + rtnl_rcopy_ratespec(&opts.peakrate, &tbf->qt_peakrate); + + } + + NLA_PUT(msg, TCA_TBF_PARMS, sizeof(opts), &opts); + NLA_PUT(msg, TCA_TBF_RTAB, sizeof(rtab), rtab); + + if (tbf->qt_mask & TBF_ATTR_PEAKRATE) + NLA_PUT(msg, TCA_TBF_PTAB, sizeof(ptab), ptab); + + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +/** + * @name Attribute Access + * @{ + */ + +/** + * Set limit of TBF qdisc. + * @arg qdisc TBF qdisc to be modified. + * @arg limit New limit in bytes. + * @return 0 on success or a negative error code. + */ +void rtnl_qdisc_tbf_set_limit(struct rtnl_qdisc *qdisc, int limit) +{ + struct rtnl_tbf *tbf; + + if (!(tbf = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + tbf->qt_limit = limit; + tbf->qt_mask |= TBF_ATTR_LIMIT; +} + +static inline double calc_limit(struct rtnl_ratespec *spec, int latency, + int bucket) +{ + double limit; + + limit = (double) spec->rs_rate64 * ((double) latency / 1000000.); + limit += bucket; + + return limit; +} + +/** + * Set limit of TBF qdisc by latency. + * @arg qdisc TBF qdisc to be modified. + * @arg latency Latency in micro seconds. + * + * Calculates and sets the limit based on the desired latency and the + * configured rate and peak rate. In order for this operation to succeed, + * the rate and if required the peak rate must have been set in advance. + * + * @f[ + * limit_n = \frac{{rate_n} \times {latency}}{10^6}+{bucketsize}_n + * @f] + * @f[ + * limit = min(limit_{rate},limit_{peak}) + * @f] + * + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_tbf_set_limit_by_latency(struct rtnl_qdisc *qdisc, int latency) +{ + struct rtnl_tbf *tbf; + double limit, limit2; + + if (!(tbf = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (!(tbf->qt_mask & TBF_ATTR_RATE)) + return -NLE_MISSING_ATTR; + + limit = calc_limit(&tbf->qt_rate, latency, tbf->qt_rate_bucket); + + if (tbf->qt_mask & TBF_ATTR_PEAKRATE) { + limit2 = calc_limit(&tbf->qt_peakrate, latency, + tbf->qt_peakrate_bucket); + + if (limit2 < limit) + limit = limit2; + } + + rtnl_qdisc_tbf_set_limit(qdisc, (int) limit); + + return 0; +} + +/** + * Get limit of TBF qdisc. + * @arg qdisc TBF qdisc. + * @return Limit in bytes or a negative error code. + */ +int rtnl_qdisc_tbf_get_limit(struct rtnl_qdisc *qdisc) +{ + struct rtnl_tbf *tbf; + + if (!(tbf = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (tbf->qt_mask & TBF_ATTR_LIMIT) + return tbf->qt_limit; + else + return -NLE_NOATTR; +} + +static inline int calc_cell_log(int cell, int bucket) +{ + cell = rtnl_tc_calc_cell_log(cell); + return cell; +} + +/** + * Set rate of TBF qdisc. + * @arg qdisc TBF qdisc to be modified. + * @arg rate New rate in bytes per second. + * @arg bucket Size of bucket in bytes. + * @arg cell Size of a rate cell or 0 to get default value. + * @return 0 on success or a negative error code. + */ +void rtnl_qdisc_tbf_set_rate(struct rtnl_qdisc *qdisc, int rate, int bucket, + int cell) +{ + struct rtnl_tbf *tbf; + int cell_log; + + if (!(tbf = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (!cell) + cell_log = UINT8_MAX; + else + cell_log = rtnl_tc_calc_cell_log(cell); + + tbf->qt_rate.rs_rate64 = (uint32_t)rate; + tbf->qt_rate_bucket = bucket; + tbf->qt_rate.rs_cell_log = cell_log; + tbf->qt_rate_txtime = nl_us2ticks(rtnl_tc_calc_txtime64(bucket, tbf->qt_rate.rs_rate64)); + tbf->qt_mask |= TBF_ATTR_RATE; +} + +/** + * Get rate of TBF qdisc. + * @arg qdisc TBF qdisc. + * @return Rate in bytes per seconds or a negative error code. + */ +int rtnl_qdisc_tbf_get_rate(struct rtnl_qdisc *qdisc) +{ + struct rtnl_tbf *tbf; + + if (!(tbf = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (tbf->qt_mask & TBF_ATTR_RATE) + return tbf->qt_rate.rs_rate64; + else + return -1; +} + +/** + * Get rate bucket size of TBF qdisc. + * @arg qdisc TBF qdisc. + * @return Size of rate bucket or a negative error code. + */ +int rtnl_qdisc_tbf_get_rate_bucket(struct rtnl_qdisc *qdisc) +{ + struct rtnl_tbf *tbf; + + if (!(tbf = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (tbf->qt_mask & TBF_ATTR_RATE) + return tbf->qt_rate_bucket; + else + return -1; +} + +/** + * Get rate cell size of TBF qdisc. + * @arg qdisc TBF qdisc. + * @return Size of rate cell in bytes or a negative error code. + */ +int rtnl_qdisc_tbf_get_rate_cell(struct rtnl_qdisc *qdisc) +{ + struct rtnl_tbf *tbf; + + if (!(tbf = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (tbf->qt_mask & TBF_ATTR_RATE) + return (1 << tbf->qt_rate.rs_cell_log); + else + return -1; +} + +/** + * Set peak rate of TBF qdisc. + * @arg qdisc TBF qdisc to be modified. + * @arg rate New peak rate in bytes per second. + * @arg bucket Size of peakrate bucket. + * @arg cell Size of a peakrate cell or 0 to get default value. + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_tbf_set_peakrate(struct rtnl_qdisc *qdisc, int rate, int bucket, + int cell) +{ + struct rtnl_tbf *tbf; + int cell_log; + + if (!(tbf = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + cell_log = calc_cell_log(cell, bucket); + if (cell_log < 0) + return cell_log; + + tbf->qt_peakrate.rs_rate64 = (uint32_t)rate; + tbf->qt_peakrate_bucket = bucket; + tbf->qt_peakrate.rs_cell_log = cell_log; + tbf->qt_peakrate_txtime = nl_us2ticks(rtnl_tc_calc_txtime64(bucket, tbf->qt_peakrate.rs_rate64)); + + tbf->qt_mask |= TBF_ATTR_PEAKRATE; + + return 0; +} + +/** + * Get peak rate of TBF qdisc. + * @arg qdisc TBF qdisc. + * @return Peak rate in bytes per seconds or a negative error code. + */ +int rtnl_qdisc_tbf_get_peakrate(struct rtnl_qdisc *qdisc) +{ + struct rtnl_tbf *tbf; + + if (!(tbf = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (tbf->qt_mask & TBF_ATTR_PEAKRATE) + return tbf->qt_peakrate.rs_rate64; + else + return -1; +} + +/** + * Get peak rate bucket size of TBF qdisc. + * @arg qdisc TBF qdisc. + * @return Size of peak rate bucket or a negative error code. + */ +int rtnl_qdisc_tbf_get_peakrate_bucket(struct rtnl_qdisc *qdisc) +{ + struct rtnl_tbf *tbf; + + if (!(tbf = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (tbf->qt_mask & TBF_ATTR_PEAKRATE) + return tbf->qt_peakrate_bucket; + else + return -1; +} + +/** + * Get peak rate cell size of TBF qdisc. + * @arg qdisc TBF qdisc. + * @return Size of peak rate cell in bytes or a negative error code. + */ +int rtnl_qdisc_tbf_get_peakrate_cell(struct rtnl_qdisc *qdisc) +{ + struct rtnl_tbf *tbf; + + if (!(tbf = rtnl_tc_data(TC_CAST(qdisc)))) + BUG(); + + if (tbf->qt_mask & TBF_ATTR_PEAKRATE) + return (1 << tbf->qt_peakrate.rs_cell_log); + else + return -1; +} + +/** @} */ + +static struct rtnl_tc_ops tbf_tc_ops = { + .to_kind = "tbf", + .to_type = RTNL_TC_TYPE_QDISC, + .to_size = sizeof(struct rtnl_tbf), + .to_msg_parser = tbf_msg_parser, + .to_dump = { + [NL_DUMP_LINE] = tbf_dump_line, + [NL_DUMP_DETAILS] = tbf_dump_details, + }, + .to_msg_fill = tbf_msg_fill, +}; + +static void __init tbf_init(void) +{ + rtnl_tc_register(&tbf_tc_ops); +} + +static void __exit tbf_exit(void) +{ + rtnl_tc_unregister(&tbf_tc_ops); +} + +/** @} */ diff --git a/libnl/lib/route/route.c b/libnl/lib/route/route.c new file mode 100644 index 0000000..74c62b9 --- /dev/null +++ b/libnl/lib/route/route.c @@ -0,0 +1,203 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + */ + +/** + * @ingroup rtnl + * @defgroup route Routing + * @brief + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +static struct nl_cache_ops rtnl_route_ops; + +static int route_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, struct nl_parser_param *pp) +{ + struct rtnl_route *route; + int err; + + if ((err = rtnl_route_parse(nlh, &route)) < 0) + return err; + + err = pp->pp_cb((struct nl_object *) route, pp); + + rtnl_route_put(route); + return err; +} + +static int route_request_update(struct nl_cache *c, struct nl_sock *h) +{ + struct rtmsg rhdr = { + .rtm_family = c->c_iarg1, + }; + + if (c->c_iarg2 & ROUTE_CACHE_CONTENT) + rhdr.rtm_flags |= RTM_F_CLONED; + + return nl_send_simple(h, RTM_GETROUTE, NLM_F_DUMP, &rhdr, sizeof(rhdr)); +} + +/** + * @name Cache Management + * @{ + */ + +/** + * Build a route cache holding all routes currently configured in the kernel + * @arg sk Netlink socket. + * @arg family Address family of routes to cover or AF_UNSPEC + * @arg flags Flags + * @arg result Result pointer + * + * Allocates a new cache, initializes it properly and updates it to + * contain all routes currently configured in the kernel. + * + * Valid flags: + * * ROUTE_CACHE_CONTENT - Cache will contain contents of routing cache + * instead of actual routes. + * + * @note The caller is responsible for destroying and freeing the + * cache after using it. + * @return 0 on success or a negative error code. + */ +int rtnl_route_alloc_cache(struct nl_sock *sk, int family, int flags, + struct nl_cache **result) +{ + struct nl_cache *cache; + int err; + + if (!(cache = nl_cache_alloc(&rtnl_route_ops))) + return -NLE_NOMEM; + + cache->c_iarg1 = family; + cache->c_iarg2 = flags; + + if (sk && (err = nl_cache_refill(sk, cache)) < 0) { + free(cache); + return err; + } + + *result = cache; + return 0; +} + +/** @} */ + +/** + * @name Route Addition + * @{ + */ + +static int build_route_msg(struct rtnl_route *tmpl, int cmd, int flags, + struct nl_msg **result) +{ + struct nl_msg *msg; + int err; + + if (!(msg = nlmsg_alloc_simple(cmd, flags))) + return -NLE_NOMEM; + + if ((err = rtnl_route_build_msg(msg, tmpl)) < 0) { + nlmsg_free(msg); + return err; + } + + *result = msg; + return 0; +} + +int rtnl_route_build_add_request(struct rtnl_route *tmpl, int flags, + struct nl_msg **result) +{ + return build_route_msg(tmpl, RTM_NEWROUTE, NLM_F_CREATE | flags, + result); +} + +int rtnl_route_add(struct nl_sock *sk, struct rtnl_route *route, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_route_build_add_request(route, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +int rtnl_route_build_del_request(struct rtnl_route *tmpl, int flags, + struct nl_msg **result) +{ + return build_route_msg(tmpl, RTM_DELROUTE, flags, result); +} + +int rtnl_route_delete(struct nl_sock *sk, struct rtnl_route *route, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_route_build_del_request(route, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +/** @} */ + +static struct nl_af_group route_groups[] = { + { AF_INET, RTNLGRP_IPV4_ROUTE }, + { AF_INET6, RTNLGRP_IPV6_ROUTE }, + { AF_MPLS, RTNLGRP_MPLS_ROUTE }, + { AF_DECnet, RTNLGRP_DECnet_ROUTE }, + { END_OF_GROUP_LIST }, +}; + +static struct nl_cache_ops rtnl_route_ops = { + .co_name = "route/route", + .co_hdrsize = sizeof(struct rtmsg), + .co_msgtypes = { + { RTM_NEWROUTE, NL_ACT_NEW, "new" }, + { RTM_DELROUTE, NL_ACT_DEL, "del" }, + { RTM_GETROUTE, NL_ACT_GET, "get" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_ROUTE, + .co_groups = route_groups, + .co_request_update = route_request_update, + .co_msg_parser = route_msg_parser, + .co_obj_ops = &route_obj_ops, +}; + +static void __init route_init(void) +{ + nl_cache_mngt_register(&rtnl_route_ops); +} + +static void __exit route_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_route_ops); +} + +/** @} */ diff --git a/libnl/lib/route/route_obj.c b/libnl/lib/route/route_obj.c new file mode 100644 index 0000000..8b096fb --- /dev/null +++ b/libnl/lib/route/route_obj.c @@ -0,0 +1,1479 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2008 Thomas Graf + */ + +/** + * @ingroup route + * @defgroup route_obj Route Object + * + * @par Attributes + * @code + * Name Default + * ------------------------------------------------------------- + * routing table RT_TABLE_MAIN + * scope RT_SCOPE_NOWHERE + * tos 0 + * protocol RTPROT_STATIC + * prio 0 + * family AF_UNSPEC + * type RTN_UNICAST + * iif NULL + * @endcode + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define ROUTE_ATTR_FAMILY 0x000001 +#define ROUTE_ATTR_TOS 0x000002 +#define ROUTE_ATTR_TABLE 0x000004 +#define ROUTE_ATTR_PROTOCOL 0x000008 +#define ROUTE_ATTR_SCOPE 0x000010 +#define ROUTE_ATTR_TYPE 0x000020 +#define ROUTE_ATTR_FLAGS 0x000040 +#define ROUTE_ATTR_DST 0x000080 +#define ROUTE_ATTR_SRC 0x000100 +#define ROUTE_ATTR_IIF 0x000200 +#define ROUTE_ATTR_OIF 0x000400 +#define ROUTE_ATTR_GATEWAY 0x000800 +#define ROUTE_ATTR_PRIO 0x001000 +#define ROUTE_ATTR_PREF_SRC 0x002000 +#define ROUTE_ATTR_METRICS 0x004000 +#define ROUTE_ATTR_MULTIPATH 0x008000 +#define ROUTE_ATTR_REALMS 0x010000 +#define ROUTE_ATTR_CACHEINFO 0x020000 +#define ROUTE_ATTR_TTL_PROPAGATE 0x040000 +/** @endcond */ + +static void route_constructor(struct nl_object *c) +{ + struct rtnl_route *r = (struct rtnl_route *) c; + + r->rt_family = AF_UNSPEC; + r->rt_scope = RT_SCOPE_NOWHERE; + r->rt_table = RT_TABLE_MAIN; + r->rt_protocol = RTPROT_STATIC; + r->rt_type = RTN_UNICAST; + r->rt_prio = 0; + + nl_init_list_head(&r->rt_nexthops); +} + +static void route_free_data(struct nl_object *c) +{ + struct rtnl_route *r = (struct rtnl_route *) c; + struct rtnl_nexthop *nh, *tmp; + + if (r == NULL) + return; + + nl_addr_put(r->rt_dst); + nl_addr_put(r->rt_src); + nl_addr_put(r->rt_pref_src); + + nl_list_for_each_entry_safe(nh, tmp, &r->rt_nexthops, rtnh_list) { + rtnl_route_remove_nexthop(r, nh); + rtnl_route_nh_free(nh); + } +} + +static int route_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct rtnl_route *dst = (struct rtnl_route *) _dst; + struct rtnl_route *src = (struct rtnl_route *) _src; + struct rtnl_nexthop *nh, *new; + + if (src->rt_dst) + if (!(dst->rt_dst = nl_addr_clone(src->rt_dst))) + return -NLE_NOMEM; + + if (src->rt_src) + if (!(dst->rt_src = nl_addr_clone(src->rt_src))) + return -NLE_NOMEM; + + if (src->rt_pref_src) + if (!(dst->rt_pref_src = nl_addr_clone(src->rt_pref_src))) + return -NLE_NOMEM; + + /* Will be inc'ed again while adding the nexthops of the source */ + dst->rt_nr_nh = 0; + + nl_init_list_head(&dst->rt_nexthops); + nl_list_for_each_entry(nh, &src->rt_nexthops, rtnh_list) { + new = rtnl_route_nh_clone(nh); + if (!new) + return -NLE_NOMEM; + + rtnl_route_add_nexthop(dst, new); + } + + return 0; +} + +static void route_dump_line(struct nl_object *a, struct nl_dump_params *p) +{ + struct rtnl_route *r = (struct rtnl_route *) a; + int cache = 0, flags; + char buf[64]; + + if (r->rt_flags & RTM_F_CLONED) + cache = 1; + + nl_dump_line(p, "%s ", nl_af2str(r->rt_family, buf, sizeof(buf))); + + if (cache) + nl_dump(p, "cache "); + + if (!(r->ce_mask & ROUTE_ATTR_DST) || + nl_addr_get_len(r->rt_dst) == 0) + nl_dump(p, "default "); + else + nl_dump(p, "%s ", nl_addr2str(r->rt_dst, buf, sizeof(buf))); + + if (r->ce_mask & ROUTE_ATTR_TABLE && !cache) + nl_dump(p, "table %s ", + rtnl_route_table2str(r->rt_table, buf, sizeof(buf))); + + if (r->ce_mask & ROUTE_ATTR_TYPE) + nl_dump(p, "type %s ", + nl_rtntype2str(r->rt_type, buf, sizeof(buf))); + + if (r->ce_mask & ROUTE_ATTR_TOS && r->rt_tos != 0) + nl_dump(p, "tos %#x ", r->rt_tos); + + if (r->ce_mask & ROUTE_ATTR_MULTIPATH) { + struct rtnl_nexthop *nh; + + nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) { + p->dp_ivar = NH_DUMP_FROM_ONELINE; + rtnl_route_nh_dump(nh, p); + } + } + + flags = r->rt_flags & ~(RTM_F_CLONED); + if (r->ce_mask & ROUTE_ATTR_FLAGS && flags) { + + nl_dump(p, "<"); + +#define PRINT_FLAG(f) if (flags & RTNH_F_##f) { \ + flags &= ~RTNH_F_##f; nl_dump(p, #f "%s", flags ? "," : ""); } + PRINT_FLAG(DEAD); + PRINT_FLAG(ONLINK); + PRINT_FLAG(PERVASIVE); +#undef PRINT_FLAG + +#define PRINT_FLAG(f) if (flags & RTM_F_##f) { \ + flags &= ~RTM_F_##f; nl_dump(p, #f "%s", flags ? "," : ""); } + PRINT_FLAG(NOTIFY); + PRINT_FLAG(EQUALIZE); + PRINT_FLAG(PREFIX); +#undef PRINT_FLAG + +#define PRINT_FLAG(f) if (flags & RTCF_##f) { \ + flags &= ~RTCF_##f; nl_dump(p, #f "%s", flags ? "," : ""); } + PRINT_FLAG(NOTIFY); + PRINT_FLAG(REDIRECTED); + PRINT_FLAG(DOREDIRECT); + PRINT_FLAG(DIRECTSRC); + PRINT_FLAG(DNAT); + PRINT_FLAG(BROADCAST); + PRINT_FLAG(MULTICAST); + PRINT_FLAG(LOCAL); +#undef PRINT_FLAG + + nl_dump(p, ">"); + } + + nl_dump(p, "\n"); +} + +static void route_dump_details(struct nl_object *a, struct nl_dump_params *p) +{ + _nl_auto_nl_cache struct nl_cache *link_cache = NULL; + struct rtnl_route *r = (struct rtnl_route *) a; + char buf[256]; + int i; + + link_cache = nl_cache_mngt_require_safe("route/link"); + + route_dump_line(a, p); + nl_dump_line(p, " "); + + if (r->ce_mask & ROUTE_ATTR_PREF_SRC) + nl_dump(p, "preferred-src %s ", + nl_addr2str(r->rt_pref_src, buf, sizeof(buf))); + + if (r->ce_mask & ROUTE_ATTR_SCOPE && r->rt_scope != RT_SCOPE_NOWHERE) + nl_dump(p, "scope %s ", + rtnl_scope2str(r->rt_scope, buf, sizeof(buf))); + + if (r->ce_mask & ROUTE_ATTR_PRIO) + nl_dump(p, "priority %#x ", r->rt_prio); + + if (r->ce_mask & ROUTE_ATTR_PROTOCOL) + nl_dump(p, "protocol %s ", + rtnl_route_proto2str(r->rt_protocol, buf, sizeof(buf))); + + if (r->ce_mask & ROUTE_ATTR_IIF) { + if (link_cache) { + nl_dump(p, "iif %s ", + rtnl_link_i2name(link_cache, r->rt_iif, + buf, sizeof(buf))); + } else + nl_dump(p, "iif %d ", r->rt_iif); + } + + if (r->ce_mask & ROUTE_ATTR_SRC) + nl_dump(p, "src %s ", nl_addr2str(r->rt_src, buf, sizeof(buf))); + + if (r->ce_mask & ROUTE_ATTR_TTL_PROPAGATE) { + nl_dump(p, " ttl-propagate %s", + r->rt_ttl_propagate ? "enabled" : "disabled"); + } + + nl_dump(p, "\n"); + + if (r->ce_mask & ROUTE_ATTR_MULTIPATH) { + struct rtnl_nexthop *nh; + + nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) { + nl_dump_line(p, " "); + p->dp_ivar = NH_DUMP_FROM_DETAILS; + rtnl_route_nh_dump(nh, p); + nl_dump(p, "\n"); + } + } + + if ((r->ce_mask & ROUTE_ATTR_CACHEINFO) && r->rt_cacheinfo.rtci_error) { + nl_dump_line(p, " cacheinfo error %d (%s)\n", + r->rt_cacheinfo.rtci_error, + nl_strerror_l(-r->rt_cacheinfo.rtci_error)); + } + + if (r->ce_mask & ROUTE_ATTR_METRICS) { + nl_dump_line(p, " metrics ["); + for (i = 0; i < RTAX_MAX; i++) + if (r->rt_metrics_mask & (1 << i)) + nl_dump(p, "%s %u ", + rtnl_route_metric2str(i+1, + buf, sizeof(buf)), + r->rt_metrics[i]); + nl_dump(p, "]\n"); + } +} + +static void route_dump_stats(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_route *route = (struct rtnl_route *) obj; + + route_dump_details(obj, p); + + if (route->ce_mask & ROUTE_ATTR_CACHEINFO) { + struct rtnl_rtcacheinfo *ci = &route->rt_cacheinfo; + + nl_dump_line(p, " used %u refcnt %u last-use %us " + "expires %us\n", + ci->rtci_used, ci->rtci_clntref, + ci->rtci_last_use / nl_get_user_hz(), + ci->rtci_expires / nl_get_user_hz()); + } +} + +static void route_keygen(struct nl_object *obj, uint32_t *hashkey, + uint32_t table_sz) +{ + struct rtnl_route *route = (struct rtnl_route *) obj; + unsigned int rkey_sz; + struct nl_addr *addr = NULL; + _nl_auto_free struct route_hash_key { + uint8_t rt_family; + uint8_t rt_tos; + uint32_t rt_table; + uint32_t rt_prio; + char rt_addr[0]; + } __attribute__((packed)) *rkey = NULL; +#ifdef NL_DEBUG + char buf[INET6_ADDRSTRLEN+5]; +#endif + + if (route->rt_dst) + addr = route->rt_dst; + + rkey_sz = sizeof(*rkey); + if (addr) + rkey_sz += nl_addr_get_len(addr); + rkey = calloc(1, rkey_sz); + if (!rkey) { + NL_DBG(2, "Warning: calloc failed for %d bytes...\n", rkey_sz); + *hashkey = 0; + return; + } + rkey->rt_family = route->rt_family; + rkey->rt_tos = route->rt_tos; + rkey->rt_table = route->rt_table; + rkey->rt_prio = route->rt_prio; + if (addr) + memcpy(rkey->rt_addr, nl_addr_get_binary_addr(addr), + nl_addr_get_len(addr)); + + *hashkey = nl_hash(rkey, rkey_sz, 0) % table_sz; + + NL_DBG(5, "route %p key (fam %d tos %d table %d addr %s) keysz %d " + "hash 0x%x\n", route, rkey->rt_family, rkey->rt_tos, + rkey->rt_table, nl_addr2str(addr, buf, sizeof(buf)), + rkey_sz, *hashkey); + + return; +} + +static uint32_t route_id_attrs_get(struct nl_object *obj) +{ + struct rtnl_route *route = (struct rtnl_route *)obj; + struct nl_object_ops *ops = obj->ce_ops; + uint32_t rv = ops->oo_id_attrs; + + /* MPLS address family does not allow RTA_PRIORITY to be set */ + if (route->rt_family == AF_MPLS) + rv &= ~ROUTE_ATTR_PRIO; + + return rv; +} + +static uint64_t route_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + struct rtnl_route *a = (struct rtnl_route *) _a; + struct rtnl_route *b = (struct rtnl_route *) _b; + struct rtnl_nexthop *nh_a, *nh_b; + int i, found; + uint64_t diff = 0; + +#define ROUTE_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ROUTE_ATTR_##ATTR, a, b, EXPR) + + diff |= ROUTE_DIFF(FAMILY, a->rt_family != b->rt_family); + diff |= ROUTE_DIFF(TOS, a->rt_tos != b->rt_tos); + diff |= ROUTE_DIFF(TABLE, a->rt_table != b->rt_table); + diff |= ROUTE_DIFF(PROTOCOL, a->rt_protocol != b->rt_protocol); + diff |= ROUTE_DIFF(SCOPE, a->rt_scope != b->rt_scope); + diff |= ROUTE_DIFF(TYPE, a->rt_type != b->rt_type); + diff |= ROUTE_DIFF(PRIO, a->rt_prio != b->rt_prio); + diff |= ROUTE_DIFF(DST, nl_addr_cmp(a->rt_dst, b->rt_dst)); + diff |= ROUTE_DIFF(SRC, nl_addr_cmp(a->rt_src, b->rt_src)); + diff |= ROUTE_DIFF(IIF, a->rt_iif != b->rt_iif); + diff |= ROUTE_DIFF(PREF_SRC, nl_addr_cmp(a->rt_pref_src, + b->rt_pref_src)); + diff |= ROUTE_DIFF(TTL_PROPAGATE, + a->rt_ttl_propagate != b->rt_ttl_propagate); + + if (flags & LOOSE_COMPARISON) { + nl_list_for_each_entry(nh_b, &b->rt_nexthops, rtnh_list) { + found = 0; + nl_list_for_each_entry(nh_a, &a->rt_nexthops, + rtnh_list) { + if (!rtnl_route_nh_compare(nh_a, nh_b, + nh_b->ce_mask, 1)) { + found = 1; + break; + } + } + + if (!found) + goto nh_mismatch; + } + + for (i = 0; i < RTAX_MAX - 1; i++) { + if (a->rt_metrics_mask & (1 << i) && + (!(b->rt_metrics_mask & (1 << i)) || + a->rt_metrics[i] != b->rt_metrics[i])) + diff |= ROUTE_DIFF(METRICS, 1); + } + + diff |= ROUTE_DIFF(FLAGS, + (a->rt_flags ^ b->rt_flags) & b->rt_flag_mask); + } else { + if (a->rt_nr_nh != b->rt_nr_nh) + goto nh_mismatch; + + /* search for a dup in each nh of a */ + nl_list_for_each_entry(nh_a, &a->rt_nexthops, rtnh_list) { + found = 0; + nl_list_for_each_entry(nh_b, &b->rt_nexthops, + rtnh_list) { + if (!rtnl_route_nh_compare(nh_a, nh_b, ~0, 0)) { + found = 1; + break; + } + } + if (!found) + goto nh_mismatch; + } + + /* search for a dup in each nh of b, covers case where a has + * dupes itself */ + nl_list_for_each_entry(nh_b, &b->rt_nexthops, rtnh_list) { + found = 0; + nl_list_for_each_entry(nh_a, &a->rt_nexthops, + rtnh_list) { + if (!rtnl_route_nh_compare(nh_a, nh_b, ~0, 0)) { + found = 1; + break; + } + } + if (!found) + goto nh_mismatch; + } + + for (i = 0; i < RTAX_MAX - 1; i++) { + if ((a->rt_metrics_mask & (1 << i)) ^ + (b->rt_metrics_mask & (1 << i))) + diff |= ROUTE_DIFF(METRICS, 1); + else + diff |= ROUTE_DIFF(METRICS, + a->rt_metrics[i] != b->rt_metrics[i]); + } + + diff |= ROUTE_DIFF(FLAGS, a->rt_flags != b->rt_flags); + } + +out: + return diff; + +nh_mismatch: + diff |= ROUTE_DIFF(MULTIPATH, 1); + goto out; + +#undef ROUTE_DIFF +} + +static int route_update(struct nl_object *old_obj, struct nl_object *new_obj) +{ + struct rtnl_route *new_route = (struct rtnl_route *) new_obj; + struct rtnl_route *old_route = (struct rtnl_route *) old_obj; + struct rtnl_nexthop *new_nh; + int action = new_obj->ce_msgtype; +#ifdef NL_DEBUG + char buf[INET6_ADDRSTRLEN+5]; +#endif + + /* + * ipv6 ECMP route notifications from the kernel come as + * separate notifications, one for every nexthop. This update + * function collapses such route msgs into a single + * route with multiple nexthops. The resulting object looks + * similar to a ipv4 ECMP route + */ + if (new_route->rt_family != AF_INET6 || + new_route->rt_table == RT_TABLE_LOCAL) + return -NLE_OPNOTSUPP; + + /* + * For routes that are already multipath, + * or dont have a nexthop dont do anything + */ + if (rtnl_route_get_nnexthops(new_route) != 1) + return -NLE_OPNOTSUPP; + + /* + * Get the only nexthop entry from the new route. For + * IPv6 we always get a route with a 0th NH + * filled or nothing at all + */ + new_nh = rtnl_route_nexthop_n(new_route, 0); + if (!new_nh || !rtnl_route_nh_get_gateway(new_nh)) + return -NLE_OPNOTSUPP; + + switch(action) { + case RTM_NEWROUTE : { + struct rtnl_nexthop *cloned_nh; + + /* + * Add the nexthop to old route + */ + cloned_nh = rtnl_route_nh_clone(new_nh); + if (!cloned_nh) + return -NLE_NOMEM; + rtnl_route_add_nexthop(old_route, cloned_nh); + + NL_DBG(2, "Route obj %p updated. Added " + "nexthop %p via %s\n", old_route, cloned_nh, + nl_addr2str(cloned_nh->rtnh_gateway, buf, + sizeof(buf))); + } + break; + case RTM_DELROUTE : { + struct rtnl_nexthop *old_nh; + + /* + * Only take care of nexthop deletes and not + * route deletes. So, if there is only one nexthop + * quite likely we did not update it. So dont do + * anything and return + */ + if (rtnl_route_get_nnexthops(old_route) <= 1) + return -NLE_OPNOTSUPP; + + /* + * Find the next hop in old route and delete it + */ + nl_list_for_each_entry(old_nh, &old_route->rt_nexthops, + rtnh_list) { + if (!rtnl_route_nh_compare(old_nh, new_nh, ~0, 0)) { + + rtnl_route_remove_nexthop(old_route, old_nh); + + NL_DBG(2, "Route obj %p updated. Removed " + "nexthop %p via %s\n", old_route, + old_nh, + nl_addr2str(old_nh->rtnh_gateway, buf, + sizeof(buf))); + + rtnl_route_nh_free(old_nh); + break; + } + } + } + break; + default: + NL_DBG(2, "Unknown action associated " + "to object %p during route update\n", new_obj); + return -NLE_OPNOTSUPP; + } + + return NLE_SUCCESS; +} + +static const struct trans_tbl route_attrs[] = { + __ADD(ROUTE_ATTR_FAMILY, family), + __ADD(ROUTE_ATTR_TOS, tos), + __ADD(ROUTE_ATTR_TABLE, table), + __ADD(ROUTE_ATTR_PROTOCOL, protocol), + __ADD(ROUTE_ATTR_SCOPE, scope), + __ADD(ROUTE_ATTR_TYPE, type), + __ADD(ROUTE_ATTR_FLAGS, flags), + __ADD(ROUTE_ATTR_DST, dst), + __ADD(ROUTE_ATTR_SRC, src), + __ADD(ROUTE_ATTR_IIF, iif), + __ADD(ROUTE_ATTR_OIF, oif), + __ADD(ROUTE_ATTR_GATEWAY, gateway), + __ADD(ROUTE_ATTR_PRIO, prio), + __ADD(ROUTE_ATTR_PREF_SRC, pref_src), + __ADD(ROUTE_ATTR_METRICS, metrics), + __ADD(ROUTE_ATTR_MULTIPATH, multipath), + __ADD(ROUTE_ATTR_REALMS, realms), + __ADD(ROUTE_ATTR_CACHEINFO, cacheinfo), + __ADD(ROUTE_ATTR_TTL_PROPAGATE, ttl_propagate), +}; + +static char *route_attrs2str(int attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, route_attrs, + ARRAY_SIZE(route_attrs)); +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct rtnl_route *rtnl_route_alloc(void) +{ + return (struct rtnl_route *) nl_object_alloc(&route_obj_ops); +} + +void rtnl_route_get(struct rtnl_route *route) +{ + nl_object_get((struct nl_object *) route); +} + +void rtnl_route_put(struct rtnl_route *route) +{ + nl_object_put((struct nl_object *) route); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +void rtnl_route_set_table(struct rtnl_route *route, uint32_t table) +{ + route->rt_table = table; + route->ce_mask |= ROUTE_ATTR_TABLE; +} + +uint32_t rtnl_route_get_table(struct rtnl_route *route) +{ + return route->rt_table; +} + +void rtnl_route_set_scope(struct rtnl_route *route, uint8_t scope) +{ + route->rt_scope = scope; + route->ce_mask |= ROUTE_ATTR_SCOPE; +} + +uint8_t rtnl_route_get_scope(struct rtnl_route *route) +{ + return route->rt_scope; +} + +void rtnl_route_set_tos(struct rtnl_route *route, uint8_t tos) +{ + route->rt_tos = tos; + route->ce_mask |= ROUTE_ATTR_TOS; +} + +uint8_t rtnl_route_get_tos(struct rtnl_route *route) +{ + return route->rt_tos; +} + +void rtnl_route_set_protocol(struct rtnl_route *route, uint8_t protocol) +{ + route->rt_protocol = protocol; + route->ce_mask |= ROUTE_ATTR_PROTOCOL; +} + +uint8_t rtnl_route_get_protocol(struct rtnl_route *route) +{ + return route->rt_protocol; +} + +void rtnl_route_set_priority(struct rtnl_route *route, uint32_t prio) +{ + route->rt_prio = prio; + route->ce_mask |= ROUTE_ATTR_PRIO; +} + +uint32_t rtnl_route_get_priority(struct rtnl_route *route) +{ + return route->rt_prio; +} + +int rtnl_route_set_family(struct rtnl_route *route, uint8_t family) +{ + switch(family) { + case AF_INET: + case AF_INET6: + case AF_DECnet: + case AF_MPLS: + route->rt_family = family; + route->ce_mask |= ROUTE_ATTR_FAMILY; + return 0; + } + + return -NLE_AF_NOSUPPORT; +} + +uint8_t rtnl_route_get_family(struct rtnl_route *route) +{ + return route->rt_family; +} + +int rtnl_route_set_dst(struct rtnl_route *route, struct nl_addr *addr) +{ + if (route->ce_mask & ROUTE_ATTR_FAMILY) { + if (addr->a_family != route->rt_family) + return -NLE_AF_MISMATCH; + } else + route->rt_family = addr->a_family; + + if (route->rt_dst) + nl_addr_put(route->rt_dst); + + nl_addr_get(addr); + route->rt_dst = addr; + + route->ce_mask |= (ROUTE_ATTR_DST | ROUTE_ATTR_FAMILY); + + return 0; +} + +struct nl_addr *rtnl_route_get_dst(struct rtnl_route *route) +{ + return route->rt_dst; +} + +int rtnl_route_set_src(struct rtnl_route *route, struct nl_addr *addr) +{ + if (addr->a_family == AF_INET) + return -NLE_SRCRT_NOSUPPORT; + + if (route->ce_mask & ROUTE_ATTR_FAMILY) { + if (addr->a_family != route->rt_family) + return -NLE_AF_MISMATCH; + } else + route->rt_family = addr->a_family; + + if (route->rt_src) + nl_addr_put(route->rt_src); + + nl_addr_get(addr); + route->rt_src = addr; + route->ce_mask |= (ROUTE_ATTR_SRC | ROUTE_ATTR_FAMILY); + + return 0; +} + +struct nl_addr *rtnl_route_get_src(struct rtnl_route *route) +{ + return route->rt_src; +} + +int rtnl_route_set_type(struct rtnl_route *route, uint8_t type) +{ + if (type > RTN_MAX) + return -NLE_RANGE; + + route->rt_type = type; + route->ce_mask |= ROUTE_ATTR_TYPE; + + return 0; +} + +uint8_t rtnl_route_get_type(struct rtnl_route *route) +{ + return route->rt_type; +} + +void rtnl_route_set_flags(struct rtnl_route *route, uint32_t flags) +{ + route->rt_flag_mask |= flags; + route->rt_flags |= flags; + route->ce_mask |= ROUTE_ATTR_FLAGS; +} + +void rtnl_route_unset_flags(struct rtnl_route *route, uint32_t flags) +{ + route->rt_flag_mask |= flags; + route->rt_flags &= ~flags; + route->ce_mask |= ROUTE_ATTR_FLAGS; +} + +uint32_t rtnl_route_get_flags(struct rtnl_route *route) +{ + return route->rt_flags; +} + +int rtnl_route_set_metric(struct rtnl_route *route, int metric, uint32_t value) +{ + if (metric > RTAX_MAX || metric < 1) + return -NLE_RANGE; + + route->rt_metrics[metric - 1] = value; + + if (!(route->rt_metrics_mask & (1 << (metric - 1)))) { + route->rt_nmetrics++; + route->rt_metrics_mask |= (1 << (metric - 1)); + } + + route->ce_mask |= ROUTE_ATTR_METRICS; + + return 0; +} + +int rtnl_route_unset_metric(struct rtnl_route *route, int metric) +{ + if (metric > RTAX_MAX || metric < 1) + return -NLE_RANGE; + + if (route->rt_metrics_mask & (1 << (metric - 1))) { + route->rt_nmetrics--; + route->rt_metrics_mask &= ~(1 << (metric - 1)); + } + + return 0; +} + +int rtnl_route_get_metric(struct rtnl_route *route, int metric, uint32_t *value) +{ + if (metric > RTAX_MAX || metric < 1) + return -NLE_RANGE; + + if (!(route->rt_metrics_mask & (1 << (metric - 1)))) + return -NLE_OBJ_NOTFOUND; + + if (value) + *value = route->rt_metrics[metric - 1]; + + return 0; +} + +int rtnl_route_set_pref_src(struct rtnl_route *route, struct nl_addr *addr) +{ + if (route->ce_mask & ROUTE_ATTR_FAMILY) { + if (addr->a_family != route->rt_family) + return -NLE_AF_MISMATCH; + } else + route->rt_family = addr->a_family; + + if (route->rt_pref_src) + nl_addr_put(route->rt_pref_src); + + nl_addr_get(addr); + route->rt_pref_src = addr; + route->ce_mask |= (ROUTE_ATTR_PREF_SRC | ROUTE_ATTR_FAMILY); + + return 0; +} + +struct nl_addr *rtnl_route_get_pref_src(struct rtnl_route *route) +{ + return route->rt_pref_src; +} + +void rtnl_route_set_iif(struct rtnl_route *route, int ifindex) +{ + route->rt_iif = ifindex; + route->ce_mask |= ROUTE_ATTR_IIF; +} + +int rtnl_route_get_iif(struct rtnl_route *route) +{ + return route->rt_iif; +} + +void rtnl_route_add_nexthop(struct rtnl_route *route, struct rtnl_nexthop *nh) +{ + nl_list_add_tail(&nh->rtnh_list, &route->rt_nexthops); + route->rt_nr_nh++; + route->ce_mask |= ROUTE_ATTR_MULTIPATH; +} + +void rtnl_route_remove_nexthop(struct rtnl_route *route, struct rtnl_nexthop *nh) +{ + if (route->ce_mask & ROUTE_ATTR_MULTIPATH) { + route->rt_nr_nh--; + nl_list_del(&nh->rtnh_list); + } +} + +struct nl_list_head *rtnl_route_get_nexthops(struct rtnl_route *route) +{ + if (route->ce_mask & ROUTE_ATTR_MULTIPATH) + return &route->rt_nexthops; + + return NULL; +} + +int rtnl_route_get_nnexthops(struct rtnl_route *route) +{ + if (route->ce_mask & ROUTE_ATTR_MULTIPATH) + return route->rt_nr_nh; + + return 0; +} + +void rtnl_route_foreach_nexthop(struct rtnl_route *r, + void (*cb)(struct rtnl_nexthop *, void *), + void *arg) +{ + struct rtnl_nexthop *nh; + + if (r->ce_mask & ROUTE_ATTR_MULTIPATH) { + nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) { + cb(nh, arg); + } + } +} + +struct rtnl_nexthop *rtnl_route_nexthop_n(struct rtnl_route *r, int n) +{ + struct rtnl_nexthop *nh; + uint32_t i; + + if (r->ce_mask & ROUTE_ATTR_MULTIPATH && r->rt_nr_nh > n) { + i = 0; + nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) { + if (i == n) return nh; + i++; + } + } + return NULL; +} + +void rtnl_route_set_ttl_propagate(struct rtnl_route *route, uint8_t ttl_prop) +{ + route->rt_ttl_propagate = ttl_prop; + route->ce_mask |= ROUTE_ATTR_TTL_PROPAGATE; +} + +int rtnl_route_get_ttl_propagate(struct rtnl_route *route) +{ + if (!route) + return -NLE_INVAL; + if (!(route->ce_mask & ROUTE_ATTR_TTL_PROPAGATE)) + return -NLE_MISSING_ATTR; + return route->rt_ttl_propagate; +} + +/** @} */ + +/** + * @name Utilities + * @{ + */ + +/** + * Guess scope of a route object. + * @arg route Route object. + * + * Guesses the scope of a route object, based on the following rules: + * @code + * 1) Local route -> local scope + * 2) At least one nexthop not directly connected -> universe scope + * 3) All others -> link scope + * @endcode + * + * @return Scope value. + */ +int rtnl_route_guess_scope(struct rtnl_route *route) +{ + if (route->rt_type == RTN_LOCAL) + return RT_SCOPE_HOST; + + if (route->rt_family == AF_MPLS) + return RT_SCOPE_UNIVERSE; + + if (!nl_list_empty(&route->rt_nexthops)) { + struct rtnl_nexthop *nh; + + /* + * Use scope uiniverse if there is at least one nexthop which + * is not directly connected + */ + nl_list_for_each_entry(nh, &route->rt_nexthops, rtnh_list) { + if (nh->rtnh_gateway) + return RT_SCOPE_UNIVERSE; + } + } + + return RT_SCOPE_LINK; +} + +/** @} */ + +static struct nl_addr *rtnl_route_parse_via(struct nlattr *nla) +{ + int alen = nla_len(nla) - offsetof(struct rtvia, rtvia_addr); + struct rtvia *via = nla_data(nla); + + return nl_addr_build(via->rtvia_family, via->rtvia_addr, alen); +} + +static int rtnl_route_put_via(struct nl_msg *msg, struct nl_addr *addr) +{ + unsigned int alen = nl_addr_get_len(addr); + struct nlattr *nla; + struct rtvia *via; + + nla = nla_reserve(msg, RTA_VIA, alen + sizeof(*via)); + if (!nla) + return -EMSGSIZE; + + via = nla_data(nla); + via->rtvia_family = nl_addr_get_family(addr); + memcpy(via->rtvia_addr, nl_addr_get_binary_addr(addr), alen); + + return 0; +} + +static struct nla_policy route_policy[RTA_MAX+1] = { + [RTA_IIF] = { .type = NLA_U32 }, + [RTA_OIF] = { .type = NLA_U32 }, + [RTA_PRIORITY] = { .type = NLA_U32 }, + [RTA_FLOW] = { .type = NLA_U32 }, + [RTA_CACHEINFO] = { .minlen = sizeof(struct rta_cacheinfo) }, + [RTA_METRICS] = { .type = NLA_NESTED }, + [RTA_MULTIPATH] = { .type = NLA_NESTED }, + [RTA_TTL_PROPAGATE] = { .type = NLA_U8 }, + [RTA_ENCAP] = { .type = NLA_NESTED }, + [RTA_ENCAP_TYPE] = { .type = NLA_U16 }, +}; + +static int parse_multipath(struct rtnl_route *route, struct nlattr *attr) +{ + struct rtnexthop *rtnh = nla_data(attr); + size_t tlen = nla_len(attr); + int err; + + while (tlen >= sizeof(*rtnh) && tlen >= rtnh->rtnh_len) { + _nl_auto_rtnl_nexthop struct rtnl_nexthop *nh = NULL; + + nh = rtnl_route_nh_alloc(); + if (!nh) + return -NLE_NOMEM; + + rtnl_route_nh_set_weight(nh, rtnh->rtnh_hops); + rtnl_route_nh_set_ifindex(nh, rtnh->rtnh_ifindex); + rtnl_route_nh_set_flags(nh, rtnh->rtnh_flags); + + if (rtnh->rtnh_len > sizeof(*rtnh)) { + struct nlattr *ntb[RTA_MAX + 1]; + + err = nla_parse(ntb, RTA_MAX, (struct nlattr *) + RTNH_DATA(rtnh), + rtnh->rtnh_len - sizeof(*rtnh), + route_policy); + if (err < 0) + return err; + + if (ntb[RTA_GATEWAY]) { + _nl_auto_nl_addr struct nl_addr *addr = NULL; + + addr = nl_addr_alloc_attr(ntb[RTA_GATEWAY], + route->rt_family); + if (!addr) + return -NLE_NOMEM; + + rtnl_route_nh_set_gateway(nh, addr); + } + + if (ntb[RTA_FLOW]) { + uint32_t realms; + + realms = nla_get_u32(ntb[RTA_FLOW]); + rtnl_route_nh_set_realms(nh, realms); + } + + if (ntb[RTA_NEWDST]) { + _nl_auto_nl_addr struct nl_addr *addr = NULL; + + addr = nl_addr_alloc_attr(ntb[RTA_NEWDST], + route->rt_family); + if (!addr) + return -NLE_NOMEM; + + err = rtnl_route_nh_set_newdst(nh, addr); + if (err < 0) + return err; + } + + if (ntb[RTA_VIA]) { + _nl_auto_nl_addr struct nl_addr *addr = NULL; + + addr = rtnl_route_parse_via(ntb[RTA_VIA]); + if (!addr) + return -NLE_NOMEM; + + err = rtnl_route_nh_set_via(nh, addr); + if (err < 0) + return err; + } + + if (ntb[RTA_ENCAP] && ntb[RTA_ENCAP_TYPE]) { + err = nh_encap_parse_msg(ntb[RTA_ENCAP], + ntb[RTA_ENCAP_TYPE], + nh); + if (err < 0) + return err; + } + } + + rtnl_route_add_nexthop(route, nh); + tlen -= RTNH_ALIGN(rtnh->rtnh_len); + rtnh = RTNH_NEXT(rtnh); + } + + return 0; +} + +int rtnl_route_parse(struct nlmsghdr *nlh, struct rtnl_route **result) +{ + _nl_auto_rtnl_route struct rtnl_route *route = NULL; + _nl_auto_rtnl_nexthop struct rtnl_nexthop *old_nh = NULL; + _nl_auto_nl_addr struct nl_addr *src = NULL; + _nl_auto_nl_addr struct nl_addr *dst = NULL; + struct nlattr *tb[RTA_MAX + 1]; + struct rtmsg *rtm; + int family; + int err; + + route = rtnl_route_alloc(); + if (!route) + return -NLE_NOMEM; + + route->ce_msgtype = nlh->nlmsg_type; + + err = nlmsg_parse(nlh, sizeof(struct rtmsg), tb, RTA_MAX, route_policy); + if (err < 0) + return err; + + rtm = nlmsg_data(nlh); + route->rt_family = family = rtm->rtm_family; + route->rt_tos = rtm->rtm_tos; + route->rt_table = rtm->rtm_table; + route->rt_type = rtm->rtm_type; + route->rt_scope = rtm->rtm_scope; + route->rt_protocol = rtm->rtm_protocol; + route->rt_flags = rtm->rtm_flags; + route->rt_prio = 0; + + route->ce_mask |= ROUTE_ATTR_FAMILY | ROUTE_ATTR_TOS | + ROUTE_ATTR_TABLE | ROUTE_ATTR_TYPE | + ROUTE_ATTR_SCOPE | ROUTE_ATTR_PROTOCOL | + ROUTE_ATTR_FLAGS; + + /* right now MPLS does not allow rt_prio to be set, so don't + * assume it is unless it comes from an attribute + */ + if (family != AF_MPLS) + route->ce_mask |= ROUTE_ATTR_PRIO; + + if (tb[RTA_DST]) { + if (!(dst = nl_addr_alloc_attr(tb[RTA_DST], family))) + return -NLE_NOMEM; + } else { + if (!(dst = nl_addr_alloc(0))) + return -NLE_NOMEM; + nl_addr_set_family(dst, rtm->rtm_family); + } + + nl_addr_set_prefixlen(dst, rtm->rtm_dst_len); + err = rtnl_route_set_dst(route, dst); + if (err < 0) + return err; + + if (tb[RTA_SRC]) { + if (!(src = nl_addr_alloc_attr(tb[RTA_SRC], family))) + return -NLE_NOMEM; + } else if (rtm->rtm_src_len) + if (!(src = nl_addr_alloc(0))) + return -NLE_NOMEM; + + if (src) { + nl_addr_set_prefixlen(src, rtm->rtm_src_len); + rtnl_route_set_src(route, src); + } + + if (tb[RTA_TABLE]) + rtnl_route_set_table(route, nla_get_u32(tb[RTA_TABLE])); + + if (tb[RTA_IIF]) + rtnl_route_set_iif(route, nla_get_u32(tb[RTA_IIF])); + + if (tb[RTA_PRIORITY]) + rtnl_route_set_priority(route, nla_get_u32(tb[RTA_PRIORITY])); + + if (tb[RTA_PREFSRC]) { + _nl_auto_nl_addr struct nl_addr *addr = NULL; + + if (!(addr = nl_addr_alloc_attr(tb[RTA_PREFSRC], family))) + return -NLE_NOMEM; + rtnl_route_set_pref_src(route, addr); + } + + if (tb[RTA_METRICS]) { + struct nlattr *mtb[RTAX_MAX + 1]; + int i; + + err = nla_parse_nested(mtb, RTAX_MAX, tb[RTA_METRICS], NULL); + if (err < 0) + return err; + + for (i = 1; i <= RTAX_MAX; i++) { + if (mtb[i] && nla_len(mtb[i]) >= sizeof(uint32_t)) { + uint32_t m = nla_get_u32(mtb[i]); + + err = rtnl_route_set_metric(route, i, m); + if (err < 0) + return err; + } + } + } + + if (tb[RTA_MULTIPATH]) { + if ((err = parse_multipath(route, tb[RTA_MULTIPATH])) < 0) + return err; + } + + if (tb[RTA_CACHEINFO]) { + nla_memcpy(&route->rt_cacheinfo, tb[RTA_CACHEINFO], + sizeof(route->rt_cacheinfo)); + route->ce_mask |= ROUTE_ATTR_CACHEINFO; + } + + if (tb[RTA_OIF]) { + if (!old_nh && !(old_nh = rtnl_route_nh_alloc())) + return -NLE_NOMEM; + + rtnl_route_nh_set_ifindex(old_nh, nla_get_u32(tb[RTA_OIF])); + } + + if (tb[RTA_GATEWAY]) { + _nl_auto_nl_addr struct nl_addr *addr = NULL; + + if (!old_nh && !(old_nh = rtnl_route_nh_alloc())) + return -NLE_NOMEM; + + if (!(addr = nl_addr_alloc_attr(tb[RTA_GATEWAY], family))) + return -NLE_NOMEM; + + rtnl_route_nh_set_gateway(old_nh, addr); + } + + if (tb[RTA_FLOW]) { + if (!old_nh && !(old_nh = rtnl_route_nh_alloc())) + return -NLE_NOMEM; + + rtnl_route_nh_set_realms(old_nh, nla_get_u32(tb[RTA_FLOW])); + } + + if (tb[RTA_NEWDST]) { + _nl_auto_nl_addr struct nl_addr *addr = NULL; + + if (!old_nh && !(old_nh = rtnl_route_nh_alloc())) + return -NLE_NOMEM; + + addr = nl_addr_alloc_attr(tb[RTA_NEWDST], route->rt_family); + if (!addr) + return -NLE_NOMEM; + + err = rtnl_route_nh_set_newdst(old_nh, addr); + if (err < 0) + return err; + } + + if (tb[RTA_VIA]) { + int alen = nla_len(tb[RTA_VIA]) - offsetof(struct rtvia, rtvia_addr); + _nl_auto_nl_addr struct nl_addr *addr = NULL; + struct rtvia *via = nla_data(tb[RTA_VIA]); + + if (!old_nh && !(old_nh = rtnl_route_nh_alloc())) + return -NLE_NOMEM; + + addr = nl_addr_build(via->rtvia_family, via->rtvia_addr, alen); + if (!addr) + return -NLE_NOMEM; + + err = rtnl_route_nh_set_via(old_nh, addr); + if (err < 0) + return err; + } + + if (tb[RTA_TTL_PROPAGATE]) { + rtnl_route_set_ttl_propagate(route, + nla_get_u8(tb[RTA_TTL_PROPAGATE])); + } + + if (tb[RTA_ENCAP] && tb[RTA_ENCAP_TYPE]) { + if (!old_nh && !(old_nh = rtnl_route_nh_alloc())) + return -NLE_NOMEM; + + err = nh_encap_parse_msg(tb[RTA_ENCAP], + tb[RTA_ENCAP_TYPE], old_nh); + if (err < 0) + return err; + } + + if (old_nh) { + rtnl_route_nh_set_flags(old_nh, rtm->rtm_flags & 0xff); + if (route->rt_nr_nh == 0) { + /* If no nexthops have been provided via RTA_MULTIPATH + * we add it as regular nexthop to maintain backwards + * compatibility */ + rtnl_route_add_nexthop(route, _nl_steal_pointer(&old_nh)); + } else { + /* Kernel supports new style nexthop configuration, + * verify that it is a duplicate and discard nexthop. */ + struct rtnl_nexthop *first; + + first = nl_list_first_entry(&route->rt_nexthops, + struct rtnl_nexthop, + rtnh_list); + if (!first) + BUG(); + + if (rtnl_route_nh_compare(old_nh, first, + old_nh->ce_mask, 0)) { + return -NLE_INVAL; + } + } + } + + *result = _nl_steal_pointer(&route); + return 0; +} + +int rtnl_route_build_msg(struct nl_msg *msg, struct rtnl_route *route) +{ + int i; + struct nlattr *metrics; + struct rtmsg rtmsg = { + .rtm_family = route->rt_family, + .rtm_tos = route->rt_tos, + .rtm_table = route->rt_table, + .rtm_protocol = route->rt_protocol, + .rtm_scope = route->rt_scope, + .rtm_type = route->rt_type, + .rtm_flags = route->rt_flags, + }; + + if (route->rt_dst == NULL) + return -NLE_MISSING_ATTR; + + rtmsg.rtm_dst_len = nl_addr_get_prefixlen(route->rt_dst); + if (route->rt_src) + rtmsg.rtm_src_len = nl_addr_get_prefixlen(route->rt_src); + + if (!(route->ce_mask & ROUTE_ATTR_SCOPE)) + rtmsg.rtm_scope = rtnl_route_guess_scope(route); + + if (rtnl_route_get_nnexthops(route) == 1) { + struct rtnl_nexthop *nh; + nh = rtnl_route_nexthop_n(route, 0); + rtmsg.rtm_flags |= nh->rtnh_flags; + } + + if (nlmsg_append(msg, &rtmsg, sizeof(rtmsg), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + /* Additional table attribute replacing the 8bit in the header, was + * required to allow more than 256 tables. MPLS does not allow the + * table attribute to be set + */ + if (route->rt_family != AF_MPLS) + NLA_PUT_U32(msg, RTA_TABLE, route->rt_table); + + if (nl_addr_get_len(route->rt_dst)) + NLA_PUT_ADDR(msg, RTA_DST, route->rt_dst); + + if (route->ce_mask & ROUTE_ATTR_PRIO) + NLA_PUT_U32(msg, RTA_PRIORITY, route->rt_prio); + + if (route->ce_mask & ROUTE_ATTR_SRC) + NLA_PUT_ADDR(msg, RTA_SRC, route->rt_src); + + if (route->ce_mask & ROUTE_ATTR_PREF_SRC) + NLA_PUT_ADDR(msg, RTA_PREFSRC, route->rt_pref_src); + + if (route->ce_mask & ROUTE_ATTR_IIF) + NLA_PUT_U32(msg, RTA_IIF, route->rt_iif); + + if (route->ce_mask & ROUTE_ATTR_TTL_PROPAGATE) + NLA_PUT_U8(msg, RTA_TTL_PROPAGATE, route->rt_ttl_propagate); + + if (route->rt_nmetrics > 0) { + uint32_t val; + + metrics = nla_nest_start(msg, RTA_METRICS); + if (metrics == NULL) + goto nla_put_failure; + + for (i = 1; i <= RTAX_MAX; i++) { + if (!rtnl_route_get_metric(route, i, &val)) + NLA_PUT_U32(msg, i, val); + } + + nla_nest_end(msg, metrics); + } + + if (rtnl_route_get_nnexthops(route) == 1) { + struct rtnl_nexthop *nh; + + nh = rtnl_route_nexthop_n(route, 0); + if (nh->rtnh_gateway) + NLA_PUT_ADDR(msg, RTA_GATEWAY, nh->rtnh_gateway); + if (nh->rtnh_ifindex) + NLA_PUT_U32(msg, RTA_OIF, nh->rtnh_ifindex); + if (nh->rtnh_realms) + NLA_PUT_U32(msg, RTA_FLOW, nh->rtnh_realms); + if (nh->rtnh_newdst) + NLA_PUT_ADDR(msg, RTA_NEWDST, nh->rtnh_newdst); + if (nh->rtnh_via && rtnl_route_put_via(msg, nh->rtnh_via) < 0) + goto nla_put_failure; + if (nh->rtnh_encap && + nh_encap_build_msg(msg, nh->rtnh_encap) < 0) + goto nla_put_failure; + } else if (rtnl_route_get_nnexthops(route) > 1) { + struct nlattr *multipath; + struct rtnl_nexthop *nh; + + if (!(multipath = nla_nest_start(msg, RTA_MULTIPATH))) + goto nla_put_failure; + + nl_list_for_each_entry(nh, &route->rt_nexthops, rtnh_list) { + struct rtnexthop *rtnh; + + rtnh = nlmsg_reserve(msg, sizeof(*rtnh), NLMSG_ALIGNTO); + if (!rtnh) + goto nla_put_failure; + + rtnh->rtnh_flags = nh->rtnh_flags; + rtnh->rtnh_hops = nh->rtnh_weight; + rtnh->rtnh_ifindex = nh->rtnh_ifindex; + + if (nh->rtnh_gateway) + NLA_PUT_ADDR(msg, RTA_GATEWAY, + nh->rtnh_gateway); + + if (nh->rtnh_newdst) + NLA_PUT_ADDR(msg, RTA_NEWDST, nh->rtnh_newdst); + + if (nh->rtnh_via && + rtnl_route_put_via(msg, nh->rtnh_via) < 0) + goto nla_put_failure; + + if (nh->rtnh_realms) + NLA_PUT_U32(msg, RTA_FLOW, nh->rtnh_realms); + + if (nh->rtnh_encap && + nh_encap_build_msg(msg, nh->rtnh_encap) < 0) + goto nla_put_failure; + + rtnh->rtnh_len = (char *) nlmsg_tail(msg->nm_nlh) - + (char *) rtnh; + } + + nla_nest_end(msg, multipath); + } + + return 0; + +nla_put_failure: + return -NLE_MSGSIZE; +} + +/** @cond SKIP */ +struct nl_object_ops route_obj_ops = { + .oo_name = "route/route", + .oo_size = sizeof(struct rtnl_route), + .oo_constructor = route_constructor, + .oo_free_data = route_free_data, + .oo_clone = route_clone, + .oo_dump = { + [NL_DUMP_LINE] = route_dump_line, + [NL_DUMP_DETAILS] = route_dump_details, + [NL_DUMP_STATS] = route_dump_stats, + }, + .oo_compare = route_compare, + .oo_keygen = route_keygen, + .oo_update = route_update, + .oo_attrs2str = route_attrs2str, + .oo_id_attrs = (ROUTE_ATTR_FAMILY | ROUTE_ATTR_TOS | + ROUTE_ATTR_TABLE | ROUTE_ATTR_DST | + ROUTE_ATTR_PRIO), + .oo_id_attrs_get = route_id_attrs_get, +}; +/** @endcond */ + +/** @} */ diff --git a/libnl/lib/route/route_utils.c b/libnl/lib/route/route_utils.c new file mode 100644 index 0000000..071bb93 --- /dev/null +++ b/libnl/lib/route/route_utils.c @@ -0,0 +1,167 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup route + * @defgroup route_utils Utilities + * @brief Routing Utility Functions + * + * + * @par 1) Translating Routing Table Names + * @code + * // libnl is only aware of the de facto standard routing table names. + * // Additional name <-> identifier associations have to be read in via + * // a configuration file, f.e. /etc/iproute2/rt_tables + * err = rtnl_route_read_table_names("/etc/iproute2/rt_tables"); + * + * // Translating a table name to its idenfier + * int table = rtnl_route_str2table("main"); + * + * // ... and the other way around. + * char buf[32]; + * printf("Name: %s\n", + * rtnl_route_table2str(table, buf, sizeof(buf))); + * @endcode + * + * + * + * + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include + +/** + * @name Routing Table Identifier Translations + * @{ + */ + +static NL_LIST_HEAD(table_names); + +static int add_routing_table_name(long id, const char *name) +{ + return __trans_list_add(id, name, &table_names); +} + +static void __init init_routing_table_names(void) +{ + add_routing_table_name(RT_TABLE_UNSPEC, "unspec"); + add_routing_table_name(RT_TABLE_COMPAT, "compat"); + add_routing_table_name(RT_TABLE_DEFAULT, "default"); + add_routing_table_name(RT_TABLE_MAIN, "main"); + add_routing_table_name(RT_TABLE_LOCAL, "local"); +} + +static void __exit release_routing_table_names(void) +{ + __trans_list_clear(&table_names); +} + +int rtnl_route_read_table_names(const char *path) +{ + __trans_list_clear(&table_names); + + return __nl_read_num_str_file(path, &add_routing_table_name); +} + +char *rtnl_route_table2str(int table, char *buf, size_t size) +{ + return __list_type2str(table, buf, size, &table_names); +} + +int rtnl_route_str2table(const char *name) +{ + return __list_str2type(name, &table_names); +} + + +/** @} */ + +/** + * @name Routing Protocol Translations + * @{ + */ + +static NL_LIST_HEAD(proto_names); + +static int add_proto_name(long id, const char *name) +{ + return __trans_list_add(id, name, &proto_names); +} + +static void __init init_proto_names(void) +{ + add_proto_name(RTPROT_UNSPEC, "unspec"); + add_proto_name(RTPROT_REDIRECT, "redirect"); + add_proto_name(RTPROT_KERNEL, "kernel"); + add_proto_name(RTPROT_BOOT, "boot"); + add_proto_name(RTPROT_STATIC, "static"); +} + +static void __exit release_proto_names(void) +{ + __trans_list_clear(&proto_names); +} + +int rtnl_route_read_protocol_names(const char *path) +{ + __trans_list_clear(&proto_names); + + return __nl_read_num_str_file(path, &add_proto_name); +} + +char *rtnl_route_proto2str(int proto, char *buf, size_t size) +{ + return __list_type2str(proto, buf, size, &proto_names); +} + +int rtnl_route_str2proto(const char *name) +{ + return __list_str2type(name, &proto_names); +} + +/** @} */ + +/** + * @name Routing Metrices Translations + * @{ + */ + +static const struct trans_tbl route_metrices[] = { + __ADD(RTAX_UNSPEC, unspec), + __ADD(RTAX_LOCK, lock), + __ADD(RTAX_MTU, mtu), + __ADD(RTAX_WINDOW, window), + __ADD(RTAX_RTT, rtt), + __ADD(RTAX_RTTVAR, rttvar), + __ADD(RTAX_SSTHRESH, ssthresh), + __ADD(RTAX_CWND, cwnd), + __ADD(RTAX_ADVMSS, advmss), + __ADD(RTAX_REORDERING, reordering), + __ADD(RTAX_HOPLIMIT, hoplimit), + __ADD(RTAX_INITCWND, initcwnd), + __ADD(RTAX_FEATURES, features), +}; + +char *rtnl_route_metric2str(int metric, char *buf, size_t size) +{ + return __type2str(metric, buf, size, route_metrices, + ARRAY_SIZE(route_metrices)); +} + +int rtnl_route_str2metric(const char *name) +{ + return __str2type(name, route_metrices, ARRAY_SIZE(route_metrices)); +} + +/** @} */ + +/** @} */ diff --git a/libnl/lib/route/rtnl.c b/libnl/lib/route/rtnl.c new file mode 100644 index 0000000..20b2750 --- /dev/null +++ b/libnl/lib/route/rtnl.c @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +/** + * @defgroup rtnl Routing Library (libnl-route) + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include + +/** + * @name Sending + * @{ + */ + +/** + * Send routing netlink request message + * @arg sk Netlink socket. + * @arg type Netlink message type. + * @arg family Address family. + * @arg flags Additional netlink message flags. + * + * Fills out a routing netlink request message and sends it out + * using nl_send_simple(). + * + * @return 0 on success or a negative error code. Due to a bug in older + * version of the library, this function returned the number of bytes sent. + * Treat any non-negative number as success. + */ +int nl_rtgen_request(struct nl_sock *sk, int type, int family, int flags) +{ + int err; + struct rtgenmsg gmsg = { + .rtgen_family = family, + }; + + err = nl_send_simple(sk, type, flags, &gmsg, sizeof(gmsg)); + + return err >= 0 ? 0 : err; +} + +/** @} */ + +/** + * @name Routing Type Translations + * @{ + */ + +static const struct trans_tbl rtntypes[] = { + __ADD(RTN_UNSPEC,unspec), + __ADD(RTN_UNICAST,unicast), + __ADD(RTN_LOCAL,local), + __ADD(RTN_BROADCAST,broadcast), + __ADD(RTN_ANYCAST,anycast), + __ADD(RTN_MULTICAST,multicast), + __ADD(RTN_BLACKHOLE,blackhole), + __ADD(RTN_UNREACHABLE,unreachable), + __ADD(RTN_PROHIBIT,prohibit), + __ADD(RTN_THROW,throw), + __ADD(RTN_NAT,nat), + __ADD(RTN_XRESOLVE,xresolve), +}; + +char *nl_rtntype2str(int type, char *buf, size_t size) +{ + return __type2str(type, buf, size, rtntypes, ARRAY_SIZE(rtntypes)); +} + +int nl_str2rtntype(const char *name) +{ + return __str2type(name, rtntypes, ARRAY_SIZE(rtntypes)); +} + +/** @} */ + +/** + * @name Scope Translations + * @{ + */ + +static const struct trans_tbl scopes[] = { + __ADD(255,nowhere), + __ADD(254,host), + __ADD(253,link), + __ADD(200,site), + __ADD(0,global), +}; + +char *rtnl_scope2str(int scope, char *buf, size_t size) +{ + return __type2str(scope, buf, size, scopes, ARRAY_SIZE(scopes)); +} + +int rtnl_str2scope(const char *name) +{ + return __str2type(name, scopes, ARRAY_SIZE(scopes)); +} + +/** @} */ + +/** + * @name Realms Translations + * @{ + */ + +char * rtnl_realms2str(uint32_t realms, char *buf, size_t len) +{ + int from = RTNL_REALM_FROM(realms); + int to = RTNL_REALM_TO(realms); + + snprintf(buf, len, "%d/%d", from, to); + + return buf; +} + +/** @} */ + +/** @} */ diff --git a/libnl/lib/route/rule.c b/libnl/lib/route/rule.c new file mode 100644 index 0000000..eedf132 --- /dev/null +++ b/libnl/lib/route/rule.c @@ -0,0 +1,993 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2010 Thomas Graf + */ + +/** + * @ingroup rtnl + * @defgroup rule Routing Rules + * @brief + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define RULE_ATTR_FAMILY 0x000001 +#define RULE_ATTR_TABLE 0x000002 +#define RULE_ATTR_ACTION 0x000004 +#define RULE_ATTR_FLAGS 0x000008 +#define RULE_ATTR_IIFNAME 0x000010 +#define RULE_ATTR_OIFNAME 0x000020 +#define RULE_ATTR_PRIO 0x000040 +#define RULE_ATTR_MARK 0x000080 +#define RULE_ATTR_MASK 0x000100 +#define RULE_ATTR_GOTO 0x000200 +#define RULE_ATTR_SRC 0x000400 +#define RULE_ATTR_DST 0x000800 +#define RULE_ATTR_DSFIELD 0x001000 +#define RULE_ATTR_FLOW 0x002000 +#define RULE_ATTR_L3MDEV 0x004000 +#define RULE_ATTR_PROTOCOL 0x008000 +#define RULE_ATTR_IP_PROTO 0x010000 +#define RULE_ATTR_SPORT 0x020000 +#define RULE_ATTR_DPORT 0x040000 + +static struct nl_cache_ops rtnl_rule_ops; +static struct nl_object_ops rule_obj_ops; +/** @endcond */ + +static void rule_free_data(struct nl_object *c) +{ + struct rtnl_rule *rule = nl_object_priv(c); + + if (!rule) + return; + + nl_addr_put(rule->r_src); + nl_addr_put(rule->r_dst); +} + +static int rule_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct rtnl_rule *dst = nl_object_priv(_dst); + struct rtnl_rule *src = nl_object_priv(_src); + + if (src->r_src) + if (!(dst->r_src = nl_addr_clone(src->r_src))) + return -NLE_NOMEM; + + if (src->r_dst) + if (!(dst->r_dst = nl_addr_clone(src->r_dst))) + return -NLE_NOMEM; + + return 0; +} + +static struct nla_policy rule_policy[FRA_MAX+1] = { + [FRA_TABLE] = { .type = NLA_U32 }, + [FRA_IIFNAME] = { .type = NLA_STRING, .maxlen = IFNAMSIZ }, + [FRA_OIFNAME] = { .type = NLA_STRING, .maxlen = IFNAMSIZ }, + [FRA_PRIORITY] = { .type = NLA_U32 }, + [FRA_FWMARK] = { .type = NLA_U32 }, + [FRA_FWMASK] = { .type = NLA_U32 }, + [FRA_GOTO] = { .type = NLA_U32 }, + [FRA_FLOW] = { .type = NLA_U32 }, + [FRA_L3MDEV] = { .type = NLA_U8 }, + [FRA_PROTOCOL] = { .type = NLA_U8 }, + [FRA_IP_PROTO] = { .type = NLA_U8 }, + [FRA_SPORT_RANGE] = { .minlen = sizeof(struct fib_rule_port_range), + .maxlen = sizeof(struct fib_rule_port_range) }, + [FRA_DPORT_RANGE] = { .minlen = sizeof(struct fib_rule_port_range), + .maxlen = sizeof(struct fib_rule_port_range) }, +}; + +static int rule_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *n, struct nl_parser_param *pp) +{ + struct rtnl_rule *rule; + struct fib_rule_hdr *frh; + struct nlattr *tb[FRA_MAX+1]; + int err = 1, family; + + rule = rtnl_rule_alloc(); + if (!rule) { + err = -NLE_NOMEM; + goto errout; + } + + rule->ce_msgtype = n->nlmsg_type; + frh = nlmsg_data(n); + + err = nlmsg_parse(n, sizeof(*frh), tb, FRA_MAX, rule_policy); + if (err < 0) + goto errout; + + rule->r_family = family = frh->family; + rule->r_table = frh->table; + rule->r_action = frh->action; + rule->r_flags = frh->flags; + + rule->ce_mask = (RULE_ATTR_FAMILY | RULE_ATTR_ACTION | RULE_ATTR_FLAGS); + if (rule->r_table) + rule->ce_mask |= RULE_ATTR_TABLE; + + /* ipv4 only */ + if (frh->tos) { + rule->r_dsfield = frh->tos; + rule->ce_mask |= RULE_ATTR_DSFIELD; + } + + if (tb[FRA_TABLE]) { + rule->r_table = nla_get_u32(tb[FRA_TABLE]); + if (rule->r_table) + rule->ce_mask |= RULE_ATTR_TABLE; + } + + if (tb[FRA_IIFNAME]) { + nla_strlcpy(rule->r_iifname, tb[FRA_IIFNAME], IFNAMSIZ); + rule->ce_mask |= RULE_ATTR_IIFNAME; + } + + if (tb[FRA_OIFNAME]) { + nla_strlcpy(rule->r_oifname, tb[FRA_OIFNAME], IFNAMSIZ); + rule->ce_mask |= RULE_ATTR_OIFNAME; + } + + if (tb[FRA_PRIORITY]) { + rule->r_prio = nla_get_u32(tb[FRA_PRIORITY]); + rule->ce_mask |= RULE_ATTR_PRIO; + } + + if (tb[FRA_FWMARK]) { + rule->r_mark = nla_get_u32(tb[FRA_FWMARK]); + rule->ce_mask |= RULE_ATTR_MARK; + } + + if (tb[FRA_FWMASK]) { + rule->r_mask = nla_get_u32(tb[FRA_FWMASK]); + rule->ce_mask |= RULE_ATTR_MASK; + } + + if (tb[FRA_GOTO]) { + rule->r_goto = nla_get_u32(tb[FRA_GOTO]); + rule->ce_mask |= RULE_ATTR_GOTO; + } + + if (tb[FRA_SRC]) { + if (!(rule->r_src = nl_addr_alloc_attr(tb[FRA_SRC], family))) + goto errout_enomem; + + nl_addr_set_prefixlen(rule->r_src, frh->src_len); + rule->ce_mask |= RULE_ATTR_SRC; + } + + if (tb[FRA_DST]) { + if (!(rule->r_dst = nl_addr_alloc_attr(tb[FRA_DST], family))) + goto errout_enomem; + nl_addr_set_prefixlen(rule->r_dst, frh->dst_len); + rule->ce_mask |= RULE_ATTR_DST; + } + + /* ipv4 only */ + if (tb[FRA_FLOW]) { + rule->r_flow = nla_get_u32(tb[FRA_FLOW]); + rule->ce_mask |= RULE_ATTR_FLOW; + } + + if (tb[FRA_L3MDEV]) { + rule->r_l3mdev = nla_get_u8(tb[FRA_L3MDEV]); + rule->ce_mask |= RULE_ATTR_L3MDEV; + } + + if (tb[FRA_PROTOCOL]) { + rule->r_protocol = nla_get_u8(tb[FRA_PROTOCOL]); + rule->ce_mask |= RULE_ATTR_PROTOCOL; + } + + if (tb[FRA_IP_PROTO]) { + rule->r_ip_proto = nla_get_u8(tb[FRA_IP_PROTO]); + rule->ce_mask |= RULE_ATTR_IP_PROTO; + } + + if (tb[FRA_SPORT_RANGE]) { + struct fib_rule_port_range *pr; + + pr = nla_data(tb[FRA_SPORT_RANGE]); + rule->r_sport = *pr; + rule->ce_mask |= RULE_ATTR_SPORT; + } + + if (tb[FRA_DPORT_RANGE]) { + struct fib_rule_port_range *pr; + + pr = nla_data(tb[FRA_DPORT_RANGE]); + rule->r_dport = *pr; + rule->ce_mask |= RULE_ATTR_DPORT; + } + + err = pp->pp_cb((struct nl_object *) rule, pp); +errout: + rtnl_rule_put(rule); + return err; + +errout_enomem: + err = -NLE_NOMEM; + goto errout; +} + +static int rule_request_update(struct nl_cache *c, struct nl_sock *h) +{ + return nl_rtgen_request(h, RTM_GETRULE, AF_UNSPEC, NLM_F_DUMP); +} + +static void rule_dump_line(struct nl_object *o, struct nl_dump_params *p) +{ + struct rtnl_rule *r = (struct rtnl_rule *) o; + char buf[128]; + + nl_dump_line(p, "%8d ", (r->ce_mask & RULE_ATTR_PRIO) ? r->r_prio : 0); + nl_dump(p, "%s ", nl_af2str(r->r_family, buf, sizeof(buf))); + + if (r->ce_mask & RULE_ATTR_SRC) + nl_dump(p, "from %s ", + nl_addr2str(r->r_src, buf, sizeof(buf))); + + if (r->ce_mask & RULE_ATTR_DST) + nl_dump(p, "to %s ", + nl_addr2str(r->r_dst, buf, sizeof(buf))); + + if (r->ce_mask & RULE_ATTR_DSFIELD) + nl_dump(p, "tos %u ", r->r_dsfield); + + if (r->ce_mask & (RULE_ATTR_MARK | RULE_ATTR_MASK)) + nl_dump(p, "mark %#x/%#x", r->r_mark, r->r_mask); + + if (r->ce_mask & RULE_ATTR_IIFNAME) + nl_dump(p, "iif %s ", r->r_iifname); + + if (r->ce_mask & RULE_ATTR_OIFNAME) + nl_dump(p, "oif %s ", r->r_oifname); + + if (r->ce_mask & RULE_ATTR_TABLE) + nl_dump(p, "lookup %s ", + rtnl_route_table2str(r->r_table, buf, sizeof(buf))); + + if (r->ce_mask & RULE_ATTR_L3MDEV) + nl_dump(p, "lookup [l3mdev-table] "); + + if (r->ce_mask & RULE_ATTR_IP_PROTO) + nl_dump(p, "ipproto %s ", + nl_ip_proto2str(r->r_ip_proto, buf, sizeof(buf))); + + if (r->ce_mask & RULE_ATTR_SPORT) { + if (r->r_sport.start == r->r_sport.end) + nl_dump(p, "sport %u ", r->r_sport.start); + else + nl_dump(p, "sport %u-%u ", + r->r_sport.start, r->r_sport.end); + } + + if (r->ce_mask & RULE_ATTR_DPORT) { + if (r->r_dport.start == r->r_dport.end) + nl_dump(p, "dport %u ", r->r_dport.start); + else + nl_dump(p, "dport %u-%u ", + r->r_dport.start, r->r_dport.end); + } + + if (r->ce_mask & RULE_ATTR_PROTOCOL) + nl_dump(p, "protocol %s ", + rtnl_route_proto2str(r->r_protocol, buf, sizeof(buf))); + + if (r->ce_mask & RULE_ATTR_FLOW) + nl_dump(p, "flow %s ", + rtnl_realms2str(r->r_flow, buf, sizeof(buf))); + + if (r->ce_mask & RULE_ATTR_GOTO) + nl_dump(p, "goto %u ", r->r_goto); + + if (r->ce_mask & RULE_ATTR_ACTION) + nl_dump(p, "action %s", + nl_rtntype2str(r->r_action, buf, sizeof(buf))); + + nl_dump(p, "\n"); +} + +static void rule_dump_details(struct nl_object *obj, struct nl_dump_params *p) +{ + rule_dump_line(obj, p); +} + +static void rule_dump_stats(struct nl_object *obj, struct nl_dump_params *p) +{ + rule_dump_details(obj, p); +} + +static uint64_t rule_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + struct rtnl_rule *a = (struct rtnl_rule *) _a; + struct rtnl_rule *b = (struct rtnl_rule *) _b; + uint64_t diff = 0; + +#define RULE_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, RULE_ATTR_##ATTR, a, b, EXPR) + + diff |= RULE_DIFF(FAMILY, a->r_family != b->r_family); + diff |= RULE_DIFF(TABLE, a->r_table != b->r_table); + diff |= RULE_DIFF(ACTION, a->r_action != b->r_action); + diff |= RULE_DIFF(IIFNAME, strcmp(a->r_iifname, b->r_iifname)); + diff |= RULE_DIFF(OIFNAME, strcmp(a->r_oifname, b->r_oifname)); + diff |= RULE_DIFF(PRIO, a->r_prio != b->r_prio); + diff |= RULE_DIFF(MARK, a->r_mark != b->r_mark); + diff |= RULE_DIFF(MASK, a->r_mask != b->r_mask); + diff |= RULE_DIFF(GOTO, a->r_goto != b->r_goto); + diff |= RULE_DIFF(SRC, nl_addr_cmp(a->r_src, b->r_src)); + diff |= RULE_DIFF(DST, nl_addr_cmp(a->r_dst, b->r_dst)); + diff |= RULE_DIFF(DSFIELD, a->r_dsfield != b->r_dsfield); + diff |= RULE_DIFF(FLOW, a->r_flow != b->r_flow); + +#undef RULE_DIFF + + return diff; +} + +static const struct trans_tbl rule_attrs[] = { + __ADD(RULE_ATTR_FAMILY, family), + __ADD(RULE_ATTR_TABLE, table), + __ADD(RULE_ATTR_ACTION, action), + __ADD(RULE_ATTR_IIFNAME, iifname), + __ADD(RULE_ATTR_OIFNAME, oifname), + __ADD(RULE_ATTR_PRIO, prio), + __ADD(RULE_ATTR_MARK, mark), + __ADD(RULE_ATTR_MASK, mask), + __ADD(RULE_ATTR_GOTO, goto), + __ADD(RULE_ATTR_SRC, src), + __ADD(RULE_ATTR_DST, dst), + __ADD(RULE_ATTR_DSFIELD, dsfield), + __ADD(RULE_ATTR_FLOW, flow), +}; + +static char *rule_attrs2str(int attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, rule_attrs, + ARRAY_SIZE(rule_attrs)); +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct rtnl_rule *rtnl_rule_alloc(void) +{ + return (struct rtnl_rule *) nl_object_alloc(&rule_obj_ops); +} + +void rtnl_rule_put(struct rtnl_rule *rule) +{ + nl_object_put((struct nl_object *) rule); +} + +/** @} */ + +/** + * @name Cache Management + * @{ + */ + +/** + * Build a rule cache including all rules currently configured in the kernel. + * @arg sock Netlink socket. + * @arg family Address family or AF_UNSPEC. + * @arg result Pointer to store resulting cache. + * + * Allocates a new rule cache, initializes it properly and updates it + * to include all rules currently configured in the kernel. + * + * @return 0 on success or a negative error code. + */ +int rtnl_rule_alloc_cache(struct nl_sock *sock, int family, + struct nl_cache **result) +{ + struct nl_cache * cache; + int err; + + if (!(cache = nl_cache_alloc(&rtnl_rule_ops))) + return -NLE_NOMEM; + + cache->c_iarg1 = family; + + if (sock && (err = nl_cache_refill(sock, cache)) < 0) { + free(cache); + return err; + } + + *result = cache; + return 0; +} + +/** @} */ + +/** + * @name Rule Addition + * @{ + */ + +static int build_rule_msg(struct rtnl_rule *tmpl, int cmd, int flags, + struct nl_msg **result) +{ + struct nl_msg *msg; + struct fib_rule_hdr frh = { + .family = tmpl->r_family, + .table = tmpl->r_table, + .action = tmpl->r_action, + .flags = tmpl->r_flags, + .tos = tmpl->r_dsfield, + }; + + if (!(tmpl->ce_mask & RULE_ATTR_FAMILY)) + return -NLE_MISSING_ATTR; + + msg = nlmsg_alloc_simple(cmd, flags); + if (!msg) + return -NLE_NOMEM; + + if (tmpl->ce_mask & RULE_ATTR_SRC) + frh.src_len = nl_addr_get_prefixlen(tmpl->r_src); + + if (tmpl->ce_mask & RULE_ATTR_DST) + frh.dst_len = nl_addr_get_prefixlen(tmpl->r_dst); + + if (nlmsg_append(msg, &frh, sizeof(frh), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + /* Additional table attribute replacing the 8bit in the header, was + * required to allow more than 256 tables. */ + NLA_PUT_U32(msg, FRA_TABLE, tmpl->r_table); + + if (tmpl->ce_mask & RULE_ATTR_SRC) + NLA_PUT_ADDR(msg, FRA_SRC, tmpl->r_src); + + if (tmpl->ce_mask & RULE_ATTR_DST) + NLA_PUT_ADDR(msg, FRA_DST, tmpl->r_dst); + + if (tmpl->ce_mask & RULE_ATTR_IIFNAME) + NLA_PUT_STRING(msg, FRA_IIFNAME, tmpl->r_iifname); + + if (tmpl->ce_mask & RULE_ATTR_OIFNAME) + NLA_PUT_STRING(msg, FRA_OIFNAME, tmpl->r_oifname); + + if (tmpl->ce_mask & RULE_ATTR_PRIO) + NLA_PUT_U32(msg, FRA_PRIORITY, tmpl->r_prio); + + if (tmpl->ce_mask & RULE_ATTR_MARK) + NLA_PUT_U32(msg, FRA_FWMARK, tmpl->r_mark); + + if (tmpl->ce_mask & RULE_ATTR_MASK) + NLA_PUT_U32(msg, FRA_FWMASK, tmpl->r_mask); + + if (tmpl->ce_mask & RULE_ATTR_GOTO) + NLA_PUT_U32(msg, FRA_GOTO, tmpl->r_goto); + + if (tmpl->ce_mask & RULE_ATTR_FLOW) + NLA_PUT_U32(msg, FRA_FLOW, tmpl->r_flow); + + if (tmpl->ce_mask & RULE_ATTR_L3MDEV) + NLA_PUT_U8(msg, FRA_L3MDEV, tmpl->r_l3mdev); + + if (tmpl->ce_mask & RULE_ATTR_IP_PROTO) + NLA_PUT_U8(msg, FRA_IP_PROTO, tmpl->r_ip_proto); + + if (tmpl->ce_mask & RULE_ATTR_SPORT) + NLA_PUT(msg, FRA_SPORT_RANGE, sizeof(tmpl->r_sport), + &tmpl->r_sport); + + if (tmpl->ce_mask & RULE_ATTR_DPORT) + NLA_PUT(msg, FRA_DPORT_RANGE, sizeof(tmpl->r_dport), + &tmpl->r_dport); + + if (tmpl->ce_mask & RULE_ATTR_PROTOCOL) + NLA_PUT_U8(msg, FRA_PROTOCOL, tmpl->r_protocol); + + *result = msg; + return 0; + +nla_put_failure: + nlmsg_free(msg); + return -NLE_MSGSIZE; +} + +/** + * Build netlink request message to add a new rule + * @arg tmpl template with data of new rule + * @arg flags additional netlink message flags + * @arg result Result pointer + * + * Builds a new netlink message requesting a addition of a new + * rule. The netlink message header isn't fully equipped with + * all relevant fields and must thus be sent out via nl_send_auto_complete() + * or supplemented as needed. \a tmpl must contain the attributes of the new + * address set via \c rtnl_rule_set_* functions. + * + * @return 0 on success or a negative error code. + */ +int rtnl_rule_build_add_request(struct rtnl_rule *tmpl, int flags, + struct nl_msg **result) +{ + return build_rule_msg(tmpl, RTM_NEWRULE, NLM_F_CREATE | flags, + result); +} + +/** + * Add a new rule + * @arg sk Netlink socket. + * @arg tmpl template with requested changes + * @arg flags additional netlink message flags + * + * Builds a netlink message by calling rtnl_rule_build_add_request(), + * sends the request to the kernel and waits for the next ACK to be + * received and thus blocks until the request has been fullfilled. + * + * @return 0 on sucess or a negative error if an error occured. + */ +int rtnl_rule_add(struct nl_sock *sk, struct rtnl_rule *tmpl, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_rule_build_add_request(tmpl, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +/** @} */ + +/** + * @name Rule Deletion + * @{ + */ + +/** + * Build a netlink request message to delete a rule + * @arg rule rule to delete + * @arg flags additional netlink message flags + * @arg result Result pointer + * + * Builds a new netlink message requesting a deletion of a rule. + * The netlink message header isn't fully equipped with all relevant + * fields and must thus be sent out via nl_send_auto_complete() + * or supplemented as needed. \a rule must point to an existing + * address. + * + * @return 0 on success or a negative error code. + */ +int rtnl_rule_build_delete_request(struct rtnl_rule *rule, int flags, + struct nl_msg **result) +{ + return build_rule_msg(rule, RTM_DELRULE, flags, result); +} + +/** + * Delete a rule + * @arg sk Netlink socket. + * @arg rule rule to delete + * @arg flags additional netlink message flags + * + * Builds a netlink message by calling rtnl_rule_build_delete_request(), + * sends the request to the kernel and waits for the next ACK to be + * received and thus blocks until the request has been fullfilled. + * + * @return 0 on sucess or a negative error if an error occured. + */ +int rtnl_rule_delete(struct nl_sock *sk, struct rtnl_rule *rule, int flags) +{ + struct nl_msg *msg; + int err; + + if ((err = rtnl_rule_build_delete_request(rule, flags, &msg)) < 0) + return err; + + err = nl_send_auto_complete(sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return wait_for_ack(sk); +} + +/** @} */ + +/** + * @name Attribute Modification + * @{ + */ + +void rtnl_rule_set_family(struct rtnl_rule *rule, int family) +{ + rule->r_family = family; + rule->ce_mask |= RULE_ATTR_FAMILY; +} + +int rtnl_rule_get_family(struct rtnl_rule *rule) +{ + if (rule->ce_mask & RULE_ATTR_FAMILY) + return rule->r_family; + else + return AF_UNSPEC; +} + +void rtnl_rule_set_prio(struct rtnl_rule *rule, uint32_t prio) +{ + rule->r_prio = prio; + rule->ce_mask |= RULE_ATTR_PRIO; +} + +uint32_t rtnl_rule_get_prio(struct rtnl_rule *rule) +{ + return rule->r_prio; +} + +void rtnl_rule_set_mark(struct rtnl_rule *rule, uint32_t mark) +{ + rule->r_mark = mark; + rule->ce_mask |= RULE_ATTR_MARK; +} + +uint32_t rtnl_rule_get_mark(struct rtnl_rule *rule) +{ + return rule->r_mark; +} + +void rtnl_rule_set_mask(struct rtnl_rule *rule, uint32_t mask) +{ + rule->r_mask = mask; + rule->ce_mask |= RULE_ATTR_MASK; +} + +uint32_t rtnl_rule_get_mask(struct rtnl_rule *rule) +{ + return rule->r_mask; +} + +void rtnl_rule_set_table(struct rtnl_rule *rule, uint32_t table) +{ + rule->r_table = table; + rule->ce_mask |= RULE_ATTR_TABLE; +} + +uint32_t rtnl_rule_get_table(struct rtnl_rule *rule) +{ + return rule->r_table; +} + +void rtnl_rule_set_dsfield(struct rtnl_rule *rule, uint8_t dsfield) +{ + rule->r_dsfield = dsfield; + rule->ce_mask |= RULE_ATTR_DSFIELD; +} + +uint8_t rtnl_rule_get_dsfield(struct rtnl_rule *rule) +{ + return rule->r_dsfield; +} + +static inline int __assign_addr(struct rtnl_rule *rule, struct nl_addr **pos, + struct nl_addr *new, int flag) +{ + if (rule->ce_mask & RULE_ATTR_FAMILY) { + if (new->a_family != rule->r_family) + return -NLE_AF_MISMATCH; + } else + rule->r_family = new->a_family; + + if (*pos) + nl_addr_put(*pos); + + nl_addr_get(new); + *pos = new; + + rule->ce_mask |= (flag | RULE_ATTR_FAMILY); + + return 0; +} + +int rtnl_rule_set_src(struct rtnl_rule *rule, struct nl_addr *src) +{ + return __assign_addr(rule, &rule->r_src, src, RULE_ATTR_SRC); +} + +struct nl_addr *rtnl_rule_get_src(struct rtnl_rule *rule) +{ + return rule->r_src; +} + +int rtnl_rule_set_dst(struct rtnl_rule *rule, struct nl_addr *dst) +{ + return __assign_addr(rule, &rule->r_dst, dst, RULE_ATTR_DST); +} + +struct nl_addr *rtnl_rule_get_dst(struct rtnl_rule *rule) +{ + return rule->r_dst; +} + +int rtnl_rule_set_iif(struct rtnl_rule *rule, const char *dev) +{ + if (strlen(dev) > IFNAMSIZ-1) + return -NLE_RANGE; + + strcpy(rule->r_iifname, dev); + rule->ce_mask |= RULE_ATTR_IIFNAME; + return 0; +} + +char *rtnl_rule_get_iif(struct rtnl_rule *rule) +{ + if (rule->ce_mask & RULE_ATTR_IIFNAME) + return rule->r_iifname; + else + return NULL; +} + +int rtnl_rule_set_oif(struct rtnl_rule *rule, const char *dev) +{ + if (strlen(dev) > IFNAMSIZ-1) + return -NLE_RANGE; + + strcpy(rule->r_oifname, dev); + rule->ce_mask |= RULE_ATTR_OIFNAME; + return 0; +} + +char *rtnl_rule_get_oif(struct rtnl_rule *rule) +{ + if (rule->ce_mask & RULE_ATTR_OIFNAME) + return rule->r_oifname; + else + return NULL; +} + +void rtnl_rule_set_action(struct rtnl_rule *rule, uint8_t action) +{ + rule->r_action = action; + rule->ce_mask |= RULE_ATTR_ACTION; +} + +uint8_t rtnl_rule_get_action(struct rtnl_rule *rule) +{ + return rule->r_action; +} + +/** + * Set l3mdev value of the rule (FRA_L3MDEV) + * @arg rule rule + * @arg value value to set + * + * Set the l3mdev value to value. Currently supported values + * are only 1 (set it) and -1 (unset it). All other values + * are reserved. + */ +void rtnl_rule_set_l3mdev(struct rtnl_rule *rule, int value) +{ + if (value >= 0) { + rule->r_l3mdev = (uint8_t) value; + rule->ce_mask |= RULE_ATTR_L3MDEV; + } else { + rule->r_l3mdev = 0; + rule->ce_mask &= ~((uint32_t) RULE_ATTR_L3MDEV); + } +} + +/** + * Get l3mdev value of the rule (FRA_L3MDEV) + * @arg rule rule + * + * @return a negative error code, including -NLE_MISSING_ATTR + * if the property is unset. Otherwise returns a non-negative + * value. As FRA_L3MDEV is a boolean, the only expected + * value at the moment is 1. + */ +int rtnl_rule_get_l3mdev(struct rtnl_rule *rule) +{ + if (!rule) + return -NLE_INVAL; + if (!(rule->ce_mask & RULE_ATTR_L3MDEV)) + return -NLE_MISSING_ATTR; + return rule->r_l3mdev; +} + +int rtnl_rule_set_protocol(struct rtnl_rule *rule, uint8_t protocol) +{ + if (protocol) { + rule->r_protocol = protocol; + rule->ce_mask |= RULE_ATTR_PROTOCOL; + } else { + rule->r_protocol = 0; + rule->ce_mask &= ~((uint32_t) RULE_ATTR_PROTOCOL); + } + return 0; +} + +int rtnl_rule_get_protocol(struct rtnl_rule *rule, uint8_t *protocol) +{ + if (!(rule->ce_mask & RULE_ATTR_PROTOCOL)) + return -NLE_INVAL; + + *protocol = rule->r_protocol; + return 0; +} + +int rtnl_rule_set_ipproto(struct rtnl_rule *rule, uint8_t ip_proto) +{ + if (ip_proto) { + rule->r_ip_proto = ip_proto; + rule->ce_mask |= RULE_ATTR_IP_PROTO; + } else { + rule->r_ip_proto = 0; + rule->ce_mask &= ~((uint32_t) RULE_ATTR_IP_PROTO); + } + return 0; +} + +int rtnl_rule_get_ipproto(struct rtnl_rule *rule, uint8_t *ip_proto) +{ + if (!(rule->ce_mask & RULE_ATTR_IP_PROTO)) + return -NLE_INVAL; + + *ip_proto = rule->r_ip_proto; + return 0; +} + +static int __rtnl_rule_set_port(struct fib_rule_port_range *prange, + uint16_t start, uint16_t end, + uint64_t attr, uint64_t *mask) +{ + if ((start && end < start) || (end && !start)) + return -NLE_INVAL; + + if (start) { + prange->start = start; + prange->end = end; + *mask |= attr; + } else { + prange->start = 0; + prange->end = 0; + *mask &= ~attr; + + } + return 0; +} + +int rtnl_rule_set_sport(struct rtnl_rule *rule, uint16_t sport) +{ + return __rtnl_rule_set_port(&rule->r_sport, sport, sport, + RULE_ATTR_SPORT, &rule->ce_mask); +} + +int rtnl_rule_set_sport_range(struct rtnl_rule *rule, uint16_t start, + uint16_t end) +{ + return __rtnl_rule_set_port(&rule->r_sport, start, end, + RULE_ATTR_SPORT, &rule->ce_mask); +} + +int rtnl_rule_get_sport(struct rtnl_rule *rule, uint16_t *start, uint16_t *end) +{ + if (!(rule->ce_mask & RULE_ATTR_SPORT)) + return -NLE_INVAL; + + *start = rule->r_sport.start; + *end = rule->r_sport.end; + return 0; +} + +int rtnl_rule_set_dport(struct rtnl_rule *rule, uint16_t dport) +{ + return __rtnl_rule_set_port(&rule->r_dport, dport, dport, + RULE_ATTR_DPORT, &rule->ce_mask); +} + +int rtnl_rule_set_dport_range(struct rtnl_rule *rule, uint16_t start, + uint16_t end) +{ + return __rtnl_rule_set_port(&rule->r_dport, start, end, + RULE_ATTR_DPORT, &rule->ce_mask); +} + +int rtnl_rule_get_dport(struct rtnl_rule *rule, uint16_t *start, uint16_t *end) +{ + if (!(rule->ce_mask & RULE_ATTR_DPORT)) + return -NLE_INVAL; + + *start = rule->r_dport.start; + *end = rule->r_dport.end; + return 0; +} + +void rtnl_rule_set_realms(struct rtnl_rule *rule, uint32_t realms) +{ + rule->r_flow = realms; + rule->ce_mask |= RULE_ATTR_FLOW; +} + +uint32_t rtnl_rule_get_realms(struct rtnl_rule *rule) +{ + return rule->r_flow; +} + +void rtnl_rule_set_goto(struct rtnl_rule *rule, uint32_t ref) +{ + rule->r_goto = ref; + rule->ce_mask |= RULE_ATTR_GOTO; +} + +uint32_t rtnl_rule_get_goto(struct rtnl_rule *rule) +{ + return rule->r_goto; +} + +/** @} */ + +static struct nl_object_ops rule_obj_ops = { + .oo_name = "route/rule", + .oo_size = sizeof(struct rtnl_rule), + .oo_free_data = rule_free_data, + .oo_clone = rule_clone, + .oo_dump = { + [NL_DUMP_LINE] = rule_dump_line, + [NL_DUMP_DETAILS] = rule_dump_details, + [NL_DUMP_STATS] = rule_dump_stats, + }, + .oo_compare = rule_compare, + .oo_attrs2str = rule_attrs2str, + .oo_id_attrs = ~0, +}; + +static struct nl_af_group rule_groups[] = { + { AF_INET, RTNLGRP_IPV4_RULE }, + { AF_INET6, RTNLGRP_IPV6_RULE }, + { END_OF_GROUP_LIST }, +}; + +static struct nl_cache_ops rtnl_rule_ops = { + .co_name = "route/rule", + .co_hdrsize = sizeof(struct fib_rule_hdr), + .co_msgtypes = { + { RTM_NEWRULE, NL_ACT_NEW, "new" }, + { RTM_DELRULE, NL_ACT_DEL, "del" }, + { RTM_GETRULE, NL_ACT_GET, "get" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_ROUTE, + .co_request_update = rule_request_update, + .co_msg_parser = rule_msg_parser, + .co_obj_ops = &rule_obj_ops, + .co_groups = rule_groups, +}; + +static void __init rule_init(void) +{ + nl_cache_mngt_register(&rtnl_rule_ops); +} + +static void __exit rule_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_rule_ops); +} + +/** @} */ diff --git a/libnl/lib/route/tc.c b/libnl/lib/route/tc.c new file mode 100644 index 0000000..3d1e890 --- /dev/null +++ b/libnl/lib/route/tc.c @@ -0,0 +1,1162 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2011 Thomas Graf + */ + +/** + * @ingroup rtnl + * @defgroup tc Traffic Control + * @{ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "netlink-private/utils.h" + +/** @cond SKIP */ + +static struct nl_list_head tc_ops_list[__RTNL_TC_TYPE_MAX]; +static struct rtnl_tc_type_ops *tc_type_ops[__RTNL_TC_TYPE_MAX]; + +static struct nla_policy tc_policy[TCA_MAX+1] = { + [TCA_KIND] = { .type = NLA_STRING, + .maxlen = TCKINDSIZ }, + [TCA_CHAIN] = { .type = NLA_U32 }, + [TCA_STATS] = { .minlen = sizeof(struct tc_stats) }, + [TCA_STATS2] = { .type = NLA_NESTED }, +}; + +int tca_parse(struct nlattr **tb, int maxattr, struct rtnl_tc *g, + const struct nla_policy *policy) +{ + + if (g->ce_mask & TCA_ATTR_OPTS) + return nla_parse(tb, maxattr, + (struct nlattr *) g->tc_opts->d_data, + g->tc_opts->d_size, policy); + else { + /* Ugly but tb[] must be in a defined state even if no + * attributes can be found. */ + memset(tb, 0, sizeof(struct nlattr *) * (maxattr + 1)); + return 0; + } +} + +static struct nla_policy tc_stats2_policy[TCA_STATS_MAX+1] = { + [TCA_STATS_BASIC] = { .minlen = sizeof(struct gnet_stats_basic) }, + [TCA_STATS_RATE_EST] = { .minlen = sizeof(struct gnet_stats_rate_est) }, + [TCA_STATS_QUEUE] = { .minlen = sizeof(struct gnet_stats_queue) }, +}; + +int rtnl_tc_msg_parse(struct nlmsghdr *n, struct rtnl_tc *tc) +{ + struct nl_cache *link_cache; + struct rtnl_tc_ops *ops; + struct nlattr *tb[TCA_MAX + 1]; + char kind[TCKINDSIZ]; + struct tcmsg *tm; + int err; + + tc->ce_msgtype = n->nlmsg_type; + + err = nlmsg_parse(n, sizeof(*tm), tb, TCA_MAX, tc_policy); + if (err < 0) + return err; + + if (tb[TCA_KIND] == NULL) + return -NLE_MISSING_ATTR; + + nla_strlcpy(kind, tb[TCA_KIND], sizeof(kind)); + rtnl_tc_set_kind(tc, kind); + + if (tb[TCA_CHAIN]) + rtnl_tc_set_chain(tc, nla_get_u32(tb[TCA_CHAIN])); + + tm = nlmsg_data(n); + tc->tc_family = tm->tcm_family; + tc->tc_ifindex = tm->tcm_ifindex; + tc->tc_handle = tm->tcm_handle; + tc->tc_parent = tm->tcm_parent; + tc->tc_info = tm->tcm_info; + + tc->ce_mask |= (TCA_ATTR_FAMILY | TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE| + TCA_ATTR_PARENT | TCA_ATTR_INFO); + + if (tb[TCA_OPTIONS]) { + tc->tc_opts = nl_data_alloc_attr(tb[TCA_OPTIONS]); + if (!tc->tc_opts) + return -NLE_NOMEM; + tc->ce_mask |= TCA_ATTR_OPTS; + } + + if (tb[TCA_STATS2]) { + struct nlattr *tbs[TCA_STATS_MAX + 1]; + + err = nla_parse_nested(tbs, TCA_STATS_MAX, tb[TCA_STATS2], + tc_stats2_policy); + if (err < 0) + return err; + + if (tbs[TCA_STATS_BASIC]) { + struct gnet_stats_basic *bs; + + bs = nla_data(tbs[TCA_STATS_BASIC]); + tc->tc_stats[RTNL_TC_BYTES] = bs->bytes; + tc->tc_stats[RTNL_TC_PACKETS] = bs->packets; + } + + if (tbs[TCA_STATS_RATE_EST]) { + struct gnet_stats_rate_est *re; + + re = nla_data(tbs[TCA_STATS_RATE_EST]); + tc->tc_stats[RTNL_TC_RATE_BPS] = re->bps; + tc->tc_stats[RTNL_TC_RATE_PPS] = re->pps; + } + + if (tbs[TCA_STATS_QUEUE]) { + struct gnet_stats_queue *q; + + q = nla_data(tbs[TCA_STATS_QUEUE]); + tc->tc_stats[RTNL_TC_QLEN] = q->qlen; + tc->tc_stats[RTNL_TC_BACKLOG] = q->backlog; + tc->tc_stats[RTNL_TC_DROPS] = q->drops; + tc->tc_stats[RTNL_TC_REQUEUES] = q->requeues; + tc->tc_stats[RTNL_TC_OVERLIMITS] = q->overlimits; + } + + tc->ce_mask |= TCA_ATTR_STATS; + + if (tbs[TCA_STATS_APP]) { + tc->tc_xstats = nl_data_alloc_attr(tbs[TCA_STATS_APP]); + if (tc->tc_xstats == NULL) + return -NLE_NOMEM; + tc->ce_mask |= TCA_ATTR_XSTATS; + } else + goto compat_xstats; + } else { + if (tb[TCA_STATS]) { + struct tc_stats *st = nla_data(tb[TCA_STATS]); + + tc->tc_stats[RTNL_TC_BYTES] = st->bytes; + tc->tc_stats[RTNL_TC_PACKETS] = st->packets; + tc->tc_stats[RTNL_TC_RATE_BPS] = st->bps; + tc->tc_stats[RTNL_TC_RATE_PPS] = st->pps; + tc->tc_stats[RTNL_TC_QLEN] = st->qlen; + tc->tc_stats[RTNL_TC_BACKLOG] = st->backlog; + tc->tc_stats[RTNL_TC_DROPS] = st->drops; + tc->tc_stats[RTNL_TC_OVERLIMITS]= st->overlimits; + + tc->ce_mask |= TCA_ATTR_STATS; + } + +compat_xstats: + if (tb[TCA_XSTATS]) { + tc->tc_xstats = nl_data_alloc_attr(tb[TCA_XSTATS]); + if (tc->tc_xstats == NULL) + return -NLE_NOMEM; + tc->ce_mask |= TCA_ATTR_XSTATS; + } + } + + ops = rtnl_tc_get_ops(tc); + if (ops && ops->to_msg_parser) { + void *data = rtnl_tc_data(tc); + + if (!data) + return -NLE_NOMEM; + + err = ops->to_msg_parser(tc, data); + if (err < 0) + return err; + } + + if ((link_cache = __nl_cache_mngt_require("route/link"))) { + struct rtnl_link *link; + + if ((link = rtnl_link_get(link_cache, tc->tc_ifindex))) { + rtnl_tc_set_link(tc, link); + + /* rtnl_tc_set_link incs refcnt */ + rtnl_link_put(link); + } + } + + return 0; +} + +int rtnl_tc_msg_build(struct rtnl_tc *tc, int type, int flags, + struct nl_msg **result) +{ + struct nl_msg *msg; + struct rtnl_tc_ops *ops; + struct tcmsg tchdr = { + .tcm_family = AF_UNSPEC, + .tcm_ifindex = tc->tc_ifindex, + .tcm_handle = tc->tc_handle, + .tcm_parent = tc->tc_parent, + }; + int err; + + msg = nlmsg_alloc_simple(type, flags); + if (!msg) + return -NLE_NOMEM; + + if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) { + err = -NLE_MSGSIZE; + goto out_err; + } + + if (tc->ce_mask & TCA_ATTR_KIND) + NLA_PUT_STRING(msg, TCA_KIND, tc->tc_kind); + + if (tc->ce_mask & TCA_ATTR_CHAIN) + NLA_PUT_U32(msg, TCA_CHAIN, tc->tc_chain); + + ops = rtnl_tc_get_ops(tc); + if (ops && (ops->to_msg_fill || ops->to_msg_fill_raw)) { + struct nlattr *opts; + void *data = rtnl_tc_data(tc); + + if (ops->to_msg_fill) { + if (!(opts = nla_nest_start(msg, TCA_OPTIONS))) { + err = -NLE_NOMEM; + goto out_err; + } + + if ((err = ops->to_msg_fill(tc, data, msg)) < 0) + goto out_err; + + if (strcmp("cgroup", tc->tc_kind)) + nla_nest_end(msg, opts); + else + nla_nest_end_keep_empty(msg, opts); + } else if ((err = ops->to_msg_fill_raw(tc, data, msg)) < 0) + goto out_err; + } + + *result = msg; + return 0; + +nla_put_failure: + err = -NLE_NOMEM; +out_err: + nlmsg_free(msg); + return err; +} + + +/** @endcond */ + +/** + * @name Attributes + * @{ + */ + +/** + * Set interface index of traffic control object + * @arg tc traffic control object + * @arg ifindex interface index. + * + * Sets the interface index of a traffic control object. The interface + * index defines the network device which this tc object is attached to. + * This function will overwrite any network device assigned with previous + * calls to rtnl_tc_set_ifindex() or rtnl_tc_set_link(). + */ +void rtnl_tc_set_ifindex(struct rtnl_tc *tc, int ifindex) +{ + /* Obsolete possible old link reference */ + rtnl_link_put(tc->tc_link); + tc->tc_link = NULL; + tc->ce_mask &= ~TCA_ATTR_LINK; + + tc->tc_ifindex = ifindex; + tc->ce_mask |= TCA_ATTR_IFINDEX; +} + +/** + * Return interface index of traffic control object + * @arg tc traffic control object + */ +int rtnl_tc_get_ifindex(struct rtnl_tc *tc) +{ + return tc->tc_ifindex; +} + +/** + * Set link of traffic control object + * @arg tc traffic control object + * @arg link link object + * + * Sets the link of a traffic control object. This function serves + * the same purpose as rtnl_tc_set_ifindex() but due to the continued + * allowed access to the link object it gives it the possibility to + * retrieve sane default values for the the MTU and the linktype. + * Always prefer this function over rtnl_tc_set_ifindex() if you can + * spare to have an additional link object around. + */ +void rtnl_tc_set_link(struct rtnl_tc *tc, struct rtnl_link *link) +{ + rtnl_link_put(tc->tc_link); + + if (!link) + return; + if (!link->l_index) + BUG(); + + nl_object_get(OBJ_CAST(link)); + tc->tc_link = link; + tc->tc_ifindex = link->l_index; + tc->ce_mask |= TCA_ATTR_LINK | TCA_ATTR_IFINDEX; +} + +/** + * Get link of traffic control object + * @arg tc traffic control object + * + * Returns the link of a traffic control object. The link is only + * returned if it has been set before via rtnl_tc_set_link() or + * if a link cache was available while parsing the tc object. This + * function may still return NULL even if an ifindex is assigned to + * the tc object. It will _not_ look up the link by itself. + * + * @note The returned link will have its reference counter incremented. + * It is in the responsibility of the caller to return the + * reference. + * + * @return link object or NULL if not set. + */ +struct rtnl_link *rtnl_tc_get_link(struct rtnl_tc *tc) +{ + if (tc->tc_link) { + nl_object_get(OBJ_CAST(tc->tc_link)); + return tc->tc_link; + } + + return NULL; +} + +/** + * Set the Maximum Transmission Unit (MTU) of traffic control object + * @arg tc traffic control object + * @arg mtu largest packet size expected + * + * Sets the MTU of a traffic control object. Not all traffic control + * objects will make use of this but it helps while calculating rate + * tables. This value is typically derived directly from the link + * the tc object is attached to if the link has been assigned via + * rtnl_tc_set_link(). It is usually not necessary to set the MTU + * manually, this function is provided to allow overwriting the derived + * value. + */ +void rtnl_tc_set_mtu(struct rtnl_tc *tc, uint32_t mtu) +{ + tc->tc_mtu = mtu; + tc->ce_mask |= TCA_ATTR_MTU; +} + +/** + * Return the MTU of traffic control object + * @arg tc traffic control object + * + * Returns the MTU of a traffic control object which has been set via: + * -# User specified value set via rtnl_tc_set_mtu() + * -# Dervied from link set via rtnl_tc_set_link() + * -# Fall back to default: ethernet = 1500 + */ +uint32_t rtnl_tc_get_mtu(struct rtnl_tc *tc) +{ + if (tc->ce_mask & TCA_ATTR_MTU) + return tc->tc_mtu; + else if (tc->ce_mask & TCA_ATTR_LINK) + return tc->tc_link->l_mtu; + else + return 1500; /* default to ethernet */ +} + +/** + * Set the Minimum Packet Unit (MPU) of a traffic control object + * @arg tc traffic control object + * @arg mpu minimum packet size expected + * + * Sets the MPU of a traffic contorl object. It specifies the minimum + * packet size to ever hit this traffic control object. Not all traffic + * control objects will make use of this but it helps while calculating + * rate tables. + */ +void rtnl_tc_set_mpu(struct rtnl_tc *tc, uint32_t mpu) +{ + tc->tc_mpu = mpu; + tc->ce_mask |= TCA_ATTR_MPU; +} + +/** + * Return the Minimum Packet Unit (MPU) of a traffic control object + * @arg tc traffic control object + * + * @return The MPU previously set via rtnl_tc_set_mpu() or 0. + */ +uint32_t rtnl_tc_get_mpu(struct rtnl_tc *tc) +{ + return tc->tc_mpu; +} + +/** + * Set per packet overhead of a traffic control object + * @arg tc traffic control object + * @arg overhead overhead per packet in bytes + * + * Sets the per packet overhead in bytes occuring on the link not seen + * by the kernel. This value can be used to correct size calculations + * if the packet size on the wire does not match the packet sizes seen + * in the network stack. Not all traffic control objects will make use + * this but it helps while calculating accurate packet sizes in the + * kernel. + */ +void rtnl_tc_set_overhead(struct rtnl_tc *tc, uint32_t overhead) +{ + tc->tc_overhead = overhead; + tc->ce_mask |= TCA_ATTR_OVERHEAD; +} + +/** + * Return per packet overhead of a traffic control object + * @arg tc traffic control object + * + * @return The overhead previously set by rtnl_tc_set_overhead() or 0. + */ +uint32_t rtnl_tc_get_overhead(struct rtnl_tc *tc) +{ + return tc->tc_overhead; +} + +/** + * Set the linktype of a traffic control object + * @arg tc traffic control object + * @arg type type of link (e.g. ARPHRD_ATM, ARPHRD_ETHER) + * + * Overwrites the type of link this traffic control object is attached to. + * This value is typically derived from the link this tc object is attached + * if the link has been assigned via rtnl_tc_set_link(). It is usually not + * necessary to set the linktype manually. This function is provided to + * allow overwriting the linktype. + */ +void rtnl_tc_set_linktype(struct rtnl_tc *tc, uint32_t type) +{ + tc->tc_linktype = type; + tc->ce_mask |= TCA_ATTR_LINKTYPE; +} + +/** + * Return the linktype of a traffic control object + * @arg tc traffic control object + * + * Returns the linktype of the link the traffic control object is attached to: + * -# User specified value via rtnl_tc_set_linktype() + * -# Value derived from link set via rtnl_tc_set_link() + * -# Default fall-back: ARPHRD_ETHER + */ +uint32_t rtnl_tc_get_linktype(struct rtnl_tc *tc) +{ + if (tc->ce_mask & TCA_ATTR_LINKTYPE) + return tc->tc_linktype; + else if (tc->ce_mask & TCA_ATTR_LINK) + return tc->tc_link->l_arptype; + else + return ARPHRD_ETHER; /* default to ethernet */ +} + +/** + * Set identifier of traffic control object + * @arg tc traffic control object + * @arg id unique identifier + */ +void rtnl_tc_set_handle(struct rtnl_tc *tc, uint32_t id) +{ + tc->tc_handle = id; + tc->ce_mask |= TCA_ATTR_HANDLE; +} + +/** + * Return identifier of a traffic control object + * @arg tc traffic control object + */ +uint32_t rtnl_tc_get_handle(struct rtnl_tc *tc) +{ + return tc->tc_handle; +} + +/** + * Set the parent identifier of a traffic control object + * @arg tc traffic control object + * @arg parent identifier of parent traffif control object + * + */ +void rtnl_tc_set_parent(struct rtnl_tc *tc, uint32_t parent) +{ + tc->tc_parent = parent; + tc->ce_mask |= TCA_ATTR_PARENT; +} + +/** + * Return parent identifier of a traffic control object + * @arg tc traffic control object + */ +uint32_t rtnl_tc_get_parent(struct rtnl_tc *tc) +{ + return tc->tc_parent; +} + +/** + * Define the type of traffic control object + * @arg tc traffic control object + * @arg kind name of the tc object type + * + * @return 0 on success or a negative error code + */ +int rtnl_tc_set_kind(struct rtnl_tc *tc, const char *kind) +{ + if (tc->ce_mask & TCA_ATTR_KIND) + return -NLE_EXIST; + + if ( !kind + || strlen (kind) >= sizeof (tc->tc_kind)) + return -NLE_INVAL; + + _nl_strncpy_assert(tc->tc_kind, kind, sizeof(tc->tc_kind)); + + tc->ce_mask |= TCA_ATTR_KIND; + + /* Force allocation of data */ + rtnl_tc_data(tc); + + return 0; +} + +/** + * Return kind of traffic control object + * @arg tc traffic control object + * + * @return Kind of traffic control object or NULL if not set. + */ +char *rtnl_tc_get_kind(struct rtnl_tc *tc) +{ + if (tc->ce_mask & TCA_ATTR_KIND) + return tc->tc_kind; + else + return NULL; +} + +/** + * Return value of a statistical counter of a traffic control object + * @arg tc traffic control object + * @arg id identifier of statistical counter + * + * @return Value of requested statistic counter or 0. + */ +uint64_t rtnl_tc_get_stat(struct rtnl_tc *tc, enum rtnl_tc_stat id) +{ + if ((unsigned int) id > RTNL_TC_STATS_MAX) + return 0; + + return tc->tc_stats[id]; +} + +/** + * Set the chain index of a traffic control object + * @arg tc traffic control object + * @arg chain chain index of traffic control object + * + */ +void rtnl_tc_set_chain(struct rtnl_tc *tc, uint32_t chain) +{ + tc->tc_chain = chain; + tc->ce_mask |= TCA_ATTR_CHAIN; +} + +/** + * Return chain index of traffic control object + * @arg tc traffic control object + * @arg out_value output argument. + * + * @return 0 of the output value was successfully returned, or a negative + * error code on failure. + */ +int rtnl_tc_get_chain(struct rtnl_tc *tc, uint32_t *out_value) +{ + if (!(tc->ce_mask & TCA_ATTR_CHAIN)) + return -NLE_MISSING_ATTR; + *out_value = tc->tc_chain; + return 0; +} + +/** @} */ + +/** + * @name Utilities + * @{ + */ + +static const struct trans_tbl tc_stats[] = { + __ADD(RTNL_TC_PACKETS, packets), + __ADD(RTNL_TC_BYTES, bytes), + __ADD(RTNL_TC_RATE_BPS, rate_bps), + __ADD(RTNL_TC_RATE_PPS, rate_pps), + __ADD(RTNL_TC_QLEN, qlen), + __ADD(RTNL_TC_BACKLOG, backlog), + __ADD(RTNL_TC_DROPS, drops), + __ADD(RTNL_TC_REQUEUES, requeues), + __ADD(RTNL_TC_OVERLIMITS, overlimits), +}; + +char *rtnl_tc_stat2str(enum rtnl_tc_stat st, char *buf, size_t len) +{ + return __type2str(st, buf, len, tc_stats, ARRAY_SIZE(tc_stats)); +} + +int rtnl_tc_str2stat(const char *name) +{ + return __str2type(name, tc_stats, ARRAY_SIZE(tc_stats)); +} + +/** + * Calculate time required to transmit buffer at a specific rate + * @arg bufsize Size of buffer to be transmited in bytes. + * @arg rate Transmit rate in bytes per second. + * + * Calculates the number of micro seconds required to transmit a + * specific buffer at a specific transmit rate. + * + * @f[ + * txtime=\frac{bufsize}{rate}10^6 + * @f] + * + * @return Required transmit time in micro seconds. + */ +int rtnl_tc_calc_txtime(int bufsize, int rate) +{ + return ((double) bufsize / (double) rate) * 1000000.0; +} + +/** + * Calculate buffer size able to transmit in a specific time and rate. + * @arg txtime Available transmit time in micro seconds. + * @arg rate Transmit rate in bytes per second. + * + * Calculates the size of the buffer that can be transmitted in a + * specific time period at a specific transmit rate. + * + * @f[ + * bufsize=\frac{{txtime} \times {rate}}{10^6} + * @f] + * + * @return Size of buffer in bytes. + */ +int rtnl_tc_calc_bufsize(int txtime, int rate) +{ + return ((double) txtime * (double) rate) / 1000000.0; +} + +/** + * Calculate the binary logarithm for a specific cell size + * @arg cell_size Size of cell, must be a power of two. + * @return Binary logirhtm of cell size or a negative error code. + */ +int rtnl_tc_calc_cell_log(int cell_size) +{ + int i; + + for (i = 0; i < 32; i++) + if ((1 << i) == cell_size) + return i; + + return -NLE_INVAL; +} + + +/** @} */ + +/** + * @name Rate Tables + * @{ + */ + +/* + * COPYRIGHT NOTE: + * align_to_atm() and adjust_size() derived/coped from iproute2 source. + */ + +/* + * The align to ATM cells is used for determining the (ATM) SAR + * alignment overhead at the ATM layer. (SAR = Segmentation And + * Reassembly). This is for example needed when scheduling packet on + * an ADSL connection. Note that the extra ATM-AAL overhead is _not_ + * included in this calculation. This overhead is added in the kernel + * before doing the rate table lookup, as this gives better precision + * (as the table will always be aligned for 48 bytes). + * --Hawk, d.7/11-2004. + */ +static unsigned int align_to_atm(unsigned int size) +{ + int linksize, cells; + cells = size / ATM_CELL_PAYLOAD; + if ((size % ATM_CELL_PAYLOAD) > 0) + cells++; + + linksize = cells * ATM_CELL_SIZE; /* Use full cell size to add ATM tax */ + return linksize; +} + +static unsigned int adjust_size(unsigned int size, unsigned int mpu, + uint32_t linktype) +{ + if (size < mpu) + size = mpu; + + switch (linktype) { + case ARPHRD_ATM: + return align_to_atm(size); + + case ARPHRD_ETHER: + default: + return size; + } +} + +/** + * Compute a transmission time lookup table + * @arg tc traffic control object + * @arg spec Rate specification + * @arg dst Destination buffer of RTNL_TC_RTABLE_SIZE uint32_t[]. + * + * Computes a table of RTNL_TC_RTABLE_SIZE entries specyfing the + * transmission times for various packet sizes, e.g. the transmission + * time for a packet of size \c pktsize could be looked up: + * @code + * txtime = table[pktsize >> log2(mtu)]; + * @endcode + */ +int rtnl_tc_build_rate_table(struct rtnl_tc *tc, struct rtnl_ratespec *spec, + uint32_t *dst) +{ + uint32_t mtu = rtnl_tc_get_mtu(tc); + uint32_t linktype = rtnl_tc_get_linktype(tc); + uint8_t cell_log = spec->rs_cell_log; + unsigned int size, i; + + spec->rs_mpu = rtnl_tc_get_mpu(tc); + spec->rs_overhead = rtnl_tc_get_overhead(tc); + + if (mtu == 0) + mtu = 2047; + + if (cell_log == UINT8_MAX) { + /* + * cell_log not specified, calculate it. It has to specify the + * minimum number of rshifts required to break the MTU to below + * RTNL_TC_RTABLE_SIZE. + */ + cell_log = 0; + while ((mtu >> cell_log) >= RTNL_TC_RTABLE_SIZE) + cell_log++; + } + + for (i = 0; i < RTNL_TC_RTABLE_SIZE; i++) { + size = adjust_size((i + 1) << cell_log, spec->rs_mpu, linktype); + dst[i] = nl_us2ticks(rtnl_tc_calc_txtime64(size, spec->rs_rate64)); + } + + spec->rs_cell_align = -1; + spec->rs_cell_log = cell_log; + + return 0; +} + +/** @} */ + +/** + * @name TC implementation of cache functions + */ + +void rtnl_tc_free_data(struct nl_object *obj) +{ + struct rtnl_tc *tc = TC_CAST(obj); + struct rtnl_tc_ops *ops; + + rtnl_link_put(tc->tc_link); + nl_data_free(tc->tc_opts); + nl_data_free(tc->tc_xstats); + + if (tc->tc_subdata) { + ops = rtnl_tc_get_ops(tc); + if (ops && ops->to_free_data) + ops->to_free_data(tc, nl_data_get(tc->tc_subdata)); + + nl_data_free(tc->tc_subdata); + } +} + +int rtnl_tc_clone(struct nl_object *dstobj, struct nl_object *srcobj) +{ + struct rtnl_tc *dst = TC_CAST(dstobj); + struct rtnl_tc *src = TC_CAST(srcobj); + struct rtnl_tc_ops *ops; + + if (src->tc_link) { + nl_object_get(OBJ_CAST(src->tc_link)); + dst->tc_link = src->tc_link; + } + + dst->tc_opts = NULL; + dst->tc_xstats = NULL; + dst->tc_subdata = NULL; + dst->ce_mask &= ~(TCA_ATTR_OPTS | + TCA_ATTR_XSTATS); + + if (src->tc_opts) { + dst->tc_opts = nl_data_clone(src->tc_opts); + if (!dst->tc_opts) + return -NLE_NOMEM; + dst->ce_mask |= TCA_ATTR_OPTS; + } + + if (src->tc_xstats) { + dst->tc_xstats = nl_data_clone(src->tc_xstats); + if (!dst->tc_xstats) + return -NLE_NOMEM; + dst->ce_mask |= TCA_ATTR_XSTATS; + } + + if (src->tc_subdata) { + if (!(dst->tc_subdata = nl_data_clone(src->tc_subdata))) { + return -NLE_NOMEM; + } + } + + ops = rtnl_tc_get_ops(src); + if (ops && ops->to_clone) { + void *a = rtnl_tc_data(dst), *b = rtnl_tc_data(src); + + if (!a) + return 0; + else if (!b) + return -NLE_NOMEM; + + return ops->to_clone(a, b); + } + + return 0; +} + +static int tc_dump(struct rtnl_tc *tc, enum nl_dump_type type, + struct nl_dump_params *p) +{ + struct rtnl_tc_type_ops *type_ops; + struct rtnl_tc_ops *ops; + void *data = rtnl_tc_data(tc); + + type_ops = tc_type_ops[tc->tc_type]; + if (type_ops && type_ops->tt_dump[type]) + type_ops->tt_dump[type](tc, p); + + ops = rtnl_tc_get_ops(tc); + if (ops && ops->to_dump[type]) { + ops->to_dump[type](tc, data, p); + return 1; + } + + return 0; +} + +void rtnl_tc_dump_line(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_tc_type_ops *type_ops; + struct rtnl_tc *tc = TC_CAST(obj); + struct nl_cache *link_cache; + char buf[32]; + + nl_new_line(p); + + type_ops = tc_type_ops[tc->tc_type]; + if (type_ops && type_ops->tt_dump_prefix) + nl_dump(p, "%s ", type_ops->tt_dump_prefix); + + nl_dump(p, "%s ", tc->tc_kind); + + if ((link_cache = nl_cache_mngt_require_safe("route/link"))) { + nl_dump(p, "dev %s ", + rtnl_link_i2name(link_cache, tc->tc_ifindex, + buf, sizeof(buf))); + } else + nl_dump(p, "dev %u ", tc->tc_ifindex); + + nl_dump(p, "id %s ", + rtnl_tc_handle2str(tc->tc_handle, buf, sizeof(buf))); + + nl_dump(p, "parent %s", + rtnl_tc_handle2str(tc->tc_parent, buf, sizeof(buf))); + + tc_dump(tc, NL_DUMP_LINE, p); + nl_dump(p, "\n"); + + if (link_cache) + nl_cache_put(link_cache); +} + +void rtnl_tc_dump_details(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_tc *tc = TC_CAST(obj); + + rtnl_tc_dump_line(OBJ_CAST(tc), p); + + nl_dump_line(p, " "); + + if (tc->ce_mask & TCA_ATTR_MTU) + nl_dump(p, " mtu %u", tc->tc_mtu); + + if (tc->ce_mask & TCA_ATTR_MPU) + nl_dump(p, " mpu %u", tc->tc_mpu); + + if (tc->ce_mask & TCA_ATTR_OVERHEAD) + nl_dump(p, " overhead %u", tc->tc_overhead); + + if (!tc_dump(tc, NL_DUMP_DETAILS, p)) + nl_dump(p, "no options"); + nl_dump(p, "\n"); +} + +void rtnl_tc_dump_stats(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_tc *tc = TC_CAST(obj); + char *unit; + float res; + + rtnl_tc_dump_details(OBJ_CAST(tc), p); + + nl_dump_line(p, + " stats: %-14s %-10s %-10s %-10s %-10s %-10s\n", + "bytes", "packets", "drops", "overlimits", "qlen", "backlog"); + + res = nl_cancel_down_bytes(tc->tc_stats[RTNL_TC_BYTES], &unit); + + nl_dump_line(p, + " %10.2f %3s %10u %-10u %-10u %-10u %-10u\n", + res, unit, + tc->tc_stats[RTNL_TC_PACKETS], + tc->tc_stats[RTNL_TC_DROPS], + tc->tc_stats[RTNL_TC_OVERLIMITS], + tc->tc_stats[RTNL_TC_QLEN], + tc->tc_stats[RTNL_TC_BACKLOG]); + + res = nl_cancel_down_bytes(tc->tc_stats[RTNL_TC_RATE_BPS], &unit); + + nl_dump_line(p, + " %10.2f %3s/s %10u/s\n", + res, + unit, + tc->tc_stats[RTNL_TC_RATE_PPS]); +} + +uint64_t rtnl_tc_compare(struct nl_object *aobj, struct nl_object *bobj, + uint64_t attrs, int flags) +{ + struct rtnl_tc *a = TC_CAST(aobj); + struct rtnl_tc *b = TC_CAST(bobj); + uint64_t diff = 0; + +#define TC_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, TCA_ATTR_##ATTR, a, b, EXPR) + + diff |= TC_DIFF(HANDLE, a->tc_handle != b->tc_handle); + diff |= TC_DIFF(PARENT, a->tc_parent != b->tc_parent); + diff |= TC_DIFF(IFINDEX, a->tc_ifindex != b->tc_ifindex); + diff |= TC_DIFF(KIND, strcmp(a->tc_kind, b->tc_kind)); + +#undef TC_DIFF + + return diff; +} + +/** @} */ + +/** + * @name Modules API + */ + +struct rtnl_tc_ops *rtnl_tc_lookup_ops(enum rtnl_tc_type type, const char *kind) +{ + struct rtnl_tc_ops *ops; + + nl_list_for_each_entry(ops, &tc_ops_list[type], to_list) + if (!strcmp(kind, ops->to_kind)) + return ops; + + return NULL; +} + +struct rtnl_tc_ops *rtnl_tc_get_ops(struct rtnl_tc *tc) +{ + if (!tc->tc_ops) + tc->tc_ops = rtnl_tc_lookup_ops(tc->tc_type, tc->tc_kind); + + return tc->tc_ops; +} + +/** + * Register a traffic control module + * @arg ops traffic control module operations + */ +int rtnl_tc_register(struct rtnl_tc_ops *ops) +{ + static int init = 0; + + /* + * Initialiation hack, make sure list is initialized when + * the first tc module registers. Putting this in a + * separate __init would required correct ordering of init + * functions + */ + if (!init) { + int i; + + for (i = 0; i < __RTNL_TC_TYPE_MAX; i++) + nl_init_list_head(&tc_ops_list[i]); + + init = 1; + } + + if (!ops->to_kind || ops->to_type > RTNL_TC_TYPE_MAX) + BUG(); + + if (rtnl_tc_lookup_ops(ops->to_type, ops->to_kind)) + return -NLE_EXIST; + + nl_list_add_tail(&ops->to_list, &tc_ops_list[ops->to_type]); + + return 0; +} + +/** + * Unregister a traffic control module + * @arg ops traffic control module operations + */ +void rtnl_tc_unregister(struct rtnl_tc_ops *ops) +{ + nl_list_del(&ops->to_list); +} + +/** + * Returns the private data of the traffic control object. + * Contrary to rtnl_tc_data(), this returns NULL if the data is + * not yet allocated + * @arg tc traffic control object + * + * @return pointer to the private data or NULL if not allocated. + */ +void *rtnl_tc_data_peek(struct rtnl_tc *tc) +{ + return tc->tc_subdata ? nl_data_get(tc->tc_subdata) : NULL; +} + +/** + * Return pointer to private data of traffic control object + * @arg tc traffic control object + * + * Allocates the private traffic control object data section + * as necessary and returns it. + * + * @return Pointer to private tc data or NULL if allocation failed. + */ +void *rtnl_tc_data(struct rtnl_tc *tc) +{ + if (!tc->tc_subdata) { + size_t size; + + if (!tc->tc_ops) { + if (!rtnl_tc_get_ops(tc)) + return NULL; + } + + if (!(size = tc->tc_ops->to_size)) + BUG(); + + if (!(tc->tc_subdata = nl_data_alloc(NULL, size))) + return NULL; + } + + return nl_data_get(tc->tc_subdata); +} + +/** + * Check traffic control object type and return private data section + * @arg tc traffic control object + * @arg ops expected traffic control object operations + * @arg err the place where saves the error code if fails + * + * Checks whether the traffic control object matches the type + * specified with the traffic control object operations. If the + * type matches, the private tc object data is returned. If type + * mismatches, APPBUG() will print a application bug warning. + * + * @see rtnl_tc_data() + * + * @return Pointer to private tc data or NULL if type mismatches. + */ +void *rtnl_tc_data_check(struct rtnl_tc *tc, struct rtnl_tc_ops *ops, int *err) +{ + void *ret; + + if (tc->tc_ops != ops) { + char buf[64]; + + snprintf(buf, sizeof(buf), + "tc object %p used in %s context but is of type %s", + tc, ops->to_kind, tc->tc_ops->to_kind); + APPBUG(buf); + + if (err) + *err = -NLE_OPNOTSUPP; + return NULL; + } + + ret = rtnl_tc_data(tc); + if (ret == NULL) { + if (err) + *err = -NLE_NOMEM; + } + + return ret; +} + +struct nl_af_group tc_groups[] = { + { AF_UNSPEC, RTNLGRP_TC }, + { END_OF_GROUP_LIST }, +}; + + +void rtnl_tc_type_register(struct rtnl_tc_type_ops *ops) +{ + if (ops->tt_type > RTNL_TC_TYPE_MAX) + BUG(); + + tc_type_ops[ops->tt_type] = ops; +} + +void rtnl_tc_type_unregister(struct rtnl_tc_type_ops *ops) +{ + if (ops->tt_type > RTNL_TC_TYPE_MAX) + BUG(); + + tc_type_ops[ops->tt_type] = NULL; +} + +/** @} */ + +/** @} */ diff --git a/libnl/lib/socket.c b/libnl/lib/socket.c new file mode 100644 index 0000000..773e13f --- /dev/null +++ b/libnl/lib/socket.c @@ -0,0 +1,938 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +/** + * @ingroup core_types + * @defgroup socket Socket + * + * Representation of a netlink socket + * + * Related sections in the development guide: + * - @core_doc{core_sockets, Netlink Sockets} + * + * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ + */ + +#define _GNU_SOURCE + +#include "sys/socket.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +static int default_cb = NL_CB_DEFAULT; + +static void __init init_default_cb(void) +{ + char *nlcb; + + if ((nlcb = getenv("NLCB"))) { + if (!strcasecmp(nlcb, "default")) + default_cb = NL_CB_DEFAULT; + else if (!strcasecmp(nlcb, "verbose")) + default_cb = NL_CB_VERBOSE; + else if (!strcasecmp(nlcb, "debug")) + default_cb = NL_CB_DEBUG; + else { + fprintf(stderr, "Unknown value for NLCB, valid values: " + "{default | verbose | debug}\n"); + } + } +} + +static uint32_t used_ports_map[32]; +static NL_RW_LOCK(port_map_lock); + +static uint32_t generate_local_port(void) +{ + int i, j, n, m; + static uint16_t idx_state = 0; + uint32_t pid = getpid() & 0x3FFFFF; + + nl_write_lock(&port_map_lock); + + if (idx_state == 0) { + uint32_t t = time(NULL); + + /* from time to time (on average each 2^15 calls), the idx_state will + * be zero again. No problem, just "seed" anew with time(). */ + idx_state = t ^ (t >> 16) ^ 0x3047; + } else + idx_state = idx_state + 20011; /* add prime number */ + + i = idx_state >> 5; + n = idx_state; + for (j = 0; j < 32; j++) { + /* walk the index somewhat randomized, with always leaving the block + * #0 as last. The reason is that libnl-1 will start at block #0, + * so just leave the first 32 ports preferably for libnl-1 owned sockets + * (this is relevant only if the applications ends up using both versions + * of the library and doesn't hurt otherwise). */ + if (j == 31) + i = 0; + else + i = (((i-1) + 7) % 31) + 1; + + if (used_ports_map[i] == 0xFFFFFFFF) + continue; + + for (m = 0; m < 32; m++) { + n = (n + 13) % 32; + if (1UL & (used_ports_map[i] >> n)) + continue; + + used_ports_map[i] |= (1UL << n); + n += (i * 32); + + /* PID_MAX_LIMIT is currently at 2^22, leaving 10 bit + * to, i.e. 1024 unique ports per application. */ + + nl_write_unlock(&port_map_lock); + + /* ensure we don't return zero. */ + pid = pid + (((uint32_t)n) << 22); + return pid ? pid : 1024; + } + } + + nl_write_unlock(&port_map_lock); + return 0; +} + +static void release_local_port(uint32_t port) +{ + int nr; + uint32_t mask; + + BUG_ON(port == 0); + + nr = port >> 22; + mask = 1UL << (nr % 32); + nr /= 32; + + nl_write_lock(&port_map_lock); + BUG_ON((used_ports_map[nr] & mask) != mask); + used_ports_map[nr] &= ~mask; + nl_write_unlock(&port_map_lock); +} + +/** \cond skip */ +void _nl_socket_used_ports_release_all(const uint32_t *used_ports) +{ + int i; + + for (i = 0; i < 32; i++) { + if (used_ports[i] != 0) { + nl_write_lock(&port_map_lock); + for (; i < 32; i++) { + BUG_ON((used_ports_map[i] & used_ports[i]) != used_ports[i]); + used_ports_map[i] &= ~(used_ports[i]); + } + nl_write_unlock(&port_map_lock); + return; + } + } +} + +void _nl_socket_used_ports_set(uint32_t *used_ports, uint32_t port) +{ + int nr; + int32_t mask; + + nr = port >> 22; + mask = 1UL << (nr % 32); + nr /= 32; + + /* + BUG_ON(port == 0 || (getpid() & 0x3FFFFF) != (port & 0x3FFFFF)); + BUG_ON(used_ports[nr] & mask); + */ + + used_ports[nr] |= mask; +} +/** \endcond */ + +/** + * @name Allocation + * @{ + */ + +static struct nl_sock *__alloc_socket(struct nl_cb *cb) +{ + struct nl_sock *sk; + + sk = calloc(1, sizeof(*sk)); + if (!sk) + return NULL; + + sk->s_fd = -1; + sk->s_cb = nl_cb_get(cb); + sk->s_local.nl_family = AF_NETLINK; + sk->s_peer.nl_family = AF_NETLINK; + sk->s_seq_expect = sk->s_seq_next = time(NULL); + + /* the port is 0 (unspecified), meaning NL_OWN_PORT */ + sk->s_flags = NL_OWN_PORT; + + return sk; +} + +/** + * Allocate new netlink socket + * + * @return Newly allocated netlink socket or NULL. + */ +struct nl_sock *nl_socket_alloc(void) +{ + struct nl_cb *cb; + struct nl_sock *sk; + + cb = nl_cb_alloc(default_cb); + if (!cb) + return NULL; + + /* will increment cb reference count on success */ + sk = __alloc_socket(cb); + + nl_cb_put(cb); + + return sk; +} + +/** + * Allocate new socket with custom callbacks + * @arg cb Callback handler + * + * The reference to the callback handler is taken into account + * automatically, it is released again upon calling nl_socket_free(). + * + *@return Newly allocted socket handle or NULL. + */ +struct nl_sock *nl_socket_alloc_cb(struct nl_cb *cb) +{ + if (cb == NULL) + BUG(); + + return __alloc_socket(cb); +} + +/** + * Free a netlink socket. + * @arg sk Netlink socket. + */ +void nl_socket_free(struct nl_sock *sk) +{ + if (!sk) + return; + + if (sk->s_fd >= 0) + close(sk->s_fd); + + if (!(sk->s_flags & NL_OWN_PORT)) + release_local_port(sk->s_local.nl_pid); + + nl_cb_put(sk->s_cb); + free(sk); +} + +/** @} */ + +/** + * @name Sequence Numbers + * @{ + */ + +static int noop_seq_check(struct nl_msg *msg, void *arg) +{ + return NL_OK; +} + + +/** + * Disable sequence number checking. + * @arg sk Netlink socket. + * + * Disables checking of sequence numbers on the netlink socket This is + * required to allow messages to be processed which were not requested by + * a preceding request message, e.g. netlink events. + * + * @note This function modifies the NL_CB_SEQ_CHECK configuration in + * the callback handle associated with the socket. + */ +void nl_socket_disable_seq_check(struct nl_sock *sk) +{ + nl_cb_set(sk->s_cb, NL_CB_SEQ_CHECK, + NL_CB_CUSTOM, noop_seq_check, NULL); +} + +/** + * Use next sequence number + * @arg sk Netlink socket. + * + * Uses the next available sequence number and increases the counter + * by one for subsequent calls. + * + * @return Unique serial sequence number + */ +unsigned int nl_socket_use_seq(struct nl_sock *sk) +{ + return sk->s_seq_next++; +} + +/** + * Disable automatic request for ACK + * @arg sk Netlink socket. + * + * The default behaviour of a socket is to request an ACK for + * each message sent to allow for the caller to synchronize to + * the completion of the netlink operation. This function + * disables this behaviour and will result in requests being + * sent which will not have the NLM_F_ACK flag set automatically. + * However, it is still possible for the caller to set the + * NLM_F_ACK flag explicitely. + */ +void nl_socket_disable_auto_ack(struct nl_sock *sk) +{ + sk->s_flags |= NL_NO_AUTO_ACK; +} + +/** + * Enable automatic request for ACK (default) + * @arg sk Netlink socket. + * @see nl_socket_disable_auto_ack + */ +void nl_socket_enable_auto_ack(struct nl_sock *sk) +{ + sk->s_flags &= ~NL_NO_AUTO_ACK; +} + +/** @} */ + +/** \cond skip */ +int _nl_socket_is_local_port_unspecified(struct nl_sock *sk) +{ + return (sk->s_local.nl_pid == 0); +} + +uint32_t _nl_socket_set_local_port_no_release(struct nl_sock *sk, int generate_other) +{ + uint32_t port; + + /* reset the port to generate_local_port(), but do not release + * the previously generated port. */ + + if (generate_other) + port = generate_local_port(); + else + port = 0; + sk->s_local.nl_pid = port; + if (port == 0) { + /* failed to find an unsed port. Restore the socket to have an + * unspecified port. */ + sk->s_flags |= NL_OWN_PORT; + } else + sk->s_flags &= ~NL_OWN_PORT; + return port; +} +/** \endcond */ + +/** + * @name Source Idenficiation + * @{ + */ + +uint32_t nl_socket_get_local_port(const struct nl_sock *sk) +{ + if (sk->s_local.nl_pid == 0) { + struct nl_sock *sk_mutable = (struct nl_sock *) sk; + + /* modify the const argument sk. This is justified, because + * nobody ever saw the local_port from externally. So, we + * initilize it on first use. + * + * Note that this also means that you cannot call this function + * from multiple threads without synchronization. But nl_sock + * is not automatically threadsafe anyway, so the user is not + * allowed to do that. + */ + sk_mutable->s_local.nl_pid = generate_local_port(); + if (sk_mutable->s_local.nl_pid == 0) { + /* could not generate a local port. Assign UINT32_MAX to preserve + * backward compatibility. A user who cares can clear that anyway + * with nl_socket_set_local_port(). */ + sk_mutable->s_local.nl_pid = UINT32_MAX; + sk_mutable->s_flags |= NL_OWN_PORT; + } else + sk_mutable->s_flags &= ~NL_OWN_PORT; + } + return sk->s_local.nl_pid; +} + +/** + * Set local port of socket + * @arg sk Netlink socket. + * @arg port Local port identifier + * + * Assigns a local port identifier to the socket. + * + * If port is 0, the port is reset to 'unspecified' as it is after newly + * calling nl_socket_alloc(). + * Unspecified means, that the port will be generated automatically later + * on first use (either on nl_socket_get_local_port() or nl_connect()). + */ +void nl_socket_set_local_port(struct nl_sock *sk, uint32_t port) +{ + if (!(sk->s_flags & NL_OWN_PORT)) + release_local_port(sk->s_local.nl_pid); + sk->s_flags |= NL_OWN_PORT; + sk->s_local.nl_pid = port; +} + +/** @} */ + +/** + * @name Group Subscriptions + * @{ + */ + +/** + * Join groups + * @arg sk Netlink socket + * @arg group Group identifier + * + * Joins the specified groups using the modern socket option which + * is available since kernel version 2.6.14. It allows joining an + * almost arbitary number of groups without limitation. The list + * of groups has to be terminated by 0 (%NFNLGRP_NONE). + * + * Make sure to use the correct group definitions as the older + * bitmask definitions for nl_join_groups() are likely to still + * be present for backward compatibility reasons. + * + * @return 0 on sucess or a negative error code. + */ +int nl_socket_add_memberships(struct nl_sock *sk, int group, ...) +{ + int err; + va_list ap; + + if (sk->s_fd == -1) + return -NLE_BAD_SOCK; + + va_start(ap, group); + + while (group != 0) { + if (group < 0) { + va_end(ap); + return -NLE_INVAL; + } + + err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, + &group, sizeof(group)); + if (err < 0) { + va_end(ap); + NL_DBG(4, "nl_socket_add_memberships(%p): setsockopt() failed with %d (%s)\n", + sk, errno, nl_strerror_l(errno)); + return -nl_syserr2nlerr(errno); + } + + group = va_arg(ap, int); + } + + va_end(ap); + + return 0; +} + +int nl_socket_add_membership(struct nl_sock *sk, int group) +{ + return nl_socket_add_memberships(sk, group, 0); +} + +/** + * Leave groups + * @arg sk Netlink socket + * @arg group Group identifier + * + * Leaves the specified groups using the modern socket option + * which is available since kernel version 2.6.14. The list of groups + * has to terminated by 0 (%NFNLGRP_NONE). + * + * @see nl_socket_add_membership + * @return 0 on success or a negative error code. + */ +int nl_socket_drop_memberships(struct nl_sock *sk, int group, ...) +{ + int err; + va_list ap; + + if (sk->s_fd == -1) + return -NLE_BAD_SOCK; + + va_start(ap, group); + + while (group != 0) { + if (group < 0) { + va_end(ap); + return -NLE_INVAL; + } + + err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP, + &group, sizeof(group)); + if (err < 0) { + va_end(ap); + NL_DBG(4, "nl_socket_drop_memberships(%p): setsockopt() failed with %d (%s)\n", + sk, errno, nl_strerror_l(errno)); + return -nl_syserr2nlerr(errno); + } + + group = va_arg(ap, int); + } + + va_end(ap); + + return 0; +} + +int nl_socket_drop_membership(struct nl_sock *sk, int group) +{ + return nl_socket_drop_memberships(sk, group, 0); +} + + +/** + * Join multicast groups (deprecated) + * @arg sk Netlink socket. + * @arg groups Bitmask of groups to join. + * + * This function defines the old way of joining multicast group which + * has to be done prior to calling nl_connect(). It works on any kernel + * version but is very limited as only 32 groups can be joined. + */ +void nl_join_groups(struct nl_sock *sk, int groups) +{ + sk->s_local.nl_groups |= groups; +} + + +/** @} */ + +/** + * @name Peer Identfication + * @{ + */ + +uint32_t nl_socket_get_peer_port(const struct nl_sock *sk) +{ + return sk->s_peer.nl_pid; +} + +void nl_socket_set_peer_port(struct nl_sock *sk, uint32_t port) +{ + sk->s_peer.nl_pid = port; +} + +uint32_t nl_socket_get_peer_groups(const struct nl_sock *sk) +{ + return sk->s_peer.nl_groups; +} + +void nl_socket_set_peer_groups(struct nl_sock *sk, uint32_t groups) +{ + sk->s_peer.nl_groups = groups; +} + + + +/** @} */ + +/** + * @name File Descriptor + * @{ + */ + +/** + * Return the file descriptor of the backing socket + * @arg sk Netlink socket + * + * Only valid after calling nl_connect() to create and bind the respective + * socket. + * + * @return File descriptor or -1 if not available. + */ +int nl_socket_get_fd(const struct nl_sock *sk) +{ + return sk->s_fd; +} + +/** + * Set the socket file descriptor externally which initializes the + * socket similar to nl_connect(). + * + * @arg sk Netlink socket (required) + * @arg protocol The socket protocol (optional). Linux 2.6.32 supports + * the socket option SO_PROTOCOL. In this case, you can set + * protocol to a negative value and let it autodetect. + * If you set it to a non-negative value, the detected protocol + * must match the one provided. + * To support older kernels, you must specify the protocol. + * @arg fd Socket file descriptor to use (required) + * + * Set the socket file descriptor. @fd must be valid and bind'ed. + * + * This is an alternative to nl_connect(). nl_connect() creates, binds and + * sets the socket. With this function you can set the socket to an externally + * created file descriptor. + * + * @see nl_connect() + * + * @return 0 on success or a negative error code. On error, @fd is not closed but + * possibly unusable. + * + * @retval -NLE_BAD_SOCK Netlink socket is already connected + * @retval -NLE_INVAL Socket is of unexpected type + */ +int nl_socket_set_fd(struct nl_sock *sk, int protocol, int fd) +{ + int err = 0; + socklen_t addrlen; + struct sockaddr_nl local = { 0 }; + int so_type = -1, so_protocol = -1; + + if (sk->s_fd != -1) + return -NLE_BAD_SOCK; + if (fd < 0) + return -NLE_INVAL; + + addrlen = sizeof(local); + err = getsockname(fd, (struct sockaddr *) &local, + &addrlen); + if (err < 0) { + NL_DBG(4, "nl_socket_set_fd(%p,%d): getsockname() failed with %d (%s)\n", + sk, fd, errno, nl_strerror_l(errno)); + return -nl_syserr2nlerr(errno); + } + if (addrlen != sizeof(local)) + return -NLE_INVAL; + if (local.nl_family != AF_NETLINK) { + NL_DBG(4, "nl_socket_set_fd(%p,%d): getsockname() returned family %d instead of %d (AF_NETLINK)\n", + sk, fd, local.nl_family, AF_NETLINK); + return -NLE_INVAL; + } + + addrlen = sizeof(so_type); + err = getsockopt(fd, SOL_SOCKET, SO_TYPE, &so_type, &addrlen); + if (err < 0) { + NL_DBG(4, "nl_socket_set_fd(%p,%d): getsockopt() for SO_TYPE failed with %d (%s)\n", + sk, fd, errno, nl_strerror_l(errno)); + return -nl_syserr2nlerr(errno); + } + if (addrlen != sizeof(so_type)) + return -NLE_INVAL; + if (so_type != SOCK_RAW) { + NL_DBG(4, "nl_socket_set_fd(%p,%d): getsockopt() returned SO_TYPE %d instead of %d (SOCK_RAW)\n", + sk, fd, so_type, SOCK_RAW); + return -NLE_INVAL; + } + +#if SO_PROTOCOL + addrlen = sizeof(so_protocol); + err = getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &so_protocol, &addrlen); + if (err < 0) { + if (errno == ENOPROTOOPT) + goto no_so_protocol; + NL_DBG(4, "nl_socket_set_fd(%p,%d): getsockopt() for SO_PROTOCOL failed with %d (%s)\n", + sk, fd, errno, nl_strerror_l(errno)); + return -nl_syserr2nlerr(errno); + } + if (addrlen != sizeof(so_protocol)) + return -NLE_INVAL; + if (protocol >= 0 && protocol != so_protocol) { + NL_DBG(4, "nl_socket_set_fd(%p,%d): getsockopt() for SO_PROTOCOL returned %d instead of %d\n", + sk, fd, so_protocol, protocol); + return -NLE_INVAL; + } + + if (0) +#endif + { +no_so_protocol: + if (protocol < 0) { + NL_DBG(4, "nl_socket_set_fd(%p,%d): unknown protocol and unable to detect it via SO_PROTOCOL socket option\n", + sk, fd); + return -NLE_INVAL; + } + so_protocol = protocol; + } + + nl_socket_set_local_port (sk, local.nl_pid); + sk->s_local = local; + sk->s_fd = fd; + sk->s_proto = so_protocol; + + return 0; +} + +/** + * Set file descriptor of socket to non-blocking state + * @arg sk Netlink socket. + * + * @return 0 on success or a negative error code. + */ +int nl_socket_set_nonblocking(const struct nl_sock *sk) +{ + if (sk->s_fd == -1) + return -NLE_BAD_SOCK; + + if (fcntl(sk->s_fd, F_SETFL, O_NONBLOCK) < 0) { + NL_DBG(4, "nl_socket_set_nonblocking(%p): fcntl() failed with %d (%s)\n", + sk, errno, nl_strerror_l(errno)); + return -nl_syserr2nlerr(errno); + } + + return 0; +} + +/** + * Enable use of MSG_PEEK when reading from socket + * @arg sk Netlink socket. + * + * See also NL_CAPABILITY_NL_RECVMSGS_PEEK_BY_DEFAULT capability + */ +void nl_socket_enable_msg_peek(struct nl_sock *sk) +{ + sk->s_flags |= (NL_MSG_PEEK | NL_MSG_PEEK_EXPLICIT); +} + +/** + * Disable use of MSG_PEEK when reading from socket + * @arg sk Netlink socket. + * + * See also NL_CAPABILITY_NL_RECVMSGS_PEEK_BY_DEFAULT capability + */ +void nl_socket_disable_msg_peek(struct nl_sock *sk) +{ + sk->s_flags |= NL_MSG_PEEK_EXPLICIT; + sk->s_flags &= ~NL_MSG_PEEK; +} + +/** @} */ + +/** + * @name Callback Handler + * @{ + */ + +struct nl_cb *nl_socket_get_cb(const struct nl_sock *sk) +{ + return nl_cb_get(sk->s_cb); +} + +void nl_socket_set_cb(struct nl_sock *sk, struct nl_cb *cb) +{ + if (cb == NULL) + BUG(); + + nl_cb_put(sk->s_cb); + sk->s_cb = nl_cb_get(cb); +} + +/** + * Modify the callback handler associated with the socket + * @arg sk Netlink socket. + * @arg type which type callback to set + * @arg kind kind of callback + * @arg func callback function + * @arg arg argument to be passed to callback function + * + * @see nl_cb_set + */ +int nl_socket_modify_cb(struct nl_sock *sk, enum nl_cb_type type, + enum nl_cb_kind kind, nl_recvmsg_msg_cb_t func, + void *arg) +{ + return nl_cb_set(sk->s_cb, type, kind, func, arg); +} + +/** + * Modify the error callback handler associated with the socket + * @arg sk Netlink socket. + * @arg kind kind of callback + * @arg func callback function + * @arg arg argument to be passed to callback function + * + * @see nl_cb_err + */ +int nl_socket_modify_err_cb(struct nl_sock *sk, enum nl_cb_kind kind, + nl_recvmsg_err_cb_t func, void *arg) +{ + return nl_cb_err(sk->s_cb, kind, func, arg); +} + +/** @} */ + +/** + * @name Utilities + * @{ + */ + +/** + * Set socket buffer size of netlink socket. + * @arg sk Netlink socket. + * @arg rxbuf New receive socket buffer size in bytes. + * @arg txbuf New transmit socket buffer size in bytes. + * + * Sets the socket buffer size of a netlink socket to the specified + * values \c rxbuf and \c txbuf. Providing a value of \c 0 assumes a + * good default value. + * + * @note It is not required to call this function prior to nl_connect(). + * @return 0 on sucess or a negative error code. + */ +int nl_socket_set_buffer_size(struct nl_sock *sk, int rxbuf, int txbuf) +{ + int err; + + if (rxbuf <= 0) + rxbuf = 32768; + + if (txbuf <= 0) + txbuf = 32768; + + if (sk->s_fd == -1) + return -NLE_BAD_SOCK; + + err = setsockopt(sk->s_fd, SOL_SOCKET, SO_SNDBUF, + &txbuf, sizeof(txbuf)); + if (err < 0) { + NL_DBG(4, "nl_socket_set_buffer_size(%p): setsockopt() failed with %d (%s)\n", + sk, errno, nl_strerror_l(errno)); + return -nl_syserr2nlerr(errno); + } + + err = setsockopt(sk->s_fd, SOL_SOCKET, SO_RCVBUF, + &rxbuf, sizeof(rxbuf)); + if (err < 0) { + NL_DBG(4, "nl_socket_set_buffer_size(%p): setsockopt() failed with %d (%s)\n", + sk, errno, nl_strerror_l(errno)); + return -nl_syserr2nlerr(errno); + } + + return 0; +} + +/** + * Set default message buffer size of netlink socket. + * @arg sk Netlink socket. + * @arg bufsize Default message buffer size in bytes. + * + * Sets the default message buffer size to the specified length in bytes. + * The default message buffer size limits the maximum message size the + * socket will be able to receive. It is generally recommneded to specify + * a buffer size no less than the size of a memory page. + * + * Setting the @bufsize to zero means to use a default of 4 times getpagesize(). + * + * When MSG_PEEK is enabled, the buffer size is used for the initial choice + * of the buffer while peeking. It still makes sense to choose an optimal value + * to avoid realloc(). + * + * When MSG_PEEK is disabled, the buffer size is important because a too small + * size will lead to failure of receiving the message via nl_recvmsgs(). + * + * By default, MSG_PEEK is enabled unless the user calls either nl_socket_disable_msg_peek()/ + * nl_socket_enable_msg_peek() or sets the message buffer size to a positive value. + * See capability NL_CAPABILITY_NL_RECVMSGS_PEEK_BY_DEFAULT for that. + * + * @return 0 on success or a negative error code. + */ +int nl_socket_set_msg_buf_size(struct nl_sock *sk, size_t bufsize) +{ + sk->s_bufsize = bufsize; + + return 0; +} + +/** + * Get default message buffer size of netlink socket. + * @arg sk Netlink socket. + * + * @return Size of default message buffer. + */ +size_t nl_socket_get_msg_buf_size(struct nl_sock *sk) +{ + return sk->s_bufsize; +} + +/** + * Enable/disable credential passing on netlink socket. + * @arg sk Netlink socket. + * @arg state New state (0 - disabled, 1 - enabled) + * + * @return 0 on success or a negative error code + */ +int nl_socket_set_passcred(struct nl_sock *sk, int state) +{ + int err; + + if (sk->s_fd == -1) + return -NLE_BAD_SOCK; + + err = setsockopt(sk->s_fd, SOL_SOCKET, SO_PASSCRED, + &state, sizeof(state)); + if (err < 0) { + NL_DBG(4, "nl_socket_set_passcred(%p): setsockopt() failed with %d (%s)\n", + sk, errno, nl_strerror_l(errno)); + return -nl_syserr2nlerr(errno); + } + + if (state) + sk->s_flags |= NL_SOCK_PASSCRED; + else + sk->s_flags &= ~NL_SOCK_PASSCRED; + + return 0; +} + +/** + * Enable/disable receival of additional packet information + * @arg sk Netlink socket. + * @arg state New state (0 - disabled, 1 - enabled) + * + * @return 0 on success or a negative error code + */ +int nl_socket_recv_pktinfo(struct nl_sock *sk, int state) +{ + int err; + + if (sk->s_fd == -1) + return -NLE_BAD_SOCK; + + err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_PKTINFO, + &state, sizeof(state)); + if (err < 0) { + NL_DBG(4, "nl_socket_recv_pktinfo(%p): setsockopt() failed with %d (%s)\n", + sk, errno, nl_strerror_l(errno)); + return -nl_syserr2nlerr(errno); + } + + return 0; +} + +/** @} */ + +/** @} */ diff --git a/libnl/lib/utils.c b/libnl/lib/utils.c new file mode 100644 index 0000000..d91e559 --- /dev/null +++ b/libnl/lib/utils.c @@ -0,0 +1,1251 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +/** + * @ingroup core + * @defgroup utils Utilities + * + * Collection of helper functions + * + * @{ + * + * Header + * ------ + * ~~~~{.c} + * #include + * ~~~~ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include /* exit() */ +#ifdef HAVE_STRERROR_L +#include +#endif + +/** + * Global variable indicating the desired level of debugging output. + * + * Level | Messages Printed + * ----- | --------------------------------------------------------- + * 0 | Debugging output disabled + * 1 | Warnings, important events and notifications + * 2 | More or less important debugging messages + * 3 | Repetitive events causing a flood of debugging messages + * 4 | Even less important messages + * + * If available, the variable will be initialized to the value of the + * environment variable `NLDBG`. The default value is 0 (disabled). + * + * For more information, see section @core_doc{_debugging, Debugging}. + */ +int nl_debug = 0; + +/** @cond SKIP */ +#ifdef NL_DEBUG +struct nl_dump_params nl_debug_dp = { + .dp_type = NL_DUMP_DETAILS, +}; + +static void __init nl_debug_init(void) +{ + char *nldbg, *end; + + if ((nldbg = getenv("NLDBG"))) { + long level = strtol(nldbg, &end, 0); + if (nldbg != end) + nl_debug = level; + } + + nl_debug_dp.dp_fd = stderr; +} +#endif + +int __nl_read_num_str_file(const char *path, int (*cb)(long, const char *)) +{ + FILE *fd; + char buf[128]; + + fd = fopen(path, "re"); + if (fd == NULL) + return -nl_syserr2nlerr(errno); + + while (fgets(buf, sizeof(buf), fd)) { + int goodlen, err; + long num; + char *end; + + if (*buf == '#' || *buf == '\n' || *buf == '\r') + continue; + + num = strtol(buf, &end, 0); + if (end == buf) { + fclose(fd); + return -NLE_INVAL; + } + + if (num == LONG_MIN || num == LONG_MAX) { + fclose(fd); + return -NLE_RANGE; + } + + while (*end == ' ' || *end == '\t') + end++; + + goodlen = strcspn(end, "#\r\n\t "); + if (goodlen == 0) { + fclose(fd); + return -NLE_INVAL; + } + + end[goodlen] = '\0'; + + err = cb(num, end); + if (err < 0) { + fclose(fd); + return err; + } + } + + fclose(fd); + + return 0; +} + +const char *nl_strerror_l(int err) +{ + const char *buf; +#ifdef HAVE_STRERROR_L + int errno_save = errno; + locale_t loc = newlocale(LC_MESSAGES_MASK, "", (locale_t)0); + + if (loc == (locale_t)0) { + if (errno == ENOENT) + loc = newlocale(LC_MESSAGES_MASK, + "POSIX", (locale_t)0); + } + if (loc != (locale_t)0) { + buf = strerror_l(err, loc); + freelocale(loc); + } else { + buf = "newlocale() failed"; + } + + errno = errno_save; +#else + buf = strerror(err); +#endif + return buf; +} +/** @endcond */ + +/** + * @name Pretty Printing of Numbers + * @{ + */ + +/** + * Cancel down a byte counter + * @arg l byte counter + * @arg unit destination unit pointer + * + * Cancels down a byte counter until it reaches a reasonable + * unit. The chosen unit is assigned to \a unit. + * This function assume 1024 bytes in one kilobyte + * + * @return The cancelled down byte counter in the new unit. + */ +double nl_cancel_down_bytes(unsigned long long l, char **unit) +{ + if (l >= 1099511627776LL) { + *unit = "TiB"; + return ((double) l) / 1099511627776LL; + } else if (l >= 1073741824) { + *unit = "GiB"; + return ((double) l) / 1073741824; + } else if (l >= 1048576) { + *unit = "MiB"; + return ((double) l) / 1048576; + } else if (l >= 1024) { + *unit = "KiB"; + return ((double) l) / 1024; + } else { + *unit = "B"; + return (double) l; + } +} + +/** + * Cancel down a bit counter + * @arg l bit counter + * @arg unit destination unit pointer + * + * Cancels down bit counter until it reaches a reasonable + * unit. The chosen unit is assigned to \a unit. + * This function assume 1000 bits in one kilobit + * + * @return The cancelled down bit counter in the new unit. + */ +double nl_cancel_down_bits(unsigned long long l, char **unit) +{ + if (l >= 1000000000000ULL) { + *unit = "Tbit"; + return ((double) l) / 1000000000000ULL; + } + + if (l >= 1000000000) { + *unit = "Gbit"; + return ((double) l) / 1000000000; + } + + if (l >= 1000000) { + *unit = "Mbit"; + return ((double) l) / 1000000; + } + + if (l >= 1000) { + *unit = "Kbit"; + return ((double) l) / 1000; + } + + *unit = "bit"; + return (double) l; +} + +int nl_rate2str(unsigned long long rate, int type, char *buf, size_t len) +{ + char *unit; + double frac; + + switch (type) { + case NL_BYTE_RATE: + frac = nl_cancel_down_bytes(rate, &unit); + break; + + case NL_BIT_RATE: + frac = nl_cancel_down_bits(rate, &unit); + break; + + default: + BUG(); + } + + return snprintf(buf, len, "%.2f%s/s", frac, unit); +} + +/** + * Cancel down a micro second value + * @arg l micro seconds + * @arg unit destination unit pointer + * + * Cancels down a microsecond counter until it reaches a + * reasonable unit. The chosen unit is assigned to \a unit. + * + * @return The cancelled down microsecond in the new unit + */ +double nl_cancel_down_us(uint32_t l, char **unit) +{ + if (l >= 1000000) { + *unit = "s"; + return ((double) l) / 1000000; + } else if (l >= 1000) { + *unit = "ms"; + return ((double) l) / 1000; + } else { + *unit = "us"; + return (double) l; + } +} + +/** @} */ + +/** + * @name Generic Unit Translations + * @{ + */ + +/** + * Convert a character string to a size + * @arg str size encoded as character string + * + * Converts the specified size as character to the corresponding + * number of bytes. + * + * Supported formats are: + * - b,kb/k,m/mb,gb/g for bytes + * - bit,kbit/mbit/gbit + * + * This function assume 1000 bits in one kilobit and + * 1024 bytes in one kilobyte + * + * @return The number of bytes or -1 if the string is unparseable + */ +long nl_size2int(const char *str) +{ + char *p; + long l = strtol(str, &p, 0); + if (p == str) + return -NLE_INVAL; + + if (*p) { + if (!strcasecmp(p, "kb") || !strcasecmp(p, "k")) + l *= 1024; + else if (!strcasecmp(p, "gb") || !strcasecmp(p, "g")) + l *= 1024*1024*1024; + else if (!strcasecmp(p, "gbit")) + l *= 1000000000L/8; + else if (!strcasecmp(p, "mb") || !strcasecmp(p, "m")) + l *= 1024*1024; + else if (!strcasecmp(p, "mbit")) + l *= 1000000/8; + else if (!strcasecmp(p, "kbit")) + l *= 1000/8; + else if (!strcasecmp(p, "bit")) + l /= 8; + else if (strcasecmp(p, "b") != 0) + return -NLE_INVAL; + } + + return l; +} + +static const struct { + double limit; + const char *unit; +} size_units[] = { + { 1024. * 1024. * 1024. * 1024. * 1024., "EiB" }, + { 1024. * 1024. * 1024. * 1024., "TiB" }, + { 1024. * 1024. * 1024., "GiB" }, + { 1024. * 1024., "MiB" }, + { 1024., "KiB" }, + { 0., "B" }, +}; + +/** + * Convert a size toa character string + * @arg size Size in number of bytes + * @arg buf Buffer to write character string to + * @arg len Size of buf + * + * This function converts a value in bytes to a human readable representation + * of it. The function uses IEC prefixes: + * + * @code + * 1024 bytes => 1 KiB + * 1048576 bytes => 1 MiB + * @endcode + * + * The highest prefix is used which ensures a result of >= 1.0, the result + * is provided as floating point number with a maximum precision of 2 digits: + * @code + * 965176 bytes => 942.55 KiB + * @endcode + * + * @return pointer to buf + */ +char *nl_size2str(const size_t size, char *buf, const size_t len) +{ + size_t i; + + if (size == 0) { + snprintf(buf, len, "0B"); + return buf; + } + + for (i = 0; i < ARRAY_SIZE(size_units); i++) { + if (size >= size_units[i].limit) { + snprintf(buf, len, "%.2g%s", + (double) size / size_units[i].limit, + size_units[i].unit); + return buf; + } + } + + BUG(); +} + +/** + * Convert a character string to a probability + * @arg str probability encoded as character string + * + * Converts the specified probability as character to the + * corresponding probability number. + * + * Supported formats are: + * - 0.0-1.0 + * - 0%-100% + * + * @return The probability relative to NL_PROB_MIN and NL_PROB_MAX + */ +long nl_prob2int(const char *str) +{ + char *p; + double d = strtod(str, &p); + + if (p == str) + return -NLE_INVAL; + + if (d > 1.0) + d /= 100.0f; + + if (d > 1.0f || d < 0.0f) + return -NLE_RANGE; + + if (*p && strcmp(p, "%") != 0) + return -NLE_INVAL; + + return (long) (((d * NL_PROB_MAX) + 0.5)); +} + +/** @} */ + +/** + * @name Time Translations + * @{ + */ + +#ifndef USER_HZ +#define USER_HZ 100 +#endif + +static uint32_t user_hz = USER_HZ; +static uint32_t psched_hz = USER_HZ; + +static double ticks_per_usec = 1.0f; + +/* Retrieves the configured HZ and ticks/us value in the kernel. + * The value is cached. Supported ways of getting it: + * + * 1) environment variable + * 2) /proc/net/psched and sysconf + * + * Supports the environment variables: + * PROC_NET_PSCHED - may point to psched file in /proc + * PROC_ROOT - may point to /proc fs */ +static void get_psched_settings(void) +{ + char name[FILENAME_MAX]; + FILE *fd; + int got_hz = 0; + static volatile int initialized = 0; + const char *ev; + NL_LOCK(mutex); + + if (initialized == 1) + return; + + nl_lock(&mutex); + + if (initialized == 1) + return; + + if ((ev = getenv("HZ"))) { + long hz = strtol(ev, NULL, 0); + + if (LONG_MIN != hz && LONG_MAX != hz) { + user_hz = hz; + got_hz = 1; + } + } + + if (!got_hz) + user_hz = sysconf(_SC_CLK_TCK); + + psched_hz = user_hz; + + if ((ev = getenv("TICKS_PER_USEC"))) { + double t = strtod(ev, NULL); + ticks_per_usec = t; + } + else { + if ((ev = getenv("PROC_NET_PSCHED"))) + snprintf(name, sizeof(name), "%s", ev); + else if ((ev = getenv("PROC_ROOT"))) + snprintf(name, sizeof(name), "%s/net/psched", ev); + else + _nl_strncpy_assert(name, "/proc/net/psched", sizeof(name)); + + if ((fd = fopen(name, "re"))) { + unsigned int ns_per_usec, ns_per_tick, nom, denom; + + if (fscanf(fd, "%08x %08x %08x %08x", + &ns_per_usec, &ns_per_tick, &nom, &denom) != 4) { + NL_DBG(1, "Fatal error: can not read psched settings from \"%s\". " \ + "Try to set TICKS_PER_USEC, PROC_NET_PSCHED or PROC_ROOT " \ + "environment variables\n", name); + exit(1); + } + + ticks_per_usec = (double) ns_per_usec / + (double) ns_per_tick; + + if (nom == 1000000) + psched_hz = denom; + + fclose(fd); + } + } + initialized = 1; + + nl_unlock(&mutex); +} + + +/** + * Return the value of HZ + */ +int nl_get_user_hz(void) +{ + get_psched_settings(); + return user_hz; +} + +/** + * Return the value of packet scheduler HZ + */ +int nl_get_psched_hz(void) +{ + get_psched_settings(); + return psched_hz; +} + +/** + * Convert micro seconds to ticks + * @arg us micro seconds + * @return number of ticks + */ +uint32_t nl_us2ticks(uint32_t us) +{ + get_psched_settings(); + return us * ticks_per_usec; +} + + +/** + * Convert ticks to micro seconds + * @arg ticks number of ticks + * @return microseconds + */ +uint32_t nl_ticks2us(uint32_t ticks) +{ + get_psched_settings(); + return ticks / ticks_per_usec; +} + +int nl_str2msec(const char *str, uint64_t *result) +{ + uint64_t total = 0, l; + int plen; + char *p; + + do { + l = strtoul(str, &p, 0); + if (p == str) + return -NLE_INVAL; + else if (*p) { + plen = strcspn(p, " \t"); + + if (!plen) + total += l; + else if (!strncasecmp(p, "sec", plen)) + total += (l * 1000); + else if (!strncasecmp(p, "min", plen)) + total += (l * 1000*60); + else if (!strncasecmp(p, "hour", plen)) + total += (l * 1000*60*60); + else if (!strncasecmp(p, "day", plen)) + total += (l * 1000*60*60*24); + else + return -NLE_INVAL; + + str = p + plen; + } else + total += l; + } while (*str && *p); + + *result = total; + + return 0; +} + +/** + * Convert milliseconds to a character string + * @arg msec number of milliseconds + * @arg buf destination buffer + * @arg len buffer length + * + * Converts milliseconds to a character string split up in days, hours, + * minutes, seconds, and milliseconds and stores it in the specified + * destination buffer. + * + * @return The destination buffer. + */ +char * nl_msec2str(uint64_t msec, char *buf, size_t len) +{ + uint64_t split[5]; + size_t i; + static const char *units[5] = {"d", "h", "m", "s", "msec"}; + char * const buf_orig = buf; + + if (msec == 0) { + snprintf(buf, len, "0msec"); + return buf_orig; + } + +#define _SPLIT(idx, unit) if ((split[idx] = msec / unit)) msec %= unit + _SPLIT(0, 86400000); /* days */ + _SPLIT(1, 3600000); /* hours */ + _SPLIT(2, 60000); /* minutes */ + _SPLIT(3, 1000); /* seconds */ +#undef _SPLIT + split[4] = msec; + + for (i = 0; i < ARRAY_SIZE(split) && len; i++) { + int l; + if (split[i] == 0) + continue; + l = snprintf(buf, len, "%s%" PRIu64 "%s", + (buf==buf_orig) ? "" : " ", split[i], units[i]); + buf += l; + len -= l; + } + + return buf_orig; +} + +/** @} */ + +/** + * @name Netlink Family Translations + * @{ + */ + +static const struct trans_tbl nlfamilies[] = { + __ADD(NETLINK_ROUTE,route), + __ADD(NETLINK_USERSOCK,usersock), + __ADD(NETLINK_FIREWALL,firewall), + __ADD(NETLINK_INET_DIAG,inetdiag), + __ADD(NETLINK_NFLOG,nflog), + __ADD(NETLINK_XFRM,xfrm), + __ADD(NETLINK_SELINUX,selinux), + __ADD(NETLINK_ISCSI,iscsi), + __ADD(NETLINK_AUDIT,audit), + __ADD(NETLINK_FIB_LOOKUP,fib_lookup), + __ADD(NETLINK_CONNECTOR,connector), + __ADD(NETLINK_NETFILTER,netfilter), + __ADD(NETLINK_IP6_FW,ip6_fw), + __ADD(NETLINK_DNRTMSG,dnrtmsg), + __ADD(NETLINK_KOBJECT_UEVENT,kobject_uevent), + __ADD(NETLINK_GENERIC,generic), + __ADD(NETLINK_SCSITRANSPORT,scsitransport), + __ADD(NETLINK_ECRYPTFS,ecryptfs), + __ADD(NETLINK_RDMA,rdma), + __ADD(NETLINK_CRYPTO,crypto), +}; + +char * nl_nlfamily2str(int family, char *buf, size_t size) +{ + return __type2str(family, buf, size, nlfamilies, + ARRAY_SIZE(nlfamilies)); +} + +int nl_str2nlfamily(const char *name) +{ + return __str2type(name, nlfamilies, ARRAY_SIZE(nlfamilies)); +} + +/** + * @} + */ + +/** + * @name Link Layer Protocol Translations + * @{ + */ + +static const struct trans_tbl llprotos[] = { + {0, "generic"}, + __ADD(ARPHRD_NETROM,netrom), + __ADD(ARPHRD_ETHER,ether), + __ADD(ARPHRD_EETHER,eether), + __ADD(ARPHRD_AX25,ax25), + __ADD(ARPHRD_PRONET,pronet), + __ADD(ARPHRD_CHAOS,chaos), + __ADD(ARPHRD_IEEE802,ieee802), + __ADD(ARPHRD_ARCNET,arcnet), + __ADD(ARPHRD_APPLETLK,atalk), + __ADD(ARPHRD_DLCI,dlci), + __ADD(ARPHRD_ATM,atm), + __ADD(ARPHRD_METRICOM,metricom), + __ADD(ARPHRD_IEEE1394,ieee1394), + __ADD(ARPHRD_EUI64,eui64), + __ADD(ARPHRD_INFINIBAND,infiniband), + __ADD(ARPHRD_SLIP,slip), + __ADD(ARPHRD_CSLIP,cslip), + __ADD(ARPHRD_SLIP6,slip6), + __ADD(ARPHRD_CSLIP6,cslip6), + __ADD(ARPHRD_RSRVD,rsrvd), + __ADD(ARPHRD_ADAPT,adapt), + __ADD(ARPHRD_ROSE,rose), + __ADD(ARPHRD_X25,x25), + __ADD(ARPHRD_HWX25,hwx25), + __ADD(ARPHRD_CAN,can), + __ADD(ARPHRD_PPP,ppp), + __ADD(ARPHRD_CISCO,cisco), + __ADD(ARPHRD_HDLC,hdlc), + __ADD(ARPHRD_LAPB,lapb), + __ADD(ARPHRD_DDCMP,ddcmp), + __ADD(ARPHRD_RAWHDLC,rawhdlc), + __ADD(ARPHRD_TUNNEL,ipip), + __ADD(ARPHRD_TUNNEL6,tunnel6), + __ADD(ARPHRD_FRAD,frad), + __ADD(ARPHRD_SKIP,skip), + __ADD(ARPHRD_LOOPBACK,loopback), + __ADD(ARPHRD_LOCALTLK,localtlk), + __ADD(ARPHRD_FDDI,fddi), + __ADD(ARPHRD_BIF,bif), + __ADD(ARPHRD_SIT,sit), + __ADD(ARPHRD_IPDDP,ip/ddp), + __ADD(ARPHRD_IPGRE,gre), + __ADD(ARPHRD_PIMREG,pimreg), + __ADD(ARPHRD_HIPPI,hippi), + __ADD(ARPHRD_ASH,ash), + __ADD(ARPHRD_ECONET,econet), + __ADD(ARPHRD_IRDA,irda), + __ADD(ARPHRD_FCPP,fcpp), + __ADD(ARPHRD_FCAL,fcal), + __ADD(ARPHRD_FCPL,fcpl), + __ADD(ARPHRD_FCFABRIC,fcfb_0), + __ADD(ARPHRD_FCFABRIC+1,fcfb_1), + __ADD(ARPHRD_FCFABRIC+2,fcfb_2), + __ADD(ARPHRD_FCFABRIC+3,fcfb_3), + __ADD(ARPHRD_FCFABRIC+4,fcfb_4), + __ADD(ARPHRD_FCFABRIC+5,fcfb_5), + __ADD(ARPHRD_FCFABRIC+6,fcfb_6), + __ADD(ARPHRD_FCFABRIC+7,fcfb_7), + __ADD(ARPHRD_FCFABRIC+8,fcfb_8), + __ADD(ARPHRD_FCFABRIC+9,fcfb_9), + __ADD(ARPHRD_FCFABRIC+10,fcfb_10), + __ADD(ARPHRD_FCFABRIC+11,fcfb_11), + __ADD(ARPHRD_FCFABRIC+12,fcfb_12), + __ADD(ARPHRD_IEEE802_TR,tr), + __ADD(ARPHRD_IEEE80211,ieee802.11), + __ADD(ARPHRD_IEEE80211_PRISM,ieee802.11_prism), + __ADD(ARPHRD_IEEE80211_RADIOTAP,ieee802.11_radiotap), + __ADD(ARPHRD_IEEE802154,ieee802.15.4), + __ADD(ARPHRD_IEEE802154_MONITOR,ieee802.15.4_monitor), + __ADD(ARPHRD_PHONET,phonet), + __ADD(ARPHRD_PHONET_PIPE,phonet_pipe), + __ADD(ARPHRD_CAIF,caif), + __ADD(ARPHRD_IP6GRE,ip6gre), + __ADD(ARPHRD_NETLINK,netlink), + __ADD(ARPHRD_6LOWPAN,6lowpan), + __ADD(ARPHRD_VOID,void), + __ADD(ARPHRD_NONE,nohdr), +}; + +char * nl_llproto2str(int llproto, char *buf, size_t len) +{ + return __type2str(llproto, buf, len, llprotos, ARRAY_SIZE(llprotos)); +} + +int nl_str2llproto(const char *name) +{ + return __str2type(name, llprotos, ARRAY_SIZE(llprotos)); +} + +/** @} */ + + +/** + * @name Ethernet Protocol Translations + * @{ + */ + +static const struct trans_tbl ether_protos[] = { + __ADD(ETH_P_LOOP,loop), + __ADD(ETH_P_PUP,pup), + __ADD(ETH_P_PUPAT,pupat), + __ADD(ETH_P_IP,ip), + __ADD(ETH_P_X25,x25), + __ADD(ETH_P_ARP,arp), + __ADD(ETH_P_BPQ,bpq), + __ADD(ETH_P_IEEEPUP,ieeepup), + __ADD(ETH_P_IEEEPUPAT,ieeepupat), + __ADD(ETH_P_DEC,dec), + __ADD(ETH_P_DNA_DL,dna_dl), + __ADD(ETH_P_DNA_RC,dna_rc), + __ADD(ETH_P_DNA_RT,dna_rt), + __ADD(ETH_P_LAT,lat), + __ADD(ETH_P_DIAG,diag), + __ADD(ETH_P_CUST,cust), + __ADD(ETH_P_SCA,sca), + __ADD(ETH_P_TEB,teb), + __ADD(ETH_P_RARP,rarp), + __ADD(ETH_P_ATALK,atalk), + __ADD(ETH_P_AARP,aarp), +#ifdef ETH_P_8021Q + __ADD(ETH_P_8021Q,802.1q), +#endif + __ADD(ETH_P_IPX,ipx), + __ADD(ETH_P_IPV6,ipv6), + __ADD(ETH_P_PAUSE,pause), + __ADD(ETH_P_SLOW,slow), +#ifdef ETH_P_WCCP + __ADD(ETH_P_WCCP,wccp), +#endif + __ADD(ETH_P_PPP_DISC,ppp_disc), + __ADD(ETH_P_PPP_SES,ppp_ses), + __ADD(ETH_P_MPLS_UC,mpls_uc), + __ADD(ETH_P_MPLS_MC,mpls_mc), + __ADD(ETH_P_ATMMPOA,atmmpoa), + __ADD(ETH_P_LINK_CTL,link_ctl), + __ADD(ETH_P_ATMFATE,atmfate), + __ADD(ETH_P_PAE,pae), + __ADD(ETH_P_AOE,aoe), + __ADD(ETH_P_TIPC,tipc), + __ADD(ETH_P_1588,ieee1588), + __ADD(ETH_P_FCOE,fcoe), + __ADD(ETH_P_FIP,fip), + __ADD(ETH_P_EDSA,edsa), + __ADD(ETH_P_EDP2,edp2), + __ADD(ETH_P_802_3,802.3), + __ADD(ETH_P_AX25,ax25), + __ADD(ETH_P_ALL,all), + __ADD(ETH_P_802_2,802.2), + __ADD(ETH_P_SNAP,snap), + __ADD(ETH_P_DDCMP,ddcmp), + __ADD(ETH_P_WAN_PPP,wan_ppp), + __ADD(ETH_P_PPP_MP,ppp_mp), + __ADD(ETH_P_LOCALTALK,localtalk), + __ADD(ETH_P_CAN,can), + __ADD(ETH_P_PPPTALK,ppptalk), + __ADD(ETH_P_TR_802_2,tr_802.2), + __ADD(ETH_P_MOBITEX,mobitex), + __ADD(ETH_P_CONTROL,control), + __ADD(ETH_P_IRDA,irda), + __ADD(ETH_P_ECONET,econet), + __ADD(ETH_P_HDLC,hdlc), + __ADD(ETH_P_ARCNET,arcnet), + __ADD(ETH_P_DSA,dsa), + __ADD(ETH_P_TRAILER,trailer), + __ADD(ETH_P_PHONET,phonet), + __ADD(ETH_P_IEEE802154,ieee802154), + __ADD(ETH_P_CAIF,caif), +}; + +char *nl_ether_proto2str(int eproto, char *buf, size_t len) +{ + return __type2str(eproto, buf, len, ether_protos, + ARRAY_SIZE(ether_protos)); +} + +int nl_str2ether_proto(const char *name) +{ + return __str2type(name, ether_protos, ARRAY_SIZE(ether_protos)); +} + +/** @} */ + +/** + * @name IP Protocol Translations + * @{ + */ + +char *nl_ip_proto2str(int proto, char *buf, size_t len) +{ + struct protoent *p = getprotobynumber(proto); + + if (p) { + snprintf(buf, len, "%s", p->p_name); + return buf; + } + + snprintf(buf, len, "0x%x", proto); + return buf; +} + +int nl_str2ip_proto(const char *name) +{ + struct protoent *p = getprotobyname(name); + unsigned long l; + char *end; + + if (p) + return p->p_proto; + + l = strtoul(name, &end, 0); + if (l == ULONG_MAX || *end != '\0') + return -NLE_OBJ_NOTFOUND; + + return (int) l; +} + +/** @} */ + +/** + * @name Dumping Helpers + * @{ + */ + +/** + * Handle a new line while dumping + * @arg params Dumping parameters + * + * This function must be called before dumping any onto a + * new line. It will ensure proper prefixing as specified + * by the dumping parameters. + * + * @note This function will NOT dump any newlines itself + */ +void nl_new_line(struct nl_dump_params *params) +{ + params->dp_line++; + + if (params->dp_prefix) { + int i; + for (i = 0; i < params->dp_prefix; i++) { + if (params->dp_fd) + fprintf(params->dp_fd, " "); + else if (params->dp_buf) + strncat(params->dp_buf, " ", + params->dp_buflen - + strlen(params->dp_buf) - 1); + } + } + + if (params->dp_nl_cb) + params->dp_nl_cb(params, params->dp_line); +} + +static void dump_one(struct nl_dump_params *parms, const char *fmt, + va_list args) +{ + if (parms->dp_fd) + vfprintf(parms->dp_fd, fmt, args); + else if (parms->dp_buf || parms->dp_cb) { + char *buf = NULL; + if (vasprintf(&buf, fmt, args) >= 0) { + if (parms->dp_cb) + parms->dp_cb(parms, buf); + else + strncat(parms->dp_buf, buf, + parms->dp_buflen - + strlen(parms->dp_buf) - 1); + free(buf); + } + } +} + + +/** + * Dump a formatted character string + * @arg params Dumping parameters + * @arg fmt printf style formatting string + * @arg ... Arguments to formatting string + * + * Dumps a printf style formatting string to the output device + * as specified by the dumping parameters. + */ +void nl_dump(struct nl_dump_params *params, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + dump_one(params, fmt, args); + va_end(args); +} + +void nl_dump_line(struct nl_dump_params *parms, const char *fmt, ...) +{ + va_list args; + + nl_new_line(parms); + + va_start(args, fmt); + dump_one(parms, fmt, args); + va_end(args); +} + + +/** @} */ + +/** @cond SKIP */ + +int __trans_list_add(int i, const char *a, struct nl_list_head *head) +{ + struct trans_list *tl; + + tl = calloc(1, sizeof(*tl)); + if (!tl) + return -NLE_NOMEM; + + tl->i = i; + tl->a = strdup(a); + + nl_list_add_tail(&tl->list, head); + + return 0; +} + +void __trans_list_clear(struct nl_list_head *head) +{ + struct trans_list *tl, *next; + + nl_list_for_each_entry_safe(tl, next, head, list) { + free(tl->a); + free(tl); + } + + nl_init_list_head(head); +} + +char *__type2str(int type, char *buf, size_t len, + const struct trans_tbl *tbl, size_t tbl_len) +{ + size_t i; + for (i = 0; i < tbl_len; i++) { + if (tbl[i].i == type) { + snprintf(buf, len, "%s", tbl[i].a); + return buf; + } + } + + snprintf(buf, len, "0x%x", type); + return buf; +} + +char *__list_type2str(int type, char *buf, size_t len, + struct nl_list_head *head) +{ + struct trans_list *tl; + + nl_list_for_each_entry(tl, head, list) { + if (tl->i == type) { + snprintf(buf, len, "%s", tl->a); + return buf; + } + } + + snprintf(buf, len, "0x%x", type); + return buf; +} + +char *__flags2str(int flags, char *buf, size_t len, + const struct trans_tbl *tbl, size_t tbl_len) +{ + size_t i; + int tmp = flags; + + memset(buf, 0, len); + + for (i = 0; i < tbl_len; i++) { + if (tbl[i].i & tmp) { + tmp &= ~tbl[i].i; + strncat(buf, tbl[i].a, len - strlen(buf) - 1); + if ((tmp & flags)) + strncat(buf, ",", len - strlen(buf) - 1); + } + } + + return buf; +} + +int __str2type(const char *buf, const struct trans_tbl *tbl, size_t tbl_len) +{ + unsigned long l; + char *end; + size_t i; + + if (*buf == '\0') + return -NLE_INVAL; + + for (i = 0; i < tbl_len; i++) + if (!strcasecmp(tbl[i].a, buf)) + return tbl[i].i; + + l = strtoul(buf, &end, 0); + if (l == ULONG_MAX || *end != '\0') + return -NLE_OBJ_NOTFOUND; + + return (int) l; +} + +int __list_str2type(const char *buf, struct nl_list_head *head) +{ + struct trans_list *tl; + unsigned long l; + char *end; + + if (*buf == '\0') + return -NLE_INVAL; + + nl_list_for_each_entry(tl, head, list) { + if (!strcasecmp(tl->a, buf)) + return tl->i; + } + + l = strtoul(buf, &end, 0); + if (l == ULONG_MAX || *end != '\0') + return -NLE_OBJ_NOTFOUND; + + return (int) l; +} + +int __str2flags(const char *buf, const struct trans_tbl *tbl, size_t tbl_len) +{ + int flags = 0; + size_t i; + size_t len; /* ptrdiff_t ? */ + char *p = (char *) buf, *t; + + for (;;) { + if (*p == ' ') + p++; + + t = strchr(p, ','); + len = t ? t - p : strlen(p); + for (i = 0; i < tbl_len; i++) + if (len == strlen(tbl[i].a) && + !strncasecmp(tbl[i].a, p, len)) + flags |= tbl[i].i; + + if (!t) + return flags; + + p = ++t; + } + + return 0; +} + +void dump_from_ops(struct nl_object *obj, struct nl_dump_params *params) +{ + int type = params->dp_type; + + if (type < 0 || type > NL_DUMP_MAX) + BUG(); + + params->dp_line = 0; + + if (params->dp_dump_msgtype) { +#if 0 + /* XXX */ + char buf[64]; + + dp_dump_line(params, 0, "%s ", + nl_cache_mngt_type2name(obj->ce_ops, + obj->ce_ops->co_protocol, + obj->ce_msgtype, + buf, sizeof(buf))); +#endif + params->dp_pre_dump = 1; + } + + if (obj->ce_ops->oo_dump[type]) + obj->ce_ops->oo_dump[type](obj, params); +} + +/** + * Check for library capabilities + * + * @arg capability capability identifier + * + * Check whether the loaded libnl library supports a certain capability. + * This is useful so that applications can workaround known issues of + * libnl that are fixed in newer library versions, without + * having a hard dependency on the new version. It is also useful, for + * capabilities that cannot easily be detected using autoconf tests. + * The capabilities are integer constants with name NL_CAPABILITY_*. + * + * As this function is intended to detect capabilities at runtime, + * you might not want to depend during compile time on the NL_CAPABILITY_* + * names. Instead you can use their numeric values which are guaranteed not to + * change meaning. + * + * @return non zero if libnl supports a certain capability, 0 otherwise. + **/ +int nl_has_capability (int capability) +{ + static const uint8_t caps[ ( NL_CAPABILITY_MAX + 7 ) / 8 ] = { +#define _NL_ASSERT(expr) ( 0 * sizeof(struct { unsigned int x: ( (!!(expr)) ? 1 : -1 ); }) ) +#define _NL_SETV(i, r, v) \ + ( _NL_ASSERT( (v) == 0 || (i) * 8 + (r) == (v) - 1 ) + \ + ( (v) == 0 ? 0 : (1 << (r)) ) ) +#define _NL_SET(i, v0, v1, v2, v3, v4, v5, v6, v7) \ + [(i)] = ( \ + _NL_SETV((i), 0, (v0)) | _NL_SETV((i), 4, (v4)) | \ + _NL_SETV((i), 1, (v1)) | _NL_SETV((i), 5, (v5)) | \ + _NL_SETV((i), 2, (v2)) | _NL_SETV((i), 6, (v6)) | \ + _NL_SETV((i), 3, (v3)) | _NL_SETV((i), 7, (v7)) ) + _NL_SET(0, + NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE, + NL_CAPABILITY_ROUTE_LINK_VETH_GET_PEER_OWN_REFERENCE, + NL_CAPABILITY_ROUTE_LINK_CLS_ADD_ACT_OWN_REFERENCE, + NL_CAPABILITY_NL_CONNECT_RETRY_GENERATE_PORT_ON_ADDRINUSE, + NL_CAPABILITY_ROUTE_LINK_GET_KERNEL_FAIL_OPNOTSUPP, + NL_CAPABILITY_ROUTE_ADDR_COMPARE_CACHEINFO, + NL_CAPABILITY_VERSION_3_2_26, + NL_CAPABILITY_NL_RECV_FAIL_TRUNC_NO_PEEK), + _NL_SET(1, + NL_CAPABILITY_LINK_BUILD_CHANGE_REQUEST_SET_CHANGE, + NL_CAPABILITY_RTNL_NEIGH_GET_FILTER_AF_UNSPEC_FIX, + NL_CAPABILITY_VERSION_3_2_27, + NL_CAPABILITY_RTNL_LINK_VLAN_PROTOCOL_SERIALZE, + NL_CAPABILITY_RTNL_LINK_PARSE_GRE_REMOTE, + NL_CAPABILITY_RTNL_LINK_VLAN_INGRESS_MAP_CLEAR, + NL_CAPABILITY_RTNL_LINK_VXLAN_IO_COMPARE, + NL_CAPABILITY_NL_OBJECT_DIFF64), + _NL_SET (2, + NL_CAPABILITY_XFRM_SA_KEY_SIZE, + NL_CAPABILITY_RTNL_ADDR_PEER_FIX, + NL_CAPABILITY_VERSION_3_2_28, + NL_CAPABILITY_RTNL_ADDR_PEER_ID_FIX, + NL_CAPABILITY_NL_ADDR_FILL_SOCKADDR, + NL_CAPABILITY_XFRM_SEC_CTX_LEN, + NL_CAPABILITY_LINK_BUILD_ADD_REQUEST_SET_CHANGE, + NL_CAPABILITY_NL_RECVMSGS_PEEK_BY_DEFAULT), + _NL_SET (3, + NL_CAPABILITY_VERSION_3_2_29, + NL_CAPABILITY_XFRM_SP_SEC_CTX_LEN, + NL_CAPABILITY_VERSION_3_3_0, + NL_CAPABILITY_VERSION_3_4_0, + NL_CAPABILITY_ROUTE_FIX_VLAN_SET_EGRESS_MAP, + NL_CAPABILITY_VERSION_3_5_0, + NL_CAPABILITY_NL_OBJECT_IDENTICAL_PARTIAL, + 0), + /* IMPORTANT: these capability numbers are intended to be universal and stable + * for libnl3. Don't allocate new numbers on your own that differ from upstream + * libnl3. + * + * Instead register a capability number upstream too. We will take patches + * for that. We especially take patches to register a capability number that is + * only implemented in your fork of libnl3. + * + * If you really don't want that, use capabilities in the range 0x7000 to 0x7FFF. + * (NL_CAPABILITY_IS_USER_RESERVED). Upstream libnl3 will not register conflicting + * capabilities in that range. + * + * Obviously, only backport capability numbers to libnl versions that actually + * implement that capability as well. */ +#undef _NL_SET +#undef _NL_SETV +#undef _NL_ASSERT + }; + + if (capability <= 0 || capability > NL_CAPABILITY_MAX) + return 0; + capability--; + return (caps[capability / 8] & (1 << (capability % 8))) != 0; +} + +/** @endcond */ + +/** @} */ diff --git a/libnl/lib/version.c b/libnl/lib/version.c new file mode 100644 index 0000000..ffde260 --- /dev/null +++ b/libnl/lib/version.c @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: LGPL-2.1-only */ +/* + * Copyright (c) 2003-2012 Thomas Graf + */ + +/** + * @ingroup core + * @defgroup utils Utilities + * + * Run-time version information + * + * @{ + */ + + +/** + * @name Run-time version information + * @{ + */ + +#include + +const int nl_ver_num = LIBNL_VER_NUM; +const int nl_ver_maj = LIBNL_VER_MAJ; +const int nl_ver_min = LIBNL_VER_MIN; +const int nl_ver_mic = LIBNL_VER_MIC; + +/** @} */ + +/** @} */ diff --git a/netiface.cpp b/netiface.cpp new file mode 100644 index 0000000..96a19aa --- /dev/null +++ b/netiface.cpp @@ -0,0 +1,420 @@ +#include "dpitunnel-cli.h" + +#include "netiface.h" +#include "profiles.h" +#include "utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include //lots of netlink functions +#include //genl_connect, genlmsg_put +#include +#include + +extern int Interrupt_pipe[2]; +extern std::atomic stop_flag; + +struct Netlink { + int id; + struct nl_sock *socket; + struct nl_cb *cb; + int result; +}; + +struct Wifi { + std::string ssid; +}; + +void parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len) { + memset(tb, 0, sizeof(struct rtattr *) * (max + 1)); + + while (RTA_OK(rta, len)) { + if (rta->rta_type <= max) + tb[rta->rta_type] = rta; + + rta = RTA_NEXT(rta, len); + } +} + +// Monitor when client changes interfaces. We need it to change profiles according to current net interface +void route_monitor_thread() { + int sock; + if ((sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) { + std::cerr << "Failed to open netlink socket. Errno: " << std::strerror(errno) << std::endl; + return; + } + + struct sockaddr_nl addr; + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; + addr.nl_groups = RTMGRP_IPV4_ROUTE; + + if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) == -1) { + std::cerr << "Failed to bind netlink socket. Errno: " << std::strerror(errno) << std::endl; + close(sock); + return; + } + + // Make sokcet non-blocking + if (fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK) == -1) { + std::cerr << "Failed to make netlink socket non-blocking. Errno: " << std::strerror(errno) + << std::endl; + close(sock); + return; + } + + struct nlmsghdr *nlh; + std::string buffer(8192, '\x00'); + nlh = (struct nlmsghdr *) &buffer[0]; + + struct pollfd fds[2]; + + // fds[0] is netlink socket + fds[0].fd = sock; + fds[0].events = POLLIN; + + // fds[1] is interrupt pipe + fds[1].fd = Interrupt_pipe[0]; + fds[1].events = POLLIN; + + // Set poll() timeout + int timeout = -1; + + int len; + while (!stop_flag.load()) { + int ret = poll(fds, 2, timeout); + + // Check state + if (ret == -1) { + std::cerr << "Poll error. Errno:" << std::strerror(errno) << std::endl; + break; + } else if (ret == 0) + continue; // Timeout happened + else { + if (fds[0].revents & POLLERR || + fds[0].revents & POLLHUP || + fds[0].revents & POLLNVAL) + break; + + // Process netlink socket + if (fds[0].revents & POLLIN) { + len = recv(sock, nlh, buffer.size(), 0); + if (len <= 0) { + std::cerr << "Netlink socket receive error. Errno: " << std::strerror(errno) + << std::endl; + break; + } + while ((NLMSG_OK(nlh, len)) && (nlh->nlmsg_type != NLMSG_DONE)) { + if (nlh->nlmsg_type == RTM_NEWROUTE) { + // Check is it default route + struct rtattr *tb[RTA_MAX + 1]; + struct rtmsg *r = (struct rtmsg *) NLMSG_DATA(nlh); + parse_rtattr(tb, RTA_MAX, RTM_RTA(r), + nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); + if (!tb[RTA_DST] && !r->rtm_dst_len && tb[RTA_OIF]) { + std::cout << "Detected network changes. Changing profile..." + << std::endl; + + char iface_c_str[IF_NAMESIZE]; + if_indextoname(*(int *) RTA_DATA(tb[RTA_OIF]), iface_c_str); + std::string iface = std::string(iface_c_str); + + std::string wifi_ap = get_current_wifi_name(iface); + + std::cout << "Netiface: " << iface; + if (!wifi_ap.empty()) + std::cout << ", Wi-Fi point name: " << wifi_ap; + std::cout << std::endl; + + std::string temp; + if (change_profile(iface, wifi_ap, &temp) == 0) + std::cout << "Current profile: " << temp << std::endl; + else + std::cerr << "Failed to change profile" << std::endl; + } + } + nlh = NLMSG_NEXT(nlh, len); + } + } + + fds[0].revents = 0; + fds[1].revents = 0; + } + } + + close(sock); +} + +std::string get_current_iface_name() { + // "Connect" to IP from global Internet and getsockname() + const std::string global_ip = "198.41.0.4"; // 'A' root server IP + + std::string res = ""; + + int sock; + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + std::cerr << "Failed to open test socket. Errno: " << std::strerror(errno) << std::endl; + return ""; + } + // Add port and address + struct sockaddr_in server_address; + server_address.sin_family = AF_INET; + server_address.sin_port = htons(80); + inet_pton(AF_INET, global_ip.c_str(), &server_address.sin_addr); + + if (connect(sock, (struct sockaddr *) &server_address, sizeof(server_address)) < 0) { + std::cerr << "Test socket can't \"connect\". Errno: " << std::strerror(errno) << std::endl; + close(sock); + return ""; + } + + struct sockaddr_in addr; + struct ifaddrs *ifaddr; + struct ifaddrs *ifa; + socklen_t addr_len; + + addr_len = sizeof(addr); + getsockname(sock, (struct sockaddr *) &addr, &addr_len); + getifaddrs(&ifaddr); + + // look which interface contains the wanted IP. + // When found, ifa->ifa_name contains the name of the interface (eth0, eth1, ppp0...) + for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr) { + if (AF_INET == ifa->ifa_addr->sa_family) { + struct sockaddr_in *inaddr = (struct sockaddr_in *) ifa->ifa_addr; + + if (inaddr->sin_addr.s_addr == addr.sin_addr.s_addr) { + if (ifa->ifa_name) { + res = ifa->ifa_name; + } + } + } + } + } + freeifaddrs(ifaddr); + close(sock); + + if (res.empty()) + std::cerr << "Failed to find default network interface" << std::endl; + return res; +} + +std::string get_current_wifi_name_ioctl(std::string iface_name) { + int sock; + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + std::cerr << "Failed to open socket. Errno: " << std::strerror(errno) << std::endl; + return ""; + } + + std::string essid(IW_ESSID_MAX_SIZE, '\x00'); + struct iwreq wreq; + strncpy(wreq.ifr_ifrn.ifrn_name, iface_name.c_str(), IFNAMSIZ); + strncpy(wreq.ifr_name, iface_name.c_str(), iface_name.size()); + wreq.u.essid.pointer = (caddr_t *) &essid[0]; + wreq.u.data.length = IW_ESSID_MAX_SIZE; + wreq.u.data.flags = 0; + if (ioctl(sock, SIOCGIWESSID, &wreq) == -1) { + std::cerr << "Get ESSID ioctl failed. Errno: " << std::strerror(errno) << std::endl; + close(sock); + return ""; + } + + close(sock); + return std::string(essid.c_str()); +} + +// Based on NetworkManager/src/platform/wifi/wifi-utils-nl80211.c +static void find_ssid(uint8_t *ies, uint32_t ies_len, uint8_t **ssid, uint32_t *ssid_len) { +#define WLAN_EID_SSID 0 + *ssid = NULL; + *ssid_len = 0; + + while (ies_len > 2 && ies[0] != WLAN_EID_SSID) { + ies_len -= ies[1] + 2; + ies += ies[1] + 2; + } + if (ies_len < 2) + return; + if (ies_len < (uint32_t) (2 + ies[1])) + return; + + *ssid_len = ies[1]; + *ssid = ies + 2; +} + +static int get_scan_callback(struct nl_msg *msg, void *arg) { + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = (genlmsghdr *) nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *bss[NL80211_BSS_MAX + 1]; + static struct nla_policy bss_policy[NL80211_BSS_MAX + 1]; + bss_policy[NL80211_BSS_TSF].type = NLA_U64; + bss_policy[NL80211_BSS_FREQUENCY].type = NLA_U32; + bss_policy[NL80211_BSS_BEACON_INTERVAL].type = NLA_U16; + bss_policy[NL80211_BSS_CAPABILITY].type = NLA_U16; + bss_policy[NL80211_BSS_SIGNAL_MBM].type = NLA_U32; + bss_policy[NL80211_BSS_SIGNAL_UNSPEC].type = NLA_U8; + bss_policy[NL80211_BSS_STATUS].type = NLA_U32; + bss_policy[NL80211_BSS_SEEN_MS_AGO].type = NLA_U32; + char mac_addr[18]; + + nla_parse(tb, + NL80211_ATTR_MAX, + genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), + NULL); + + if (!tb[NL80211_ATTR_BSS]) { + std::cerr << "NL80211_ATTR_BSS missing" << std::endl; + return NL_SKIP; + } + + if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS], bss_policy)) { + std::cerr << "Failed to parse nested attributes" << std::endl; + return NL_SKIP; + } + + // Check is it associated AP + if (bss[NL80211_BSS_STATUS] == NULL || + nla_get_u32(bss[NL80211_BSS_STATUS]) != NL80211_BSS_STATUS_ASSOCIATED) + return NL_SKIP; + + if (!bss[NL80211_BSS_INFORMATION_ELEMENTS]) { + std::cerr << "NL80211_BSS_INFORMATION_ELEMENTS missing" << std::endl; + return NL_SKIP; + } + + uint8_t *ssid; + uint32_t ssid_len; + + find_ssid((uint8_t *) nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]), + nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]), + &ssid, &ssid_len); + + if (!ssid || !ssid_len) { + std::cerr << "Failed to find SSID" << std::endl; + return NL_SKIP; + } + + (*(Wifi *) arg).ssid = std::string((const char *) ssid, ssid_len); + + return NL_SKIP; +} + +static int finish_handler(struct nl_msg *msg, void *arg) { + int *ret = (int *) arg; + *ret = 0; + return NL_SKIP; +} + +static int init_nl80211(Netlink *nl, Wifi *w) { + nl->socket = nl_socket_alloc(); + if (!nl->socket) { + std::cerr << "Failed to open netlink socket" << std::endl; + return -ENOMEM; + } + + nl_socket_set_buffer_size(nl->socket, 8192, 8192); + + if (genl_connect(nl->socket)) { + std::cerr << "Failed to connect to netlink socket" << std::endl; + nl_close(nl->socket); + nl_socket_free(nl->socket); + return -ENOLINK; + } + + nl->id = genl_ctrl_resolve(nl->socket, "nl80211"); + if (nl->id < 0) { + std::cerr << "Nl80211 interface not found" << std::endl; + nl_close(nl->socket); + nl_socket_free(nl->socket); + return -ENOENT; + } + + nl->cb = nl_cb_alloc(NL_CB_DEFAULT); + if (nl->cb == NULL) { + std::cerr << "Failed to allocate netlink callback" << std::endl; + nl_close(nl->socket); + nl_socket_free(nl->socket); + return -ENOMEM; + } + + nl_cb_set(nl->cb, NL_CB_VALID, NL_CB_CUSTOM, get_scan_callback, w); + nl_cb_set(nl->cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &(nl->result)); + + return 0; +} + +std::string get_current_wifi_name_netlink(std::string iface_name) { + Netlink nl; + Wifi w; + + if (init_nl80211(&nl, &w) != 0) { + std::cerr << "Error initializing netlink 802.11" << std::endl; + return ""; + } + + nl.result = 1; + + // Get scan results + struct nl_msg *msg = nlmsg_alloc(); + if (!msg) { + std::cerr << "Failed to allocate netlink message" << std::endl; + nl_cb_put(nl.cb); + nl_close(nl.socket); + nl_socket_free(nl.socket); + return ""; + } + + genlmsg_put(msg, + NL_AUTO_PORT, + NL_AUTO_SEQ, + nl.id, + 0, + NLM_F_DUMP, + NL80211_CMD_GET_SCAN, + 0); + + int ifindex = if_nametoindex(iface_name.c_str()); + if (ifindex == 0) { + std::cerr << "if_nametoindex failed. Errno: " << std::strerror(errno) << std::endl; + nl_cb_put(nl.cb); + nl_close(nl.socket); + nl_socket_free(nl.socket); + nlmsg_free(msg); + return ""; + } + + nla_put_u32(msg, NL80211_ATTR_IFINDEX, ifindex); + nl_send_auto(nl.socket, msg); + while (nl.result > 0) { nl_recvmsgs(nl.socket, nl.cb); } + nlmsg_free(msg); + + return w.ssid; +} + +std::string get_current_wifi_name(std::string iface_name) { + std::string response; + if ((response = get_current_wifi_name_ioctl(iface_name)).empty()) + response = get_current_wifi_name_netlink(iface_name); + + return response; +} diff --git a/packet.cpp b/packet.cpp new file mode 100644 index 0000000..a0d4394 --- /dev/null +++ b/packet.cpp @@ -0,0 +1,153 @@ +#include "packet.h" +#include "utils.h" + +#include +#include + +void parse_header(const std::string &line, std::map &http_message) { + if (line.empty()) return; + + size_t pos_sep = line.find(':', 0); //Look for separator ':' + if (pos_sep == std::string::npos) + return; + + std::string key = line.substr(0, pos_sep); + transform(key.begin(), key.end(), key.begin(), + [](unsigned char c) { return std::tolower(c); }); + + size_t value_start = line.find_first_not_of(' ', pos_sep + 1); + if (value_start == std::string::npos) // Skip ' ' after header + return; + size_t value_end = line.find_last_not_of(' '); // Skip ' ' after value + if (value_end == std::string::npos) + return; + std::string value = line.substr(value_start, value_end - value_start + 1); + + http_message[key] = value; +} + +void parse_first_line(const std::string &line, std::map &http_message) { + size_t position, lpost; + + // Find request method + position = line.find(' '); + http_message["method"] = line.substr(0, position); + lpost = ++position; //Skip character ' ' + + // Find path + position = line.find(' ', lpost); + http_message["path"] = line.substr(lpost, (position - lpost)); + position++; //Skip character ' ' + + // Find HTTP version + http_message["version"] = line.substr(position); +} + +std::map parse_http_message(std::string message) { + std::map http_message; + std::regex rgx("(\\r\\n|\\r|\\n)"); + std::sregex_token_iterator iter(message.begin(), + message.end(), + rgx, + -1); + std::sregex_token_iterator end; + for (bool is_first = true; iter != end; ++iter) { + if (std::string(*iter).empty()) + break; + if (is_first) { + parse_first_line(*iter, http_message); + is_first = false; + continue; + } + parse_header(*iter, http_message); + } + return http_message; +} + + +int parse_request(const std::string &request, std::string &method, std::string &host, int &port, + bool is_proxy) { + std::map http_request = parse_http_message(request); + + if (http_request.find("method") == http_request.end()) + return -1; + method = http_request["method"]; + + std::string found_url; + if (is_proxy) { + // Extract hostname an port if exists + std::string regex_string = "[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[-a-z0-9]{1,16}(:[0-9]{1,5})?"; + std::regex url_find_regex(regex_string); + std::smatch match; + + if (std::regex_search(http_request["path"], match, url_find_regex) == 0) + return -1; + + // Get string from regex output + found_url = match.str(0); + } else { + if (http_request.find("host") == http_request.end()) + return -2; // seems it is https in transparent mode + found_url = http_request["host"]; + } + + // Check if port exists + size_t port_start_position = found_url.find(':'); + if (port_start_position == std::string::npos) { + // If no set default port + if (method == "CONNECT") port = 443; + else port = 80; + host = found_url; + } else { + // If yes extract port + port = std::stoi( + found_url.substr(port_start_position + 1, found_url.size() - port_start_position)); + host = found_url.substr(0, port_start_position); + } + + return 0; +} + +void remove_proxy_strings(std::string &request, unsigned int &last_char) { + std::string method, host; + int port; + if (parse_request(request, method, host, port, true) || !validate_http_method(method)) + return; + + unsigned int request_size = request.size(); + + // Remove schema + const std::string http_str("http://"); + if (last_char >= http_str.size() && + request.find(http_str, method.size()) == method.size() + 1) { + request.erase(method.size() + 1, http_str.size()); + last_char -= http_str.size(); + } + + // Remove domain + if (last_char >= host.size() && request.find(host, method.size()) == method.size() + 1) { + request.erase(method.size() + 1, host.size()); + last_char -= host.size(); + } + + // Remove port + std::string port_str(":" + std::to_string(port)); + if (last_char >= port_str.size() && + request.find(port_str, method.size()) == method.size() + 1) { + request.erase(method.size() + 1, port_str.size()); + last_char -= port_str.size(); + } + + std::string request_lower = request; + std::transform(request_lower.begin(), request_lower.end(), request_lower.begin(), + [](unsigned char c){ return std::tolower(c); }); + + const std::string proxy_conn_str("proxy-connection: keep-alive\r\n"); + size_t proxy_connection_hdr_start = request_lower.find(proxy_conn_str); + if (last_char >= proxy_conn_str.size() && proxy_connection_hdr_start != std::string::npos) { + request.erase(proxy_connection_hdr_start, proxy_conn_str.size()); + last_char -= proxy_conn_str.size(); + } + + request.resize(request_size, ' '); +} diff --git a/profiles.cpp b/profiles.cpp new file mode 100644 index 0000000..8b13fba --- /dev/null +++ b/profiles.cpp @@ -0,0 +1,45 @@ +#include "dpitunnel-cli.h" + +#include "profiles.h" +#include "utils.h" + +#include +#include + +// Map contains net interface name to that apply profile and profile settings +std::map Profiles; +extern struct Profile_s Profile; + +void add_profile(std::string name, Profile_s profile) { + Profiles[name] = profile; +} + +int change_profile(const std::string &iface, const std::string &wifi_ap, + std::string *choosen_profile_name /*= NULL*/) { + if (Profiles.empty()) + return 0; + + auto search = std::find_if(Profiles.begin(), Profiles.end(), + [iface, wifi_ap](const auto &element) -> bool { + return wildcard_match(element.first.c_str(), (iface + + (wifi_ap.empty() + ? "" : (':' + + wifi_ap))).c_str()); + }); + if (search != Profiles.end()) + Profile = search->second; + else { + search = Profiles.find("default"); + if (search != Profiles.end()) + Profile = search->second; + else { + std::cerr << "Failed to find profile" << std::endl; + return -1; + } + } + + if (choosen_profile_name != NULL) + *choosen_profile_name = search->first; + + return 0; +} diff --git a/socket.cpp b/socket.cpp new file mode 100644 index 0000000..b43144f --- /dev/null +++ b/socket.cpp @@ -0,0 +1,456 @@ +#include "dpitunnel-cli.h" + +#include "socket.h" +#include "desync.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern struct Settings_perst_s Settings_perst; +extern struct Profile_s Profile; + +int connect_with_timeout(int sockfd, const struct sockaddr *addr, socklen_t addrlen, + unsigned int timeout_ms) { + int rc = 0; + // Set O_NONBLOCK + int sockfd_flags_before; + if ((sockfd_flags_before = fcntl(sockfd, F_GETFL, 0) < 0)) return -1; + if (fcntl(sockfd, F_SETFL, sockfd_flags_before | O_NONBLOCK) < 0) return -1; + // Start connecting (asynchronously) + do { + if (connect(sockfd, addr, addrlen) < 0) { + // Did connect return an error? If so, we'll fail. + if ((errno != EWOULDBLOCK) && (errno != EAGAIN) && (errno != EINPROGRESS)) { + rc = -1; + } + // Otherwise, we'll wait for it to complete. + else { + // Set a deadline timestamp 'timeout' ms from now (needed b/c poll can be interrupted) + struct timespec now; + if (clock_gettime(CLOCK_MONOTONIC, &now) < 0) { + rc = -1; + break; + } + struct timespec deadline = {.tv_sec = now.tv_sec, + .tv_nsec = now.tv_nsec + ((long) timeout_ms) * 1000000l}; + // Wait for the connection to complete. + do { + // Calculate how long until the deadline + if (clock_gettime(CLOCK_MONOTONIC, &now) < 0) { + rc = -1; + break; + } + int ms_until_deadline = (int) ((deadline.tv_sec - now.tv_sec) * 1000l + + (deadline.tv_nsec - now.tv_nsec) / 1000000l); + if (ms_until_deadline < 0) { + rc = 0; + break; + } + // Wait for connect to complete (or for the timeout deadline) + struct pollfd pfds[] = {{.fd = sockfd, .events = POLLOUT}}; + rc = poll(pfds, 1, ms_until_deadline); + // If poll 'succeeded', make sure it *really* succeeded + if (rc > 0) { + int error = 0; + socklen_t len = sizeof(error); + int retval = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len); + if (retval == 0) errno = error; + if (error != 0) rc = -1; + } + } + // If poll was interrupted, try again. + while (rc == -1 && errno == EINTR); + // Did poll timeout? If so, fail. + if (rc == 0) { + errno = ETIMEDOUT; + rc = -1; + } + } + } + } while (0); + // Restore original O_NONBLOCK state + if (fcntl(sockfd, F_SETFL, sockfd_flags_before) < 0) return -1; + // Success + return rc; +} + +short count_hops_private(struct sockaddr_in server_address, std::string ip, int port) { + // Init remote server socket + int server_socket = socket(AF_INET, SOCK_STREAM, 0); + if (server_socket == -1) { + std::cerr << "Can't create remote server socket. Errno " << std::strerror(errno) + << std::endl; + return -1; + } + + // Start sniff thread to sniff SYN, ACK from server to calculate sequence numbers for payload + std::atomic flag(true); + std::atomic local_port(-1); + std::atomic status; + std::string sniffed_packet; + std::promise sniff_thread_ready = std::promise(); + std::thread sniff_thread = std::thread(sniff_handshake_packet, &sniffed_packet, + ip, port, &local_port, &flag, &status, + &sniff_thread_ready); + // Wait for sniff thread to init + sniff_thread_ready.get_future().wait(); + + // Connect to remote server + auto start = std::chrono::high_resolution_clock::now(); + if (connect_with_timeout(server_socket, (struct sockaddr *) &server_address, + sizeof(server_address), Settings_perst.count_hops_connect_timeout) < + 0) { + // Stop sniff thread + flag.store(false); + if (sniff_thread.joinable()) sniff_thread.join(); + close(server_socket); + return -1; + } + auto stop = std::chrono::high_resolution_clock::now(); + unsigned int connect_time = std::chrono::duration_cast( + stop - start).count(); + + // Get local port to choose proper packet + struct sockaddr_in local_addr; + socklen_t len = sizeof(local_addr); + if (getsockname(server_socket, (struct sockaddr *) &local_addr, &len) == -1) { + std::cerr << "Failed to get local port. Errno: " << std::strerror(errno) << std::endl; + // Stop sniff thread + flag.store(false); + if (sniff_thread.joinable()) sniff_thread.join(); + close(server_socket); + return -1; + } + local_port.store(ntohs(local_addr.sin_port)); + + // Get received SYN, ACK packet + if (sniff_thread.joinable()) sniff_thread.join(); + if (status.load() == -1) { + std::cerr << "Failed to capture handshake packet" << std::endl; + close(server_socket); + return -1; + } + + // Create raw socket to send fake packets + int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP); + if (sockfd == -1) { + std::cerr << "Fake raw socket creation failure. Errno: " << std::strerror(errno) + << std::endl; + close(server_socket); + return -1; + } + + // Disable send buffer to send packets immediately + int sndbuf_size = 0; + if (setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &sndbuf_size, sizeof(sndbuf_size)) < 0) { + std::cerr << "Failed to set raw socket buffer size to 0. Errno: " + << std::strerror(errno) << std::endl; + close(sockfd); + close(server_socket); + return -1; + } + // Tell system we will include IP header in packet + int yes = 1; + if (setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &yes, sizeof(yes)) < 0) { + std::cerr << "Failed to enable IP_HDRINCL. Errno: " << std::strerror(errno) << std::endl; + close(sockfd); + close(server_socket); + return -1; + } + + // Store window + int window_size; + socklen_t size = sizeof(window_size); + if (getsockopt(server_socket, IPPROTO_TCP, TCP_MAXSEG, &window_size, &size) < 0) { + std::cerr << "Failed to get default MSS from remote server socket. Errno: " + << std::strerror(errno) << std::endl; + close(sockfd); + close(server_socket); + return -1; + } + + // Send packet and wait for connect_time*2 for response + int timeout = connect_time * 2; + std::string null_byte(1, '\x00'); + std::string buffer(100, ' '); + for (short ttl = 1; ttl <= 255; ttl++) { + std::string payload_packet = form_packet(sniffed_packet, null_byte.c_str(), + null_byte.size(), + rand() % 65535, ttl, 0, 1, window_size, true); + if (sendto(sockfd, &payload_packet[0], payload_packet.size(), MSG_NOSIGNAL, + (const sockaddr *) &server_address, sizeof(sockaddr)) < 0) { + std::cerr << "Failed to send packet from raw socket. Errno: " + << std::strerror(errno) << std::endl; + break; + } + + struct pollfd fds[1]; + fds[0].fd = sockfd; + fds[0].events = POLLIN; + + start = std::chrono::high_resolution_clock::now(); + bool is_received_reply = false; + for (;;) { + auto stop = std::chrono::high_resolution_clock::now(); + if (std::chrono::duration_cast(stop - start).count() >= + timeout) + break; + + int ret = poll(fds, 1, timeout - std::chrono::duration_cast( + stop - start).count()); + if (ret == -1) { + std::cerr << "Poll error. Errno:" << std::strerror(errno) << std::endl; + break; + } else if (ret == 0) + continue; + else { + if (fds[0].revents & POLLERR || + fds[0].revents & POLLHUP || + fds[0].revents & POLLNVAL) + break; + + // Match packet by remote ip, remote port, local port and print + if (fds[0].revents & POLLIN) { + ssize_t read_size = recv(sockfd, &buffer[0], buffer.size(), 0); + if (read_size < 0) { + std::cerr << "Response packet read error. Errno: " + << std::strerror(errno) << std::endl; + break; + } + + // Get IP header of received packet + iphdr *ip_h = (iphdr *) &buffer[0]; + // Get TCP header of received packet + tcphdr *tcp_h = (tcphdr *) (&buffer[0] + ip_h->ihl * 4); + // Get source port (server port) + int port_src_recv = ntohs(tcp_h->source); + // Get dest port (client port) + int port_dst_recv = ntohs(tcp_h->dest); + if (ip_h->saddr == server_address.sin_addr.s_addr && + port_src_recv == port && port_dst_recv == local_port) { + is_received_reply = true; + break; + } + + } + + fds[0].revents = 0; + } + } + + if (is_received_reply) { + close(sockfd); + close(server_socket); + return ttl; + } + } + + close(sockfd); + close(server_socket); + return -1; +} + +int count_hops(std::string server_ip, int server_port) { + // Add port and address + struct sockaddr_in server_address; + server_address.sin_family = AF_INET; + server_address.sin_port = htons(server_port); + if (inet_pton(AF_INET, server_ip.c_str(), &server_address.sin_addr) <= 0) { + std::cerr << "Invalid remote server ip address" << std::endl; + return -1; + } + + // Find the minimum TTL value that allows packet to come to server + short ttl = 256; + short res; + for (int i = 1; i <= 3; i++) + if ((res = count_hops_private(server_address, server_ip, server_port)) != -1) + if (res < ttl) ttl = res; + + return ttl == 256 ? -1 : ttl; +} + +int init_remote_server_socket(int &server_socket, std::string server_ip, int server_port) { + // Init remote server socket + server_socket = socket(AF_INET, SOCK_STREAM, 0); + if (server_socket == -1) { + std::cerr << "Can't create remote server socket. Errno " << std::strerror(errno) + << std::endl; + return -1; + } + + // Add port and address + struct sockaddr_in server_address; + server_address.sin_family = AF_INET; + server_address.sin_port = htons(server_port); + + if (inet_pton(AF_INET, server_ip.c_str(), &server_address.sin_addr) <= 0) { + std::cerr << "Invalid remote server ip address" << std::endl; + return -1; + } + + // If window size specified by user, set maximum possible window scale to 128 to make server split Server Hello + if (Profile.window_scale_factor != -1) { + int buflen = 65536 << (Profile.window_scale_factor - 1); + if (setsockopt(server_socket, SOL_SOCKET, SO_RCVBUFFORCE, &buflen, sizeof(buflen)) < 0) { + std::cerr << "Can't setsockopt on socket. Errno: " << std::strerror(errno) << std::endl; + return -1; + } + } + + // Connect to remote server + if (connect(server_socket, (struct sockaddr *) &server_address, sizeof(server_address)) < 0) { + std::cerr << "Can't connect to remote server. Errno: " << std::strerror(errno) << std::endl; + return -1; + } + + // Set timeouts + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 10; + if (setsockopt(server_socket, SOL_SOCKET, SO_SNDTIMEO, (char *) &timeout, sizeof(timeout)) < + 0 || + setsockopt(server_socket, SOL_SOCKET, SO_RCVTIMEO, (char *) &timeout, sizeof(timeout)) < + 0) { + std::cerr << "Can't setsockopt on socket. Errno: " << std::strerror(errno) << std::endl; + return -1; + } + + return 0; +} + +int recv_string(int socket, std::string &message, unsigned int &last_char, + struct timeval *timeout /*= NULL*/, unsigned int *recv_time /*= NULL*/) { + + std::chrono::time_point start, stop; + if (recv_time != NULL) + start = std::chrono::high_resolution_clock::now(); + + ssize_t read_size; + + // Set receive timeout on socket + struct timeval timeout_predef; + timeout_predef.tv_sec = 0; + timeout_predef.tv_usec = 10; + if (timeout != NULL) + if (setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, + (char *) timeout, + sizeof(timeout_predef)) < 0) { + std::cerr << "Can't setsockopt on socket. Errno: " << std::strerror(errno) << std::endl; + return -1; + } + + while (true) { + read_size = recv(socket, &message[0], message.size(), 0); + if (recv_time != NULL) + stop = std::chrono::high_resolution_clock::now(); + if (read_size < 0) { + if (errno == EWOULDBLOCK || errno == EAGAIN) break; + if (errno == EINTR) continue; // All is good. It is just interrrupt + else { + std::cerr << "There is critical read error. Can't process client. Errno: " + << std::strerror(errno) << std::endl; + return -1; + } + } else if (read_size == 0) { + last_char = read_size; + return -1; + } + + if (recv_time != NULL && *recv_time != 0) + *recv_time = std::chrono::duration_cast( + stop - start).count(); + + if (timeout != NULL) { + if (setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char *) &timeout_predef, + sizeof(timeout_predef)) < 0) { + std::cerr << "Can't setsockopt on socket. Errno: " << std::strerror(errno) + << std::endl; + return -1; + } + } + + break; + } + + // Set position of last character + last_char = read_size < 0 ? 0 : read_size; + + return 0; +} + +int send_string(int socket, const std::string &string_to_send, unsigned int last_char, + unsigned int split_position /*= 0*/) { + + // Check if string is empty + if (last_char == 0) + return 0; + + size_t offset = 0; + + while (last_char - offset != 0) { + ssize_t send_size; + if (split_position == 0) + send_size = send(socket, string_to_send.c_str() + offset, last_char - offset, + MSG_NOSIGNAL); + else + send_size = send(socket, string_to_send.c_str() + offset, + last_char - offset < split_position ? last_char - offset < + split_position : split_position, + MSG_NOSIGNAL); + + if (send_size < 0) { + if (errno == EWOULDBLOCK || errno == EAGAIN) { + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + continue; + } + if (errno == EINTR) continue; // All is good. It is just interrrupt. + else { + std::cerr << "There is critical send error. Can't process client. Errno: " + << std::strerror(errno) << std::endl; + return -1; + } + } + + if (send_size == 0) + return -1; + + offset += send_size; + } + + return 0; +} + +int send_string_raw(int socket, const std::string &string_to_send, + unsigned int last_char, struct sockaddr *serv_addr, + unsigned int serv_addr_size) { + + // Check if string is empty + if (last_char == 0) + return 0; + + if (sendto(socket, &string_to_send[0], last_char, MSG_NOSIGNAL, serv_addr, serv_addr_size) < + 0) { + std::cerr << "Failed to send packet from raw socket. Errno: " + << std::strerror(errno) << std::endl; + return -1; + } + + return 0; +} diff --git a/ssl.cpp b/ssl.cpp new file mode 100644 index 0000000..f86dc0c --- /dev/null +++ b/ssl.cpp @@ -0,0 +1,55 @@ +#include "dpitunnel-cli.h" + +#include "ssl.h" + +#include +#include +#include + +extern struct Settings_perst_s Settings_perst; + +int load_ca_bundle() { + std::ifstream file; + file.open(Settings_perst.ca_bundle_path); + if (!file) { + std::cerr << "Failed to open CA Bundle (SSL certs). File " + << Settings_perst.ca_bundle_path << " not found" << std::endl; + return -1; + } + + std::stringstream stream; + stream << file.rdbuf(); + Settings_perst.ca_bundle = stream.str(); + file.close(); + + return 0; +} + +X509_STORE *gen_x509_store() { + BIO *cbio = BIO_new_mem_buf(Settings_perst.ca_bundle.c_str(), Settings_perst.ca_bundle.size()); + X509_STORE *cts; + if ((cts = X509_STORE_new()) == NULL) + return NULL; + STACK_OF(X509_INFO) *inf; + if (!cts || !cbio) + return NULL; + + inf = PEM_X509_INFO_read_bio(cbio, NULL, NULL, NULL); + if (!inf) { + BIO_free(cbio); + return NULL; + } + + for (int i = 0; i < sk_X509_INFO_num(inf); i++) { + X509_INFO *itmp = sk_X509_INFO_value(inf, i); + if (itmp->x509) + X509_STORE_add_cert(cts, itmp->x509); + if (itmp->crl) + X509_STORE_add_crl(cts, itmp->crl); + } + + sk_X509_INFO_pop_free(inf, X509_INFO_free); + BIO_free(cbio); + + return cts; +} diff --git a/utils.cpp b/utils.cpp new file mode 100644 index 0000000..e5cb76c --- /dev/null +++ b/utils.cpp @@ -0,0 +1,323 @@ +#include "dpitunnel-cli.h" + +#include "utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern struct Settings_perst_s Settings_perst; + +bool wildcard_match(char const *needle, char const *haystack) { + for (; *needle != '\0'; ++needle) { + switch (*needle) { + case '?': + if (*haystack == '\0') + return false; + ++haystack; + break; + case '*': { + if (needle[1] == '\0') + return true; + size_t max = strlen(haystack); + for (size_t i = 0; i < max; i++) + if (wildcard_match(needle + 1, haystack + i)) + return true; + return false; + } + default: + if (*haystack != *needle) + return false; + ++haystack; + } + } + return *haystack == '\0'; +} + +bool is_space_or_tab(char c) { return c == ' ' || c == '\t'; } + +std::pair trim(const char *b, const char *e, size_t left, + size_t right) { + while (b + left < e && is_space_or_tab(b[left])) + left++; + while (right > 0 && is_space_or_tab(b[right - 1])) + right--; + return std::make_pair(left, right); +} + +template +void split(const char *b, const char *e, char d, Fn fn) { + size_t i = 0; + size_t beg = 0; + + while (e ? (b + i < e) : (b[i] != '\0')) { + if (b[i] == d) { + auto r = trim(b, e, beg, i); + if (r.first < r.second) { fn(&b[r.first], &b[r.second]); } + beg = i + 1; + } + i++; + } + + if (i) { + auto r = trim(b, e, beg, i); + if (r.first < r.second) { fn(&b[r.first], &b[r.second]); } + } +} + +bool check_host_name(const char *pattern, size_t pattern_len, std::string host) { + if (host.size() == pattern_len && host == pattern) { return true; } + std::vector pattern_components; + std::vector host_components; + split(&pattern[0], &pattern[pattern_len], '.', + [&](const char *b, const char *e) { + pattern_components.emplace_back(std::string(b, e)); + }); + split(&host[0], &host[host.size()], '.', + [&](const char *b, const char *e) { + host_components.emplace_back(std::string(b, e)); + }); + if (host_components.size() != pattern_components.size()) { return false; } + + auto itr = pattern_components.begin(); + for (const auto &h: host_components) { + auto &p = *itr; + if (p != h && p != "*") { + auto partial_match = (p.size() > 0 && p[p.size() - 1] == '*' && + !p.compare(0, p.size() - 1, h)); + if (!partial_match) { return false; } + } + ++itr; + } + return true; +} + +std::string last_n_chars(const std::string &input, unsigned int n) { + unsigned int inputSize = input.size(); + return (n > 0 && inputSize > n) ? input.substr(inputSize - n) : input; +} + +void get_tls_sni(const std::string &bytes, unsigned int last_char, unsigned int &start_pos, + unsigned int &len) { + unsigned int it; + if (last_char <= 43) { + start_pos = 0; + len = 0; + return; + } + unsigned short sidlen = bytes[43]; + it = 1 + 43 + sidlen; + if (last_char <= it) { + start_pos = 0; + len = 0; + return; + } + unsigned short cslen = ntohs(*(unsigned short *) &bytes[it]); + it += 2 + cslen; + if (last_char <= it) { + start_pos = 0; + len = 0; + return; + } + unsigned short cmplen = bytes[it]; + it += 1 + cmplen; + if (last_char <= it) { + start_pos = 0; + len = 0; + return; + } + unsigned short maxcharit = it + 2 + ntohs(*(unsigned short *) &bytes[it]); + it += 2; + unsigned short ext_type = 1; + unsigned short ext_len; + while (it < maxcharit && ext_type != 0) { + if (last_char <= it + 9) { + start_pos = 0; + len = 0; + return; + } + ext_type = ntohs(*(unsigned short *) &bytes[it]); + it += 2; + ext_len = ntohs(*(unsigned short *) &bytes[it]); + it += 2; + if (ext_type == 0) { + it += 3; + unsigned short namelen = ntohs(*(unsigned short *) &bytes[it]); + it += 2; + len = namelen; + start_pos = it; + return; + } else it += ext_len; + } + start_pos = 0; + len = 0; +} + +bool validate_http_method(std::string method) { + static const std::vector valid_http_methods{ + "CONNECT", + "DELETE", + "GET", + "HEAD", + "OPTIONS", + "PATCH", + "POST", + "PUT", + "TRACE" + }; + for (auto &c: method) c = toupper(c); + + return std::find(valid_http_methods.begin(), valid_http_methods.end(), method) != + valid_http_methods.end(); +} + +void daemonize() { + int pid; + + pid = fork(); + if (pid == -1) { + perror("fork"); + exit(2); + } else if (pid != 0) + exit(0); + + if (setsid() == -1) + exit(2); + if (chdir("/") == -1) + exit(2); + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + /* redirect fd's 0,1,2 to /dev/null */ + open("/dev/null", O_RDWR); + int fd; + /* stdin */ + fd = dup(0); + /* stdout */ + fd = dup(0); + /* stderror */ +} + +int ignore_sigpipe() { + struct sigaction act; + std::memset(&act, 0, sizeof(act)); + act.sa_handler = SIG_IGN; + act.sa_flags = SA_RESTART; + if (sigaction(SIGPIPE, &act, NULL)) { + std::cerr << "Failed ignore SIGPIPE. Errno: " << std::strerror(errno) << std::endl; + return -1; + } + + return 0; +} + +/* + * Credits for tcp_get_auto_ttl realization to ValdikSS (https://github.com/ValdikSS/GoodbyeDPI/blob/5494be72ba374b688d87d6ecb5024d71cd1803ff/src/ttltrack.c#L222) + * */ +int tcp_get_auto_ttl(const uint8_t ttl, const uint8_t autottl1, + const uint8_t autottl2, const uint8_t minhops, + const uint8_t maxttl) { + uint8_t nhops = 0; + uint8_t ttl_of_fake_packet = 0; + + if (ttl > 98 && ttl < 128) { + nhops = 128 - ttl; + } else if (ttl > 34 && ttl < 64) { + nhops = 64 - ttl; + } else { + return 0; + } + + if (nhops <= autottl1 || nhops < minhops) { + return 0; + } + + ttl_of_fake_packet = nhops - autottl2; + if (ttl_of_fake_packet < autottl2 && nhops <= 9) { + ttl_of_fake_packet = nhops - autottl1 - trunc((autottl2 - autottl1) * ((float) nhops / 10)); + } + + if (maxttl && ttl_of_fake_packet > maxttl) { + ttl_of_fake_packet = maxttl; + } + + return ttl_of_fake_packet; +} + +bool match_whitelist_domain(const std::string &domain) { + return Settings_perst.whitelist_domains.find(domain) != Settings_perst.whitelist_domains.end(); +} + +bool match_whitelist_ip(const std::string &ip) { + return Settings_perst.whitelist_ips.find(ip) != Settings_perst.whitelist_ips.end(); +} + +int load_whitelist() { + std::ifstream file; + file.open(Settings_perst.whitelist_path); + if (!file) { + std::cerr << "Failed to load whitelist. File " + << Settings_perst.whitelist_path << " not found" << std::endl; + return -1; + } + + std::string input; + size_t pos; + std::pair entry; + while (std::getline(file, input)) { + pos = input.find(' '); + if (pos == std::string::npos) + continue; + entry = std::make_pair( + input.substr(0, pos), + input.substr(pos + 1) + ); + if (entry.first == "ip") + Settings_perst.whitelist_ips.insert(entry.second); + else if (entry.first == "domain") + Settings_perst.whitelist_domains.insert(entry.second); + } + + file.close(); + return 0; +} + +std::string find_custom_ip(const std::string &domain) { + auto found = Settings_perst.custom_ips.find(domain); + return found != Settings_perst.custom_ips.end() ? found->second : ""; +} + +int load_custom_ips() { + std::ifstream file; + file.open(Settings_perst.custom_ips_path); + if (!file) { + std::cerr << "Failed to load custom IPs. File " + << Settings_perst.custom_ips_path << " not found" << std::endl; + return -1; + } + + std::string input; + size_t pos; + std::pair entry; + while (std::getline(file, input)) { + pos = input.find(' '); + if (pos == std::string::npos) + continue; + entry = std::make_pair( + input.substr(0, pos), + input.substr(pos + 1) + ); + if (!entry.first.empty() && !entry.second.empty()) + Settings_perst.custom_ips.insert({entry.first, entry.second}); + } + + file.close(); + return 0; +} \ No newline at end of file