From 019f9d509be94dfd38606552b40ef24cdceddc06 Mon Sep 17 00:00:00 2001 From: basil00 Date: Fri, 19 Aug 2011 20:11:17 +0800 Subject: [PATCH] First commit of the Windows Divert project --- LICENSE | 619 ++++++++ README | 5 + dirs | 4 + dll/Makefile | 1 + dll/divert.c | 1912 +++++++++++++++++++++++++ dll/divert.def | 9 + dll/sources | 32 + doc/divert.html | 938 ++++++++++++ examples/dirs | 4 + examples/netdump/Makefile | 1 + examples/netdump/netdump.c | 242 ++++ examples/netdump/sources | 30 + examples/netfilter/Makefile | 1 + examples/netfilter/netfilter.c | 445 ++++++ examples/netfilter/sources | 30 + examples/webfilter/Makefile | 1 + examples/webfilter/sources | 30 + examples/webfilter/webfilter.c | 596 ++++++++ include/divert.h | 266 ++++ include/divert_device.h | 145 ++ sys/Makefile | 1 + sys/Makefile.inc | 4 + sys/divert.c | 2457 ++++++++++++++++++++++++++++++++ sys/divert.inf | 25 + sys/divert.rc | 30 + sys/sources | 33 + 26 files changed, 7861 insertions(+) create mode 100644 LICENSE create mode 100644 README create mode 100644 dirs create mode 100644 dll/Makefile create mode 100644 dll/divert.c create mode 100644 dll/divert.def create mode 100644 dll/sources create mode 100644 doc/divert.html create mode 100644 examples/dirs create mode 100644 examples/netdump/Makefile create mode 100644 examples/netdump/netdump.c create mode 100644 examples/netdump/sources create mode 100644 examples/netfilter/Makefile create mode 100644 examples/netfilter/netfilter.c create mode 100644 examples/netfilter/sources create mode 100644 examples/webfilter/Makefile create mode 100644 examples/webfilter/sources create mode 100644 examples/webfilter/webfilter.c create mode 100644 include/divert.h create mode 100644 include/divert_device.h create mode 100644 sys/Makefile create mode 100644 sys/Makefile.inc create mode 100644 sys/divert.c create mode 100644 sys/divert.inf create mode 100644 sys/divert.rc create mode 100644 sys/sources diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..bc08fe2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,619 @@ + 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. diff --git a/README b/README new file mode 100644 index 0000000..e83711c --- /dev/null +++ b/README @@ -0,0 +1,5 @@ +Divert: Windows Packet Divert +----------------------------- + +For more information about this project, see doc/divert.html + diff --git a/dirs b/dirs new file mode 100644 index 0000000..c768d5d --- /dev/null +++ b/dirs @@ -0,0 +1,4 @@ +DIRS= \ + dll \ + examples \ + sys \ No newline at end of file diff --git a/dll/Makefile b/dll/Makefile new file mode 100644 index 0000000..53b9a3d --- /dev/null +++ b/dll/Makefile @@ -0,0 +1 @@ +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/dll/divert.c b/dll/divert.c new file mode 100644 index 0000000..efcc393 --- /dev/null +++ b/dll/divert.c @@ -0,0 +1,1912 @@ +/* + * divert.c + * (C) 2011, all rights reserved, + * + * 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 . + */ + +#ifndef UNICODE +#define UNICODE +#endif + +#include +#include +#include +#include +#include +#include +#include + +// #define DIVERT_DEBUG + +#define DIVERTEXPORT +#include "divert.h" +#include "divert_device.h" + +#define DIVERT_DRIVER_NAME L"divert" +#define DIVERT_DRIVER_SYS L"\\" DIVERT_DRIVER_NAME L".sys" +#define DIVERT_DRIVER_INF L"\\" DIVERT_DRIVER_NAME L".inf" +#define DIVERT_DRIVER_MATCH_DLL L"\\WdfCoInstaller*.dll" + +/* + * Filter parsing. + */ +typedef enum +{ + FILTER_TOKEN_ICMP, + FILTER_TOKEN_ICMP_BODY, + FILTER_TOKEN_ICMP_CHECKSUM, + FILTER_TOKEN_ICMP_CODE, + FILTER_TOKEN_ICMP_TYPE, + FILTER_TOKEN_ICMPV6, + FILTER_TOKEN_ICMPV6_BODY, + FILTER_TOKEN_ICMPV6_CHECKSUM, + FILTER_TOKEN_ICMPV6_CODE, + FILTER_TOKEN_ICMPV6_TYPE, + FILTER_TOKEN_IP, + FILTER_TOKEN_IP_CHECKSUM, + FILTER_TOKEN_IP_DF, + FILTER_TOKEN_IP_DST_ADDR, + FILTER_TOKEN_IP_FRAG_OFF, + FILTER_TOKEN_IP_HDR_LENGTH, + FILTER_TOKEN_IP_ID, + FILTER_TOKEN_IP_LENGTH, + FILTER_TOKEN_IP_MF, + FILTER_TOKEN_IP_PROTOCOL, + FILTER_TOKEN_IP_SRC_ADDR, + FILTER_TOKEN_IP_TOS, + FILTER_TOKEN_IP_TTL, + FILTER_TOKEN_IPV6, + FILTER_TOKEN_IPV6_DST_ADDR, + FILTER_TOKEN_IPV6_FLOW_LABEL, + FILTER_TOKEN_IPV6_HOP_LIMIT, + FILTER_TOKEN_IPV6_LENGTH, + FILTER_TOKEN_IPV6_NEXT_HDR, + FILTER_TOKEN_IPV6_SRC_ADDR, + FILTER_TOKEN_IPV6_TRAFFIC_CLASS, + FILTER_TOKEN_TCP, + FILTER_TOKEN_TCP_ACK, + FILTER_TOKEN_TCP_ACK_NUM, + FILTER_TOKEN_TCP_CHECKSUM, + FILTER_TOKEN_TCP_DST_PORT, + FILTER_TOKEN_TCP_FIN, + FILTER_TOKEN_TCP_HDR_LENGTH, + FILTER_TOKEN_TCP_PAYLOAD_LENGTH, + FILTER_TOKEN_TCP_PSH, + FILTER_TOKEN_TCP_RST, + FILTER_TOKEN_TCP_SEQ_NUM, + FILTER_TOKEN_TCP_SRC_PORT, + FILTER_TOKEN_TCP_SYN, + FILTER_TOKEN_TCP_URG, + FILTER_TOKEN_TCP_URG_PTR, + FILTER_TOKEN_TCP_WINDOW, + FILTER_TOKEN_UDP, + FILTER_TOKEN_UDP_CHECKSUM, + FILTER_TOKEN_UDP_DST_PORT, + FILTER_TOKEN_UDP_LENGTH, + FILTER_TOKEN_UDP_PAYLOAD_LENGTH, + FILTER_TOKEN_UDP_SRC_PORT, + FILTER_TOKEN_TRUE, + FILTER_TOKEN_FALSE, + FILTER_TOKEN_INBOUND, + FILTER_TOKEN_OUTBOUND, + FILTER_TOKEN_IF_IDX, + FILTER_TOKEN_SUB_IF_IDX, + FILTER_TOKEN_OPEN, + FILTER_TOKEN_CLOSE, + FILTER_TOKEN_EQ, + FILTER_TOKEN_NEQ, + FILTER_TOKEN_LT, + FILTER_TOKEN_LEQ, + FILTER_TOKEN_GT, + FILTER_TOKEN_GEQ, + FILTER_TOKEN_NOT, + FILTER_TOKEN_AND, + FILTER_TOKEN_OR, + FILTER_TOKEN_NUMBER, + FILTER_TOKEN_END, +} FILTER_TOKEN_KIND; + +typedef struct +{ + FILTER_TOKEN_KIND kind; + UINT32 val[4]; +} FILTER_TOKEN; + +#define FILTER_TOKEN_MAXLEN 32 // Fits longest IPv6 + +typedef struct +{ + char *name; + FILTER_TOKEN_KIND kind; +} FILTER_TOKEN_NAME, *PFILTER_TOKEN_NAME; + +/* + * IPv4/IPv6 pseudo headers. + */ +typedef struct +{ + UINT32 SrcAddr; + UINT32 DstAddr; + UINT8 Zero; + UINT8 Protocol; + UINT16 Length; +} DIVERT_PSEUDOHDR, *PDIVERT_PSEUDOHDR; + +typedef struct +{ + UINT32 SrcAddr[4]; + UINT32 DstAddr[4]; + UINT32 Length; + UINT32 NextHdr:8; + UINT32 Zero:24; +} DIVERT_PSEUDOV6HDR, *PDIVERT_PSEUDOV6HDR; + +/* + * Misc. + */ +#define UINT8_MAX 0xFF +#define UINT32_MAX 0xFFFFFFFF +#define IPPROTO_ICMP 1 +#define IPPROTO_TCP 6 +#define IPPROTO_UDP 17 +#define IPPROTO_ICMPV6 58 + +/* + * Prototypes. + */ +static HMODULE DivertLoadCoInstaller(LPWSTR divert_dll); +static BOOLEAN DivertDriverFiles(LPWSTR *divert_dir_ptr, + LPWSTR *divert_sys_ptr, LPWSTR *divert_inf_ptr, LPWSTR *divert_dll_ptr); +static BOOLEAN DivertDriverInstall(VOID); +static BOOL DivertCompileFilter(const char *filter_str, + divert_ioctl_filter_t filter, UINT8 *fp); +static int DivertFilterTokenNameCompare(const void *a, const void *b); +static BOOL DivertTokenizeFilter(const char *filter, FILTER_TOKEN *tokens, + UINT8 tokensmax); +static BOOL DivertParseIPv4Address(char *str, UINT32 *addr_ptr); +static BOOL DivertParseIPv6Address(char *str, UINT32 *addr_ptr); +static BOOL DivertParseFilter(FILTER_TOKEN *tokens, UINT8 *tp, + divert_ioctl_filter_t filter, UINT8 *fp, FILTER_TOKEN_KIND op); +static void DivertFilterNegate(divert_ioctl_filter_t filter, UINT8 s, UINT8 e); +static void DivertFilterUpdate(divert_ioctl_filter_t filter, UINT8 s, UINT8 e, + UINT8 success, UINT8 failure); +static void DivertInitPseudoHeader(PDIVERT_IPHDR ip_header, + PDIVERT_PSEUDOHDR pseudo_header, UINT8 protocol, UINT len); +static void DivertInitPseudoHeaderV6(PDIVERT_IPV6HDR ipv6_header, + PDIVERT_PSEUDOV6HDR pseudov6_header, UINT8 protocol, UINT len); +static UINT16 DivertHelperCalcChecksum(PVOID pseudo_header, + UINT16 pseudo_header_len, PVOID data, UINT len); + +#ifdef DIVERT_DEBUG +static void DivertFilterDump(divert_ioctl_filter_t filter, UINT8 len); +#endif + +/* + * Co-installer functions. + */ +PFN_WDFPREDEVICEINSTALLEX pfnWdfPreDeviceInstallEx; +PFN_WDFPOSTDEVICEINSTALL pfnWdfPostDeviceInstall; + +/* + * Dll Entry + */ +extern BOOL APIENTRY DivertDllEntry(HANDLE module, DWORD reason, + LPVOID reserved) +{ + return TRUE; +} + +/* + * Load the co-installer functions. + */ +static HMODULE DivertLoadCoInstaller(LPWSTR divert_dll) +{ + HMODULE library = LoadLibrary(divert_dll); + + if (library == NULL) + { + return NULL; + } + + pfnWdfPreDeviceInstallEx = (PFN_WDFPREDEVICEINSTALLEX)GetProcAddress( + library, "WdfPreDeviceInstallEx"); + if (pfnWdfPreDeviceInstallEx == NULL) + { + goto DivertLoadInstallerError; + } + pfnWdfPostDeviceInstall = (PFN_WDFPOSTDEVICEINSTALL)GetProcAddress( + library, "WdfPostDeviceInstall"); + if (pfnWdfPostDeviceInstall == NULL) + { + goto DivertLoadInstallerError; + } + + return library; + +DivertLoadInstallerError: + + FreeLibrary(library); + return NULL; +} + +/* + * Locate the Divert driver files. + */ +static BOOLEAN DivertDriverFiles(LPWSTR *divert_dir_ptr, + LPWSTR *divert_sys_ptr, LPWSTR *divert_inf_ptr, LPWSTR *divert_dll_ptr) +{ + DWORD length, l2; + HANDLE find; + WIN32_FIND_DATA find_data; + LPWSTR divert_dir, divert_sys, divert_inf, divert_dll; + + // Construct the filenames from the current directory name + length = GetCurrentDirectory(0, NULL); + length--; + divert_dir = (WCHAR *)malloc((length+1)*sizeof(WCHAR)); + divert_sys = (WCHAR *)malloc((length+wcslen(DIVERT_DRIVER_SYS)+1)* + sizeof(WCHAR)); + divert_inf = (WCHAR *)malloc((length+wcslen(DIVERT_DRIVER_INF)+1)* + sizeof(WCHAR)); + divert_dll = (WCHAR *)malloc((length+wcslen(DIVERT_DRIVER_MATCH_DLL)+1)* + sizeof(WCHAR)); + if (divert_dir == NULL || divert_sys == NULL || divert_inf == NULL || + divert_dll == NULL) + { + goto DivertDriverFilesError; + } + if (GetCurrentDirectory(length+1, divert_dir) != length) + { + SetLastError(ERROR_FILE_NOT_FOUND); + goto DivertDriverFilesError; + } + wcscpy(divert_sys, divert_dir); + wcscpy(divert_inf, divert_dir); + wcscpy(divert_dll, divert_dir); + wcscpy(divert_sys + length, DIVERT_DRIVER_SYS); + wcscpy(divert_inf + length, DIVERT_DRIVER_INF); + wcscpy(divert_dll + length, DIVERT_DRIVER_MATCH_DLL); + + // Check the the files exist; and find the co-installer filename. + find = FindFirstFile(divert_sys, &find_data); + if (find == INVALID_HANDLE_VALUE) + { + goto DivertDriverFilesError; + } + FindClose(find); + find = FindFirstFile(divert_inf, &find_data); + if (find == INVALID_HANDLE_VALUE) + { + goto DivertDriverFilesError; + } + FindClose(find); + find = FindFirstFile(divert_dll, &find_data); + free(divert_dll); + if (find == INVALID_HANDLE_VALUE) + { + goto DivertDriverFilesError; + } + FindClose(find); + divert_dll = (WCHAR *)malloc((length+wcslen(find_data.cFileName)+2)* + sizeof(WCHAR)); + if (divert_dll == NULL) + { + goto DivertDriverFilesError; + } + wcscpy(divert_dll, divert_dir); + divert_dll[length] = L'\\'; + wcscpy(divert_dll + length + 1, find_data.cFileName); + + *divert_dir_ptr = divert_dir; + *divert_sys_ptr = divert_sys; + *divert_inf_ptr = divert_inf; + *divert_dll_ptr = divert_dll; + return TRUE; + +DivertDriverFilesError: + free(divert_dir); + free(divert_sys); + free(divert_inf); + free(divert_dll); + return FALSE; +} + +/* + * Install the Divert driver. + */ +static BOOLEAN DivertDriverInstall(VOID) +{ + static BOOLEAN installed = FALSE; + DWORD err; + SC_HANDLE manager = NULL, service = NULL; + WDF_COINSTALLER_INSTALL_OPTIONS client_options; + LPWSTR divert_dir = NULL, divert_sys = NULL, divert_inf = NULL, + divert_dll = NULL; + HMODULE library = NULL; + + // Do nothing if the driver is already installed: + if (installed) + { + return TRUE; + } + + // Open the service manager: + manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); + if (manager == NULL) + { + goto DivertDriverInstallExit; + } + + // Check if the divert service already exists; if so, start it. + service = OpenService(manager, DIVERT_DRIVER_NAME, SERVICE_ALL_ACCESS); + if (service != NULL) + { + if (!StartService(service, 0, NULL)) + { + err = GetLastError(); + installed = (err == ERROR_SERVICE_ALREADY_RUNNING); + goto DivertDriverInstallExit; + } + installed = TRUE; + goto DivertDriverInstallExit; + } + + // Get driver files: + if (!DivertDriverFiles(&divert_dir, &divert_sys, &divert_inf, &divert_dll)) + { + return FALSE; + } + + // Load the co-installer: + if (DivertLoadCoInstaller(divert_dll) == NULL) + { + return FALSE; + } + + // Pre-install: + WDF_COINSTALLER_INSTALL_OPTIONS_INIT(&client_options); + err = pfnWdfPreDeviceInstallEx(divert_inf, L"divert.NT.Wdf", + &client_options); + if (err != ERROR_SUCCESS) + { + SetLastError(err); + goto DivertDriverInstallExit; + } + + // Create the service: + service = CreateService(manager, DIVERT_DRIVER_NAME, DIVERT_DRIVER_NAME, + SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, + SERVICE_ERROR_NORMAL, divert_sys, NULL, NULL, NULL, NULL, NULL); + if (service == NULL && GetLastError() != ERROR_SERVICE_EXISTS) + { + goto DivertDriverInstallExit; + } + + // Post-install: + err = pfnWdfPostDeviceInstall(divert_inf, NULL); + if (err != ERROR_SUCCESS) + { + SetLastError(err); + goto DivertDriverInstallExit; + } + FreeLibrary(library); + library = NULL; + + // Start the service: + if (!StartService(service, 0, NULL)) + { + err = GetLastError(); + installed = (err == ERROR_SERVICE_ALREADY_RUNNING); + goto DivertDriverInstallExit; + } + + installed = TRUE; + +DivertDriverInstallExit: + free(divert_dir); + free(divert_sys); + free(divert_inf); + free(divert_dll); + if (service != NULL) + { + CloseServiceHandle(service); + } + if (manager != NULL) + { + CloseServiceHandle(manager); + } + if (library != NULL) + { + FreeLibrary(library); + } + return installed; +} + +/* + * Open a handle to the Divert device. + */ +extern HANDLE DivertOpen(const char *filter) +{ + struct + { + struct divert_message_s header; + struct divert_ioctl_filter_s filter[DIVERT_FILTER_MAXLEN]; + } ioctl; + UINT8 filter_len; + DWORD err, iolen; + HANDLE handle; + + // Parse the filter: + if (!DivertCompileFilter(filter, ioctl.filter, &filter_len)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return INVALID_HANDLE_VALUE; + } + +#ifdef DIVERT_DEBUG + DivertFilterDump(ioctl.filter, filter_len); +#endif + + // Attempt to open the Divert device: + handle = CreateFile(L"\\\\.\\Divert", GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE); + if (handle == INVALID_HANDLE_VALUE) + { + err = GetLastError(); + if (err != ERROR_FILE_NOT_FOUND && err != ERROR_PATH_NOT_FOUND) + { + return INVALID_HANDLE_VALUE; + } + + // Open failed because the device isn't installed; install it now. + if (!DivertDriverInstall()) + { + SetLastError(ERROR_OPEN_FAILED); + return INVALID_HANDLE_VALUE; + } + handle = CreateFile(L"\\\\.\\Divert", GENERIC_READ | GENERIC_WRITE, 0, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE); + if (handle == INVALID_HANDLE_VALUE) + { + return INVALID_HANDLE_VALUE; + } + } + + // Set the filter: + ioctl.header.version = DIVERT_VERSION; + ioctl.header.magic = DIVERT_MAGIC; + ioctl.header.reserved = 0x0; + if (!DeviceIoControl(handle, IOCTL_DIVERT_SET_FILTER, &ioctl, + sizeof(struct divert_message_s) + + filter_len*sizeof(struct divert_ioctl_filter_s), NULL, 0, &iolen, + NULL)) + { + CloseHandle(handle); + return INVALID_HANDLE_VALUE; + } + + // Success! + return handle; +} + +/* + * Receive a packet from the Divert device. + */ +extern BOOL DivertRecv(HANDLE handle, PDIVERT_PACKET pPacket, UINT packetLen, + UINT *readlen) +{ + divert_message_t message; + DWORD readlen0; + + if (!ReadFile(handle, (PVOID)pPacket, (DWORD)packetLen, &readlen0, NULL)) + { + return FALSE; + } + if (readlen0 <= sizeof(DIVERT_PACKET)) + { + SetLastError(ERROR_INVALID_DATA); + return FALSE; + } + message = (divert_message_t)pPacket->Reserved; + if (message->magic != DIVERT_MAGIC || + message->version != DIVERT_VERSION) + { + SetLastError(ERROR_INVALID_DATA); + return FALSE; + } + if (readlen != NULL) + { + *readlen = readlen0; + } + return TRUE; +} + +/* + * Send (inject) a packet to the Divert device. + */ +extern BOOL DivertSend(HANDLE handle, PDIVERT_PACKET pPacket, UINT packetLen, + UINT *writelen) +{ + divert_message_t message; + DWORD writelen0; + + if (packetLen <= sizeof(DIVERT_PACKET)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + message = (divert_message_t)pPacket->Reserved; + message->magic = DIVERT_MAGIC; + message->version = DIVERT_VERSION; + message->reserved = 0x0; + if (writelen == NULL) + { + writelen = &writelen0; + } + return WriteFile(handle, (PVOID)pPacket, (DWORD)packetLen, + (DWORD *)writelen, NULL); +} + +/* + * Close a handle to the Divert device. + */ +extern BOOL DivertClose(HANDLE handle) +{ + return CloseHandle(handle); +} + +/* + * Compile a filter. + */ +static BOOL DivertCompileFilter(const char *filter_str, + divert_ioctl_filter_t filter, UINT8 *fp) +{ + FILTER_TOKEN tokens[DIVERT_FILTER_MAXLEN*3]; + UINT8 tp; + + if (!DivertTokenizeFilter(filter_str, tokens, DIVERT_FILTER_MAXLEN*3-1)) + { + return FALSE; + } + + tp = 0; + *fp = 0; + if (!DivertParseFilter(tokens, &tp, filter, fp, FILTER_TOKEN_AND)) + { + return FALSE; + } + if (tokens[tp].kind != FILTER_TOKEN_END) + { + return FALSE; + } + return TRUE; +} + +/* + * Compare two FILTER_TOKEN_NAMEs. + */ +static int DivertFilterTokenNameCompare(const void *a, const void *b) +{ + PFILTER_TOKEN_NAME na = (PFILTER_TOKEN_NAME)a; + PFILTER_TOKEN_NAME nb = (PFILTER_TOKEN_NAME)b; + return strcmp(na->name, nb->name); +} + +/* + * Tokenize the given filter string. + */ +static BOOL DivertTokenizeFilter(const char *filter, FILTER_TOKEN *tokens, + UINT8 tokensmax) +{ + static const FILTER_TOKEN_NAME token_names[] = + { + {"and", FILTER_TOKEN_AND}, + {"false", FILTER_TOKEN_FALSE}, + {"icmp", FILTER_TOKEN_ICMP}, + {"icmp.Body", FILTER_TOKEN_ICMP_BODY}, + {"icmp.Checksum", FILTER_TOKEN_ICMP_CHECKSUM}, + {"icmp.Code", FILTER_TOKEN_ICMP_CODE}, + {"icmp.Type", FILTER_TOKEN_ICMP_TYPE}, + {"icmpv6", FILTER_TOKEN_ICMPV6}, + {"icmpv6.Body", FILTER_TOKEN_ICMPV6_BODY}, + {"icmpv6.Checksum", FILTER_TOKEN_ICMPV6_CHECKSUM}, + {"icmpv6.Code", FILTER_TOKEN_ICMPV6_CODE}, + {"icmpv6.Type", FILTER_TOKEN_ICMPV6_TYPE}, + {"ifIdx", FILTER_TOKEN_IF_IDX}, + {"inbound", FILTER_TOKEN_INBOUND}, + {"ip", FILTER_TOKEN_IP}, + {"ip.Checksum", FILTER_TOKEN_IP_CHECKSUM}, + {"ip.DF", FILTER_TOKEN_IP_DF}, + {"ip.DstAddr", FILTER_TOKEN_IP_DST_ADDR}, + {"ip.FragOff", FILTER_TOKEN_IP_FRAG_OFF}, + {"ip.HdrLength", FILTER_TOKEN_IP_HDR_LENGTH}, + {"ip.Id", FILTER_TOKEN_IP_ID}, + {"ip.Length", FILTER_TOKEN_IP_LENGTH}, + {"ip.MF", FILTER_TOKEN_IP_MF}, + {"ip.Protocol", FILTER_TOKEN_IP_PROTOCOL}, + {"ip.SrcAddr", FILTER_TOKEN_IP_SRC_ADDR}, + {"ip.TOS", FILTER_TOKEN_IP_TOS}, + {"ip.TTL", FILTER_TOKEN_IP_TTL}, + {"ipv6", FILTER_TOKEN_IPV6}, + {"ipv6.DstAddr", FILTER_TOKEN_IPV6_DST_ADDR}, + {"ipv6.FlowLabel", FILTER_TOKEN_IPV6_FLOW_LABEL}, + {"ipv6.HopLimit", FILTER_TOKEN_IPV6_HOP_LIMIT}, + {"ipv6.Length", FILTER_TOKEN_IPV6_LENGTH}, + {"ipv6.NextHdr", FILTER_TOKEN_IPV6_NEXT_HDR}, + {"ipv6.SrcAddr", FILTER_TOKEN_IPV6_SRC_ADDR}, + {"ipv6.TrafficClass", FILTER_TOKEN_IPV6_TRAFFIC_CLASS}, + {"not", FILTER_TOKEN_NOT}, + {"or", FILTER_TOKEN_OR}, + {"outbound", FILTER_TOKEN_OUTBOUND}, + {"subIfIdx", FILTER_TOKEN_SUB_IF_IDX}, + {"tcp", FILTER_TOKEN_TCP}, + {"tcp.Ack", FILTER_TOKEN_TCP_ACK}, + {"tcp.AckNum", FILTER_TOKEN_TCP_ACK_NUM}, + {"tcp.Checksum", FILTER_TOKEN_TCP_CHECKSUM}, + {"tcp.DstPort", FILTER_TOKEN_TCP_DST_PORT}, + {"tcp.Fin", FILTER_TOKEN_TCP_FIN}, + {"tcp.HdrLength", FILTER_TOKEN_TCP_HDR_LENGTH}, + {"tcp.PayloadLength", FILTER_TOKEN_TCP_PAYLOAD_LENGTH}, + {"tcp.Psh", FILTER_TOKEN_TCP_PSH}, + {"tcp.Rst", FILTER_TOKEN_TCP_RST}, + {"tcp.SeqNum", FILTER_TOKEN_TCP_SEQ_NUM}, + {"tcp.SrcPort", FILTER_TOKEN_TCP_SRC_PORT}, + {"tcp.Syn", FILTER_TOKEN_TCP_SYN}, + {"tcp.Urg", FILTER_TOKEN_TCP_URG}, + {"tcp.UrgPtr", FILTER_TOKEN_TCP_URG_PTR}, + {"tcp.Window", FILTER_TOKEN_TCP_WINDOW}, + {"true", FILTER_TOKEN_TRUE}, + {"udp", FILTER_TOKEN_UDP}, + {"udp.Checksum", FILTER_TOKEN_UDP_CHECKSUM}, + {"udp.DstPort", FILTER_TOKEN_UDP_DST_PORT}, + {"udp.Length", FILTER_TOKEN_UDP_LENGTH}, + {"udp.PayloadLength", FILTER_TOKEN_UDP_PAYLOAD_LENGTH}, + {"udp.SrcPort", FILTER_TOKEN_UDP_SRC_PORT}, + }; + FILTER_TOKEN_NAME key, *result; + char c; + char token[FILTER_TOKEN_MAXLEN]; + UINT i = 0, j; + UINT8 tp = 0; + + while (TRUE) + { + if (tp >= tokensmax-1) + { + return FALSE; + } + memset(tokens[tp].val, 0, sizeof(tokens[tp].val)); + while (isspace(filter[i])) + { + i++; + } + c = filter[i++]; + switch (c) + { + case '\0': + tokens[tp].kind = FILTER_TOKEN_END; + return TRUE; + case '(': + tokens[tp++].kind = FILTER_TOKEN_OPEN; + continue; + case ')': + tokens[tp++].kind = FILTER_TOKEN_CLOSE; + continue; + case '!': + if (filter[i] == '=') + { + i++; + tokens[tp++].kind = FILTER_TOKEN_NEQ; + } + else + { + tokens[tp++].kind = FILTER_TOKEN_NOT; + } + continue; + case '=': + if (filter[i] == '=') + { + i++; + } + tokens[tp++].kind = FILTER_TOKEN_EQ; + continue; + case '<': + if (filter[i] == '=') + { + i++; + tokens[tp++].kind = FILTER_TOKEN_LEQ; + } + else + { + tokens[tp++].kind = FILTER_TOKEN_LT; + } + continue; + case '>': + if (filter[i] == '=') + { + i++; + tokens[tp++].kind = FILTER_TOKEN_GEQ; + } + else + { + tokens[tp++].kind = FILTER_TOKEN_GT; + } + continue; + case '&': + if (filter[i++] != '&') + { + return FALSE; + } + tokens[tp++].kind = FILTER_TOKEN_AND; + continue; + case '|': + if (filter[i++] != '|') + { + return FALSE; + } + tokens[tp++].kind = FILTER_TOKEN_OR; + continue; + default: + break; + } + token[0] = c; + if (isalnum(c) || c == '.' || c == ':') + { + UINT32 num; + char *end; + for (j = 1; j < FILTER_TOKEN_MAXLEN && (isalnum(filter[i]) || + filter[i] == '.' || filter[i] == ':'); j++, i++) + { + token[j] = filter[i]; + } + if (j >= FILTER_TOKEN_MAXLEN) + { + return FALSE; + } + token[j] = '\0'; + + // Check for symbol: + key.name = token; + result = (PFILTER_TOKEN_NAME)bsearch((const void *)&key, + token_names, sizeof(token_names) / sizeof(FILTER_TOKEN_NAME), + sizeof(FILTER_TOKEN_NAME), DivertFilterTokenNameCompare); + if (result != NULL) + { + tokens[tp++].kind = result->kind; + continue; + } + + // Check for base 10 number: + errno = 0; + num = strtoul(token, &end, 10); + if (errno == 0 && *end == '\0') + { + tokens[tp].kind = FILTER_TOKEN_NUMBER; + tokens[tp].val[0] = num; + tp++; + continue; + } + + // Check for base 16 number: + errno = 0; + num = strtoul(token, &end, 16); + if (errno == 0 && *end == '\0') + { + tokens[tp].kind = FILTER_TOKEN_NUMBER; + tokens[tp].val[0] = num; + tp++; + continue; + } + + // Check for IPv4 address: + if (DivertParseIPv4Address(token, tokens[tp].val)) + { + tokens[tp].kind = FILTER_TOKEN_NUMBER; + tp++; + continue; + } + + // Check for IPv6 address: + if (DivertParseIPv6Address(token, tokens[tp].val)) + { + tokens[tp].kind = FILTER_TOKEN_NUMBER; + tp++; + continue; + } + + return FALSE; + } + else + { + return FALSE; + } + } +} + +/* + * Parse an IPv4 address. + */ +static BOOL DivertParseIPv4Address(char *str, UINT32 *addr_ptr) +{ + UINT32 addr = 0; + UINT part, i; + + errno = 0; + for (i = 0; i < 4; i++) + { + part = strtoul(str, &str, 10); + if (errno != 0 || part > UINT8_MAX) + { + return FALSE; + } + if (i != 3 && *str++ != '.') + { + return FALSE; + } + addr |= part << (8*(3-i)); + } + *addr_ptr = addr; + return TRUE; +} + +/* + * Parse an IPv6 address. + */ +static BOOL DivertParseIPv6Address(char *str, UINT32 *addr_ptr) +{ + UINT16 addr[8] = {0}; + UINT part; + UINT i, j; + BOOL end = FALSE; + + if (*str == ':') + { + str++; + } + + for (i = 0, j = 7; i < 8; i++) + { + if (*str == ':') + { + if (end) + { + return FALSE; + } + end = TRUE; + str++; + } + errno = 0; + part = strtoul(str, &str, 16); + if (part > UINT32_MAX || errno != 0 || (*str != ':' && *str != '\0')) + { + return FALSE; + } + if (!end) + { + addr[7 - i] = (UINT16)part; + } + else + { + addr[7 - j--] = (UINT16)part; + } + if (*str == '\0') + { + if (!end) + { + return FALSE; + } + break; + } + str++; + } + + if (*str != '\0') + { + return FALSE; + } + + if (end) + { + j++; + for (i = 7; j < i; j++, i--) + { + UINT16 tmp = addr[7 - i]; + addr[7 - i] = addr[7 - j]; + addr[7 - j] = tmp; + } + } + memcpy(addr_ptr, addr, sizeof(addr)); + + return TRUE; +} + +/* + * Parse the given filter. + */ +static BOOL DivertParseFilter(FILTER_TOKEN *tokens, UINT8 *tp, + divert_ioctl_filter_t filter, UINT8 *fp, FILTER_TOKEN_KIND op) +{ + BOOL testop, fused, result, negate; + FILTER_TOKEN token; + UINT8 t, f, s, tmp; + s = *fp; + +DivertParseFilterNext: + + testop = TRUE; + fused = TRUE; + negate = FALSE; + token = tokens[*tp]; + + *tp = *tp + 1; + f = *fp; + if (f >= DIVERT_FILTER_MAXLEN) + { + return FALSE; + } + filter[f].success = DIVERT_FILTER_RESULT_ACCEPT; + filter[f].failure = DIVERT_FILTER_RESULT_REJECT; + filter[f].arg[1] = 0; + filter[f].arg[2] = 0; + filter[f].arg[3] = 0; + if (token.kind == FILTER_TOKEN_NOT) + { + negate = TRUE; + token = tokens[*tp]; + *tp = *tp + 1; + } + switch (token.kind) + { + case FILTER_TOKEN_OPEN: + result = DivertParseFilter(tokens, tp, filter, fp, + FILTER_TOKEN_AND); + result = (result? (tokens[*tp].kind == FILTER_TOKEN_CLOSE): FALSE); + if (!result) + { + return FALSE; + } + *tp = *tp + 1; + testop = FALSE; + fused = FALSE; + break; + case FILTER_TOKEN_TRUE: case FILTER_TOKEN_FALSE: + filter[f].field = DIVERT_FILTER_FIELD_ZERO; + filter[f].test = DIVERT_FILTER_TEST_EQ; + filter[f].arg[0] = (token.kind == FILTER_TOKEN_FALSE); + testop = FALSE; + break; + case FILTER_TOKEN_OUTBOUND: + filter[f].field = DIVERT_FILTER_FIELD_OUTBOUND; + break; + case FILTER_TOKEN_INBOUND: + filter[f].field = DIVERT_FILTER_FIELD_INBOUND; + break; + case FILTER_TOKEN_IF_IDX: + filter[f].field = DIVERT_FILTER_FIELD_IFIDX; + break; + case FILTER_TOKEN_SUB_IF_IDX: + filter[f].field = DIVERT_FILTER_FIELD_SUBIFIDX; + break; + case FILTER_TOKEN_IP: + filter[f].field = DIVERT_FILTER_FIELD_IP; + break; + case FILTER_TOKEN_IPV6: + filter[f].field = DIVERT_FILTER_FIELD_IPV6; + break; + case FILTER_TOKEN_ICMP: + filter[f].field = DIVERT_FILTER_FIELD_ICMP; + break; + case FILTER_TOKEN_ICMPV6: + filter[f].field = DIVERT_FILTER_FIELD_ICMPV6; + break; + case FILTER_TOKEN_TCP: + filter[f].field = DIVERT_FILTER_FIELD_TCP; + break; + case FILTER_TOKEN_UDP: + filter[f].field = DIVERT_FILTER_FIELD_UDP; + break; + case FILTER_TOKEN_IP_HDR_LENGTH: + filter[f].field = DIVERT_FILTER_FIELD_IP_HDRLENGTH; + break; + case FILTER_TOKEN_IP_TOS: + filter[f].field = DIVERT_FILTER_FIELD_IP_TOS; + break; + case FILTER_TOKEN_IP_LENGTH: + filter[f].field = DIVERT_FILTER_FIELD_IP_LENGTH; + break; + case FILTER_TOKEN_IP_ID: + filter[f].field = DIVERT_FILTER_FIELD_IP_ID; + break; + case FILTER_TOKEN_IP_DF: + filter[f].field = DIVERT_FILTER_FIELD_IP_DF; + break; + case FILTER_TOKEN_IP_MF: + filter[f].field = DIVERT_FILTER_FIELD_IP_MF; + break; + case FILTER_TOKEN_IP_FRAG_OFF: + filter[f].field = DIVERT_FILTER_FIELD_IP_FRAGOFF; + break; + case FILTER_TOKEN_IP_TTL: + filter[f].field = DIVERT_FILTER_FIELD_IP_TTL; + break; + case FILTER_TOKEN_IP_PROTOCOL: + filter[f].field = DIVERT_FILTER_FIELD_IP_PROTOCOL; + break; + case FILTER_TOKEN_IP_CHECKSUM: + filter[f].field = DIVERT_FILTER_FIELD_IP_CHECKSUM; + break; + case FILTER_TOKEN_IP_SRC_ADDR: + filter[f].field = DIVERT_FILTER_FIELD_IP_SRCADDR; + break; + case FILTER_TOKEN_IP_DST_ADDR: + filter[f].field = DIVERT_FILTER_FIELD_IP_DSTADDR; + break; + case FILTER_TOKEN_IPV6_TRAFFIC_CLASS: + filter[f].field = DIVERT_FILTER_FIELD_IPV6_TRAFFICCLASS; + break; + case FILTER_TOKEN_IPV6_FLOW_LABEL: + filter[f].field = DIVERT_FILTER_FIELD_IPV6_FLOWLABEL; + break; + case FILTER_TOKEN_IPV6_LENGTH: + filter[f].field = DIVERT_FILTER_FIELD_IPV6_LENGTH; + break; + case FILTER_TOKEN_IPV6_NEXT_HDR: + filter[f].field = DIVERT_FILTER_FIELD_IPV6_NEXTHDR; + break; + case FILTER_TOKEN_IPV6_HOP_LIMIT: + filter[f].field = DIVERT_FILTER_FIELD_IPV6_HOPLIMIT; + break; + case FILTER_TOKEN_IPV6_SRC_ADDR: + filter[f].field = DIVERT_FILTER_FIELD_IPV6_SRCADDR; + break; + case FILTER_TOKEN_IPV6_DST_ADDR: + filter[f].field = DIVERT_FILTER_FIELD_IPV6_DSTADDR; + break; + case FILTER_TOKEN_ICMP_TYPE: + filter[f].field = DIVERT_FILTER_FIELD_ICMP_TYPE; + break; + case FILTER_TOKEN_ICMP_CODE: + filter[f].field = DIVERT_FILTER_FIELD_ICMP_CODE; + break; + case FILTER_TOKEN_ICMP_CHECKSUM: + filter[f].field = DIVERT_FILTER_FIELD_ICMP_CHECKSUM; + break; + case FILTER_TOKEN_ICMP_BODY: + filter[f].field = DIVERT_FILTER_FIELD_ICMP_BODY; + break; + case FILTER_TOKEN_ICMPV6_TYPE: + filter[f].field = DIVERT_FILTER_FIELD_ICMPV6_TYPE; + break; + case FILTER_TOKEN_ICMPV6_CODE: + filter[f].field = DIVERT_FILTER_FIELD_ICMPV6_CODE; + break; + case FILTER_TOKEN_ICMPV6_CHECKSUM: + filter[f].field = DIVERT_FILTER_FIELD_ICMPV6_CHECKSUM; + break; + case FILTER_TOKEN_ICMPV6_BODY: + filter[f].field = DIVERT_FILTER_FIELD_ICMPV6_BODY; + break; + case FILTER_TOKEN_TCP_SRC_PORT: + filter[f].field = DIVERT_FILTER_FIELD_TCP_SRCPORT; + break; + case FILTER_TOKEN_TCP_DST_PORT: + filter[f].field = DIVERT_FILTER_FIELD_TCP_DSTPORT; + break; + case FILTER_TOKEN_TCP_SEQ_NUM: + filter[f].field = DIVERT_FILTER_FIELD_TCP_SEQNUM; + break; + case FILTER_TOKEN_TCP_ACK_NUM: + filter[f].field = DIVERT_FILTER_FIELD_TCP_ACKNUM; + break; + case FILTER_TOKEN_TCP_HDR_LENGTH: + filter[f].field = DIVERT_FILTER_FIELD_TCP_HDRLENGTH; + break; + case FILTER_TOKEN_TCP_URG: + filter[f].field = DIVERT_FILTER_FIELD_TCP_URG; + break; + case FILTER_TOKEN_TCP_ACK: + filter[f].field = DIVERT_FILTER_FIELD_TCP_ACK; + break; + case FILTER_TOKEN_TCP_PSH: + filter[f].field = DIVERT_FILTER_FIELD_TCP_PSH; + break; + case FILTER_TOKEN_TCP_RST: + filter[f].field = DIVERT_FILTER_FIELD_TCP_RST; + break; + case FILTER_TOKEN_TCP_SYN: + filter[f].field = DIVERT_FILTER_FIELD_TCP_SYN; + break; + case FILTER_TOKEN_TCP_FIN: + filter[f].field = DIVERT_FILTER_FIELD_TCP_FIN; + break; + case FILTER_TOKEN_TCP_WINDOW: + filter[f].field = DIVERT_FILTER_FIELD_TCP_WINDOW; + break; + case FILTER_TOKEN_TCP_CHECKSUM: + filter[f].field = DIVERT_FILTER_FIELD_TCP_CHECKSUM; + break; + case FILTER_TOKEN_TCP_URG_PTR: + filter[f].field = DIVERT_FILTER_FIELD_TCP_URGPTR; + break; + case FILTER_TOKEN_TCP_PAYLOAD_LENGTH: + filter[f].field = DIVERT_FILTER_FIELD_TCP_PAYLOADLENGTH; + break; + case FILTER_TOKEN_UDP_SRC_PORT: + filter[f].field = DIVERT_FILTER_FIELD_UDP_SRCPORT; + break; + case FILTER_TOKEN_UDP_DST_PORT: + filter[f].field = DIVERT_FILTER_FIELD_UDP_DSTPORT; + break; + case FILTER_TOKEN_UDP_LENGTH: + filter[f].field = DIVERT_FILTER_FIELD_UDP_LENGTH; + break; + case FILTER_TOKEN_UDP_CHECKSUM: + filter[f].field = DIVERT_FILTER_FIELD_UDP_CHECKSUM; + break; + case FILTER_TOKEN_UDP_PAYLOAD_LENGTH: + filter[f].field = DIVERT_FILTER_FIELD_UDP_PAYLOADLENGTH; + break; + default: + return FALSE; + } + + if (fused) + { + *fp = f+1; + } + + if (testop) + { + token = tokens[*tp]; + if (!negate) + { + switch (token.kind) + { + case FILTER_TOKEN_EQ: + filter[f].test = DIVERT_FILTER_TEST_EQ; + break; + case FILTER_TOKEN_NEQ: + filter[f].test = DIVERT_FILTER_TEST_NEQ; + break; + case FILTER_TOKEN_LT: + filter[f].test = DIVERT_FILTER_TEST_LT; + break; + case FILTER_TOKEN_LEQ: + filter[f].test = DIVERT_FILTER_TEST_LEQ; + break; + case FILTER_TOKEN_GT: + filter[f].test = DIVERT_FILTER_TEST_GT; + break; + case FILTER_TOKEN_GEQ: + filter[f].test = DIVERT_FILTER_TEST_GEQ; + break; + default: + filter[f].test = DIVERT_FILTER_TEST_NEQ; + filter[f].arg[0] = 0; + testop = FALSE; + break; + } + } + else + { + switch (token.kind) + { + case FILTER_TOKEN_EQ: + filter[f].test = DIVERT_FILTER_TEST_NEQ; + break; + case FILTER_TOKEN_NEQ: + filter[f].test = DIVERT_FILTER_TEST_EQ; + break; + case FILTER_TOKEN_LT: + filter[f].test = DIVERT_FILTER_TEST_GEQ; + break; + case FILTER_TOKEN_LEQ: + filter[f].test = DIVERT_FILTER_TEST_GT; + break; + case FILTER_TOKEN_GT: + filter[f].test = DIVERT_FILTER_TEST_LEQ; + break; + case FILTER_TOKEN_GEQ: + filter[f].test = DIVERT_FILTER_TEST_LT; + break; + default: + filter[f].test = DIVERT_FILTER_TEST_EQ; + filter[f].arg[0] = 0; + testop = FALSE; + break; + } + } + + if (testop) + { + *tp = *tp + 1; + token = tokens[*tp]; + *tp = *tp + 1; + if (token.kind != FILTER_TOKEN_NUMBER) + { + return FALSE; + } + filter[f].arg[0] = token.val[0]; + filter[f].arg[1] = token.val[1]; + filter[f].arg[2] = token.val[2]; + filter[f].arg[3] = token.val[3]; + } + } + + token = tokens[*tp]; + if (token.kind != FILTER_TOKEN_AND && token.kind != FILTER_TOKEN_OR) + { + return TRUE; + } + if (op < token.kind) + { + op = token.kind; + f = s; + } + *tp = *tp + 1; + switch (token.kind) + { + case FILTER_TOKEN_AND: + DivertFilterUpdate(filter, f, *fp, *fp, + DIVERT_FILTER_RESULT_REJECT); + goto DivertParseFilterNext; + case FILTER_TOKEN_OR: + DivertFilterUpdate(filter, f, *fp, DIVERT_FILTER_RESULT_ACCEPT, + *fp); + goto DivertParseFilterNext; + } + return TRUE; +} + +/* + * Negate a filter. + */ +static void DivertFilterNegate(divert_ioctl_filter_t filter, UINT8 s, UINT8 e) +{ + // This is easy; simple swap REJECTs and ACCEPTs + DivertFilterUpdate(filter, s, e, DIVERT_FILTER_RESULT_REJECT, + DIVERT_FILTER_RESULT_ACCEPT); +} + +/* + * Update success. + */ +static void DivertFilterUpdate(divert_ioctl_filter_t filter, UINT8 s, UINT8 e, + UINT8 success, UINT8 failure) +{ + UINT8 i; + + for (i = s; i < e; i++) + { + switch (filter[i].success) + { + case DIVERT_FILTER_RESULT_ACCEPT: + filter[i].success = success; + break; + case DIVERT_FILTER_RESULT_REJECT: + filter[i].success = failure; + break; + } + switch (filter[i].failure) + { + case DIVERT_FILTER_RESULT_ACCEPT: + filter[i].failure = success; + break; + case DIVERT_FILTER_RESULT_REJECT: + filter[i].failure = failure; + break; + } + } +} + +#ifdef DIVERT_DEBUG +/* + * Print a filter (debugging). + */ +static void DivertFilterDump(divert_ioctl_filter_t filter, UINT8 len) +{ + UINT8 i; + + for (i = 0; i < len; i++) + { + printf("label_%u:\n\tif (", i); + switch (filter[i].field) + { + case DIVERT_FILTER_FIELD_ZERO: + printf("zero "); + break; + case DIVERT_FILTER_FIELD_INBOUND: + printf("inbound "); + break; + case DIVERT_FILTER_FIELD_OUTBOUND: + printf("outbound "); + break; + case DIVERT_FILTER_FIELD_IFIDX: + printf("ifIdx "); + break; + case DIVERT_FILTER_FIELD_SUBIFIDX: + printf("subIfIdx "); + break; + case DIVERT_FILTER_FIELD_IP: + printf("ip "); + break; + case DIVERT_FILTER_FIELD_IPV6: + printf("ipv6 "); + break; + case DIVERT_FILTER_FIELD_ICMP: + printf("icmp "); + break; + case DIVERT_FILTER_FIELD_ICMPV6: + printf("icmpv6 "); + break; + case DIVERT_FILTER_FIELD_TCP: + printf("tcp "); + break; + case DIVERT_FILTER_FIELD_UDP: + printf("udp "); + break; + case DIVERT_FILTER_FIELD_IP_HDRLENGTH: + printf("ip.HdrLength "); + break; + case DIVERT_FILTER_FIELD_IP_TOS: + printf("ip.TOS "); + break; + case DIVERT_FILTER_FIELD_IP_LENGTH: + printf("ip.Length "); + break; + case DIVERT_FILTER_FIELD_IP_ID: + printf("ip.Id "); + break; + case DIVERT_FILTER_FIELD_IP_DF: + printf("ip.DF "); + break; + case DIVERT_FILTER_FIELD_IP_MF: + printf("ip.MF "); + break; + case DIVERT_FILTER_FIELD_IP_FRAGOFF: + printf("ip.FragOff "); + break; + case DIVERT_FILTER_FIELD_IP_TTL: + printf("ip.TTL "); + break; + case DIVERT_FILTER_FIELD_IP_PROTOCOL: + printf("ip.Protocol "); + break; + case DIVERT_FILTER_FIELD_IP_CHECKSUM: + printf("ip.Checksum "); + break; + case DIVERT_FILTER_FIELD_IP_SRCADDR: + printf("ip.SrcAddr "); + break; + case DIVERT_FILTER_FIELD_IP_DSTADDR: + printf("ip.DstAddr "); + break; + case DIVERT_FILTER_FIELD_IPV6_TRAFFICCLASS: + printf("ipv6.TrafficClass "); + break; + case DIVERT_FILTER_FIELD_IPV6_FLOWLABEL: + printf("ipv6.FlowLabel "); + break; + case DIVERT_FILTER_FIELD_IPV6_LENGTH: + printf("ipv6.Length "); + break; + case DIVERT_FILTER_FIELD_IPV6_NEXTHDR: + printf("ipv6.NextHdr "); + break; + case DIVERT_FILTER_FIELD_IPV6_HOPLIMIT: + printf("ipv6.HopLimit "); + break; + case DIVERT_FILTER_FIELD_IPV6_SRCADDR: + printf("ipv6.SrcAddr "); + break; + case DIVERT_FILTER_FIELD_IPV6_DSTADDR: + printf("ipv6.DstAddr "); + break; + case DIVERT_FILTER_FIELD_ICMP_TYPE: + printf("icmp.Type "); + break; + case DIVERT_FILTER_FIELD_ICMP_CODE: + printf("icmp.Code "); + break; + case DIVERT_FILTER_FIELD_ICMP_CHECKSUM: + printf("icmp.Checksum "); + break; + case DIVERT_FILTER_FIELD_ICMP_BODY: + printf("icmp.Body "); + break; + case DIVERT_FILTER_FIELD_ICMPV6_TYPE: + printf("icmpv6.Type "); + break; + case DIVERT_FILTER_FIELD_ICMPV6_CODE: + printf("icmpv6.Code "); + break; + case DIVERT_FILTER_FIELD_ICMPV6_CHECKSUM: + printf("icmpv6.Checksum "); + break; + case DIVERT_FILTER_FIELD_ICMPV6_BODY: + printf("icmpv6.Body "); + break; + case DIVERT_FILTER_FIELD_TCP_SRCPORT: + printf("tcp.SrcPort "); + break; + case DIVERT_FILTER_FIELD_TCP_DSTPORT: + printf("tcp.DstPort "); + break; + case DIVERT_FILTER_FIELD_TCP_SEQNUM: + printf("tcp.SeqNum "); + break; + case DIVERT_FILTER_FIELD_TCP_ACKNUM: + printf("tcp.AckNum "); + break; + case DIVERT_FILTER_FIELD_TCP_HDRLENGTH: + printf("tcp.HdrLength "); + break; + case DIVERT_FILTER_FIELD_TCP_URG: + printf("tcp.Urg "); + break; + case DIVERT_FILTER_FIELD_TCP_ACK: + printf("tcp.Ack "); + break; + case DIVERT_FILTER_FIELD_TCP_PSH: + printf("tcp.Psh "); + break; + case DIVERT_FILTER_FIELD_TCP_RST: + printf("tcp.Rst "); + break; + case DIVERT_FILTER_FIELD_TCP_SYN: + printf("tcp.Syn "); + break; + case DIVERT_FILTER_FIELD_TCP_FIN: + printf("tcp.Fin "); + break; + case DIVERT_FILTER_FIELD_TCP_WINDOW: + printf("tcp.Window "); + break; + case DIVERT_FILTER_FIELD_TCP_CHECKSUM: + printf("tcp.Checksum "); + break; + case DIVERT_FILTER_FIELD_TCP_URGPTR: + printf("tcp.UrgPtr "); + break; + case DIVERT_FILTER_FIELD_TCP_PAYLOADLENGTH: + printf("tcp.PayloadLength " ); + break; + case DIVERT_FILTER_FIELD_UDP_SRCPORT: + printf("udp.SrcPort "); + break; + case DIVERT_FILTER_FIELD_UDP_DSTPORT: + printf("udp.DstPort "); + break; + case DIVERT_FILTER_FIELD_UDP_LENGTH: + printf("udp.Length "); + break; + case DIVERT_FILTER_FIELD_UDP_CHECKSUM: + printf("udp.Checksum "); + break; + case DIVERT_FILTER_FIELD_UDP_PAYLOADLENGTH: + printf("udp.PayloadLength "); + break; + default: + printf("unknown.Field "); + break; + } + switch (filter[i].test) + { + case DIVERT_FILTER_TEST_EQ: + printf("== "); + break; + case DIVERT_FILTER_TEST_NEQ: + printf("!= "); + break; + case DIVERT_FILTER_TEST_LT: + printf("< "); + break; + case DIVERT_FILTER_TEST_LEQ: + printf("<= "); + break; + case DIVERT_FILTER_TEST_GT: + printf("> "); + break; + case DIVERT_FILTER_TEST_GEQ: + printf(">= "); + break; + default: + printf("?? "); + break; + } + printf("%u)\n", filter[i].arg[0]); + switch (filter[i].success) + { + case DIVERT_FILTER_RESULT_ACCEPT: + printf("\t\treturn ACCEPT;\n"); + break; + case DIVERT_FILTER_RESULT_REJECT: + printf("\t\treturn REJECT;\n"); + break; + default: + printf("\t\tgoto label_%u;\n", filter[i].success); + break; + } + printf("\telse\n"); + switch (filter[i].failure) + { + case DIVERT_FILTER_RESULT_ACCEPT: + printf("\t\treturn ACCEPT;\n"); + break; + case DIVERT_FILTER_RESULT_REJECT: + printf("\t\treturn REJECT;\n"); + break; + default: + printf("\t\tgoto label_%u;\n", filter[i].failure); + break; + } + } +} + +#endif /* DIVERT_DEBUG */ + +/****************************************************************************/ +/* DIVERT HELPER IMPLEMENTATION */ +/****************************************************************************/ + +/* + * Parse IPv4/IPv6/ICMP/ICMPv6/TCP/UDP headers from a raw packet. + */ +extern BOOL DivertHelperParse(PDIVERT_PACKET pPacket, UINT packetLen, + PDIVERT_IPHDR *ppIpHdr, PDIVERT_IPV6HDR *ppIpv6Hdr, + PDIVERT_ICMPHDR *ppIcmpHdr, PDIVERT_ICMPV6HDR *ppIcmpv6Hdr, + PDIVERT_TCPHDR *ppTcpHdr, PDIVERT_UDPHDR *ppUdpHdr, PVOID *ppData, + UINT *pDataLen) +{ + PDIVERT_IPHDR ip_header = NULL; + PDIVERT_IPV6HDR ipv6_header = NULL; + PDIVERT_ICMPHDR icmp_header = NULL; + PDIVERT_ICMPV6HDR icmpv6_header = NULL; + PDIVERT_TCPHDR tcp_header = NULL; + PDIVERT_UDPHDR udp_header = NULL; + UINT16 header_len; + UINT8 trans_proto; + PVOID data = NULL; + UINT data_len = 0; + BOOL success; + + if (pPacket == NULL || + packetLen < sizeof(DIVERT_PACKET) + sizeof(UINT8)) + { + goto DivertHelperParseExit; + } + data = DIVERT_PACKET_DATA(pPacket); + data_len = packetLen - sizeof(DIVERT_PACKET); + + ip_header = (PDIVERT_IPHDR)data; + switch (ip_header->Version) + { + case 4: + if (data_len < sizeof(DIVERT_IPHDR) || + ip_header->HdrLength < 5 || + data_len < ip_header->HdrLength*sizeof(UINT32) || + ntohs(ip_header->Length) != data_len) + { + ip_header = NULL; + goto DivertHelperParseExit; + } + trans_proto = ip_header->Protocol; + header_len = ip_header->HdrLength*sizeof(UINT32); + data = (PVOID)((UINT8 *)data + header_len); + data_len -= header_len; + break; + case 6: + ip_header = NULL; + ipv6_header = (PDIVERT_IPV6HDR)data; + if (data_len < sizeof(DIVERT_IPV6HDR) || + ntohs(ipv6_header->Length) != + data_len - sizeof(DIVERT_IPV6HDR)) + { + ipv6_header = NULL; + goto DivertHelperParseExit; + } + trans_proto = ipv6_header->NextHdr; + data = (PVOID)((UINT8 *)data + sizeof(DIVERT_IPV6HDR)); + data_len -= sizeof(DIVERT_IPV6HDR); + break; + default: + ip_header = NULL; + goto DivertHelperParseExit; + } + + switch (trans_proto) + { + case IPPROTO_TCP: + tcp_header = (PDIVERT_TCPHDR)data; + if (data_len < sizeof(DIVERT_TCPHDR) || + tcp_header->HdrLength < 5 || + data_len < tcp_header->HdrLength*sizeof(UINT32)) + { + tcp_header = NULL; + goto DivertHelperParseExit; + } + header_len = tcp_header->HdrLength*sizeof(UINT32); + data = ((UINT8 *)data + header_len); + data_len -= header_len; + break; + case IPPROTO_UDP: + udp_header = (PDIVERT_UDPHDR)data; + if (data_len < sizeof(DIVERT_UDPHDR) || + ntohs(udp_header->Length) != data_len) + { + udp_header = NULL; + goto DivertHelperParseExit; + } + data = ((UINT8 *)data + sizeof(DIVERT_UDPHDR)); + data_len -= sizeof(DIVERT_UDPHDR); + break; + case IPPROTO_ICMP: + icmp_header = (PDIVERT_ICMPHDR)data; + if (ip_header == NULL || + data_len < sizeof(DIVERT_ICMPHDR)) + { + icmp_header = NULL; + goto DivertHelperParseExit; + } + data = ((UINT8 *)data + sizeof(DIVERT_ICMPHDR)); + data_len -= sizeof(DIVERT_ICMPHDR); + break; + case IPPROTO_ICMPV6: + icmpv6_header = (PDIVERT_ICMPV6HDR)data; + if (ipv6_header == NULL || + data_len < sizeof(DIVERT_ICMPV6HDR)) + { + icmpv6_header = NULL; + goto DivertHelperParseExit; + } + data = ((UINT8 *)data + sizeof(DIVERT_ICMPV6HDR)); + data_len -= sizeof(DIVERT_ICMPV6HDR); + break; + default: + break; + } + + if (data_len == 0) + { + data = NULL; + } + +DivertHelperParseExit: + success = TRUE; + if (ppIpHdr != NULL) + { + *ppIpHdr = ip_header; + success = success && (ip_header != NULL); + } + if (ppIpv6Hdr != NULL) + { + *ppIpv6Hdr = ipv6_header; + success = success && (ipv6_header != NULL); + } + if (ppIcmpHdr != NULL) + { + *ppIcmpHdr = icmp_header; + success = success && (icmp_header != NULL); + } + if (ppIcmpv6Hdr != NULL) + { + *ppIcmpv6Hdr = icmpv6_header; + success = success && (icmpv6_header != NULL); + } + if (ppTcpHdr != NULL) + { + *ppTcpHdr = tcp_header; + success = success && (tcp_header != NULL); + } + if (ppUdpHdr != NULL) + { + *ppUdpHdr = udp_header; + success = success && (udp_header != NULL); + } + if (ppData != NULL) + { + *ppData = data; + success = success && (data != NULL); + } + if (pDataLen != NULL) + { + *pDataLen = data_len; + } + return success; +} + +/* + * Calculate IPv4/IPv6/ICMP/ICMPv6/TCP/UDP checksums. + */ +extern UINT DivertHelperCalcChecksums(PDIVERT_PACKET pPacket, UINT packetLen, + UINT64 flags) +{ + DIVERT_PSEUDOHDR pseudo_header; + DIVERT_PSEUDOV6HDR pseudov6_header; + BOOL have_pseudo_header = FALSE, have_v6pseudo_header = FALSE; + PDIVERT_IPHDR ip_header; + PDIVERT_IPV6HDR ipv6_header; + PDIVERT_ICMPHDR icmp_header; + PDIVERT_ICMPV6HDR icmpv6_header; + PDIVERT_TCPHDR tcp_header; + PDIVERT_UDPHDR udp_header; + UINT payload_len, checksum_len; + UINT count = 0; + + DivertHelperParse(pPacket, packetLen, &ip_header, &ipv6_header, + &icmp_header, &icmpv6_header, &tcp_header, &udp_header, NULL, + &payload_len); + + if (ip_header != NULL && !(flags & DIVERT_HELPER_NO_IP_CHECKSUM)) + { + ip_header->Checksum = 0; + ip_header->Checksum = DivertHelperCalcChecksum(NULL, 0, + ip_header, ip_header->HdrLength*sizeof(UINT32)); + count++; + } + + if (icmp_header != NULL) + { + if (flags & DIVERT_HELPER_NO_ICMP_CHECKSUM) + { + return count; + } + icmp_header->Checksum = 0; + icmp_header->Checksum = DivertHelperCalcChecksum(NULL, 0, + icmp_header, payload_len + sizeof(DIVERT_ICMPHDR)); + count++; + return count; + } + + if (icmpv6_header != NULL) + { + if (flags & DIVERT_HELPER_NO_ICMPV6_CHECKSUM) + { + return count; + } + checksum_len = payload_len + sizeof(DIVERT_ICMPV6HDR); + DivertInitPseudoHeaderV6(ipv6_header, &pseudov6_header, + IPPROTO_ICMPV6, checksum_len); + icmpv6_header->Checksum = 0; + icmpv6_header->Checksum = DivertHelperCalcChecksum(&pseudov6_header, + sizeof(pseudov6_header), icmpv6_header, checksum_len); + count++; + return count; + } + + if (tcp_header != NULL) + { + if (flags & DIVERT_HELPER_NO_TCP_CHECKSUM) + { + return count; + } + checksum_len = payload_len + tcp_header->HdrLength*sizeof(UINT32); + if (ip_header != NULL) + { + DivertInitPseudoHeader(ip_header, &pseudo_header, IPPROTO_TCP, + checksum_len); + tcp_header->Checksum = 0; + tcp_header->Checksum = DivertHelperCalcChecksum(&pseudo_header, + sizeof(pseudo_header), tcp_header, checksum_len); + } + else + { + DivertInitPseudoHeaderV6(ipv6_header, &pseudov6_header, + IPPROTO_TCP, checksum_len); + tcp_header->Checksum = 0; + tcp_header->Checksum = DivertHelperCalcChecksum(&pseudov6_header, + sizeof(pseudov6_header), tcp_header, checksum_len); + } + count++; + return count; + } + + if (udp_header != NULL) + { + if (flags & DIVERT_HELPER_NO_UDP_CHECKSUM) + { + return count; + } + checksum_len = payload_len + sizeof(DIVERT_UDPHDR); + if (ip_header != NULL) + { + DivertInitPseudoHeader(ip_header, &pseudo_header, IPPROTO_UDP, + checksum_len); + udp_header->Checksum = 0; + udp_header->Checksum = DivertHelperCalcChecksum(&pseudo_header, + sizeof(pseudo_header), udp_header, checksum_len); + if (udp_header->Checksum == 0) + { + udp_header->Checksum = 0xFFFF; + } + } + else + { + DivertInitPseudoHeaderV6(ipv6_header, &pseudov6_header, + IPPROTO_UDP, checksum_len); + udp_header->Checksum = 0; + udp_header->Checksum = DivertHelperCalcChecksum(&pseudov6_header, + sizeof(pseudov6_header), udp_header, checksum_len); + } + count++; + } + return count; +} + +/* + * Initialize the IP pseudo header. + */ +static void DivertInitPseudoHeader(PDIVERT_IPHDR ip_header, + PDIVERT_PSEUDOHDR pseudo_header, UINT8 protocol, UINT len) +{ + pseudo_header->SrcAddr = ip_header->SrcAddr; + pseudo_header->DstAddr = ip_header->DstAddr; + pseudo_header->Zero = 0; + pseudo_header->Protocol = protocol; + pseudo_header->Length = htons((UINT16)len); +} + +/* + * Initialize the IPv6 pseudo header. + */ +static void DivertInitPseudoHeaderV6(PDIVERT_IPV6HDR ipv6_header, + PDIVERT_PSEUDOV6HDR pseudov6_header, UINT8 protocol, UINT len) +{ + memcpy(pseudov6_header->SrcAddr, ipv6_header->SrcAddr, + sizeof(pseudov6_header->SrcAddr)); + memcpy(pseudov6_header->DstAddr, ipv6_header->DstAddr, + sizeof(pseudov6_header->DstAddr)); + pseudov6_header->Length = htonl((UINT32)len); + pseudov6_header->NextHdr = protocol; + pseudov6_header->Zero = 0; +} + +/* + * Generic checksum computation. + */ +static UINT16 DivertHelperCalcChecksum(PVOID pseudo_header, + UINT16 pseudo_header_len, PVOID data, UINT len) +{ + register const UINT16 *data16 = (const UINT16 *)pseudo_header; + register size_t len16 = pseudo_header_len >> 1; + register UINT32 sum = 0; + size_t i; + + // Pseudo header: + for (i = 0; i < len16; i++) + { + sum += (UINT32)data16[i]; + } + + // Main data: + data16 = (const UINT16 *)data; + len16 = len >> 1; + for (i = 0; i < len16; i++) + { + sum += (UINT32)data16[i]; + } + + if (len & 0x1) + { + const UINT8 *data8 = (const UINT8 *)data; + sum += (UINT16)data8[len-1]; + } + + sum = (sum & 0xFFFF) + (sum >> 16); + sum += (sum >> 16); + sum = ~sum; + return (UINT16)sum; +} + diff --git a/dll/divert.def b/dll/divert.def new file mode 100644 index 0000000..42ee852 --- /dev/null +++ b/dll/divert.def @@ -0,0 +1,9 @@ +LIBRARY divert +EXPORTS + DivertDllEntry + DivertOpen + DivertRecv + DivertSend + DivertClose + DivertHelperParse + DivertHelperCalcChecksums diff --git a/dll/sources b/dll/sources new file mode 100644 index 0000000..488f594 --- /dev/null +++ b/dll/sources @@ -0,0 +1,32 @@ +# sources +# (C) 2011, all rights reserved, +# +# 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 . + +TARGETNAME=divert +TARGETTYPE=DYNLINK +TARGETPATH=..\install +TARGETLIBS=\ + $(SDK_LIB_PATH)\advapi32.lib \ + $(SDK_LIB_PATH)\setupapi.lib \ + $(SDK_LIB_PATH)\user32.lib \ + $(SDK_LIB_PATH)\kernel32.lib \ + $(SDK_LIB_PATH)\ws2_32.lib +C_DEFINES=/DWIN32 /D_WINDOWS /D_USRDLL /DUSE_STDAFX /DUNICODE /D_UNICODE +DLLENTRY=DivertDllEntry +DLLDEF=divert.def +USE_MSVCRT=1 +INCLUDES=$(DDK_INC_PATH);$(KMDF_INC_PATH)\$(KMDF_VER_PATH);..\include +SOURCES=divert.c + diff --git a/doc/divert.html b/doc/divert.html new file mode 100644 index 0000000..5adc97f --- /dev/null +++ b/doc/divert.html @@ -0,0 +1,938 @@ + + + + + Divert Documentation + + +

Divert: Windows Packet Divert

+

Table of Contents

+ + +
+

1. Introduction

+

+This package (divert) provides user-mode packet +capture/modification/blocking/re-injection for Windows Vista and later. +

+

+The main features of the divert package are: +

    +
  • User-mode packet capture, dropping, modification. +
  • Simple, high-level, programming API. +
  • Fully documented with sample programs. +
  • Full IPv6 support. +
  • Modern WDF/WFP driver implementation, as opposed to an older + WDM/NDIS intermediate driver. +
  • Open source; Licensed under GNU General Public License version 3. + See the License for more information. +
+

+The functionality provided by divert is very similar to +DIVERT sockets in FreeBSD/MacOS and NETLINK sockets in Linux, +as well as some commercial packages, e.g. WinPktFilter, for Windows. +

+ +
+

2. Building

+

+To build the divert package from source, you must follow these +steps: +

    +
  1. Download and install the latest Windows Driver Kit. +
  2. Open a Free Build Environment console (or + Checked Build Environment for debugging). +
  3. In the divert root directory, run the command: +
    +build -cZg
    +
    +This will build the following files and place them in the +divert\install subdirectory: +
      +
    • divert.dll: User-mode library. +
    • divert.sys: Kernel-mode WDF/WFP callout driver. +
    • divert.inf: INF file for divert.sys. +
    • WdfCoInstaller*.dll: Microsoft WDF co-installer for + divert.sys. +
    • *.exe: Sample applications from the divert\examples + directory. +
    +
+

+ +

2.2 Driver Signing

+

+Before the divert package can be used, the divert.sys driver +must be digitally signed. +See Driver Signing Requirements for Windows +for more information. +

+

+Driver signing is not provided by this package, although this is +something we wish to change in the future. +If you wish to use this package, you must sign the driver yourself. +

+

+If you wish to test this package, you can set up a test certificate. +See +Test-Signing Driver Packages for more information. +

+ +
+

3. Installing

+

+The divert package does not require any special installation. +Simply ensure that the divert.dll, divert.sys, +divert.inf, and WdfCoInstaller*.dll files are in your +application's home directory. +

+

+The divert driver is installed on demand, i.e., when your application +makes a call to DivertOpen() from divert.dll +(see programming API below). +

+

+The WdfCoInstaller*.dll file is relatively bloated compared to the +other files. +Blame Microsoft. +

+ +
+

4. Uninstalling

+

+To uninstall, simply delete the divert.dll, divert.sys, +divert.inf, and WdfCoInstaller*.dll files. +If the divert driver was already demand-started, it will be removed +automatically after the next reboot. +To immediately remove it, your uninstaller can issue the following commands: +

+sc stop divert
+sc delete divert
+
+Note however this is not recommended, as this may interfere with other +applications using the divert package. +

+ +
+

5. Programming API

+

+To use the divert package, a program/application must: +

    +
  1. Include the divert.h header file +
    +#include "divert.h"
    +
    +
  2. Link or dynamically load the divert.dll dynamic link library. +
+ +

5.1 DIVERT_PACKET

+
+
+typedef struct
+{
+    UINT8  Reserved[7];
+    UINT8  Direction;
+    UINT32 IfIdx;
+    UINT32 SubIfIdx;
+} DIVERT_PACKET, *PDIVERT_PACKET;
+
+
+
+

+Fields +

    +
  • Reserved: Reserved for internal use. This field may be + left uninitialized.
  • +
  • Direction: The packet's direction. +The possible values are +
      +
    • DIVERT_PACKET_DIRECTION_OUTBOUND with value 0 for outbound +packets.
    • +
    • DIVERT_PACKET_DIRECTION_INBOUND with value 1 for inbound +packets.
    • +
  • +
  • IfIdx: The interface index on which the packet arrived + (for inbound packets), or is to be sent (for outbound packets).
  • +
  • SubIfIdx: The sub-interface index for IfIdx.
  • +
+

+Remarks
+The DIVERT_PACKET structure represents a captured or injected packet. +The packet's contents, i.e., IP/TCP/UDP headers and data, immediately follow +a DIVERT_PACKET header in memory. +

+
+ +

5.2 DivertOpen

+
+
+HANDLE DivertOpen(
+    __in const char *filter
+);
+
+
+
+

+Parameters
+

    +
  • filter: A packet filter string specified in the divert + filter language.
  • +
+

+Return Value
+A valid divert HANDLE on success, or +INVALID_HANDLE_VALUE if an error occurred. +Use GetLastError() to get the reason for the error. +

+Remarks
+Opens a divert packet capture handle for the given filter. +Any packet that matches the filter will be diverted to the handle, and +can be read by calling DivertRecv(). +

+

+A typical application is only interested in a subset of all traffic. +In this case the filter should match this subset as closely as +possible. +This avoids unnecessary overheads introduced by diverting packets to an +application, only to have the application re-inject them. +

+

+Calling DivertOpen() for the first time will automatically load +the divert.sys driver. +This driver will remain installed until the next reboot, or if the driver +is explicitly removed, e.g. by issuing the following commands: +

+sc stop divert
+sc delete divert
+
+This model helps ensure the driver is not loaded unless it is required to be. +

+
+ +

5.3 DivertRecv

+
+
+BOOL DivertRecv(
+    __in HANDLE handle,
+    __out PDIVERT_PACKET pPacket,
+    __in UINT packetLen,
+    __out_opt UINT *recvLen
+);
+
+
+
+

+Parameters
+

    +
  • handle: A valid divert handle created by + DivertOpen().
  • +
  • pPacket: A pointer to a DIVERT_PACKET header and free + space to write the captured packet to. + The free space is assumed to immediately follow the + DIVERT_PACKET header.
  • +
  • packetLen: The total length of the DIVERT_PACKET + header and the free space.
  • +
  • recvLen: The total number of bytes written to pPacket. + Can be NULL if this information is not required.
  • +
+

+Return Value
+TRUE if a packet was successfully received, or FALSE if +an error occurred. +Use GetLastError() to get the reason for the error. +

+Remarks
+Receives a diverted packet that matches the filter passed to +DivertOpen(). +The received packet is guaranteed to match the filter. +

+

+The pPacket parameter is intended to be a buffer large enough to +store a DIVERT_PACKET header, and enough space to store the diverted +packet. +This would typically be achieved by the following declarations: +

+    char packet[MAX_SIZE];                              // packet buffer space
+    PDIVERT_PACKET pPacket = (PDIVERT_PACKET)packet;    // cast packet to a PDIVERT_PACKET
+    ...
+    if (!DivertRecv(handle, pPacket, sizeof(packet), &recvLen))
+    {
+        // Recv error
+    }
+    ...
+
+

+

+An application should call DivertRecv() as soon as possible +after a successful call to DivertOpen(). +When a divert handle is open, any packet that matches the filter will +be captured and queued until handled by DivertRecv(). +The packet queue never exceeds a fixed length (currently 1024 packets), after +which packets are dropped. +Furthermore packets are not queued indefinitely. +A packet that has been queued longer than a specific time (currently 100ms) +will be dropped. +To avoid packets being dropped that application must process packets as fast +as possible. +

+
+ +

5.4 DivertSend

+
+
+BOOL DivertSend(
+    __in HANDLE handle,
+    __in PDIVERT_PACKET pPacket,
+    __in UINT packetLen,
+    __out_opt UINT *sendLen
+);
+
+
+
+

+Parameters
+

    +
  • handle: A valid divert handle created by + DivertOpen().
  • +
  • pPacket: A pointer to a DIVERT_PACKET header and the + packet to be injected. + The packet is assumed to immediately follow the + DIVERT_PACKET header.
  • +
  • packetLen: The total length of the DIVERT_PACKET + header and packet to be injected.
  • +
  • sendLen: The total number of bytes injected. + Can be NULL if this information is not required.
  • +
+

+Return Value
+TRUE if a packet was successfully injected, or FALSE if +an error occurred. +Use GetLastError() to get the reason for the error. +

+Remarks
+Injects a packet into the network stack. +The injected packet may be one received from DivertRecv(), or a +modified version, or a completely new packet. +Injected packets cannot be read again by DivertRecv(). +

+The DIVERT_PACKET header determines how the packet is injected. +If the Direction field is DIVERT_PACKET_DIRECTION_OUTBOUND, +the packet is injected into the outbound path (i.e. a packet leaving +this computer). +Else, if Direction is DIVERT_PACKET_DIRECTION_INBOUND, +the packet is injected into the inbound path (i.e. a packet arriving at +this computer). +Note that the Direction field, and not the IP addresses in +the injected packet, is used to determine the packet's direction. +

+For packets injected into the inbound path, the IfIdx and +SubIfIdx fields are assumed to contain valid interface numbers. +These may be retrieved from DivertRecv() (for packet modification), +or from the IP Helper API. +

+For outbound injected packets, the IfIdx and SubIfIdx +fields are ignored and may be arbitrary values. +Injecting an inbound packet on the outbound path may work (for some +types of packets), however this should be considered "undocumented" behavior, +and may change in future. +

+
+ +

5.5 DivertClose

+
+
+BOOL DivertClose(
+    __in HANDLE handle
+);
+
+
+
+

+Parameters
+

    +
  • handle: A valid divert handle created by + DivertOpen().
  • +
+

+Return Value
+TRUE if successful, FALSE if an error occurred. +Use GetLastError() to get the reason for the error. +

+Remarks
+Closes a handle created by DivertOpen(). +

+
+ +
+

6. Helper Programming API

+ +The divert helper programming API is a collection of definitions +and functions designed to make writing divert applications easier. +The use of the helper API is completely optional. + +

6.1 DIVERT_IPHDR

+
+
+typedef struct
+{
+    UINT8  HdrLength:4;
+    UINT8  Version:4;
+    UINT8  TOS;
+    UINT16 Length;
+    UINT16 Id;
+    UINT16 ...;
+    UINT8  TTL;
+    UINT8  Protocol;
+    UINT16 Checksum;
+    UINT32 SrcAddr;
+    UINT32 DstAddr;
+} DIVERT_IPHDR, *PDIVERT_IPHDR;
+
+
+
+

+Fields
+See
here +for more information. +

+Remarks
+IPv4 header definition. +

+The following fields can only be get/set using the following macro +definitions: +

    +
  • FragOff with DIVERT_IPHDR_GET_FRAGOFF(hdr) and + DIVERT_IPHDR_SET_FRAGOFF(hdr, val)
  • +
  • MF with DIVERT_IPHDR_GET_MF(hdr) and + DIVERT_IPHDR_SET_MF(hdr, val)
  • +
  • DF with DIVERT_IPHDR_GET_DF(hdr) and + DIVERT_IPHDR_SET_DF(hdr, val)
  • +
  • Reserved with DIVERT_IPHDR_GET_RESERVED(hdr) and + DIVERT_IPHDR_SET_RESERVED(hdr, val)
  • +
+

+
+ +

6.2 DIVERT_IPV6HDR

+
+
+typedef struct
+{
+    UINT32 Version:4;
+    UINT32 ...:28;
+    UINT16 Length;
+    UINT8  NextHdr;
+    UINT8  HopLimit;
+    UINT32 SrcAddr[4];
+    UINT32 DstAddr[4];
+} DIVERT_IPV6HDR, *PDIVERT_IPV6HDR;
+
+
+
+Fields
+See here +for more information. +

+Remarks
+IPv6 header definition. +

+The following fields can only be get/set using the following macro +definitions: +

    +
  • TrafficClass with + DIVERT_IPV6HDR_GET_TRAFFICCLASS(hdr) and + DIVERT_IPV6HDR_SET_TRAFFICCLASS(hdr, val)
  • +
  • FlowLabel with DIVERT_IPV6HDR_GET_FLOWLABEL(hdr) and + DIVERT_IPV6HDR_SET_FLOWLABEL(hdr, val)
  • +
+

+
+ +

6.3 DIVERT_ICMPHDR

+
+
+typedef struct
+{
+    UINT8  Type;
+    UINT8  Code;
+    UINT16 Checksum;
+    UINT32 Body;
+} DIVERT_ICMPHDR, *PDIVERT_ICMPHDR;
+
+
+
+Fields
+See here +for more information. +

+Remarks
+ICMP header definition. +

+
+ +

6.4 DIVERT_ICMPV6HDR

+
+
+typedef struct
+{
+    UINT8  Type;
+    UINT8  Code;
+    UINT16 Checksum;
+    UINT32 Body;
+} DIVERT_ICMPV6HDR, *PDIVERT_ICMPV6HDR;
+
+
+
+Fields
+See here for +more information. +

+Remarks
+ICMPv6 header definition. +

+
+ +

6.5 DIVERT_TCPHDR

+
+
+typedef struct
+{
+    UINT16 SrcPort;
+    UINT16 DstPort;
+    UINT32 SeqNum;
+    UINT32 AckNum;
+    UINT16 Reserved1:4;
+    UINT16 HdrLength:4;
+    UINT16 Fin:1;
+    UINT16 Syn:1;
+    UINT16 Rst:1;
+    UINT16 Psh:1;
+    UINT16 Ack:1;
+    UINT16 Urg:1;
+    UINT16 Reserved2:2;
+    UINT16 Window;
+    UINT16 Checksum;
+    UINT16 UrgPtr;
+} DIVERT_TCPHDR, *PDIVERT_TCPHDR;
+
+
+
+Fields
+See here +for more information. +

+Remarks
+TCP header definition. +

+
+ +

6.6 DIVERT_UDPHDR

+
+
+typedef struct
+{
+    UINT16 SrcPort;
+    UINT16 DstPort;
+    UINT16 Length;
+    UINT16 Checksum;
+} DIVERT_UDPHDR, *PDIVERT_UDPHDR;
+
+
+
+Fields
+See here +for more information. +

+Remarks
+UDP header definition. +

+
+ +

6.7 DivertHelperParse

+
+
+BOOL DivertHelperParse(
+    __in PDIVERT_PACKET pPacket,
+    __in UINT packetLen,
+    __out_opt PDIVERT_IPHDR *ppIpHdr,
+    __out_opt PDIVERT_IPV6HDR *ppIpv6Hdr,
+    __out_opt PDIVERT_ICMPHDR *ppIcmpHdr,
+    __out_opt PDIVERT_ICMPV6HDR *ppIcmpv6Hdr,
+    __out_opt PDIVERT_TCPHDR *ppTcpHdr,
+    __out_opt PDIVERT_UDPHDR *ppUdpHdr,
+    __out_opt PVOID *ppData,
+    __out_opt UINT *pDataLen
+);
+
+
+
+

+Parameters
+

    +
  • pPacket: The packet to be parsed.
  • +
  • packetLen: The total length of the packet and the + DIVERT_PACKET header.
  • +
  • ppIpHdr: Output pointer to a DIVERT_IPHDR.
  • +
  • ppIpv6Hdr: Output pointer to a DIVERT_IPV6HDR.
  • +
  • ppIcmpHdr: Output pointer to a DIVERT_ICMPHDR.
  • +
  • ppIcmpv6Hdr: Output pointer to a DIVERT_ICMPV6HDR.
  • +
  • ppTcpHdr: Output pointer to a DIVERT_TCPHDR.
  • +
  • ppUdpHdr: Output pointer to a DIVERT_UDPHDR.
  • +
  • ppData: Output pointer to the packet's data/payload.
  • +
  • pDataLen Output data/payload length.
  • +
+

+Return Value
+TRUE if all expected (non-NULL) outputs were present, +FALSE otherwise. +Note that FALSE may sometimes be a legitimate return value, e.g., +when both ppIpHdr and ppIpv6Hdr are non-NULL. +

+Remarks
+Parses a raw packet (e.g. one captured using DivertRecv) into the +various packet headers and/or payloads that may or may not be present. +

+Each output parameter may be NULL or non-NULL. +For non-NULL parameters, this function will write the pointer to +the corresponding header/payload if it exists, or will write NULL +otherwise. +Any non-NULL pointer that is returned +

    +
  1. Is a pointer into the original pPacket packet; and
  2. +
  3. There is enough space in pPacket to fit the header.
  4. +
+

+This function does not do any verification of the header/payload contents, +other length and the minimal information required to parse the headers +themselves. +

+

+ +

6.8 DivertHelperCalcChecksums

+
+
+UINT DivertHelperCalcChecksums(
+    __inout PDIVERT_PACKET pPacket,
+    __in UINT packetLen,
+    __in UINT64 flags
+);
+
+
+
+

+Parameters
+

    +
  • pPacket: The packet to be modified.
  • +
  • packetLen: The total length of the packet and the + DIVERT_PACKET header.
  • +
  • flags: One or more of the following flags: +
      +
    • DIVERT_HELPER_NO_IP_CHECKSUM: Do not calculate the IPv4 + checksum.
    • +
    • DIVERT_HELPER_NO_ICMP_CHECKSUM: Do not calculate the ICMP + checksum.
    • +
    • DIVERT_HELPER_NO_ICMPV6_CHECKSUM: Do not calculate the ICMPv6 + checksum.
    • +
    • DIVERT_HELPER_NO_TCP_CHECKSUM: Do not calculate the TCP + checksum.
    • +
    • DIVERT_HELPER_NO_UDP_CHECKSUM: Do not calculate the UDP + checksum.
    • +
  • +
+

+Return Value
+The number of checksums calculated. +

+Remarks
+(Re)calculates the checksum for any IPv4/ICMP/ICMPv6/TCP/UDP checksum present +in the given packet. +Individual checksum calculations may be disabled via the appropriate flag. +Typically this function should be used before a packet is injected. +

+This function will calculate each checksum from scratch, even if the existing +checksum is correct. +This may be inefficient for some applications. +For better performance, incremental checksum calculations should be used +instead (not provided by this API). +

+

+ +
+

7. Filter Language

+ +

+The DivertOpen() function accepts a string containing a +filter expression. +Only packets that match the filter expression are diverted. +Any other packet is allowed to continue as per normal. +

+Filter allows an application to select only the subset of traffic that is of +interest. +For example, a URL blacklist filter would only be interested in packets that +contain URLs. +This could be achieved via the following filter. +

+HANDLE handle = DivertOpen(
+    "outbound and "
+    "data and "
+    "tcp.DstPort == 80");
+
+This filter specifies that we should only divert traffic that is +
    +
  1. outbound;
  2. +
  3. contains data; and
  4. +
  5. has TCP destination port 80 (i.e. HTTP web traffic). +
+

+A filter is a Boolean expression of the form: +

+        FILTER := true | false | FILTER and FILTER | FILTER or FILTER | (FILTER) | TEST
+
+C-style syntax &&, ||, and ! may also +be used instead of and, or, and not, respectively. +A test is of the following form: +
+        TEST := TEST0 | not TEST0
+        TEST0 := FIELD | FIELD op VAL
+
+where op is one of the following: +

+

+ + + + + + + +
== or =Equal
!=Not equal
<Less-than
>Greater-than
<=Less-than-or-equal
>=Greater-than-or-equal
+
+

+and VAL is a decimal number, hexadecimal number, or IP +address. +If the "op VAL" is missing, the test is implicitly +"FIELD != 0". +

+Finally a field is some property about the packet. +The possible fields are: +

+

+ + + + + + + + + + + + + + + + + + + +
outboundIs outbound?
inboundIs inbound?
ifIdxInterface index
subIfIdxSub-interface index
ipIs IPv4?
ipv6Is IPv6?
icmpIs ICMP?
icmpv6Is ICMPv6?
tcpIs TCP?
udpIs UDP?
ip.*IPv4 fields (see DIVERT_IPHDR)
ipv6.*IPv6 fields (see DIVERT_IPV6HDR)
icmp.*ICMP fields (see DIVERT_ICMPHDR)
icmpv6.*ICMPV6 fields (see DIVERT_ICMPV6HDR)
tcp.*TCP fields (see DIVERT_TCPHDR)
tcp.PayloadLengthThe TCP payload length
udp.*UDP fields (see DIVERT_UDPHDR)
udp.PayloadLengthThe UDP payload length
+
+

+A test also fails if the field is missing. +E.g. the test "tcp.DstPort == 80" will fail if the packet does not +contain a TCP header. +

+ +

7.1 Filter Examples

+ +

+

    +
  1. +Divert all outbound web traffic: +
    +HANDLE handle = DivertOpen(
    +        "outbound and "
    +        "(tcp.DstPort == 80 or udp.DstPort == 53)"
    +    );
    +
    +
  2. +
  3. +Divert all inbound TCP SYNs: +
    +HANDLE handle = DivertOpen(
    +        "inbound and "
    +        "tcp.Syn"
    +    );
    +
    +
  4. +
  5. +Divert only (inbound) local traffic: +
    +HANDLE handle = DivertOpen(
    +        "inbound and ("
    +        "(ip.DstAddr >= 127.0.0.1 and ip.DstAddr <= 127.255.255.255) or"
    +        "ipv6.DstAddr == ::1)"
    +    );
    +
    +
  6. +
  7. +Divert all traffic: +
    +HANDLE handle = DivertOpen("true");
    +
    +
  8. +
  9. +Divert no traffic: +
    +HANDLE handle = DivertOpen("false");
    +
    +(This is not very useful). +
+ +
+

8. Samples

+ +

+Some samples have been provided to demonstrate the divert API. +The sample programs are: +

    +
  • webfilter.exe: A simple URL blacklist filter. + This program monitors outbound HTTP traffic. + If it finds a URL request that matches the blacklist, it hijacks the + TCP connection, reseting the connection at the server's end, and + sending a simple block-page to the browser. + The blacklist(s) are specified at the command-line.
  • +
  • netdump.exe: A simple packet sniffer based on the + divert filter language. + This program takes a filter specified at the command line, and prints + information about any packet that matches the filter. + Unlike the other samples, this program does not modify or block packets, + it simply re-injects them.
  • +
  • netfilter.exe: A simple firewall based on the divert + filter language. + This program takes a filter specified at the command line, and blocks + any packet that matches the filter. + It blocks TCP by sending a TCP reset, UDP by an ICMP message, and all + other traffic it simply drops. + This is similar to the Linux iptables command with the + -j REJECT option.
  • +
+

+The samples are intended for educational purposes only, and are not +fully-featured applications. +

+ +
+

9. Known Issues

+ +

+There are some limitations to the divert package. +They are +

    +
  • Injecting inbound ICMP/ICMPv6 messages: + For some ICMP/ICMPv6 messages, inbound injection does not work. + An error will be returned and the packet will be lost. + It is suspected that this is an issue with the WFP framework on which + divert is built. + The work-around is to inject inbound ICMP messages as outbound. +
  • +
  • No IPv6 extension header support: + Currently there is no filter support for IPv6 packets with extension + headers. + The work around is to capture all IPv6 traffic. +
  • Injected packets are never re-captured: + An injected packet will never be captured again by any + divert handle. + This is necessary to prevent packet loops and deadlocks. + In the future we intend to implement priorities for divert + handles to allow packets to be seen by multiple divert handles. +
  • Speed: + The divert driver is not re-entrant, and thus is not as + efficient as it could be. + In the future we plan to rectify this. +
+

+ +
+

10. License

+

+This package is distributed strictly under the +GNU Public License (GPL) Version 3. +Please note the following: +

+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+

+

+Other licenses (including commercial licenses) may be available on +request. +For more information please contact:
+basil AT reqrypt DOT org +

+

















+

















+

















+ + diff --git a/examples/dirs b/examples/dirs new file mode 100644 index 0000000..92b6118 --- /dev/null +++ b/examples/dirs @@ -0,0 +1,4 @@ +DIRS= \ + netdump \ + netfilter \ + webfilter diff --git a/examples/netdump/Makefile b/examples/netdump/Makefile new file mode 100644 index 0000000..53b9a3d --- /dev/null +++ b/examples/netdump/Makefile @@ -0,0 +1 @@ +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/examples/netdump/netdump.c b/examples/netdump/netdump.c new file mode 100644 index 0000000..d3c5a57 --- /dev/null +++ b/examples/netdump/netdump.c @@ -0,0 +1,242 @@ +/* + * netdump.c + * (C) 2011, all rights reserved, + * + * 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 . + */ + +/* + * DESCRIPTION: + * This is a simple traffic monitor. + * + * usage: netdump.exe divert-filter + * + * NOTE: Using Divert for this purpose is rather inefficient, as each captured + * packet must be reinjected. For packet sniffing, it's better to use a + * package that copies packets, not copies-and-drops such as Divert. + */ + +#include +#include +#include +#include +#include + +#include "divert.h" + +#define MAXBUF 2048 + +/* + * Entry. + */ +int main(int argc, char **argv) +{ + HANDLE handle, console; + size_t slen, flen; + UINT i; + char filter[MAXBUF]; + char packet[MAXBUF]; + PDIVERT_PACKET ppacket = (PDIVERT_PACKET)packet; + UINT ppacket_len; + PDIVERT_IPHDR ip_header; + PDIVERT_IPV6HDR ipv6_header; + PDIVERT_ICMPHDR icmp_header; + PDIVERT_ICMPV6HDR icmpv6_header; + PDIVERT_TCPHDR tcp_header; + PDIVERT_UDPHDR udp_header; + UINT8 *data; + UINT data_len; + + // Concat all command line args into a filter string. + flen = 0; + for (i = 1; (int)i < argc; i++) + { + slen = strlen(argv[i]); + if (flen + slen + 1 >= MAXBUF) + { + fprintf(stderr, "error: filter too long\n"); + exit(EXIT_FAILURE); + } + strcpy(filter+flen, argv[i]); + flen += slen; + filter[flen] = ' '; + flen++; + } + filter[flen] = '\0'; + + // Get console for pretty colors. + console = GetStdHandle(STD_OUTPUT_HANDLE); + + // Divert traffic matching the filter: + handle = DivertOpen(filter); + if (handle == INVALID_HANDLE_VALUE) + { + if (GetLastError() == ERROR_INVALID_PARAMETER) + { + fprintf(stderr, "error: filter syntax error\n"); + exit(EXIT_FAILURE); + } + fprintf(stderr, "error: failed to open Divert device (%d)\n", + GetLastError()); + exit(EXIT_FAILURE); + } + + // Main loop: + while (TRUE) + { + // Read a matching packet. + if (!DivertRecv(handle, ppacket, sizeof(packet), &ppacket_len)) + { + fprintf(stderr, "warning: failed to read packet (%d)\n", + GetLastError()); + continue; + } + + // Re-inject the matching packet. + if (!DivertSend(handle, ppacket, ppacket_len, NULL)) + { + fprintf(stderr, "warning: failed to reinject packet (%d)\n", + GetLastError()); + } + + // Print info about the matching packet. + DivertHelperParse(ppacket, ppacket_len, &ip_header, &ipv6_header, + &icmp_header, &icmpv6_header, &tcp_header, &udp_header, NULL, + NULL); + if (ip_header == NULL && ipv6_header == NULL) + { + fprintf(stderr, "warning: junk packet\n"); + } + + // Dump packet info: + putchar('\n'); + SetConsoleTextAttribute(console, FOREGROUND_RED); + printf("Packet [Direction=%u IfIdx=%u SubIfIdx=%u]\n", + ppacket->Direction, ppacket->IfIdx, ppacket->SubIfIdx); + if (ip_header != NULL) + { + UINT8 *src_addr = (UINT8 *)&ip_header->SrcAddr; + UINT8 *dst_addr = (UINT8 *)&ip_header->DstAddr; + SetConsoleTextAttribute(console, + FOREGROUND_GREEN | FOREGROUND_RED); + printf("IPv4 [Version=%u HdrLength=%u TOS=%u Length=%u Id=0x%.4X " + "Reserved=%u DF=%u MF=%u FragOff=%u TTL=%u Protocol=%u " + "Checksum=0x%.4X SrcAddr=%u.%u.%u.%u DstAddr=%u.%u.%u.%u]\n", + ip_header->Version, ip_header->HdrLength, + ntohs(ip_header->TOS), ntohs(ip_header->Length), + ntohs(ip_header->Id), DIVERT_IPHDR_GET_RESERVED(ip_header), + DIVERT_IPHDR_GET_DF(ip_header), DIVERT_IPHDR_GET_MF(ip_header), + ntohs(DIVERT_IPHDR_GET_FRAGOFF(ip_header)), ip_header->TTL, + ip_header->Protocol, ntohs(ip_header->Checksum), + src_addr[0], src_addr[1], src_addr[2], src_addr[3], + dst_addr[0], dst_addr[1], dst_addr[2], dst_addr[3]); + } + if (ipv6_header != NULL) + { + UINT16 *src_addr = (UINT16 *)&ipv6_header->SrcAddr; + UINT16 *dst_addr = (UINT16 *)&ipv6_header->DstAddr; + SetConsoleTextAttribute(console, + FOREGROUND_GREEN | FOREGROUND_RED); + printf("IPv6 [Version=%u TrafficClass=%u FlowLabel=%u Length=%u " + "NextHdr=%u HopLimit=%u SrcAddr=", + ipv6_header->Version, + DIVERT_IPV6HDR_GET_TRAFFICCLASS(ipv6_header), + ntohl(DIVERT_IPV6HDR_GET_FLOWLABEL(ipv6_header)), + ntohs(ipv6_header->Length), ipv6_header->NextHdr, + ipv6_header->HopLimit); + for (i = 0; i < 8; i++) + { + printf("%x%c", ntohs(src_addr[i]), (i == 7? ' ': ':')); + } + fputs("DstAddr=", stdout); + for (i = 0; i < 8; i++) + { + printf("%x", ntohs(dst_addr[i])); + if (i != 7) + { + putchar(':'); + } + } + fputs("]\n", stdout); + } + if (icmp_header != NULL) + { + SetConsoleTextAttribute(console, FOREGROUND_RED); + printf("ICMP [Type=%u Code=%u Checksum=0x%.4X Body=0x%.8X]\n", + icmp_header->Type, icmp_header->Code, + ntohs(icmp_header->Checksum), ntohl(icmp_header->Body)); + } + if (icmpv6_header != NULL) + { + SetConsoleTextAttribute(console, FOREGROUND_RED); + printf("ICMPV6 [Type=%u Code=%u Checksum=0x%.4X Body=0x%.8X]\n", + icmpv6_header->Type, icmpv6_header->Code, + ntohs(icmpv6_header->Checksum), ntohl(icmpv6_header->Body)); + } + if (tcp_header != NULL) + { + SetConsoleTextAttribute(console, FOREGROUND_GREEN); + printf("TCP [SrcPort=%u DstPort=%u SeqNum=%u AckNum=%u " + "HdrLength=%u Reserved1=%u Reserved2=%u Urg=%u Ack=%u " + "Psh=%u Rst=%u Syn=%u Fin=%u Window=%u Checksum=0x%.4X " + "UrgPtr=%u]\n", + ntohs(tcp_header->SrcPort), ntohs(tcp_header->DstPort), + ntohl(tcp_header->SeqNum), ntohl(tcp_header->AckNum), + tcp_header->HdrLength, tcp_header->Reserved1, + tcp_header->Reserved2, tcp_header->Urg, tcp_header->Ack, + tcp_header->Psh, tcp_header->Rst, tcp_header->Syn, + tcp_header->Fin, ntohs(tcp_header->Window), + ntohs(tcp_header->Checksum), ntohs(tcp_header->UrgPtr)); + } + if (udp_header != NULL) + { + SetConsoleTextAttribute(console, FOREGROUND_GREEN); + printf("UDP [SrcPort=%u DstPort=%u Length=%u " + "Checksum=0x%.4X]\n", + ntohs(udp_header->SrcPort), ntohs(udp_header->DstPort), + ntohs(udp_header->Length), ntohs(udp_header->Checksum)); + } + SetConsoleTextAttribute(console, FOREGROUND_GREEN | FOREGROUND_BLUE); + data = DIVERT_PACKET_DATA(ppacket); + data_len = ppacket_len - sizeof(DIVERT_PACKET); + for (i = 0; i < data_len; i++) + { + if (i % 20 == 0) + { + printf("\n\t"); + } + printf("%.2X", (unsigned)data[i]); + } + SetConsoleTextAttribute(console, FOREGROUND_RED | FOREGROUND_BLUE); + for (i = 0; i < data_len; i++) + { + if (i % 40 == 0) + { + printf("\n\t"); + } + if (isprint(data[i])) + { + putchar(data[i]); + } + else + { + putchar('.'); + } + } + putchar('\n'); + SetConsoleTextAttribute(console, + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); + } +} + diff --git a/examples/netdump/sources b/examples/netdump/sources new file mode 100644 index 0000000..e0d4311 --- /dev/null +++ b/examples/netdump/sources @@ -0,0 +1,30 @@ +# sources +# (C) 2011, all rights reserved, +# +# 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 . + +TARGETNAME=netdump +TARGETTYPE=PROGRAM +TARGETPATH=..\..\install +TARGETLIBS=\ + $(SDK_LIB_PATH)\setupapi.lib \ + $(SDK_LIB_PATH)\user32.lib \ + $(SDK_LIB_PATH)\ws2_32.lib \ + $(TARGETPATH)\$(_BUILDARCH)\divert.lib +UMTYPE=console +UMENTRY=main +USE_MSVCRT=1 +INCLUDES=$(DDK_INC_PATH);$(KMDF_INC_PATH)\$(KMDF_VER_PATH);..\..\include +SOURCES=netdump.c + diff --git a/examples/netfilter/Makefile b/examples/netfilter/Makefile new file mode 100644 index 0000000..53b9a3d --- /dev/null +++ b/examples/netfilter/Makefile @@ -0,0 +1 @@ +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/examples/netfilter/netfilter.c b/examples/netfilter/netfilter.c new file mode 100644 index 0000000..1a0baa5 --- /dev/null +++ b/examples/netfilter/netfilter.c @@ -0,0 +1,445 @@ +/* + * netfilter.c + * (C) 2011, all rights reserved, + * + * 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 . + */ + +/* + * DESCRIPTION: + * This is a simple traffic filter + * + * usage: netfilter.exe divert-filter + * + * Any traffic that matches the divert-filter will be blocked using one of + * the following methods: + * - TCP: send a TCP RST to the packet's source. + * - UDP: send a ICMP(v6) "destination unreachable" to the packet's source. + * - ICMP/ICMPv6: Drop the packet. + * + * This program is similar to Linux's iptables with the "-j REJECT" target. + */ + +#include +#include +#include +#include +#include + +#include "divert.h" + +#define MAXBUF 2048 + +/* + * Pre-fabricated packets. + */ +typedef struct +{ + DIVERT_PACKET divert; + DIVERT_IPHDR ip; +} PACKET, *PPACKET; + +typedef struct +{ + DIVERT_PACKET divert; + DIVERT_IPV6HDR ipv6; +} PACKETV6, *PPACKETV6; + +typedef struct +{ + PACKET header; + DIVERT_TCPHDR tcp; +} TCPPACKET, *PTCPPACKET; + +typedef struct +{ + PACKETV6 header; + DIVERT_TCPHDR tcp; +} TCPV6PACKET, *PTCPV6PACKET; + +typedef struct +{ + PACKET header; + DIVERT_ICMPHDR icmp; + UINT8 data[]; +} ICMPPACKET, *PICMPPACKET; + +typedef struct +{ + PACKETV6 header; + DIVERT_ICMPV6HDR icmpv6; + UINT8 data[]; +} ICMPV6PACKET, *PICMPV6PACKET; + +/* + * Prototypes. + */ +static void PacketIpInit(PPACKET packet); +static void PacketIpTcpInit(PTCPPACKET packet); +static void PacketIpIcmpInit(PICMPPACKET packet); +static void PacketIpv6Init(PPACKETV6 packet); +static void PacketIpv6TcpInit(PTCPV6PACKET packet); +static void PacketIpv6Icmpv6Init(PICMPV6PACKET packet); + +/* + * Entry. + */ +int main(int argc, char **argv) +{ + HANDLE handle, console; + size_t slen, flen; + UINT i; + char filter[MAXBUF]; + char packet[MAXBUF]; + PDIVERT_PACKET ppacket = (PDIVERT_PACKET)packet; + UINT ppacket_len; + PDIVERT_IPHDR ip_header; + PDIVERT_IPV6HDR ipv6_header; + PDIVERT_ICMPHDR icmp_header; + PDIVERT_ICMPV6HDR icmpv6_header; + PDIVERT_TCPHDR tcp_header; + PDIVERT_UDPHDR udp_header; + UINT payload_len; + + TCPPACKET reset0; + PTCPPACKET reset = &reset0; + UINT8 dnr0[sizeof(ICMPPACKET) + 0x0F*sizeof(UINT32) + 8 + 1]; + PICMPPACKET dnr = (PICMPPACKET)dnr0; + + TCPV6PACKET resetv6_0; + PTCPV6PACKET resetv6 = &resetv6_0; + UINT8 dnrv6_0[sizeof(ICMPV6PACKET) + sizeof(DIVERT_IPV6HDR) + + sizeof(DIVERT_TCPHDR)]; + PICMPV6PACKET dnrv6 = (PICMPV6PACKET)dnrv6_0; + + // Concat all command line args into a filter string. + flen = 0; + for (i = 1; (int)i < argc; i++) + { + slen = strlen(argv[i]); + if (flen + slen + 1 >= MAXBUF) + { + fprintf(stderr, "error: filter too long\n"); + exit(EXIT_FAILURE); + } + strcpy(filter+flen, argv[i]); + flen += slen; + filter[flen] = ' '; + flen++; + } + filter[flen] = '\0'; + + // Initialize all packets. + PacketIpTcpInit(reset); + reset->tcp.Rst = 1; + reset->tcp.Ack = 1; + PacketIpIcmpInit(dnr); + dnr->icmp.Type = 3; // Destination not reachable. + dnr->icmp.Code = 3; // Port not reachable. + PacketIpv6TcpInit(resetv6); + resetv6->tcp.Rst = 1; + resetv6->tcp.Ack = 1; + PacketIpv6Icmpv6Init(dnrv6); + dnrv6->header.ipv6.Length = htons(sizeof(DIVERT_ICMPV6HDR) + 4 + + sizeof(DIVERT_IPV6HDR) + sizeof(DIVERT_TCPHDR)); + dnrv6->icmpv6.Type = 1; // Destination not reachable. + dnrv6->icmpv6.Code = 4; // Port not reachable. + + // Get console for pretty colors. + console = GetStdHandle(STD_OUTPUT_HANDLE); + + // Divert traffic matching the filter: + handle = DivertOpen(filter); + if (handle == INVALID_HANDLE_VALUE) + { + if (GetLastError() == ERROR_INVALID_PARAMETER) + { + fprintf(stderr, "error: filter syntax error\n"); + exit(EXIT_FAILURE); + } + fprintf(stderr, "error: failed to open Divert device (%d)\n", + GetLastError()); + exit(EXIT_FAILURE); + } + + // Main loop: + while (TRUE) + { + // Read a matching packet. + if (!DivertRecv(handle, ppacket, sizeof(packet), &ppacket_len)) + { + fprintf(stderr, "warning: failed to read packet\n"); + continue; + } + + // Print info about the matching packet. + DivertHelperParse(ppacket, ppacket_len, &ip_header, &ipv6_header, + &icmp_header, &icmpv6_header, &tcp_header, &udp_header, NULL, + &payload_len); + if (ip_header == NULL && ipv6_header == NULL) + { + continue; + } + + // Dump packet info: + SetConsoleTextAttribute(console, FOREGROUND_RED); + fputs("BLOCK ", stdout); + SetConsoleTextAttribute(console, + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); + if (ip_header != NULL) + { + UINT8 *src_addr = (UINT8 *)&ip_header->SrcAddr; + UINT8 *dst_addr = (UINT8 *)&ip_header->DstAddr; + printf("ip.SrcAddr=%u.%u.%u.%u ip.DstAddr=%u.%u.%u.%u ", + src_addr[0], src_addr[1], src_addr[2], src_addr[3], + dst_addr[0], dst_addr[1], dst_addr[2], dst_addr[3]); + } + if (ipv6_header != NULL) + { + UINT16 *src_addr = (UINT16 *)&ipv6_header->SrcAddr; + UINT16 *dst_addr = (UINT16 *)&ipv6_header->DstAddr; + fputs("ipv6.SrcAddr=", stdout); + for (i = 0; i < 8; i++) + { + printf("%x%c", ntohs(src_addr[i]), (i == 7? ' ': ':')); + } + fputs(" ipv6.DstAddr=", stdout); + for (i = 0; i < 8; i++) + { + printf("%x%c", ntohs(dst_addr[i]), (i == 7? ' ': ':')); + } + putchar(' '); + } + if (icmp_header != NULL) + { + printf("icmp.Type=%u icmp.Code=%u ", + icmp_header->Type, icmp_header->Code); + // Simply drop ICMP + } + if (icmpv6_header != NULL) + { + printf("icmpv6.Type=%u icmpv6.Code=%u ", + icmpv6_header->Type, icmpv6_header->Code); + // Simply drop ICMPv6 + } + if (tcp_header != NULL) + { + printf("tcp.SrcPort=%u tcp.DstPort=%u tcp.Flags=", + ntohs(tcp_header->SrcPort), ntohs(tcp_header->DstPort)); + if (tcp_header->Fin) + { + fputs("[FIN]", stdout); + } + if (tcp_header->Rst) + { + fputs("[RST]", stdout); + } + if (tcp_header->Urg) + { + fputs("[URG]", stdout); + } + if (tcp_header->Syn) + { + fputs("[SYN]", stdout); + } + if (tcp_header->Psh) + { + fputs("[PSH]", stdout); + } + if (tcp_header->Ack) + { + fputs("[ACK]", stdout); + } + putchar(' '); + + + if (ip_header != NULL) + { + reset->header.divert.IfIdx = ppacket->IfIdx; + reset->header.divert.SubIfIdx = ppacket->SubIfIdx; + reset->header.divert.Direction = !ppacket->Direction; + reset->header.ip.SrcAddr = ip_header->DstAddr; + reset->header.ip.DstAddr = ip_header->SrcAddr; + reset->tcp.SrcPort = tcp_header->DstPort; + reset->tcp.DstPort = tcp_header->SrcPort; + reset->tcp.SeqNum = + (tcp_header->Ack? tcp_header->AckNum: 0); + reset->tcp.AckNum = + (tcp_header->Syn? + htonl(ntohl(tcp_header->SeqNum) + 1): + htonl(ntohl(tcp_header->SeqNum) + payload_len)); + + DivertHelperCalcChecksums((PDIVERT_PACKET)reset, + sizeof(TCPPACKET), 0); + if (!DivertSend(handle, (PDIVERT_PACKET)reset, + sizeof(TCPPACKET), NULL)) + { + fprintf(stderr, "warning: failed to send TCP reset (%d)\n", + GetLastError()); + } + } + + if (ipv6_header != NULL) + { + resetv6->header.divert.IfIdx = ppacket->IfIdx; + resetv6->header.divert.SubIfIdx = ppacket->SubIfIdx; + resetv6->header.divert.Direction = !ppacket->Direction; + memcpy(resetv6->header.ipv6.SrcAddr, ipv6_header->DstAddr, + sizeof(resetv6->header.ipv6.SrcAddr)); + memcpy(resetv6->header.ipv6.DstAddr, ipv6_header->SrcAddr, + sizeof(resetv6->header.ipv6.DstAddr)); + resetv6->tcp.SrcPort = tcp_header->DstPort; + resetv6->tcp.DstPort = tcp_header->SrcPort; + resetv6->tcp.SeqNum = + (tcp_header->Ack? tcp_header->AckNum: 0); + resetv6->tcp.AckNum = + (tcp_header->Syn? + htonl(ntohl(tcp_header->SeqNum) + 1): + htonl(ntohl(tcp_header->SeqNum) + payload_len)); + + DivertHelperCalcChecksums((PDIVERT_PACKET)resetv6, + sizeof(TCPV6PACKET), 0); + if (!DivertSend(handle, (PDIVERT_PACKET)resetv6, + sizeof(TCPV6PACKET), NULL)) + { + fprintf(stderr, "warning: failed to send TCP (IPV6) " + "reset (%d)\n", GetLastError()); + } + } + } + if (udp_header != NULL) + { + printf("udp.SrcPort=%u udp.DstPort=%u ", + ntohs(udp_header->SrcPort), ntohs(udp_header->DstPort)); + + if (ip_header != NULL) + { + // NOTE: For some ICMP error messages, WFP does not seem to + // support INBOUND injection. As a work-around, we + // always inject OUTBOUND. + UINT icmp_length = ip_header->HdrLength*sizeof(UINT32) + 8; + memcpy(dnr->data, ip_header, icmp_length); + icmp_length += sizeof(ICMPPACKET); + dnr->header.divert.IfIdx = ppacket->IfIdx; + dnr->header.divert.SubIfIdx = ppacket->SubIfIdx; + dnr->header.divert.Direction = + DIVERT_PACKET_DIRECTION_OUTBOUND; + dnr->header.ip.Length = + htons(icmp_length - sizeof(DIVERT_PACKET)); + dnr->header.ip.SrcAddr = ip_header->DstAddr; + dnr->header.ip.DstAddr = ip_header->SrcAddr; + DivertHelperCalcChecksums((PDIVERT_PACKET)dnr, icmp_length, 0); + if (!DivertSend(handle, (PDIVERT_PACKET)dnr, icmp_length, + NULL)) + { + fprintf(stderr, "warning: failed to send ICMP message " + "(%d)\n", GetLastError()); + } + } + + if (ipv6_header != NULL) + { + UINT icmpv6_length = sizeof(DIVERT_IPV6HDR) + + sizeof(DIVERT_TCPHDR); + memcpy(dnrv6->data, ipv6_header, icmpv6_length); + icmpv6_length += sizeof(ICMPV6PACKET); + dnrv6->header.divert.IfIdx = ppacket->IfIdx; + dnrv6->header.divert.SubIfIdx = ppacket->SubIfIdx; + dnrv6->header.divert.Direction = + DIVERT_PACKET_DIRECTION_OUTBOUND; + memcpy(dnrv6->header.ipv6.SrcAddr, ipv6_header->DstAddr, + sizeof(dnrv6->header.ipv6.SrcAddr)); + memcpy(dnrv6->header.ipv6.DstAddr, ipv6_header->SrcAddr, + sizeof(dnrv6->header.ipv6.DstAddr)); + DivertHelperCalcChecksums((PDIVERT_PACKET)dnrv6, icmpv6_length, + 0); + if (!DivertSend(handle, (PDIVERT_PACKET)dnrv6, icmpv6_length, + NULL)) + { + fprintf(stderr, "warning: failed to send ICMPv6 message " + "(%d)\n", GetLastError()); + } + } + } + putchar('\n'); + } +} + +/* + * Initialize a PACKET. + */ +static void PacketIpInit(PPACKET packet) +{ + memset(packet, 0, sizeof(PACKET)); + packet->ip.Version = 4; + packet->ip.HdrLength = sizeof(DIVERT_IPHDR) / sizeof(UINT32); + packet->ip.Id = ntohs(0xDEAD); + packet->ip.TTL = 64; +} + +/* + * Initialize a TCPPACKET. + */ +static void PacketIpTcpInit(PTCPPACKET packet) +{ + memset(packet, 0, sizeof(TCPPACKET)); + PacketIpInit(&packet->header); + packet->header.ip.Length = htons(sizeof(TCPPACKET) - + sizeof(DIVERT_PACKET)); + packet->header.ip.Protocol = IPPROTO_TCP; + packet->tcp.HdrLength = sizeof(DIVERT_TCPHDR) / sizeof(UINT32); +} + +/* + * Initialize an ICMPPACKET. + */ +static void PacketIpIcmpInit(PICMPPACKET packet) +{ + memset(packet, 0, sizeof(ICMPPACKET)); + PacketIpInit(&packet->header); + packet->header.ip.Protocol = IPPROTO_ICMP; +} + +/* + * Initialize a PACKETV6. + */ +static void PacketIpv6Init(PPACKETV6 packet) +{ + memset(packet, 0, sizeof(PACKETV6)); + packet->ipv6.Version = 6; + packet->ipv6.HopLimit = 64; +} + +/* + * Initialize a TCPV6PACKET. + */ +static void PacketIpv6TcpInit(PTCPV6PACKET packet) +{ + memset(packet, 0, sizeof(TCPV6PACKET)); + PacketIpv6Init(&packet->header); + packet->header.ipv6.Length = htons(sizeof(DIVERT_TCPHDR)); + packet->header.ipv6.NextHdr = IPPROTO_TCP; + packet->tcp.HdrLength = sizeof(DIVERT_TCPHDR) / sizeof(UINT32); +} + +/* + * Initialize an ICMP PACKET. + */ +static void PacketIpv6Icmpv6Init(PICMPV6PACKET packet) +{ + memset(packet, 0, sizeof(ICMPV6PACKET)); + PacketIpv6Init(&packet->header); + packet->header.ipv6.NextHdr = IPPROTO_ICMPV6; +} + diff --git a/examples/netfilter/sources b/examples/netfilter/sources new file mode 100644 index 0000000..0178768 --- /dev/null +++ b/examples/netfilter/sources @@ -0,0 +1,30 @@ +# sources +# (C) 2011, all rights reserved, +# +# 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 . + +TARGETNAME=netfilter +TARGETTYPE=PROGRAM +TARGETPATH=..\..\install +TARGETLIBS=\ + $(SDK_LIB_PATH)\setupapi.lib \ + $(SDK_LIB_PATH)\user32.lib \ + $(SDK_LIB_PATH)\ws2_32.lib \ + $(TARGETPATH)\$(_BUILDARCH)\divert.lib +UMTYPE=console +UMENTRY=main +USE_MSVCRT=1 +INCLUDES=$(DDK_INC_PATH);$(KMDF_INC_PATH)\$(KMDF_VER_PATH);..\..\include +SOURCES=netfilter.c + diff --git a/examples/webfilter/Makefile b/examples/webfilter/Makefile new file mode 100644 index 0000000..53b9a3d --- /dev/null +++ b/examples/webfilter/Makefile @@ -0,0 +1 @@ +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/examples/webfilter/sources b/examples/webfilter/sources new file mode 100644 index 0000000..27d96d6 --- /dev/null +++ b/examples/webfilter/sources @@ -0,0 +1,30 @@ +# sources +# (C) 2011, all rights reserved, +# +# 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 . + +TARGETNAME=webfilter +TARGETTYPE=PROGRAM +TARGETPATH=..\..\install +TARGETLIBS=\ + $(SDK_LIB_PATH)\setupapi.lib \ + $(SDK_LIB_PATH)\user32.lib \ + $(SDK_LIB_PATH)\ws2_32.lib \ + $(TARGETPATH)\$(_BUILDARCH)\divert.lib +UMTYPE=console +UMENTRY=main +USE_MSVCRT=1 +INCLUDES=$(DDK_INC_PATH);$(KMDF_INC_PATH)\$(KMDF_VER_PATH);..\..\include +SOURCES=webfilter.c + diff --git a/examples/webfilter/webfilter.c b/examples/webfilter/webfilter.c new file mode 100644 index 0000000..1e9a2ea --- /dev/null +++ b/examples/webfilter/webfilter.c @@ -0,0 +1,596 @@ +/* + * webfilter.c + * (C) 2011, all rights reserved, + * + * 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 . + */ + +/* + * DESCRIPTION: + * This is a simple web (HTTP) filter using the Divert device. + * + * It works by intercepting outbound HTTP GET/POST requests and matching + * the URL against a blacklist. If the URL is matched, we hijack the TCP + * connection, reseting the connection at the server end, and sending a + * blockpage to the browser. + */ + +#include +#include +#include +#include + +#include "divert.h" + +#define MAXBUF 2048 + +/* + * URL and blacklist representation. + */ +typedef struct +{ + char *domain; + char *uri; +} URL, *PURL; +typedef struct +{ + UINT size; + UINT length; + PURL *urls; +} BLACKLIST, *PBLACKLIST; + +/* + * Pre-fabricated packets. + */ +typedef struct +{ + DIVERT_PACKET divert; + DIVERT_IPHDR ip; + DIVERT_TCPHDR tcp; +} PACKET, *PPACKET; +typedef struct +{ + PACKET header; + UINT8 data[]; +} DATAPACKET, *PDATAPACKET; + +/* + * THe block page contents. + */ +const char block_data[] = + "HTTP/1.1 200 OK\r\n" + "Connection: close\r\n" + "Content-Type: text/html\r\n" + "\r\n" + "\n" + "\n" + "\t\n" + "\t\tBLOCKED!\n" + "\t\n" + "\t\n" + "\t\t

BLOCKED!

\n" + "\t\t
\n" + "\t\t

This URL has been blocked!

\n" + "\t\n" + "\n"; + +/* + * Prototypes + */ +static void PacketInit(PPACKET packet); +static int UrlCompare(const void *a, const void *b); +static int UrlMatch(PURL urla, PURL urlb); +static PBLACKLIST BlackListInit(void); +static void BlackListInsert(PBLACKLIST blacklist, PURL url); +static void BlackListSort(PBLACKLIST blacklist); +static BOOL BlackListMatch(PBLACKLIST blacklist, PURL url); +static void BlackListRead(PBLACKLIST blacklist, const char *filename); +static BOOL BlackListPayloadMatch(PBLACKLIST blacklist, char *data, + UINT16 len); + +/* + * Entry. + */ +int main(int argc, char **argv) +{ + HANDLE handle; + UINT8 packet[MAXBUF]; + PDIVERT_PACKET ppacket = (PDIVERT_PACKET)packet; + UINT ppacket_len; + PDIVERT_IPHDR ip_header; + PDIVERT_TCPHDR tcp_header; + PVOID payload; + UINT payload_len; + PACKET reset0; + PPACKET reset = &reset0; + PDATAPACKET blockpage; + UINT16 blockpage_len; + PBLACKLIST blacklist; + unsigned i; + + // Read the blacklists. + if (argc <= 1) + { + fprintf(stderr, "usage: %s blacklist.txt [blacklist2.txt ...]\n", + argv[0]); + exit(EXIT_FAILURE); + } + blacklist = BlackListInit(); + for (i = 1; i < (UINT)argc; i++) + { + BlackListRead(blacklist, argv[i]); + } + BlackListSort(blacklist); + + // Initialize the pre-frabricated packets: + blockpage_len = sizeof(DATAPACKET)+sizeof(block_data)-1; + blockpage = (PDATAPACKET)malloc(blockpage_len); + if (blockpage == NULL) + { + fprintf(stderr, "error: memory allocation failed\n"); + exit(EXIT_FAILURE); + } + PacketInit(&blockpage->header); + blockpage->header.ip.Length = + htons(blockpage_len - sizeof(DIVERT_PACKET)); + blockpage->header.tcp.SrcPort = htons(80); + blockpage->header.tcp.Psh = 1; + blockpage->header.tcp.Ack = 1; + memcpy(blockpage->data, block_data, sizeof(block_data)-1); + PacketInit(reset); + reset->tcp.Rst = 1; + reset->tcp.Ack = 1; + + // Open the Divert device: + handle = DivertOpen( + "outbound && " // Outbound traffic only + "ip && " // Only IPv4 supported + "tcp.DstPort == 80 && " // HTTP (port 80) only + "tcp.PayloadLength > 0" // TCP data packets only + ); + if (handle == INVALID_HANDLE_VALUE) + { + fprintf(stderr, "error: failed to open Divert device (%d)\n", + GetLastError()); + exit(EXIT_FAILURE); + } + printf("OPENED divert\n"); + + // Main loop: + while (TRUE) + { + if (!DivertRecv(handle, ppacket, sizeof(packet), &ppacket_len)) + { + fprintf(stderr, "warning: failed to read packet (%d)\n", + GetLastError()); + continue; + } + + if (!DivertHelperParse(ppacket, ppacket_len, &ip_header, NULL, NULL, + NULL, &tcp_header, NULL, &payload, &payload_len) || + !BlackListPayloadMatch(blacklist, payload, (UINT16)payload_len)) + { + // Packet does not match the blacklist; simply reinject it. + if (!DivertSend(handle, ppacket, ppacket_len, NULL)) + { + fprintf(stderr, "warning: failed to reinject packet (%d)\n", + GetLastError()); + } + continue; + } + + // The URL matched the blacklist; we block it by hijacking the TCP + // connection. + + // (1) Send a TCP RST to the server; immediately closing the + // connection at the server's end. + reset->divert.IfIdx = ppacket->IfIdx; + reset->divert.SubIfIdx = ppacket->SubIfIdx; + reset->divert.Direction = ppacket->Direction; + reset->ip.SrcAddr = ip_header->SrcAddr; + reset->ip.DstAddr = ip_header->DstAddr; + reset->tcp.SrcPort = tcp_header->SrcPort; + reset->tcp.DstPort = htons(80); + reset->tcp.SeqNum = tcp_header->SeqNum; + reset->tcp.AckNum = tcp_header->AckNum; + DivertHelperCalcChecksums((PDIVERT_PACKET)reset, sizeof(PACKET), 0); + if (!DivertSend(handle, (PDIVERT_PACKET)reset, sizeof(PACKET), NULL)) + { + fprintf(stderr, "warning: failed to send reset packet (%d)\n", + GetLastError()); + } + + // (2) Send the blockpage to the browser: + blockpage->header.divert.IfIdx = ppacket->IfIdx; + blockpage->header.divert.SubIfIdx = ppacket->SubIfIdx; + blockpage->header.divert.Direction = !ppacket->Direction; + blockpage->header.ip.SrcAddr = ip_header->DstAddr; + blockpage->header.ip.DstAddr = ip_header->SrcAddr; + blockpage->header.tcp.DstPort = tcp_header->SrcPort; + blockpage->header.tcp.SeqNum = tcp_header->AckNum; + blockpage->header.tcp.AckNum = + htonl(ntohl(tcp_header->SeqNum) + payload_len); + DivertHelperCalcChecksums((PDIVERT_PACKET)blockpage, blockpage_len, 0); + if (!DivertSend(handle, (PDIVERT_PACKET)blockpage, blockpage_len, + NULL)) + { + fprintf(stderr, "warning: failed to send block page packet (%d)\n", + GetLastError()); + } + + // (3) Send a TCP RST to the browser; closing the connection at the + // browser's end. + reset->divert.IfIdx = ppacket->IfIdx; + reset->divert.SubIfIdx = ppacket->SubIfIdx; + reset->divert.Direction = !ppacket->Direction; + reset->ip.SrcAddr = ip_header->DstAddr; + reset->ip.DstAddr = ip_header->SrcAddr; + reset->tcp.SrcPort = htons(80); + reset->tcp.DstPort = tcp_header->SrcPort; + reset->tcp.SeqNum = + htonl(ntohl(tcp_header->AckNum) + sizeof(block_data) - 1); + reset->tcp.AckNum = + htonl(ntohl(tcp_header->SeqNum) + payload_len); + DivertHelperCalcChecksums((PDIVERT_PACKET)reset, sizeof(PACKET), 0); + if (!DivertSend(handle, (PDIVERT_PACKET)reset, sizeof(PACKET), NULL)) + { + fprintf(stderr, "warning: failed to send reset packet (%d)\n", + GetLastError()); + } + } +} + +/* + * Initialize a PACKET. + */ +static void PacketInit(PPACKET packet) +{ + memset(packet, 0, sizeof(PACKET)); + packet->ip.Version = 4; + packet->ip.HdrLength = sizeof(DIVERT_IPHDR) / sizeof(UINT32); + packet->ip.Length = htons(sizeof(PACKET) - sizeof(DIVERT_PACKET)); + packet->ip.TTL = 64; + packet->ip.Protocol = IPPROTO_TCP; + packet->tcp.HdrLength = sizeof(DIVERT_TCPHDR) / sizeof(UINT32); +} + +/* + * Initialize an empty blacklist. + */ +static PBLACKLIST BlackListInit(void) +{ + PBLACKLIST blacklist = (PBLACKLIST)malloc(sizeof(BLACKLIST)); + if (blacklist == NULL) + { + goto memory_error; + } + blacklist->urls = (PURL *)malloc(MAXBUF*sizeof(PURL)); + if (blacklist->urls == NULL) + { + goto memory_error; + } + blacklist->size = MAXBUF; + blacklist->length = 0; + + return blacklist; + +memory_error: + fprintf(stderr, "error: failed to allocate memory\n"); + exit(EXIT_FAILURE); +} + +/* + * Insert a URL into a blacklist. + */ +static void BlackListInsert(PBLACKLIST blacklist, PURL url) +{ + if (blacklist->length >= blacklist->size) + { + blacklist->size = (blacklist->size*3) / 2; + printf("GROW blacklist to %u\n", blacklist->size); + blacklist->urls = (PURL *)realloc(blacklist->urls, + blacklist->size*sizeof(PURL)); + if (blacklist->urls == NULL) + { + fprintf(stderr, "error: failed to reallocate memory\n"); + exit(EXIT_FAILURE); + } + } + + blacklist->urls[blacklist->length++] = url; +} + +/* + * Sort the blacklist (for searching). + */ +static void BlackListSort(PBLACKLIST blacklist) +{ + qsort(blacklist->urls, blacklist->length, sizeof(PURL), UrlCompare); +} + +/* + * Match a URL against the blacklist. + */ +static BOOL BlackListMatch(PBLACKLIST blacklist, PURL url) +{ + int lo = 0, hi = ((int)blacklist->length)-1; + + while (lo <= hi) + { + INT mid = (lo + hi) / 2; + int cmp = UrlMatch(url, blacklist->urls[mid]); + if (cmp > 0) + { + hi = mid-1; + } + else if (cmp < 0) + { + lo = mid+1; + } + else + { + return TRUE; + } + } + return FALSE; +} + + +/* + * Read URLs from a file. + */ +static void BlackListRead(PBLACKLIST blacklist, const char *filename) +{ + char domain[MAXBUF+1]; + char uri[MAXBUF+1]; + int c; + UINT16 i, j; + PURL url; + FILE *file = fopen(filename, "r"); + + if (file == NULL) + { + fprintf(stderr, "error: could not open blacklist file %s\n", + filename); + exit(EXIT_FAILURE); + } + + // Read URLs from the file and add them to the blacklist: + while (TRUE) + { + while (isspace(c = getc(file))) + ; + if (c == EOF) + { + break; + } + if (c != '-' && !isalnum(c)) + { + while (!isspace(c = getc(file)) && c != EOF) + ; + if (c == EOF) + { + break; + } + continue; + } + i = 0; + domain[i++] = (char)c; + while ((isalnum(c = getc(file)) || c == '-' || c == '.') && i < MAXBUF) + { + domain[i++] = (char)c; + } + domain[i] = '\0'; + j = 0; + if (c == '/') + { + while (!isspace(c = getc(file)) && c != EOF && j < MAXBUF) + { + uri[j++] = (char)c; + } + uri[j] = '\0'; + } + else if (isspace(c)) + { + uri[j] = '\0'; + } + else + { + while (!isspace(c = getc(file)) && c != EOF) + ; + continue; + } + + printf("ADD %s/%s\n", domain, uri); + + url = (PURL)malloc(sizeof(URL)); + if (url == NULL) + { + goto memory_error; + } + url->domain = (char *)malloc((i+1)*sizeof(char)); + url->uri = (char *)malloc((j+1)*sizeof(char)); + if (url->domain == NULL || url->uri == NULL) + { + goto memory_error; + } + strcpy(url->uri, uri); + for (j = 0; j < i; j++) + { + url->domain[j] = domain[i-j-1]; + } + url->domain[j] = '\0'; + + BlackListInsert(blacklist, url); + } + + fclose(file); + return; + +memory_error: + fprintf(stderr, "error: memory allocation failed\n"); + exit(EXIT_FAILURE); +} + +/* + * Attempt to parse a URL and match it with the blacklist. + */ +static BOOL BlackListPayloadMatch(PBLACKLIST blacklist, char *data, UINT16 len) +{ + static const char get_str[] = "GET /"; + static const char post_str[] = "POST /"; + static const char http_host_str[] = " HTTP/1.1\r\nHost: "; + char domain[MAXBUF]; + char uri[MAXBUF]; + URL url = {domain, uri}; + UINT16 i = 0, j; + BOOL result; + HANDLE console; + + if (len <= sizeof(post_str) + sizeof(http_host_str)) + { + return FALSE; + } + if (strncmp(data, get_str, sizeof(get_str)-1) == 0) + { + i += sizeof(get_str)-1; + } + else if (strncmp(data, post_str, sizeof(post_str)-1) == 0) + { + i += sizeof(post_str)-1; + } + else + { + return FALSE; + } + + for (j = 0; i < len && data[i] != ' '; j++, i++) + { + uri[j] = data[i]; + } + uri[j] = '\0'; + if (i + sizeof(http_host_str)-1 >= len) + { + return FALSE; + } + + if (strncmp(data+i, http_host_str, sizeof(http_host_str)-1) != 0) + { + return FALSE; + } + i += sizeof(http_host_str)-1; + + for (j = 0; i < len && data[i] != '\r'; j++, i++) + { + domain[j] = data[i]; + } + if (i >= len) + { + return FALSE; + } + if (j == 0) + { + return FALSE; + } + if (domain[j-1] == '.') + { + // Nice try... + j--; + if (j == 0) + { + return FALSE; + } + } + domain[j] = '\0'; + + printf("URL %s/%s: ", domain, uri); + + // Reverse the domain: + for (i = 0; i < j / 2; i++) + { + char t = domain[i]; + domain[i] = domain[j-i-1]; + domain[j-i-1] = t; + } + + // Search the blacklist: + result = BlackListMatch(blacklist, &url); + + // Print the verdict: + console = GetStdHandle(STD_OUTPUT_HANDLE); + if (result) + { + SetConsoleTextAttribute(console, FOREGROUND_RED); + puts("BLOCKED!"); + } + else + { + SetConsoleTextAttribute(console, FOREGROUND_GREEN); + puts("allowed"); + } + SetConsoleTextAttribute(console, + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); + return result; +} + +/* + * URL comparison. + */ +static int UrlCompare(const void *a, const void *b) +{ + PURL urla = *(PURL *)a; + PURL urlb = *(PURL *)b; + int cmp = strcmp(urla->domain, urlb->domain); + if (cmp != 0) + { + return cmp; + } + return strcmp(urla->uri, urlb->uri); +} + +/* + * URL matching + */ +static int UrlMatch(PURL urla, PURL urlb) +{ + UINT16 i; + + for (i = 0; urla->domain[i] && urlb->domain[i]; i++) + { + int cmp = (int)urlb->domain[i] - (int)urla->domain[i]; + if (cmp != 0) + { + return cmp; + } + } + if (urla->domain[i] == '\0' && urlb->domain[i] != '\0') + { + return 1; + } + + for (i = 0; urla->uri[i] && urlb->uri[i]; i++) + { + int cmp = (int)urlb->uri[i] - (int)urla->uri[i]; + if (cmp != 0) + { + return cmp; + } + } + if (urla->uri[i] == '\0' && urlb->uri[i] != '\0') + { + return 1; + } + return 0; +} + diff --git a/include/divert.h b/include/divert.h new file mode 100644 index 0000000..ca64090 --- /dev/null +++ b/include/divert.h @@ -0,0 +1,266 @@ +/* + * divert.c + * (C) 2011, all rights reserved, + * + * 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 . + */ + +#ifndef __DIVERT_H +#define __DIVERT_H + +#include + +#ifndef DIVERTEXPORT +#define DIVERTEXPORT __declspec(dllimport) +#endif /* DIVERTEXPORT */ + +#ifdef __cplusplus +extern "C" { +#endif + +/****************************************************************************/ +/* DIVERT API */ +/****************************************************************************/ + +/* + * Divert packet. + */ +typedef struct +{ + UINT8 Reserved[7]; // Reserved for internal use. + UINT8 Direction; // Packet's direction. + UINT32 IfIdx; // Packet's interface index. + UINT32 SubIfIdx; // Packet's sub-interface index. +} DIVERT_PACKET, *PDIVERT_PACKET; + +#ifndef DIVERT_PACKET_DIRECTION_OUTBOUND +#define DIVERT_PACKET_DIRECTION_OUTBOUND 0 +#define DIVERT_PACKET_DIRECTION_INBOUND 1 +#endif /* DIVERT_PACKET_DIRECTION_OUTBOUND */ + +#define DIVERT_PACKET_DIRECTION(pPacket) (pPacket->Direction) +#define DIVERT_PACKET_INTERFACE_INDEX(pPacket) (pPacket->IfIdx) +#define DIVERT_PACKET_SUB_INTERFACE_INDEX(pPacket) (pPacket->SubIfIdx) +#define DIVERT_PACKET_DATA(pPacket) \ + ((PVOID)(pPacket+1)) + +/* + * Open a handle to the divert device with the given filter. + */ +extern DIVERTEXPORT HANDLE DivertOpen( + __in const char *filter); + +/* + * Receive (read) a packet from the Divert handle. + */ +extern DIVERTEXPORT BOOL DivertRecv( + __in HANDLE handle, + __inout PDIVERT_PACKET pPacket, + __in UINT packetLen, + __out_opt UINT *readLen); + +/* + * Send (write/inject) a packet to the Divert handle. + */ +extern DIVERTEXPORT BOOL DivertSend( + __in HANDLE handle, + __in PDIVERT_PACKET pPacket, + __in UINT packetLen, + __out_opt UINT *writeLen); + +/* + * Close a Divert handle. + */ +extern DIVERTEXPORT BOOL DivertClose( + __in HANDLE handle); + +/****************************************************************************/ +/* DIVERT HELPER API */ +/****************************************************************************/ + +#ifndef DIVERT_NO_HELPER_API + +/* + * IPv4/IPv6/ICMP/ICMPv6/TCP/UDP header definitions. + */ +typedef struct +{ + UINT8 HdrLength:4; + UINT8 Version:4; + UINT8 TOS; + UINT16 Length; + UINT16 Id; + UINT16 FragOff0; + UINT8 TTL; + UINT8 Protocol; + UINT16 Checksum; + UINT32 SrcAddr; + UINT32 DstAddr; +} DIVERT_IPHDR, *PDIVERT_IPHDR; + +#define DIVERT_IPHDR_GET_FRAGOFF(hdr) \ + (((hdr)->FragOff0) & 0xFF1F) +#define DIVERT_IPHDR_GET_MF(hdr) \ + ((((hdr)->FragOff0) & 0x0020) != 0) +#define DIVERT_IPHDR_GET_DF(hdr) \ + ((((hdr)->FragOff0) & 0x0040) != 0) +#define DIVERT_IPHDR_GET_RESERVED(hdr) \ + ((((hdr)->FragOff0) & 0x0080) != 0) + +#define DIVERT_IPHDR_SET_FRAGOFF(hdr, val) \ + do \ + { \ + (hdr)->FragOff0 = (((hdr)->FragOff0) & 0x00E0) | \ + ((val) & 0xFF1F); \ + } \ + while (FALSE) +#define DIVERT_IPHDR_SET_MF(hdr, val) \ + do \ + { \ + (hdr)->FragOff0 = (((hdr)->FragOff0) & 0xFFDF) | \ + (((val) & 0x0001) << 5); \ + } \ + while (FALSE) +#define DIVERT_IPHDR_SET_DF(hdr, val) \ + do \ + { \ + (hdr)->FragOff0 = (((hdr)->FragOff0) & 0xFFBF) | \ + (((val) & 0x0001) << 6); \ + } \ + while (FALSE) +#define DIVERT_IPHDR_SET_RESERVED(hdr, val) \ + do \ + { \ + (hdr)->FragOff0 = (((hdr)->FragOff0) & 0xFF7F) | \ + (((val) & 0x0001) << 7); \ + } \ + while (FALSE) + +typedef struct +{ + UINT8 TrafficClass0:4; + UINT8 Version:4; + UINT8 FlowLabel0:4; + UINT8 TrafficClass1:4; + UINT16 FlowLabel1; + UINT16 Length; + UINT8 NextHdr; + UINT8 HopLimit; + UINT32 SrcAddr[4]; + UINT32 DstAddr[4]; +} DIVERT_IPV6HDR, *PDIVERT_IPV6HDR; + +#define DIVERT_IPV6HDR_GET_TRAFFICCLASS(hdr) \ + ((((hdr)->TrafficClass0) << 4) | ((hdr)->TrafficClass1)) +#define DIVERT_IPV6HDR_GET_FLOWLABEL(hdr) \ + ((((UINT32)(hdr)->FlowLabel0) << 16) | ((UINT32)(hdr)->FlowLabel1)) + +#define DIVERT_IPV6HDR_SET_TRAFFICCLASS(hdr, val) \ + do \ + { \ + (hdr)->TrafficClass0 = ((UINT8)(val) >> 4); \ + (hdr)->TrafficClass1 = (UINT8)(val); \ + } \ + while (FALSE) +#define DIVERT_IPV6HDR_SET_FLOWLABEL(hdr, val) \ + do \ + { \ + (hdr)->FlowLabel0 = (UINT8)((val) >> 16); \ + (hdr)->FlowLabel1 = (UINT16)(val); \ + } \ + while (FALSE) + +typedef struct +{ + UINT8 Type; + UINT8 Code; + UINT16 Checksum; + UINT32 Body; +} DIVERT_ICMPHDR, *PDIVERT_ICMPHDR; + +typedef struct +{ + UINT8 Type; + UINT8 Code; + UINT16 Checksum; + UINT32 Body; +} DIVERT_ICMPV6HDR, *PDIVERT_ICMPV6HDR; + +typedef struct +{ + UINT16 SrcPort; + UINT16 DstPort; + UINT32 SeqNum; + UINT32 AckNum; + UINT16 Reserved1:4; + UINT16 HdrLength:4; + UINT16 Fin:1; + UINT16 Syn:1; + UINT16 Rst:1; + UINT16 Psh:1; + UINT16 Ack:1; + UINT16 Urg:1; + UINT16 Reserved2:2; + UINT16 Window; + UINT16 Checksum; + UINT16 UrgPtr; +} DIVERT_TCPHDR, *PDIVERT_TCPHDR; + +typedef struct +{ + UINT16 SrcPort; + UINT16 DstPort; + UINT16 Length; + UINT16 Checksum; +} DIVERT_UDPHDR, *PDIVERT_UDPHDR; + +/* + * Flags for DivertHelperCalcChecksums() + */ +#define DIVERT_HELPER_NO_IP_CHECKSUM 1 +#define DIVERT_HELPER_NO_ICMP_CHECKSUM 2 +#define DIVERT_HELPER_NO_ICMPV6_CHECKSUM 4 +#define DIVERT_HELPER_NO_TCP_CHECKSUM 8 +#define DIVERT_HELPER_NO_UDP_CHECKSUM 16 + +/* + * Parse IPv4/IPv6/ICMP/ICMPv6/TCP/UDP headers from a raw packet. + */ +extern DIVERTEXPORT BOOL DivertHelperParse( + __in PDIVERT_PACKET pPacket, + __in UINT packetLen, + __out_opt PDIVERT_IPHDR *ppIpHdr, + __out_opt PDIVERT_IPV6HDR *ppIpv6Hdr, + __out_opt PDIVERT_ICMPHDR *ppIcmpHdr, + __out_opt PDIVERT_ICMPV6HDR *ppIcmpv6Hdr, + __out_opt PDIVERT_TCPHDR *ppTcpHdr, + __out_opt PDIVERT_UDPHDR *ppUdpHdr, + __out_opt PVOID *ppData, + __out_opt UINT *pDataLen); + +/* + * Calculate IPv4/IPv6/ICMP/ICMPv6/TCP/UDP checksums. + */ +extern DIVERTEXPORT UINT DivertHelperCalcChecksums( + __inout PDIVERT_PACKET pPacket, + __in UINT packetLen, + __in UINT64 flags); + +#endif /* DIVERT_NO_HELPER_API */ + +#ifdef __cplusplus +} +#endif + +#endif /* __DIVERT_H */ diff --git a/include/divert_device.h b/include/divert_device.h new file mode 100644 index 0000000..23ec89a --- /dev/null +++ b/include/divert_device.h @@ -0,0 +1,145 @@ +/* + * divert_device.h + * (C) 2011, all rights reserved, + * + * 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 . + */ + +/* + * NOTE: This file is NOT part of the divert API. For the divert API, include + * "divert.h" instead. + */ + +#ifndef __DIVERT_DEVICE_H +#define __DIVERT_DEVICE_H + +#define DIVERT_DEVICE_NAME L"\\Device\\Divert" +#define DIVERT_DOS_DEVICE_NAME L"\\??\\Divert" + +#define DIVERT_VERSION 0 +#define DIVERT_MAGIC 0xF8D3 + +#define DIVERT_FILTER_FIELD_ZERO 0 +#define DIVERT_FILTER_FIELD_INBOUND 1 +#define DIVERT_FILTER_FIELD_OUTBOUND 2 +#define DIVERT_FILTER_FIELD_IFIDX 3 +#define DIVERT_FILTER_FIELD_SUBIFIDX 4 +#define DIVERT_FILTER_FIELD_IP 5 +#define DIVERT_FILTER_FIELD_IPV6 6 +#define DIVERT_FILTER_FIELD_ICMP 7 +#define DIVERT_FILTER_FIELD_TCP 8 +#define DIVERT_FILTER_FIELD_UDP 9 +#define DIVERT_FILTER_FIELD_ICMPV6 10 +#define DIVERT_FILTER_FIELD_IP_HDRLENGTH 11 +#define DIVERT_FILTER_FIELD_IP_TOS 12 +#define DIVERT_FILTER_FIELD_IP_LENGTH 13 +#define DIVERT_FILTER_FIELD_IP_ID 14 +#define DIVERT_FILTER_FIELD_IP_DF 15 +#define DIVERT_FILTER_FIELD_IP_MF 16 +#define DIVERT_FILTER_FIELD_IP_FRAGOFF 17 +#define DIVERT_FILTER_FIELD_IP_TTL 18 +#define DIVERT_FILTER_FIELD_IP_PROTOCOL 19 +#define DIVERT_FILTER_FIELD_IP_CHECKSUM 20 +#define DIVERT_FILTER_FIELD_IP_SRCADDR 21 +#define DIVERT_FILTER_FIELD_IP_DSTADDR 22 +#define DIVERT_FILTER_FIELD_IPV6_TRAFFICCLASS 23 +#define DIVERT_FILTER_FIELD_IPV6_FLOWLABEL 24 +#define DIVERT_FILTER_FIELD_IPV6_LENGTH 25 +#define DIVERT_FILTER_FIELD_IPV6_NEXTHDR 26 +#define DIVERT_FILTER_FIELD_IPV6_HOPLIMIT 27 +#define DIVERT_FILTER_FIELD_IPV6_SRCADDR 28 +#define DIVERT_FILTER_FIELD_IPV6_DSTADDR 29 +#define DIVERT_FILTER_FIELD_ICMP_TYPE 30 +#define DIVERT_FILTER_FIELD_ICMP_CODE 31 +#define DIVERT_FILTER_FIELD_ICMP_CHECKSUM 32 +#define DIVERT_FILTER_FIELD_ICMP_BODY 33 +#define DIVERT_FILTER_FIELD_ICMPV6_TYPE 34 +#define DIVERT_FILTER_FIELD_ICMPV6_CODE 35 +#define DIVERT_FILTER_FIELD_ICMPV6_CHECKSUM 36 +#define DIVERT_FILTER_FIELD_ICMPV6_BODY 37 +#define DIVERT_FILTER_FIELD_TCP_SRCPORT 38 +#define DIVERT_FILTER_FIELD_TCP_DSTPORT 39 +#define DIVERT_FILTER_FIELD_TCP_SEQNUM 40 +#define DIVERT_FILTER_FIELD_TCP_ACKNUM 41 +#define DIVERT_FILTER_FIELD_TCP_HDRLENGTH 42 +#define DIVERT_FILTER_FIELD_TCP_URG 43 +#define DIVERT_FILTER_FIELD_TCP_ACK 44 +#define DIVERT_FILTER_FIELD_TCP_PSH 45 +#define DIVERT_FILTER_FIELD_TCP_RST 46 +#define DIVERT_FILTER_FIELD_TCP_SYN 47 +#define DIVERT_FILTER_FIELD_TCP_FIN 48 +#define DIVERT_FILTER_FIELD_TCP_WINDOW 49 +#define DIVERT_FILTER_FIELD_TCP_CHECKSUM 50 +#define DIVERT_FILTER_FIELD_TCP_URGPTR 51 +#define DIVERT_FILTER_FIELD_TCP_PAYLOADLENGTH 52 +#define DIVERT_FILTER_FIELD_UDP_SRCPORT 53 +#define DIVERT_FILTER_FIELD_UDP_DSTPORT 54 +#define DIVERT_FILTER_FIELD_UDP_LENGTH 55 +#define DIVERT_FILTER_FIELD_UDP_CHECKSUM 56 +#define DIVERT_FILTER_FIELD_UDP_PAYLOADLENGTH 57 +#define DIVERT_FILTER_FIELD_MAX \ + DIVERT_FILTER_FIELD_UDP_PAYLOADLENGTH + +#define DIVERT_FILTER_TEST_EQ 0 +#define DIVERT_FILTER_TEST_NEQ 1 +#define DIVERT_FILTER_TEST_LT 2 +#define DIVERT_FILTER_TEST_LEQ 3 +#define DIVERT_FILTER_TEST_GT 4 +#define DIVERT_FILTER_TEST_GEQ 5 +#define DIVERT_FILTER_TEST_MAX DIVERT_FILTER_TEST_GEQ + +#define DIVERT_FILTER_MAXLEN 64 + +#define DIVERT_FILTER_RESULT_ACCEPT (DIVERT_FILTER_MAXLEN+1) +#define DIVERT_FILTER_RESULT_REJECT (DIVERT_FILTER_MAXLEN+2) + +/* + * Packet definitions. + */ +#ifndef DIVERT_PACKET_DIRECTION_OUTBOUND +#define DIVERT_PACKET_DIRECTION_OUTBOUND 0 +#define DIVERT_PACKET_DIRECTION_INBOUND 1 +#endif /* DIVERT_PACKET_DIRECTION_OUTBOUND */ + +/* + * Message definitions. + */ +struct divert_message_s +{ + UINT16 magic; // DIVERT_MAGIC + UINT8 version; // DIVERT_VERSION + UINT8 reserved; // Reserved (set to 0x0) +}; +typedef struct divert_message_s *divert_message_t; + +/* + * IOCTL structures. + */ +struct divert_ioctl_filter_s +{ + UINT8 field; // DIVERT_FILTER_FIELD_IP_* + UINT8 test; // DIVERT_FILTER_TEST_* + UINT8 success; // Success continuation. + UINT8 failure; // Fail continuation. + UINT32 arg[4]; // Argument. +}; +typedef struct divert_ioctl_filter_s *divert_ioctl_filter_t; + +/* + * IOCTL codes. + */ +#define IOCTL_DIVERT_SET_FILTER \ + CTL_CODE(FILE_DEVICE_NETWORK, 0x90A, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#endif // __DIVERT_DEVICE_H diff --git a/sys/Makefile b/sys/Makefile new file mode 100644 index 0000000..53b9a3d --- /dev/null +++ b/sys/Makefile @@ -0,0 +1 @@ +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/sys/Makefile.inc b/sys/Makefile.inc new file mode 100644 index 0000000..8639070 --- /dev/null +++ b/sys/Makefile.inc @@ -0,0 +1,4 @@ +$(TARGETPATH)\$(_BUILDARCH)\divert.inf: divert.inf + copy /y divert.inf $@ +$(TARGETPATH)\$(_BUILDARCH)\WdfCoInstaller01009.dll: + copy /y $(BASEDIR)\redist\wdf\$(_BUILDARCH)\WdfCoInstaller01009.dll $@ diff --git a/sys/divert.c b/sys/divert.c new file mode 100644 index 0000000..05bc600 --- /dev/null +++ b/sys/divert.c @@ -0,0 +1,2457 @@ +/* + * divert.c + * (C) 2011, all rights reserved, + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include + +#include "divert_device.h" + +/* + * WDK function declaration cruft. + */ +DRIVER_INITIALIZE DriverEntry; +EVT_WDF_DRIVER_UNLOAD divert_unload; +EVT_WDF_IO_QUEUE_IO_READ divert_read; +EVT_WDF_IO_QUEUE_IO_WRITE divert_write; +EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL divert_ioctl; +EVT_WDF_DEVICE_FILE_CREATE divert_create; +EVT_WDF_TIMER divert_timer; +EVT_WDF_FILE_CLEANUP divert_cleanup; +EVT_WDF_FILE_CLOSE divert_close; + +/* + * Debugging macros. + */ +// #define DEBUG_ON +#define DEBUG_BUFSIZE 512 + +#ifdef DEBUG_ON +static void DEBUG(PCCH format, ...) +{ + va_list args; + char buf[DEBUG_BUFSIZE+1]; + va_start(args, format); + RtlStringCbVPrintfA(buf, DEBUG_BUFSIZE, format, args); + DbgPrint("DIVERT: %s", buf); + va_end(args); +} +static void DEBUG_ERROR(PCCH format, NTSTATUS status, ...) +{ + va_list args; + char buf[DEBUG_BUFSIZE+1]; + va_start(args, status); + RtlStringCbVPrintfA(buf, DEBUG_BUFSIZE, format, args); + DbgPrint("DIVERT: *** ERROR ***: (status = %x): %s", status, buf); + va_end(args); +} +#else // DEBUG_ON +#define DEBUG(format, ...) +#define DEBUG_ERROR(format, status, ...) +#endif // DEBUG_ON + +/* + * Packet filter. + */ +struct filter_s +{ + UINT8 protocol:4; // field's protocol + UINT8 test:4; // Filter test + UINT8 field; // Field of interest + UINT8 success; // Success continuation + UINT8 failure; // Fail continuation + UINT32 arg[4]; // Comparison argument +}; +typedef struct filter_s *filter_t; +#define DIVERT_FILTER_PROTOCOL_NONE 0 +#define DIVERT_FILTER_PROTOCOL_IP 1 +#define DIVERT_FILTER_PROTOCOL_IPV6 2 +#define DIVERT_FILTER_PROTOCOL_ICMP 3 +#define DIVERT_FILTER_PROTOCOL_ICMPV6 4 +#define DIVERT_FILTER_PROTOCOL_TCP 5 +#define DIVERT_FILTER_PROTOCOL_UDP 6 + +/* + * Context information. + */ +#define DIVERT_CONTEXT_MAGIC 0xB75D18F185A65197ull +#define DIVERT_CONTEXT_SIZE (sizeof(struct context_s)) +#define DIVERT_CONTEXT_QUEUE_MAXLENGTH 1024 +#define DIVERT_CONTEXT_NUMLAYERS 4 +#define DIVERT_CONTEXT_OUTBOUND_IPV4_LAYER 0 +#define DIVERT_CONTEXT_INBOUND_IPV4_LAYER 1 +#define DIVERT_CONTEXT_OUTBOUND_IPV6_LAYER 2 +#define DIVERT_CONTEXT_INBOUND_IPV6_LAYER 3 +typedef enum +{ + DIVERT_CONTEXT_STATE_OPENING = 0xA0, // Context is opening. + DIVERT_CONTEXT_STATE_OPEN = 0xB1, // Context is open. + DIVERT_CONTEXT_STATE_CLOSING = 0xC2, // Context is closing. + DIVERT_CONTEXT_STATE_CLOSED = 0xD3, // Context is closed. + DIVERT_CONTEXT_STATE_INVALID = 0xE4 // Context is invalid. +} context_state_t; +struct context_s +{ + UINT64 magic; // DIVERT_CONTEXT_MAGIC + context_state_t state; // Context's state. + KSPIN_LOCK lock; // Context-wide lock. + WDFDEVICE device; // Context's device. + LIST_ENTRY packet_queue; // Packet queue. + ULONG packet_queue_length; // Packet queue length. + ULONG packet_queue_maxlength; // Packet queue max length. + WDFTIMER timer; // Packet timer. + BOOL timer_ticktock; // Packet timer ticktock. + NDIS_HANDLE pool_handle; // NET_BUFFER_LIST pool handle. + WDFQUEUE read_queue; // Read queue. + GUID sublayer_guid[DIVERT_CONTEXT_NUMLAYERS]; + // Sublayer GUIDs. + GUID callout_guid[DIVERT_CONTEXT_NUMLAYERS]; + // Callout GUIDs. + HANDLE engine_handle; // WFP engine handle. + struct filter_s filter[DIVERT_FILTER_MAXLEN]; + // Packet filter. +}; +typedef struct context_s context_s; +typedef struct context_s *context_t; +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(context_s, divert_context_get); + +/* + * Packets + */ +#define DIVERT_PACKET_TAG 'Pvid' +#define DIVERT_PACKET_SIZE (sizeof(struct packet_s)) +#define DIVERT_PACKET_TIMEOUT 128 +struct packet_s +{ + LIST_ENTRY entry; // Entry for queue + PNET_BUFFER buffer; // The packet + PNET_BUFFER_LIST buffers; // The NBL contain the packet + UINT8 direction; // Packet direction + UINT32 if_idx; // Interface index + UINT32 sub_if_idx; // Sub-interface index + BOOL ip_checksum; // IP checksum is valid + BOOL tcp_checksum; // TCP checksum is valid + BOOL udp_checksum; // UDP checksum is valid + BOOL timer_ticktock; // Time-out ticktock +}; +typedef struct packet_s *packet_t; +#define DIVERT_NET_BUFFER_LIST_TAG 'Lvid' + +/* + * Header definitions. + */ +struct hdr // Warning: must match DIVERT_PACKET in divert.h +{ + UINT8 Reserved[7]; + UINT8 Direction; + UINT32 IfIdx; + UINT32 SubIfIdx; +}; +struct iphdr +{ + UINT8 HdrLength:4; + UINT8 Version:4; + UINT8 TOS; + UINT16 Length; + UINT16 Id; + UINT16 FragOff0; + UINT8 TTL; + UINT8 Protocol; + UINT16 Checksum; + UINT32 SrcAddr; + UINT32 DstAddr; +}; +struct ipv6hdr +{ + UINT8 TrafficClass0:4; + UINT8 Version:4; + UINT8 FlowLabel0:4; + UINT8 TrafficClass1:4; + UINT16 FlowLabel1; + UINT16 Length; + UINT8 NextHdr; + UINT8 HopLimit; + UINT32 SrcAddr[4]; + UINT32 DstAddr[4]; +}; +struct icmphdr +{ + UINT8 Type; + UINT8 Code; + UINT16 Checksum; + UINT32 Body; +}; +struct icmpv6hdr +{ + UINT8 Type; + UINT8 Code; + UINT16 Checksum; + UINT32 Body; +}; +struct tcphdr +{ + UINT16 SrcPort; + UINT16 DstPort; + UINT32 SeqNum; + UINT32 AckNum; + UINT16 Reserved1:4; + UINT16 HdrLength:4; + UINT16 Fin:1; + UINT16 Syn:1; + UINT16 Rst:1; + UINT16 Psh:1; + UINT16 Ack:1; + UINT16 Urg:1; + UINT16 Reserved2:2; + UINT16 Window; + UINT16 Checksum; + UINT16 UrgPtr; +}; +struct udphdr +{ + UINT16 SrcPort; + UINT16 DstPort; + UINT16 Length; + UINT16 Checksum; +}; + +#define IPHDR_GET_FRAGOFF(hdr) (((hdr)->FragOff0) & 0xFF1F) +#define IPHDR_GET_MF(hdr) (((hdr)->FragOff0) & 0x0020) +#define IPHDR_GET_DF(hdr) (((hdr)->FragOff0) & 0x0040) +#define IPV6HDR_GET_TRAFFICCLASS(hdr) \ + ((((hdr)->TrafficClass0) << 4) | ((hdr)->TrafficClass1)) +#define IPV6HDR_GET_FLOWLABEL(hdr) \ + ((((UINT32)(hdr)->FlowLabel0) << 16) | ((UINT32)(hdr)->FlowLabel1)) + +/* + * Misc. + */ +#define UINT8_MAX 0xFF +#define UINT16_MAX 0xFFFF + +typedef void (*divert_callout_t)( + IN const FWPS_INCOMING_VALUES0 *fixed_vals, + IN const FWPS_INCOMING_METADATA_VALUES0 *meta_vals, IN OUT void *data, + const FWPS_FILTER0 *filter, IN UINT64 flow_context, + OUT FWPS_CLASSIFY_OUT0 *result); + +/* + * Global packet injection handles. + */ +HANDLE inject_handle; +HANDLE injectv6_handle; + +#define DIVERT_PACKET_ALLOW ((HANDLE)0) +#define DIVERT_PACKET_INJECTED ((HANDLE)1) + +/* + * Prototypes. + */ +extern VOID divert_ioctl(IN WDFQUEUE queue, IN WDFREQUEST request, + IN size_t in_length, IN size_t out_len, IN ULONG code); +extern VOID divert_read(IN WDFQUEUE queue, IN WDFREQUEST request, + IN size_t length); +static void divert_read_service(context_t context); +static BOOLEAN divert_context_verify(context_t context, context_state_t state); +extern VOID divert_create(IN WDFDEVICE device, IN WDFREQUEST request, + IN WDFFILEOBJECT object); +extern NTSTATUS divert_register_callout(context_t context, UINT idx, + wchar_t *sublayer_name, wchar_t *sublayer_desc, + wchar_t *callout_name, wchar_t *callout_desc, + wchar_t *filter_name, wchar_t *filter_desc); +extern VOID divert_timer(IN WDFTIMER timer); +extern VOID divert_cleanup(IN WDFFILEOBJECT object); +extern VOID divert_close(IN WDFFILEOBJECT object); +extern VOID divert_write(IN WDFQUEUE queue, IN WDFREQUEST request, + IN size_t length); +extern void NTAPI divert_inject_complete(VOID *context, + NET_BUFFER_LIST *packets, BOOLEAN dispatch_level); +static NTSTATUS divert_notify_callout(IN FWPS_CALLOUT_NOTIFY_TYPE type, + IN const GUID *filter_key, IN const FWPS_FILTER0 *filter); +static void divert_classify_outbound_v4_callout( + IN const FWPS_INCOMING_VALUES0 *fixed_vals, + IN const FWPS_INCOMING_METADATA_VALUES0 *meta_vals, IN OUT void *data, + const FWPS_FILTER0 *filter, IN UINT64 flow_context, + OUT FWPS_CLASSIFY_OUT0 *result); +static void divert_classify_inbound_v4_callout( + IN const FWPS_INCOMING_VALUES0 *fixed_vals, + IN const FWPS_INCOMING_METADATA_VALUES0 *meta_vals, IN OUT void *data, + const FWPS_FILTER0 *filter, IN UINT64 flow_context, + OUT FWPS_CLASSIFY_OUT0 *result); +static void divert_classify_outbound_v6_callout( + IN const FWPS_INCOMING_VALUES0 *fixed_vals, + IN const FWPS_INCOMING_METADATA_VALUES0 *meta_vals, IN OUT void *data, + const FWPS_FILTER0 *filter, IN UINT64 flow_context, + OUT FWPS_CLASSIFY_OUT0 *result); +static void divert_classify_inbound_v6_callout( + IN const FWPS_INCOMING_VALUES0 *fixed_vals, + IN const FWPS_INCOMING_METADATA_VALUES0 *meta_vals, IN OUT void *data, + const FWPS_FILTER0 *filter, IN UINT64 flow_context, + OUT FWPS_CLASSIFY_OUT0 *result); +static void divert_classify_callout(IN UINT8 direction, IN UINT32 if_idx, + IN UINT32 sub_if_idx, IN BOOL isipv4, + IN const FWPS_INCOMING_VALUES0 *fixed_vals, + IN const FWPS_INCOMING_METADATA_VALUES0 *meta_vals, IN OUT void *data, + const FWPS_FILTER0 *filter, IN UINT64 flow_context, + OUT FWPS_CLASSIFY_OUT0 *result); +static BOOL divert_reinject_packet(context_t context, UINT8 direction, + BOOL isipv4, UINT32 if_idx, UINT32 sub_if_idx, PNET_BUFFER_LIST buffers, + PNET_BUFFER buffer); +static void NTAPI divert_reinject_complete(VOID *context, + NET_BUFFER_LIST *buffers_cpy, BOOLEAN dispatch_level); +static BOOL divert_queue_packet(context_t context, PNET_BUFFER_LIST buffers, + PNET_BUFFER buffer, UINT8 direction, UINT32 if_idx, UINT32 sub_if_idx); +static UINT16 divert_checksum(const void *pseudo_header, + size_t pseudo_header_len, const void *data, size_t size); +static void divert_update_checksums(void *header, size_t len, + BOOL update_ip, BOOL update_tcp, BOOL update_udp); +static BOOL divert_filter(PNET_BUFFER buffer, UINT32 if_idx, UINT32 sub_if_idx, + BOOL outbound, filter_t filter); +static BOOL divert_filter_compile(divert_ioctl_filter_t ioctl_filter, + size_t ioctl_filter_len, filter_t filter); + +/* + * Driver entry routine. + */ +extern NTSTATUS DriverEntry(IN PDRIVER_OBJECT driver_obj, + IN PUNICODE_STRING reg_path) +{ + WDF_DRIVER_CONFIG config; + WDFDRIVER driver; + PWDFDEVICE_INIT device_init; + WDFDEVICE device; + WDF_FILEOBJECT_CONFIG file_config; + WDF_IO_QUEUE_CONFIG queue_config; + WDFQUEUE queue; + WDF_OBJECT_ATTRIBUTES obj_attrs; + NTSTATUS status; + DECLARE_CONST_UNICODE_STRING(device_name, DIVERT_DEVICE_NAME); + DECLARE_CONST_UNICODE_STRING(dos_device_name, DIVERT_DOS_DEVICE_NAME); + + DEBUG("LOAD: loading divert driver"); + + // Configure ourself as a non-PnP driver: + WDF_DRIVER_CONFIG_INIT(&config, WDF_NO_EVENT_CALLBACK); + config.DriverInitFlags |= WdfDriverInitNonPnpDriver; + config.EvtDriverUnload = divert_unload; + status = WdfDriverCreate(driver_obj, reg_path, WDF_NO_OBJECT_ATTRIBUTES, + &config, &driver); + if (!NT_SUCCESS(status)) + { + DEBUG_ERROR("failed to create WDF driver", status); + return status; + } + device_init = WdfControlDeviceInitAllocate(driver, + &SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RW_RES_R); + if (device_init == NULL) + { + status = STATUS_INSUFFICIENT_RESOURCES; + DEBUG_ERROR("failed to allocate WDF control device init structure", + status); + return status; + } + WdfDeviceInitSetDeviceType(device_init, FILE_DEVICE_NETWORK); + WdfDeviceInitSetIoType(device_init, WdfDeviceIoDirect); + status = WdfDeviceInitAssignName(device_init, &device_name); + if (!NT_SUCCESS(status)) + { + DEBUG_ERROR("failed to create WDF device name", status); + WdfDeviceInitFree(device_init); + return status; + } + WDF_FILEOBJECT_CONFIG_INIT(&file_config, divert_create, divert_close, + divert_cleanup); + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&obj_attrs, context_s); + WdfDeviceInitSetFileObjectConfig(device_init, &file_config, &obj_attrs); + WDF_OBJECT_ATTRIBUTES_INIT(&obj_attrs); + status = WdfDeviceCreate(&device_init, &obj_attrs, &device); + if (!NT_SUCCESS(status)) + { + DEBUG_ERROR("failed to create WDF control device", status); + WdfDeviceInitFree(device_init); + return status; + } + WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queue_config, + WdfIoQueueDispatchSequential); + queue_config.EvtIoRead = divert_read; + queue_config.EvtIoWrite = divert_write; + queue_config.EvtIoDeviceControl = divert_ioctl; + WDF_OBJECT_ATTRIBUTES_INIT(&obj_attrs); + status = WdfIoQueueCreate(device, &queue_config, &obj_attrs, &queue); + if (!NT_SUCCESS(status)) + { + DEBUG_ERROR("failed to create default WDF queue", status); + return status; + } + status = WdfDeviceCreateSymbolicLink(device, &dos_device_name); + if (!NT_SUCCESS(status)) + { + DEBUG_ERROR("failed to create device symbolic link", status); + return status; + } + WdfControlFinishInitializing(device); + + // Create the packet injection handles. + status = FwpsInjectionHandleCreate0(AF_INET, + FWPS_INJECTION_TYPE_NETWORK, &inject_handle); + if (!NT_SUCCESS(status)) + { + DEBUG_ERROR("failed to create WFP packet injection handle", status); + return status; + } + status = FwpsInjectionHandleCreate0(AF_INET6, + FWPS_INJECTION_TYPE_NETWORK, &injectv6_handle); + if (!NT_SUCCESS(status)) + { + DEBUG_ERROR("failed to create WFP ipv6 packet injection handle", + status); + return status; + } + + return STATUS_SUCCESS; +} + +/* + * Driver unload routine. + */ +extern VOID divert_unload(IN WDFDRIVER Driver) +{ + DEBUG("UNLOAD: unloading the divert driver"); + FwpsInjectionHandleDestroy0(inject_handle); + FwpsInjectionHandleDestroy0(injectv6_handle); +} + +/* + * Divert context verify. + */ +static BOOLEAN divert_context_verify(context_t context, context_state_t state) +{ + if (context == NULL) + { + DEBUG_ERROR("failed to verify context; context is NULL", + STATUS_INVALID_HANDLE); + return FALSE; + } + if (context->magic != DIVERT_CONTEXT_MAGIC) + { + DEBUG_ERROR("failed to verify context; invalid magic number", + STATUS_INVALID_HANDLE); + return FALSE; + } + if (context->state != state) + { + DEBUG_ERROR("failed to verify context; expected context state %x, " + "found context state %x", STATUS_INVALID_HANDLE, state, + context->state); + return FALSE; + } + return TRUE; +} + +/* + * Divert create routine. + */ +extern VOID divert_create(IN WDFDEVICE device, IN WDFREQUEST request, + IN WDFFILEOBJECT object) +{ + static wchar_t *sublayer_name[DIVERT_CONTEXT_NUMLAYERS] = + { + L"DivertSubLayerOutboundIPv4", + L"DivertSubLayerInboundIPv4", + L"DivertSubLayerOutboundIPv6", + L"DivertSubLayerInboundIPv6" + }; + static wchar_t *sublayer_desc[DIVERT_CONTEXT_NUMLAYERS] = + { + L"Divert sublayer (outbound IPv4)", + L"Divert sublayer (inbound IPv4)", + L"Divert sublayer (outbound IPv6)", + L"Divert sublayer (inbound IPv6)" + }; + static wchar_t *callout_name[DIVERT_CONTEXT_NUMLAYERS] = + { + L"DivertCalloutOutboundIPv4", + L"DivertCalloutInboundIPv4", + L"DivertCalloutOutboundIPv6", + L"DivertCalloutInboundIPv6" + }; + static wchar_t *callout_desc[DIVERT_CONTEXT_NUMLAYERS] = + { + L"Divert callout (outbound IPv4)", + L"Divert callout (inbound IPv4)", + L"Divert callout (outbound IPv6)", + L"Divert callout (inbound IPv6)" + }; + static wchar_t *filter_name[DIVERT_CONTEXT_NUMLAYERS] = + { + L"DivertFilterOutboundIPv4", + L"DivertFilterInboundIPv4", + L"DivertFilterOutboundIPv6", + L"DivertFilterInboundIPv6" + }; + static wchar_t *filter_desc[DIVERT_CONTEXT_NUMLAYERS] = + { + L"Divert filter (outbound IPv4)", + L"Divert filter (inbound IPv4)", + L"Divert filter (outbound IPv6)", + L"Divert filter (inbound IPv6)" + }; + NET_BUFFER_LIST_POOL_PARAMETERS pool_params; + WDF_IO_QUEUE_CONFIG queue_config; + WDF_TIMER_CONFIG timer_config; + WDF_OBJECT_ATTRIBUTES timer_attributes; + FWPM_SESSION0 session; + NTSTATUS status = STATUS_SUCCESS; + UINT8 i, j = 0; + context_t context = divert_context_get(object); + + DEBUG("CREATE: creating a new divert context (context=%p)", context); + + // Initialise the new context: + context->magic = DIVERT_CONTEXT_MAGIC; + context->state = DIVERT_CONTEXT_STATE_OPENING; + context->device = device; + context->packet_queue_length = 0; + context->packet_queue_maxlength = DIVERT_CONTEXT_QUEUE_MAXLENGTH; + for (i = 0; i < DIVERT_FILTER_MAXLEN; i++) + { + context->filter[i].protocol = DIVERT_FILTER_PROTOCOL_NONE; + context->filter[i].field = DIVERT_FILTER_FIELD_ZERO; + context->filter[i].test = DIVERT_FILTER_TEST_EQ; + context->filter[i].arg[0] = 0; + context->filter[i].arg[1] = 0; + context->filter[i].arg[2] = 0; + context->filter[i].arg[3] = 0; + context->filter[i].success = DIVERT_FILTER_RESULT_REJECT; + context->filter[i].failure = DIVERT_FILTER_RESULT_REJECT; + } + KeInitializeSpinLock(&context->lock); + InitializeListHead(&context->packet_queue); + for (i = 0; i < DIVERT_CONTEXT_NUMLAYERS; i++) + { + status = ExUuidCreate(&context->sublayer_guid[i]); + if (!NT_SUCCESS(status)) + { + DEBUG_ERROR("failed to create sub-layer GUID", status); + goto divert_create_exit; + } + status = ExUuidCreate(&context->callout_guid[i]); + if (!NT_SUCCESS(status)) + { + DEBUG_ERROR("failed to create callout GUID", status); + goto divert_create_exit; + } + } + RtlZeroMemory(&pool_params, sizeof(pool_params)); + pool_params.Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + pool_params.Header.Revision = NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1; + pool_params.Header.Size = sizeof(pool_params); + pool_params.fAllocateNetBuffer = TRUE; + pool_params.PoolTag = DIVERT_NET_BUFFER_LIST_TAG; + pool_params.DataSize = 0; + context->pool_handle = NdisAllocateNetBufferListPool(NULL, &pool_params); + if (context->pool_handle == NULL) + { + status = STATUS_INSUFFICIENT_RESOURCES; + DEBUG_ERROR("failed to allocate net buffer list pool", status); + goto divert_create_exit; + } + WDF_IO_QUEUE_CONFIG_INIT(&queue_config, WdfIoQueueDispatchManual); + status = WdfIoQueueCreate(device, &queue_config, WDF_NO_OBJECT_ATTRIBUTES, + &context->read_queue); + if (!NT_SUCCESS(status)) + { + DEBUG_ERROR("failed to create I/O read queue", status); + goto divert_create_exit; + } + WDF_TIMER_CONFIG_INIT_PERIODIC(&timer_config, divert_timer, + DIVERT_PACKET_TIMEOUT); + timer_config.AutomaticSerialization = TRUE; + WDF_OBJECT_ATTRIBUTES_INIT(&timer_attributes); + timer_attributes.ParentObject = (WDFOBJECT)object; + status = WdfTimerCreate(&timer_config, &timer_attributes, &context->timer); + if (!NT_SUCCESS(status)) + { + DEBUG_ERROR("failed to create packet time-out timer", status); + goto divert_create_exit; + } + RtlZeroMemory(&session, sizeof(session)); + session.flags |= FWPM_SESSION_FLAG_DYNAMIC; + status = FwpmEngineOpen0(NULL, RPC_C_AUTHN_DEFAULT, NULL, &session, + &context->engine_handle); + if (!NT_SUCCESS(status)) + { + DEBUG_ERROR("failed to create WFP engine handle", status); + goto divert_create_exit; + } + status = FwpmTransactionBegin0(context->engine_handle, 0); + if (!NT_SUCCESS(status)) + { + DEBUG_ERROR("failed to begin WFP transaction", status); + goto divert_create_exit; + } + for (j = 0; j < DIVERT_CONTEXT_NUMLAYERS; j++) + { + status = divert_register_callout(context, j, sublayer_name[j], + sublayer_desc[j], callout_name[j], callout_desc[j], filter_name[j], + filter_desc[j]); + if (!NT_SUCCESS(status)) + { + FwpmTransactionAbort0(context->engine_handle); + goto divert_create_exit; + } + } + status = FwpmTransactionCommit0(context->engine_handle); + if (!NT_SUCCESS(status)) + { + DEBUG_ERROR("failed to commit WFP transaction", status); + goto divert_create_exit; + } + + // Open for business: + context->state = DIVERT_CONTEXT_STATE_OPEN; + WdfTimerStart(context->timer, + WDF_REL_TIMEOUT_IN_MS(DIVERT_PACKET_TIMEOUT)); + +divert_create_exit: + + // Clean-up on error: + if (!NT_SUCCESS(status)) + { + if (context->pool_handle != NULL) + { + NdisFreeNetBufferPool(context->pool_handle); + } + if (context->read_queue != NULL) + { + WdfObjectDelete(context->read_queue); + } + if (context->timer != NULL) + { + WdfObjectDelete(context->timer); + } + if (context->engine_handle != NULL) + { + FwpmEngineClose0(context->engine_handle); + } + for (i = 0; i < j; i++) + { + FwpsCalloutUnregisterByKey0(&context->callout_guid[i]); + } + context->state = DIVERT_CONTEXT_STATE_INVALID; + } + + WdfRequestComplete(request, status); +} + +/* + * Add a WFP filter. + */ +extern NTSTATUS divert_register_callout(context_t context, UINT idx, + wchar_t *sublayer_name, wchar_t *sublayer_desc, + wchar_t *callout_name, wchar_t *callout_desc, + wchar_t *filter_name, wchar_t *filter_desc) +{ + GUID layer; + FWPM_SUBLAYER0 sublayer; + FWPS_CALLOUT0 scallout; + FWPM_CALLOUT0 mcallout; + FWPM_FILTER0 filter; + BOOL registered = FALSE; + divert_callout_t callout; + NTSTATUS status; + + switch (idx) + { + case DIVERT_CONTEXT_OUTBOUND_IPV4_LAYER: + layer = FWPM_LAYER_OUTBOUND_IPPACKET_V4; + callout = divert_classify_outbound_v4_callout; + break; + case DIVERT_CONTEXT_INBOUND_IPV4_LAYER: + layer = FWPM_LAYER_INBOUND_IPPACKET_V4; + callout = divert_classify_inbound_v4_callout; + break; + case DIVERT_CONTEXT_OUTBOUND_IPV6_LAYER: + layer = FWPM_LAYER_OUTBOUND_IPPACKET_V6; + callout = divert_classify_outbound_v6_callout; + break; + case DIVERT_CONTEXT_INBOUND_IPV6_LAYER: + layer = FWPM_LAYER_INBOUND_IPPACKET_V6; + callout = divert_classify_inbound_v6_callout; + break; + default: + return STATUS_INVALID_PARAMETER; + } + + RtlZeroMemory(&sublayer, sizeof(sublayer)); + sublayer.subLayerKey = context->sublayer_guid[idx]; + sublayer.displayData.name = sublayer_name; + sublayer.displayData.description = sublayer_desc; + sublayer.weight = FWP_EMPTY; + RtlZeroMemory(&scallout, sizeof(scallout)); + scallout.calloutKey = context->callout_guid[idx]; + scallout.classifyFn = callout; + scallout.notifyFn = divert_notify_callout; + scallout.flowDeleteFn = NULL; + RtlZeroMemory(&mcallout, sizeof(mcallout)); + mcallout.calloutKey = context->callout_guid[idx]; + mcallout.displayData.name = callout_name; + mcallout.displayData.description = callout_desc; + mcallout.applicableLayer = layer; + RtlZeroMemory(&filter, sizeof(filter)); + filter.layerKey = layer; + filter.displayData.name = filter_name; + filter.displayData.description = filter_desc; + filter.action.type = FWP_ACTION_CALLOUT_TERMINATING; + filter.action.calloutKey = context->callout_guid[idx]; + filter.subLayerKey = context->sublayer_guid[idx]; + filter.weight.type = FWP_EMPTY; + filter.rawContext = (UINT64)context; + status = FwpmSubLayerAdd0(context->engine_handle, &sublayer, NULL); + if (!NT_SUCCESS(status)) + { + DEBUG_ERROR("failed to add WFP sub-layer", status); + goto divert_register_callout_error; + } + status = FwpsCalloutRegister0(WdfDeviceWdmGetDeviceObject(context->device), + &scallout, NULL); + if (!NT_SUCCESS(status)) + { + DEBUG_ERROR("failed to register WFP callout", status); + goto divert_register_callout_error; + } + registered = TRUE; + status = FwpmCalloutAdd0(context->engine_handle, &mcallout, NULL, NULL); + if (!NT_SUCCESS(status)) + { + DEBUG_ERROR("failed to add WFP callout", status); + goto divert_register_callout_error; + } + status = FwpmFilterAdd0(context->engine_handle, &filter, NULL, NULL); + if (!NT_SUCCESS(status)) + { + DEBUG_ERROR("failed to add WFP filter", status); + goto divert_register_callout_error; + } + + return STATUS_SUCCESS; + +divert_register_callout_error: + if (registered) + { + FwpsCalloutUnregisterByKey0(&context->callout_guid[idx]); + } + return status; +} + +/* + * Divert old-packet cleanup routine. + */ +extern VOID divert_timer(IN WDFTIMER timer) +{ + KLOCK_QUEUE_HANDLE lock_handle; + PLIST_ENTRY entry; + PNET_BUFFER_LIST packets; + WDFFILEOBJECT object = (WDFFILEOBJECT)WdfTimerGetParentObject(timer); + context_t context = divert_context_get(object); + packet_t packet; + + if (!divert_context_verify(context, DIVERT_CONTEXT_STATE_OPEN)) + { + return; + } + + DEBUG("TIMER (context=%p, ticktock=%u)", context, context->timer_ticktock); + + // Sweep away old packets. + KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); + while (!IsListEmpty(&context->packet_queue)) + { + entry = RemoveHeadList(&context->packet_queue); + packet = CONTAINING_RECORD(entry, struct packet_s, entry); + if (packet->timer_ticktock == context->timer_ticktock) + { + InsertHeadList(&context->packet_queue, entry); + break; + } + context->packet_queue_length--; + KeReleaseInStackQueuedSpinLock(&lock_handle); + + // Packet is old, dispose of it. + DEBUG("TIMEOUT (context=%p, packet=%p)", context, packet); + FwpsDereferenceNetBufferList0(packet->buffers, FALSE); + ExFreePoolWithTag(packet, DIVERT_PACKET_TAG); + KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); + } + + KeReleaseInStackQueuedSpinLock(&lock_handle); + context->timer_ticktock = !context->timer_ticktock; +} + +/* + * Divert cleanup routine. + */ +extern VOID divert_cleanup(IN WDFFILEOBJECT object) +{ + KLOCK_QUEUE_HANDLE lock_handle; + PLIST_ENTRY entry; + PNET_BUFFER_LIST packets; + UINT i; + context_t context = divert_context_get(object); + packet_t packet; + + DEBUG("CLEANUP: cleaning up divert context (context=%p)", context); + + if (!divert_context_verify(context, DIVERT_CONTEXT_STATE_OPEN)) + { + return; + } + WdfTimerStop(context->timer, FALSE); + KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); + context->state = DIVERT_CONTEXT_STATE_CLOSING; + while (!IsListEmpty(&context->packet_queue)) + { + entry = RemoveHeadList(&context->packet_queue); + KeReleaseInStackQueuedSpinLock(&lock_handle); + packet = CONTAINING_RECORD(entry, struct packet_s, entry); + FwpsDereferenceNetBufferList0(packet->buffers, FALSE); + ExFreePoolWithTag(packet, DIVERT_PACKET_TAG); + KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); + } + KeReleaseInStackQueuedSpinLock(&lock_handle); + WdfIoQueuePurge(context->read_queue, NULL, NULL); + WdfObjectDelete(context->read_queue); + WdfObjectDelete(context->timer); + FwpmEngineClose0(context->engine_handle); + for (i = 0; i < DIVERT_CONTEXT_NUMLAYERS; i++) + { + FwpsCalloutUnregisterByKey0(&context->callout_guid[i]); + } + NdisFreeNetBufferPool(context->pool_handle); +} + +/* + * Divert close routine. + */ +extern VOID divert_close(IN WDFFILEOBJECT object) +{ + context_t context = divert_context_get(object); + + DEBUG("CLOSE: closing divert context (context=%p)", context); + + if (!divert_context_verify(context, DIVERT_CONTEXT_STATE_CLOSING)) + { + return; + } + context->state = DIVERT_CONTEXT_STATE_CLOSED; +} + +/* + * Divert read routine. + */ +extern VOID divert_read(IN WDFQUEUE queue, IN WDFREQUEST request, + IN size_t length) +{ + NTSTATUS status = STATUS_SUCCESS; + context_t context = divert_context_get(WdfRequestGetFileObject(request)); + + DEBUG("READ: reading diverted packet (context=%p, request=%p)", context, + request); + + if (!divert_context_verify(context, DIVERT_CONTEXT_STATE_OPEN)) + { + status = STATUS_INVALID_DEVICE_STATE; + goto divert_read_exit; + } + + // Forward the request to the pending read queue: + status = WdfRequestForwardToIoQueue(request, context->read_queue); + if (!NT_SUCCESS(status)) + { + DEBUG_ERROR("failed to forward I/O request to read queue", status); + goto divert_read_exit; + } + + // Service the read request: + divert_read_service(context); + +divert_read_exit: + + if (!NT_SUCCESS(status)) + { + WdfRequestCompleteWithInformation(request, status, 0); + } +} + +/* + * Divert read request service. + */ +static void divert_read_service(context_t context) +{ + KLOCK_QUEUE_HANDLE lock_handle; + WDFREQUEST request; + PLIST_ENTRY entry; + PMDL dst_mdl; + PVOID dst, src; + ULONG dst_len, src_len; + NTSTATUS status; + packet_t packet; + divert_message_t message; + struct hdr *header; + + KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); + while (context->state == DIVERT_CONTEXT_STATE_OPEN && + !IsListEmpty(&context->packet_queue)) + { + status = WdfIoQueueRetrieveNextRequest(context->read_queue, &request); + if (!NT_SUCCESS(status)) + { + break; + } + entry = RemoveHeadList(&context->packet_queue); + context->packet_queue_length--; + KeReleaseInStackQueuedSpinLock(&lock_handle); + packet = CONTAINING_RECORD(entry, struct packet_s, entry); + + DEBUG("SERVICE: servicing read request (context=%p, request=%p, " + "packet=%p)", context, request, packet); + + // We have now have a read request and a packet; service the read. + status = WdfRequestRetrieveOutputWdmMdl(request, &dst_mdl); + dst_len = 0; + if (!NT_SUCCESS(status)) + { + DEBUG_ERROR("failed to retrieve output MDL", status); + goto divert_read_service_complete; + } + dst = MmGetSystemAddressForMdlSafe(dst_mdl, NormalPagePriority); + if (dst == NULL) + { + status = STATUS_INSUFFICIENT_RESOURCES; + DEBUG_ERROR("failed to get address of output MDL", status); + goto divert_read_service_complete; + } + dst_len = MmGetMdlByteCount(dst_mdl); + if (dst_len < sizeof(struct hdr)) + { + status = STATUS_BUFFER_TOO_SMALL; + DEBUG_ERROR("failed to write to output buffer; buffer too small - " + "cannot fit packet header", status); + goto divert_read_service_complete; + } + header = (struct hdr *)dst; + header->Direction = packet->direction; + header->IfIdx = packet->if_idx; + header->SubIfIdx = packet->sub_if_idx; + message = (divert_message_t)header->Reserved; + message->magic = DIVERT_MAGIC; + message->version = DIVERT_VERSION; + message->reserved = 0x0; + dst = (PVOID)((UINT8 *)dst + sizeof(struct hdr)); + dst_len -= sizeof(struct hdr); + src_len = NET_BUFFER_DATA_LENGTH(packet->buffer); + dst_len = (src_len < dst_len? src_len: dst_len); + src = NdisGetDataBuffer(packet->buffer, dst_len, NULL, 1, 0); + if (src == NULL) + { + NdisGetDataBuffer(packet->buffer, dst_len, dst, 1, 0); + } + else + { + RtlCopyMemory(dst, src, dst_len); + } + + // Compute the IP/TCP/UDP checksums here if required. + divert_update_checksums(dst, dst_len, packet->ip_checksum, + packet->tcp_checksum, packet->udp_checksum); + + status = STATUS_SUCCESS; + +divert_read_service_complete: + FwpsDereferenceNetBufferList0(packet->buffers, FALSE); + ExFreePoolWithTag(packet, DIVERT_PACKET_TAG); + if (NT_SUCCESS(status)) + { + WdfRequestCompleteWithInformation(request, status, + src_len + sizeof(struct hdr)); + } + else + { + WdfRequestComplete(request, status); + } + KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); + } + KeReleaseInStackQueuedSpinLock(&lock_handle); +} + +/* + * Divert write routine. + */ +extern VOID divert_write(IN WDFQUEUE queue, IN WDFREQUEST request, + IN size_t length) +{ + PMDL mdl, sub_mdl = NULL; + UINT8 *sub_addr; + UINT sub_len; + PNET_BUFFER_LIST buffers = NULL; + NTSTATUS status = STATUS_SUCCESS; + divert_message_t message; + struct hdr *header; + struct iphdr *ip_header; + BOOL isipv4; + context_t context = divert_context_get(WdfRequestGetFileObject(request)); + + DEBUG("WRITE: writing/injecting a packet (context=%p, request=%p)", + context, request); + + if (!divert_context_verify(context, DIVERT_CONTEXT_STATE_OPEN)) + { + status = STATUS_INVALID_DEVICE_STATE; + goto divert_write_exit; + } + + status = WdfRequestRetrieveInputWdmMdl(request, &mdl); + if (!NT_SUCCESS(status)) + { + DEBUG_ERROR("failed to retrieve input MDL", status); + goto divert_write_exit; + } + if (MmGetMdlByteCount(mdl) != length) + { + status = STATUS_INVALID_BUFFER_SIZE; + DEBUG_ERROR("failed to validate MDL buffer size", status); + goto divert_write_exit; + } + if (length <= sizeof(struct hdr)) + { + status = STATUS_BUFFER_TOO_SMALL; + DEBUG_ERROR("failed to read packet header; buffer too small", status); + goto divert_write_exit; + } + + header = (struct hdr *)MmGetSystemAddressForMdlSafe(mdl, + NormalPagePriority); + if (header == NULL) + { + status = STATUS_INSUFFICIENT_RESOURCES; + DEBUG_ERROR("failed to get MDL address", status); + goto divert_write_exit; + } + + message = (divert_message_t)header->Reserved; + if (message->magic != DIVERT_MAGIC || + message->version != DIVERT_VERSION) + { + status = STATUS_INVALID_PARAMETER; + DEBUG_ERROR("failed to validate packet header", status); + goto divert_write_exit; + } + + ip_header = (struct iphdr *)(header + 1); + switch (ip_header->Version) + { + case 4: + isipv4 = TRUE; + break; + case 6: + isipv4 = FALSE; + break; + default: + status = STATUS_INVALID_PARAMETER; + DEBUG_ERROR("failed to inject packet; not IPv4 nor IPv6", status); + goto divert_write_exit; + } + + sub_addr = (UINT8 *)MmGetMdlVirtualAddress(mdl) + sizeof(struct hdr); + sub_len = length - sizeof(struct hdr); + sub_mdl = IoAllocateMdl(sub_addr, sub_len, FALSE, FALSE, NULL); + if (sub_mdl == NULL) + { + status = STATUS_INSUFFICIENT_RESOURCES; + DEBUG_ERROR("failed to allocate sub-mdl", status); + goto divert_write_exit; + } + IoBuildPartialMdl(mdl, sub_mdl, sub_addr, sub_len); + + status = FwpsAllocateNetBufferAndNetBufferList0(context->pool_handle, + 0, 0, sub_mdl, 0, sub_len, &buffers); + if (!NT_SUCCESS(status)) + { + DEBUG_ERROR("failed to create NET_BUFFER_LIST for injected packet", + status); + goto divert_write_exit; + } + + switch (header->Direction) + { + case DIVERT_PACKET_DIRECTION_OUTBOUND: + if (isipv4) + { + status = FwpsInjectNetworkSendAsync0(inject_handle, + DIVERT_PACKET_INJECTED, 0, UNSPECIFIED_COMPARTMENT_ID, + buffers, divert_inject_complete, (HANDLE)request); + } + else + { + status = FwpsInjectNetworkSendAsync0(injectv6_handle, + DIVERT_PACKET_INJECTED, 0, UNSPECIFIED_COMPARTMENT_ID, + buffers, divert_inject_complete, (HANDLE)request); + } + break; + case DIVERT_PACKET_DIRECTION_INBOUND: + if (isipv4) + { + status = FwpsInjectNetworkReceiveAsync0(inject_handle, + DIVERT_PACKET_INJECTED, 0, UNSPECIFIED_COMPARTMENT_ID, + header->IfIdx, header->SubIfIdx, buffers, + divert_inject_complete, (HANDLE)request); + } + else + { + status = FwpsInjectNetworkReceiveAsync0(injectv6_handle, + DIVERT_PACKET_INJECTED, 0, UNSPECIFIED_COMPARTMENT_ID, + header->IfIdx, header->SubIfIdx, buffers, + divert_inject_complete, (HANDLE)request); + } + break; + default: + status = STATUS_INVALID_PARAMETER; + DEBUG_ERROR("failed to inject packet; invalid direction", status); + goto divert_write_exit; + } + +divert_write_exit: + + if (!NT_SUCCESS(status)) + { + DEBUG_ERROR("failed to (re)inject packet", status); + if (buffers != NULL) + { + FwpsFreeNetBufferList0(buffers); + } + if (sub_mdl != NULL) + { + IoFreeMdl(sub_mdl); + } + WdfRequestComplete(request, status); + } +} + +/* + * Divert inject complete routine. + */ +static void NTAPI divert_inject_complete(VOID *context, + NET_BUFFER_LIST *buffers, BOOLEAN dispatch_level) +{ + PMDL sub_mdl; + WDFREQUEST request = (WDFREQUEST)context; + PNET_BUFFER buffer; + size_t length = 0; + NTSTATUS status; + UNREFERENCED_PARAMETER(dispatch_level); + + DEBUG("COMPLETE: write/inject packet complete (request=%p)", request); + + buffer = NET_BUFFER_LIST_FIRST_NB(buffers); + sub_mdl = NET_BUFFER_FIRST_MDL(buffer); + status = NET_BUFFER_LIST_STATUS(buffers); + if (NT_SUCCESS(status)) + { + length = NET_BUFFER_DATA_LENGTH(buffer); + } + else + { + DEBUG_ERROR("failed to inject packet", status); + } + IoFreeMdl(sub_mdl); + FwpsFreeNetBufferList0(buffers); + WdfRequestCompleteWithInformation(request, status, + (ULONG_PTR)(length + sizeof(struct hdr))); +} + +/* + * Divert I/O control. + */ +extern VOID divert_ioctl(IN WDFQUEUE queue, IN WDFREQUEST request, + IN size_t out_length, IN size_t in_length, IN ULONG code) +{ + PCHAR buf; + size_t buflen, filter_len; + divert_message_t message; + divert_ioctl_filter_t filter; + NTSTATUS status = STATUS_SUCCESS; + context_t context = divert_context_get(WdfRequestGetFileObject(request)); + UNREFERENCED_PARAMETER(queue); + + DEBUG("IOCTL: I/O control request (context=%p)", context); + + if (!divert_context_verify(context, DIVERT_CONTEXT_STATE_OPEN)) + { + status = STATUS_INVALID_DEVICE_STATE; + goto divert_ioctl_exit; + } + + switch (code) + { + case IOCTL_DIVERT_SET_FILTER: + status = WdfRequestRetrieveInputBuffer(request, 0, &buf, &buflen); + if (!NT_SUCCESS(status)) + { + DEBUG_ERROR("failed to retrieve input buffer", status); + goto divert_ioctl_exit; + } + if (buflen != in_length || + buflen < sizeof(struct divert_message_s)) + { + status = STATUS_BUFFER_TOO_SMALL; + DEBUG_ERROR("input buffer has an invalid length %u bytes", + status, buflen); + goto divert_ioctl_exit; + } + message = (divert_message_t)buf; + if (message->version != DIVERT_VERSION || + message->magic != DIVERT_MAGIC) + { + status = STATUS_INVALID_DEVICE_REQUEST; + DEBUG_ERROR("input buffer contains an invalid request header", + status); + goto divert_ioctl_exit; + } + filter = (divert_ioctl_filter_t)(message+1); + filter_len = buflen - sizeof(struct divert_message_s); + if (!divert_filter_compile(filter, filter_len, context->filter)) + { + status = STATUS_INVALID_DEVICE_REQUEST; + DEBUG_ERROR("failed to compile filter", status); + goto divert_ioctl_exit; + } + break; + default: + status = STATUS_INVALID_DEVICE_REQUEST; + DEBUG_ERROR("failed to complete I/O control; invalid request", + status); + break; + } + +divert_ioctl_exit: + WdfRequestComplete(request, status); +} + +/* + * Divert notify callout. + */ +static NTSTATUS divert_notify_callout(IN FWPS_CALLOUT_NOTIFY_TYPE type, + IN const GUID *filter_key, IN const FWPS_FILTER0 *filter) +{ + UNREFERENCED_PARAMETER(type); + UNREFERENCED_PARAMETER(filter_key); + UNREFERENCED_PARAMETER(filter); + return STATUS_SUCCESS; +} + +/* + * Divert classify outbound IPv4 callout. + */ +static void divert_classify_outbound_v4_callout( + IN const FWPS_INCOMING_VALUES0 *fixed_vals, + IN const FWPS_INCOMING_METADATA_VALUES0 *meta_vals, IN OUT void *data, + const FWPS_FILTER0 *filter, IN UINT64 flow_context, + OUT FWPS_CLASSIFY_OUT0 *result) +{ + divert_classify_callout(DIVERT_PACKET_DIRECTION_OUTBOUND, + fixed_vals->incomingValue[ + FWPS_FIELD_OUTBOUND_IPPACKET_V4_INTERFACE_INDEX].value.uint32, + fixed_vals->incomingValue[ + FWPS_FIELD_OUTBOUND_IPPACKET_V4_SUB_INTERFACE_INDEX].value.uint32, + TRUE, fixed_vals, meta_vals, data, filter, flow_context, result); +} + +/* + * Divert classify outbound IPv6 callout. + */ +static void divert_classify_outbound_v6_callout( + IN const FWPS_INCOMING_VALUES0 *fixed_vals, + IN const FWPS_INCOMING_METADATA_VALUES0 *meta_vals, IN OUT void *data, + const FWPS_FILTER0 *filter, IN UINT64 flow_context, + OUT FWPS_CLASSIFY_OUT0 *result) +{ + divert_classify_callout(DIVERT_PACKET_DIRECTION_OUTBOUND, + fixed_vals->incomingValue[ + FWPS_FIELD_OUTBOUND_IPPACKET_V6_INTERFACE_INDEX].value.uint32, + fixed_vals->incomingValue[ + FWPS_FIELD_OUTBOUND_IPPACKET_V6_SUB_INTERFACE_INDEX].value.uint32, + FALSE, fixed_vals, meta_vals, data, filter, flow_context, result); +} + +/* + * Divert classify inbound IPv4 callout. + */ +static void divert_classify_inbound_v4_callout( + IN const FWPS_INCOMING_VALUES0 *fixed_vals, + IN const FWPS_INCOMING_METADATA_VALUES0 *meta_vals, IN OUT void *data, + const FWPS_FILTER0 *filter, IN UINT64 flow_context, + OUT FWPS_CLASSIFY_OUT0 *result) +{ + PNET_BUFFER_LIST buffers = (PNET_BUFFER_LIST)data; + PNET_BUFFER buffer; + NTSTATUS status; + + if (!(result->rights & FWPS_RIGHT_ACTION_WRITE) || data == NULL) + { + return; + } + + buffer = NET_BUFFER_LIST_FIRST_NB(buffers); + status = NdisRetreatNetBufferDataStart(buffer, meta_vals->ipHeaderSize, + 0, NULL); + if (!NT_SUCCESS(status)) + { + result->actionType = FWP_ACTION_PERMIT; + return; + } + divert_classify_callout(DIVERT_PACKET_DIRECTION_INBOUND, + fixed_vals->incomingValue[ + FWPS_FIELD_INBOUND_IPPACKET_V4_INTERFACE_INDEX].value.uint32, + fixed_vals->incomingValue[ + FWPS_FIELD_INBOUND_IPPACKET_V4_SUB_INTERFACE_INDEX].value.uint32, + TRUE, fixed_vals, meta_vals, data, filter, flow_context, result); + if (result->actionType != FWP_ACTION_BLOCK) + { + NdisAdvanceNetBufferDataStart(buffer, meta_vals->ipHeaderSize, + FALSE, NULL); + } +} + +/* + * Divert classify inbound IPv6 callout. + */ +static void divert_classify_inbound_v6_callout( + IN const FWPS_INCOMING_VALUES0 *fixed_vals, + IN const FWPS_INCOMING_METADATA_VALUES0 *meta_vals, IN OUT void *data, + const FWPS_FILTER0 *filter, IN UINT64 flow_context, + OUT FWPS_CLASSIFY_OUT0 *result) +{ + PNET_BUFFER_LIST buffers = (PNET_BUFFER_LIST)data; + PNET_BUFFER buffer; + NTSTATUS status; + + if (!(result->rights & FWPS_RIGHT_ACTION_WRITE) || data == NULL) + { + return; + } + + buffer = NET_BUFFER_LIST_FIRST_NB(buffers); + status = NdisRetreatNetBufferDataStart(buffer, sizeof(struct ipv6hdr), + 0, NULL); + if (!NT_SUCCESS(status)) + { + result->actionType = FWP_ACTION_PERMIT; + return; + } + divert_classify_callout(DIVERT_PACKET_DIRECTION_INBOUND, + fixed_vals->incomingValue[ + FWPS_FIELD_INBOUND_IPPACKET_V6_INTERFACE_INDEX].value.uint32, + fixed_vals->incomingValue[ + FWPS_FIELD_INBOUND_IPPACKET_V6_SUB_INTERFACE_INDEX].value.uint32, + FALSE, fixed_vals, meta_vals, data, filter, flow_context, result); + if (result->actionType != FWP_ACTION_BLOCK) + { + NdisAdvanceNetBufferDataStart(buffer, sizeof(struct ipv6hdr), FALSE, + NULL); + } +} +/* + * Divert classify callout. + */ +static void divert_classify_callout(IN UINT8 direction, IN UINT32 if_idx, + IN UINT32 sub_if_idx, IN BOOL isipv4, + IN const FWPS_INCOMING_VALUES0 *fixed_vals, + IN const FWPS_INCOMING_METADATA_VALUES0 *meta_vals, IN OUT void *data, + const FWPS_FILTER0 *filter, IN UINT64 flow_context, + OUT FWPS_CLASSIFY_OUT0 *result) +{ + KLOCK_QUEUE_HANDLE lock_handle; + FWPS_PACKET_INJECTION_STATE packet_state; + HANDLE packet_context; + PNET_BUFFER_LIST buffers, buffers_fst, buffers_cpy, buffers_itr; + PNET_BUFFER buffer, buffer0; + PLIST_ENTRY entry; + BOOL outbound; + context_t context; + packet_t packet; + + // Basic checks: + if (!(result->rights & FWPS_RIGHT_ACTION_WRITE) || data == NULL) + { + return; + } + + context = (context_t)filter->context; + buffers = (PNET_BUFFER_LIST)data; + if (isipv4) + { + packet_state = FwpsQueryPacketInjectionState0(inject_handle, buffers, + &packet_context); + } + else + { + packet_state = FwpsQueryPacketInjectionState0(injectv6_handle, + buffers, &packet_context); + } + if ((packet_state == FWPS_PACKET_INJECTED_BY_SELF || + packet_state == FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF) && + packet_context == DIVERT_PACKET_INJECTED) + { + result->actionType = FWP_ACTION_PERMIT; + return; + } + if (!divert_context_verify(context, DIVERT_CONTEXT_STATE_OPEN)) + { + result->actionType = FWP_ACTION_PERMIT; + return; + } + + /* + * This code is complicated by the fact the a single NET_BUFFER_LIST + * may contain several NET_BUFFER structures. Each NET_BUFFER needs to + * be filtered independently. To achieve this we do the following: + * 1) First check if any NET_BUFFER passes the filter. + * 2) If no, then PERMIT the entire NET_BUFFER_LIST. + * 3) Else, split the NET_BUFFER_LIST into individual NET_BUFFERs; and + * either queue or re-inject based on the filter. + */ + + // Find the first NET_BUFFER we need to queue: + buffers_fst = buffers; + outbound = (direction == DIVERT_PACKET_DIRECTION_OUTBOUND); + do + { + buffer = NET_BUFFER_LIST_FIRST_NB(buffers_fst); + if (divert_filter(buffer, if_idx, sub_if_idx, outbound, + context->filter)) + { + break; + } + buffers_fst= NET_BUFFER_LIST_NEXT_NBL(buffers_fst); + } + while (buffers_fst != NULL); + + // No NET_BUFFER needs to be queued, permit the entire NET_BUFFER_LIST: + if (buffers_fst == NULL) + { + result->actionType = FWP_ACTION_PERMIT; + return; + } + + // Re-inject all packets up to 'buffers_fst' + buffers_itr = buffers; + while (buffers_itr != buffers_fst) + { + buffer = NET_BUFFER_LIST_FIRST_NB(buffers_itr); + if (!divert_reinject_packet(context, direction, isipv4, if_idx, + sub_if_idx, buffers, buffer)) + { + goto divert_classify_callout_exit; + } + buffers_itr = NET_BUFFER_LIST_NEXT_NBL(buffers_itr); + } + + // Queue buffers_itr = buffers_fst, which matched our filter. + buffer = NET_BUFFER_LIST_FIRST_NB(buffers_itr); + if (!divert_queue_packet(context, buffers, buffer, direction, if_idx, + sub_if_idx)) + { + goto divert_classify_callout_exit; + } + buffers_itr = NET_BUFFER_LIST_NEXT_NBL(buffers_itr); + + // Queue or re-inject remaining packets. + while (buffers_itr != NULL) + { + buffer = NET_BUFFER_LIST_FIRST_NB(buffers_itr); + if (divert_filter(buffer, if_idx, sub_if_idx, outbound, + context->filter)) + { + if (!divert_queue_packet(context, buffers, buffer, direction, + if_idx, sub_if_idx)) + { + goto divert_classify_callout_exit; + } + } + else + { + if (!divert_reinject_packet(context, direction, isipv4, if_idx, + sub_if_idx, buffers, buffer)) + { + goto divert_classify_callout_exit; + } + } + } + + // Since new packets have been queued, service any read. + divert_read_service(context); + +divert_classify_callout_exit: + result->actionType = FWP_ACTION_BLOCK; + result->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB; +} + +/* + * Queue a NET_BUFFER. + */ +static BOOL divert_queue_packet(context_t context, PNET_BUFFER_LIST buffers, + PNET_BUFFER buffer, UINT8 direction, UINT32 if_idx, UINT32 sub_if_idx) +{ + KLOCK_QUEUE_HANDLE lock_handle; + NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO checksum_info; + PLIST_ENTRY entry; + packet_t packet; + + packet = (packet_t)ExAllocatePoolWithTag(NonPagedPool, DIVERT_PACKET_SIZE, + DIVERT_PACKET_TAG); + if (packet == NULL) + { + return FALSE; + } + checksum_info.Value = NET_BUFFER_LIST_INFO(buffers, + TcpIpChecksumNetBufferListInfo); + packet->buffer = buffer; + packet->buffers = buffers; + packet->direction = direction; + packet->if_idx = if_idx; + packet->sub_if_idx = sub_if_idx; + if (direction == DIVERT_PACKET_DIRECTION_OUTBOUND) + { + // IPv4 Checksum is not calculated yet + packet->ip_checksum = TRUE; + packet->tcp_checksum = (BOOL)checksum_info.Transmit.TcpChecksum; + packet->udp_checksum = (BOOL)checksum_info.Transmit.UdpChecksum; + } + else + { + packet->ip_checksum = FALSE; + packet->tcp_checksum = FALSE; + packet->udp_checksum = FALSE; + } + packet->timer_ticktock = context->timer_ticktock; + entry = &packet->entry; + FwpsReferenceNetBufferList0(buffers, FALSE); + KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); + if (context->state != DIVERT_CONTEXT_STATE_OPEN) + { + // We are no longer open + KeReleaseInStackQueuedSpinLock(&lock_handle); + FwpsDereferenceNetBufferList0(buffers, FALSE); + ExFreePoolWithTag(packet, DIVERT_PACKET_TAG); + return FALSE; + } + InsertTailList(&context->packet_queue, entry); + entry = NULL; + context->packet_queue_length++; + if (context->packet_queue_length > context->packet_queue_maxlength) + { + entry = RemoveHeadList(&context->packet_queue); + context->packet_queue_length--; + } + KeReleaseInStackQueuedSpinLock(&lock_handle); + if (entry != NULL) + { + // Queue is full; 'entry' contains a dropped packet. + DEBUG("DROP: packet queue is full, dropping packet"); + packet = CONTAINING_RECORD(entry, struct packet_s, entry); + FwpsDereferenceNetBufferList0(packet->buffers, FALSE); + ExFreePoolWithTag(packet, DIVERT_PACKET_TAG); + } + DEBUG("PACKET: diverting packet (packet=%p)", packet); + + return TRUE; +} + +/* + * Re-inject a NET_BUFFER. + */ +static BOOL divert_reinject_packet(context_t context, UINT8 direction, + BOOL isipv4, UINT32 if_idx, UINT32 sub_if_idx, PNET_BUFFER_LIST buffers, + PNET_BUFFER buffer) +{ + PNET_BUFFER_LIST buffers_cpy; + NTSTATUS status; + + status = FwpsAllocateNetBufferAndNetBufferList0( + context->pool_handle, 0, 0, NET_BUFFER_FIRST_MDL(buffer), + NET_BUFFER_DATA_OFFSET(buffer), NET_BUFFER_DATA_LENGTH(buffer), + &buffers_cpy); + if (!NT_SUCCESS(status)) + { + return FALSE; + } + FwpsReferenceNetBufferList0(buffers, FALSE); + if (direction == DIVERT_PACKET_DIRECTION_OUTBOUND) + { + if (isipv4) + { + status = FwpsInjectNetworkSendAsync0(inject_handle, + DIVERT_PACKET_ALLOW, 0, UNSPECIFIED_COMPARTMENT_ID, + buffers_cpy, divert_reinject_complete, (HANDLE)buffers); + } + else + { + status = FwpsInjectNetworkSendAsync0(injectv6_handle, + DIVERT_PACKET_ALLOW, 0, UNSPECIFIED_COMPARTMENT_ID, + buffers_cpy, divert_reinject_complete, (HANDLE)buffers); + } + } + else + { + // NOTE: this case should never occur since inbound net buffers only + // ever contain one packet. We keep for completeness. + if (isipv4) + { + status = FwpsInjectNetworkReceiveAsync0(inject_handle, + DIVERT_PACKET_ALLOW, 0, UNSPECIFIED_COMPARTMENT_ID, if_idx, + sub_if_idx, buffers_cpy, divert_reinject_complete, + (HANDLE)buffers); + } + else + { + status = FwpsInjectNetworkReceiveAsync0(injectv6_handle, + DIVERT_PACKET_ALLOW, 0, UNSPECIFIED_COMPARTMENT_ID, if_idx, + sub_if_idx, buffers_cpy, divert_reinject_complete, + (HANDLE)buffers); + } + } + if (!NT_SUCCESS(status)) + { + FwpsDereferenceNetBufferList0(buffers, FALSE); + FwpsFreeNetBufferList0(buffers_cpy); + return FALSE; + } + return TRUE; +} + +/* + * Divert (re)inject complete. + */ +static void NTAPI divert_reinject_complete(VOID *context, + NET_BUFFER_LIST *buffers_cpy, BOOLEAN dispatch_level) +{ + PNET_BUFFER_LIST buffers; + UNREFERENCED_PARAMETER(dispatch_level); + + buffers = (PNET_BUFFER_LIST)context; + FwpsDereferenceNetBufferList0(buffers, FALSE); + FwpsFreeNetBufferList0(buffers_cpy); +} + +/* + * Generic checksum calculation. + */ +static UINT16 divert_checksum(const void *pseudo_header, + size_t pseudo_header_len, const void *data, size_t len) +{ + register const UINT16 *data16 = (const UINT16 *)pseudo_header; + register size_t len16 = pseudo_header_len >> 1; + register UINT32 sum = 0; + size_t i; + + for (i = 0; i < len16; i++) + { + sum += (UINT32)data16[i]; + } + + data16 = (const UINT16 *)data; + len16 = len >> 1; + for (i = 0; i < len16; i++) + { + sum += (UINT32)data16[i]; + } + + if (len & 0x1) + { + const UINT8 *data8 = (const UINT8 *)data; + sum += (UINT32)data8[len-1]; + } + + sum = (sum & 0xFFFF) + (sum >> 16); + sum += (sum >> 16); + sum = ~sum; + return (UINT16)sum; +} + +/* + * Given a well-formed packet, update the IP and/or TCP/UDP checksums if + * required. + */ +static void divert_update_checksums(void *header, size_t len, + BOOL update_ip, BOOL update_tcp, BOOL update_udp) +{ + struct + { + UINT32 SrcAddr; + UINT32 DstAddr; + UINT8 Zero; + UINT8 Protocol; + UINT16 TransLength; + } pseudo_header; + struct iphdr *ip_header = (struct iphdr *)header; + size_t ip_header_len, trans_len; + void *trans_header; + struct tcphdr *tcp_header; + struct udphdr *udp_header; + UINT16 *trans_check_ptr; + UINT sum; + + if (!update_ip && !update_tcp && !update_udp) + { + return; + } + + if (len < sizeof(struct iphdr)) + { + return; + } + + if (ip_header->Version != 4) + { + return; + } + + ip_header_len = ip_header->HdrLength*sizeof(UINT32); + if (len < ip_header_len) + { + return; + } + + if (update_ip) + { + ip_header->Checksum = 0; + ip_header->Checksum = divert_checksum(NULL, 0, ip_header, + ip_header_len); + } + + trans_len = RtlUshortByteSwap(ip_header->Length) - ip_header_len; + trans_header = (UINT8 *)ip_header + ip_header_len; + switch (ip_header->Protocol) + { + case IPPROTO_TCP: + if (!update_tcp) + { + return; + } + tcp_header = (struct tcphdr *)trans_header; + if (trans_len < sizeof(struct tcphdr)) + { + return; + } + trans_check_ptr = &tcp_header->Checksum; + break; + case IPPROTO_UDP: + if (!update_udp) + { + return; + } + udp_header = (struct udphdr *)trans_header; + if (trans_len < sizeof(struct udphdr)) + { + return; + } + trans_check_ptr = &udp_header->Checksum; + break; + default: + return; + } + + pseudo_header.SrcAddr = ip_header->SrcAddr; + pseudo_header.DstAddr = ip_header->DstAddr; + pseudo_header.Zero = 0x0; + pseudo_header.Protocol = ip_header->Protocol; + pseudo_header.TransLength = RtlUshortByteSwap((UINT16)trans_len); + *trans_check_ptr = 0x0; + sum = divert_checksum(&pseudo_header, sizeof(pseudo_header), + trans_header, trans_len); + if (sum == 0 && ip_header->Protocol == IPPROTO_UDP) + { + *trans_check_ptr = 0xFFFF; + } + else + { + *trans_check_ptr = (UINT16)sum; + } +} + +/* + * Checks if the given packet is of interest. + */ +static BOOL divert_filter(PNET_BUFFER buffer, UINT32 if_idx, UINT32 sub_if_idx, + BOOL outbound, filter_t filter) +{ + // Buffer contains enough space for a full size iphdr and tcphdr/udphdr + // (without options) + UINT8 storage[0xF*sizeof(UINT32) + sizeof(struct tcphdr)]; + UINT8 *headers; + size_t tot_len, cpy_len, ip_header_len; + struct iphdr *ip_header = NULL; + struct ipv6hdr *ipv6_header = NULL; + struct icmphdr *icmp_header = NULL; + struct icmpv6hdr *icmpv6_header = NULL; + struct tcphdr *tcp_header = NULL; + struct udphdr *udp_header = NULL; + UINT8 ip, protocol, ttl; + + // Parse the headers: + tot_len = NET_BUFFER_DATA_LENGTH(buffer); + if (tot_len < sizeof(struct iphdr)) + { + DEBUG("FILTER: REJECT (packet length too small)"); + return FALSE; + } + cpy_len = (tot_len < sizeof(storage)? tot_len: sizeof(storage)); + headers = (UINT8 *)NdisGetDataBuffer(buffer, cpy_len, storage, 1, 0); + if (headers == NULL) + { + headers = storage; + } + + ip_header = (struct iphdr *)headers; + switch (ip_header->Version) + { + case 4: + ip_header_len = ip_header->HdrLength*sizeof(UINT32); + if (RtlUshortByteSwap(ip_header->Length) != tot_len || + ip_header->HdrLength < 5 || + ip_header_len > tot_len) + { + DEBUG("FILTER: REJECT (bad IPv4 packet)"); + return FALSE; + } + protocol = ip_header->Protocol; + break; + case 6: + ip_header = NULL; + ipv6_header = (struct ipv6hdr *)headers; + ip_header_len = sizeof(struct ipv6hdr); + if (ip_header_len > tot_len || + RtlUshortByteSwap(ipv6_header->Length) + + sizeof(struct ipv6hdr) != tot_len) + { + DEBUG("FILTER: REJECT (bad IPv6 packet)"); + return FALSE; + } + protocol = ipv6_header->NextHdr; + break; + default: + DEBUG("FILTER: REJECT (packet is neither IPv4 nor IPv6)"); + return FALSE; + } + + switch (protocol) + { + case IPPROTO_ICMP: + icmp_header = (struct icmphdr *)(headers + ip_header_len); + if (ip_header == NULL || + sizeof(struct icmphdr) + ip_header_len > tot_len) + { + DEBUG("FILTER: REJECT (bad ICMP packet)"); + return FALSE; + } + break; + case IPPROTO_ICMPV6: + icmpv6_header = (struct icmpv6hdr *)(headers + ip_header_len); + if (ipv6_header == NULL || + sizeof(struct icmpv6hdr) + ip_header_len > tot_len) + { + DEBUG("FILTER: REJECT (bad ICMPV6 packet)"); + return FALSE; + } + break; + case IPPROTO_TCP: + tcp_header = (struct tcphdr *)(headers + ip_header_len); + if (tcp_header->HdrLength < 5 || + tcp_header->HdrLength*sizeof(UINT32) + ip_header_len > tot_len) + { + DEBUG("FILTER: REJECT (bad TCP packet)"); + return FALSE; + } + break; + case IPPROTO_UDP: + udp_header = (struct udphdr *)(headers + ip_header_len); + if (sizeof(struct udphdr) + ip_header_len > tot_len) + { + DEBUG("FILTER: REJECT (bad UDP packet)"); + return FALSE; + } + break; + default: + break; + } + + // Execute the filter: + ip = 0; + ttl = DIVERT_FILTER_MAXLEN+1; // Additional safety + while (ttl-- != 0) + { + BOOL result; + UINT32 field[4]; + field[1] = 0; + field[2] = 0; + field[3] = 0; + switch (filter[ip].protocol) + { + case DIVERT_FILTER_PROTOCOL_NONE: + result = TRUE; + break; + case DIVERT_FILTER_PROTOCOL_IP: + result = (ip_header != NULL); + break; + case DIVERT_FILTER_PROTOCOL_IPV6: + result = (ipv6_header != NULL); + break; + case DIVERT_FILTER_PROTOCOL_ICMP: + result = (icmp_header != NULL); + break; + case DIVERT_FILTER_PROTOCOL_ICMPV6: + result = (icmpv6_header != NULL); + break; + case DIVERT_FILTER_PROTOCOL_TCP: + result = (tcp_header != NULL); + break; + case DIVERT_FILTER_PROTOCOL_UDP: + result = (udp_header != NULL); + break; + default: + result = FALSE; + break; + } + if (result) + { + switch (filter[ip].field) + { + case DIVERT_FILTER_FIELD_ZERO: + field[0] = 0; + break; + case DIVERT_FILTER_FIELD_INBOUND: + field[0] = (UINT32)(!outbound); + break; + case DIVERT_FILTER_FIELD_OUTBOUND: + field[0] = (UINT32)outbound; + break; + case DIVERT_FILTER_FIELD_IFIDX: + field[0] = (UINT32)if_idx; + break; + case DIVERT_FILTER_FIELD_SUBIFIDX: + field[0] = (UINT32)sub_if_idx; + break; + case DIVERT_FILTER_FIELD_IP: + field[0] = (UINT32)(ip_header != NULL); + break; + case DIVERT_FILTER_FIELD_IPV6: + field[0] = (UINT32)(ipv6_header != NULL); + break; + case DIVERT_FILTER_FIELD_ICMP: + field[0] = (UINT32)(icmp_header != NULL); + break; + case DIVERT_FILTER_FIELD_ICMPV6: + field[0] = (UINT32)(icmpv6_header != NULL); + break; + case DIVERT_FILTER_FIELD_TCP: + field[0] = (UINT32)(tcp_header != NULL); + break; + case DIVERT_FILTER_FIELD_UDP: + field[0] = (UINT32)(udp_header != NULL); + break; + case DIVERT_FILTER_FIELD_IP_HDRLENGTH: + field[0] = (UINT32)ip_header->HdrLength; + break; + case DIVERT_FILTER_FIELD_IP_TOS: + field[0] = (UINT32)RtlUshortByteSwap(ip_header->TOS); + break; + case DIVERT_FILTER_FIELD_IP_LENGTH: + field[0] = (UINT32)RtlUshortByteSwap(ip_header->Length); + break; + case DIVERT_FILTER_FIELD_IP_ID: + field[0] = (UINT32)RtlUshortByteSwap(ip_header->Id); + break; + case DIVERT_FILTER_FIELD_IP_DF: + field[0] = (UINT32)IPHDR_GET_DF(ip_header); + break; + case DIVERT_FILTER_FIELD_IP_MF: + field[0] = (UINT32)IPHDR_GET_MF(ip_header); + break; + case DIVERT_FILTER_FIELD_IP_FRAGOFF: + field[0] = (UINT32)RtlUshortByteSwap( + IPHDR_GET_FRAGOFF(ip_header)); + break; + case DIVERT_FILTER_FIELD_IP_TTL: + field[0] = (UINT32)ip_header->TTL; + break; + case DIVERT_FILTER_FIELD_IP_PROTOCOL: + field[0] = (UINT32)ip_header->Protocol; + break; + case DIVERT_FILTER_FIELD_IP_CHECKSUM: + field[0] = (UINT32)RtlUshortByteSwap(ip_header->Checksum); + break; + case DIVERT_FILTER_FIELD_IP_SRCADDR: + field[0] = (UINT32)RtlUlongByteSwap(ip_header->SrcAddr); + break; + case DIVERT_FILTER_FIELD_IP_DSTADDR: + field[0] = (UINT32)RtlUlongByteSwap(ip_header->DstAddr); + break; + case DIVERT_FILTER_FIELD_IPV6_TRAFFICCLASS: + field[0] = (UINT32)IPV6HDR_GET_TRAFFICCLASS(ipv6_header); + break; + case DIVERT_FILTER_FIELD_IPV6_FLOWLABEL: + field[0] = (UINT32)RtlUlongByteSwap( + IPV6HDR_GET_FLOWLABEL(ipv6_header)); + break; + case DIVERT_FILTER_FIELD_IPV6_LENGTH: + field[0] = (UINT32)RtlUshortByteSwap(ipv6_header->Length); + break; + case DIVERT_FILTER_FIELD_IPV6_NEXTHDR: + field[0] = (UINT32)ipv6_header->NextHdr; + break; + case DIVERT_FILTER_FIELD_IPV6_HOPLIMIT: + field[0] = (UINT32)ipv6_header->HopLimit; + break; + case DIVERT_FILTER_FIELD_IPV6_SRCADDR: + field[0] = + (UINT32)RtlUlongByteSwap(ipv6_header->SrcAddr[3]); + field[1] = + (UINT32)RtlUlongByteSwap(ipv6_header->SrcAddr[2]); + field[2] = + (UINT32)RtlUlongByteSwap(ipv6_header->SrcAddr[1]); + field[3] = + (UINT32)RtlUlongByteSwap(ipv6_header->SrcAddr[0]); + break; + case DIVERT_FILTER_FIELD_IPV6_DSTADDR: + field[0] = + (UINT32)RtlUlongByteSwap(ipv6_header->DstAddr[3]); + field[1] = + (UINT32)RtlUlongByteSwap(ipv6_header->DstAddr[2]); + field[2] = + (UINT32)RtlUlongByteSwap(ipv6_header->DstAddr[1]); + field[3] = + (UINT32)RtlUlongByteSwap(ipv6_header->DstAddr[0]); + break; + case DIVERT_FILTER_FIELD_ICMP_TYPE: + field[0] = (UINT32)icmp_header->Type; + break; + case DIVERT_FILTER_FIELD_ICMP_CODE: + field[0] = (UINT32)icmp_header->Code; + break; + case DIVERT_FILTER_FIELD_ICMP_CHECKSUM: + field[0] = + (UINT32)RtlUshortByteSwap(icmp_header->Checksum); + break; + case DIVERT_FILTER_FIELD_ICMP_BODY: + field[0] = (UINT32)RtlUlongByteSwap(icmp_header->Body); + break; + case DIVERT_FILTER_FIELD_ICMPV6_TYPE: + field[0] = (UINT32)icmpv6_header->Type; + break; + case DIVERT_FILTER_FIELD_ICMPV6_CODE: + field[0] = (UINT32)icmpv6_header->Code; + break; + case DIVERT_FILTER_FIELD_ICMPV6_CHECKSUM: + field[0] = (UINT32)icmpv6_header->Checksum; + break; + case DIVERT_FILTER_FIELD_ICMPV6_BODY: + field[0] = (UINT32)icmpv6_header->Body; + break; + case DIVERT_FILTER_FIELD_TCP_SRCPORT: + field[0] = (UINT32)RtlUshortByteSwap(tcp_header->SrcPort); + break; + case DIVERT_FILTER_FIELD_TCP_DSTPORT: + field[0] = (UINT32)RtlUshortByteSwap(tcp_header->DstPort); + break; + case DIVERT_FILTER_FIELD_TCP_SEQNUM: + field[0] = (UINT32)RtlUlongByteSwap(tcp_header->SeqNum); + break; + case DIVERT_FILTER_FIELD_TCP_ACKNUM: + field[0] = (UINT32)RtlUlongByteSwap(tcp_header->AckNum); + break; + case DIVERT_FILTER_FIELD_TCP_HDRLENGTH: + field[0] = (UINT32)tcp_header->HdrLength; + break; + case DIVERT_FILTER_FIELD_TCP_URG: + field[0] = (UINT32)tcp_header->Urg; + break; + case DIVERT_FILTER_FIELD_TCP_ACK: + field[0] = (UINT32)tcp_header->Ack; + break; + case DIVERT_FILTER_FIELD_TCP_PSH: + field[0] = (UINT32)tcp_header->Psh; + break; + case DIVERT_FILTER_FIELD_TCP_RST: + field[0] = (UINT32)tcp_header->Rst; + break; + case DIVERT_FILTER_FIELD_TCP_SYN: + field[0] = (UINT32)tcp_header->Syn; + break; + case DIVERT_FILTER_FIELD_TCP_FIN: + field[0] = (UINT32)tcp_header->Fin; + break; + case DIVERT_FILTER_FIELD_TCP_WINDOW: + field[0] = (UINT32)RtlUshortByteSwap(tcp_header->Window); + break; + case DIVERT_FILTER_FIELD_TCP_CHECKSUM: + field[0] = (UINT32)RtlUshortByteSwap(tcp_header->Checksum); + break; + case DIVERT_FILTER_FIELD_TCP_URGPTR: + field[0] = (UINT32)RtlUshortByteSwap(tcp_header->UrgPtr); + break; + case DIVERT_FILTER_FIELD_TCP_PAYLOADLENGTH: + field[0] = (UINT32)(tot_len - ip_header_len - + tcp_header->HdrLength*sizeof(UINT32)); + break; + case DIVERT_FILTER_FIELD_UDP_SRCPORT: + field[0] = (UINT32)RtlUshortByteSwap(udp_header->SrcPort); + break; + case DIVERT_FILTER_FIELD_UDP_DSTPORT: + field[0] = (UINT32)RtlUshortByteSwap(udp_header->DstPort); + break; + case DIVERT_FILTER_FIELD_UDP_LENGTH: + field[0] = (UINT32)RtlUshortByteSwap(udp_header->Length); + break; + case DIVERT_FILTER_FIELD_UDP_CHECKSUM: + field[0] = (UINT32)RtlUshortByteSwap(udp_header->Checksum); + break; + case DIVERT_FILTER_FIELD_UDP_PAYLOADLENGTH: + field[0] = (UINT32)(tot_len - ip_header_len - + sizeof(struct udphdr)); + break; + default: + field[0] = 0; + break; + } + switch (filter[ip].test) + { + case DIVERT_FILTER_TEST_EQ: + result = (field[0] == filter[ip].arg[0] && + field[1] == filter[ip].arg[1] && + field[2] == filter[ip].arg[2] && + field[3] == filter[ip].arg[3]); + break; + case DIVERT_FILTER_TEST_NEQ: + result = (field[0] != filter[ip].arg[0] || + field[1] != filter[ip].arg[1] || + field[2] != filter[ip].arg[2] || + field[3] != filter[ip].arg[3]); + break; + case DIVERT_FILTER_TEST_LT: + result = (field[3] < filter[ip].arg[3] || + (field[3] == filter[ip].arg[3] && + field[2] < filter[ip].arg[2] || + (field[2] == filter[ip].arg[2] && + field[1] < filter[ip].arg[1] || + (field[1] == filter[ip].arg[1] && + field[0] < filter[ip].arg[0])))); + break; + case DIVERT_FILTER_TEST_LEQ: + result = (field[3] < filter[ip].arg[3] || + (field[3] == filter[ip].arg[3] && + field[2] < filter[ip].arg[2] || + (field[2] == filter[ip].arg[2] && + field[1] < filter[ip].arg[1] || + (field[1] == filter[ip].arg[1] && + field[0] <= filter[ip].arg[0])))); + break; + case DIVERT_FILTER_TEST_GT: + result = (field[3] > filter[ip].arg[3] || + (field[3] == filter[ip].arg[3] && + field[2] > filter[ip].arg[2] || + (field[2] == filter[ip].arg[2] && + field[1] > filter[ip].arg[1] || + (field[1] == filter[ip].arg[1] && + field[0] > filter[ip].arg[0])))); + break; + case DIVERT_FILTER_TEST_GEQ: + result = (field[3] > filter[ip].arg[3] || + (field[3] == filter[ip].arg[3] && + field[2] > filter[ip].arg[2] || + (field[2] == filter[ip].arg[2] && + field[1] > filter[ip].arg[1] || + (field[1] == filter[ip].arg[1] && + field[0] >= filter[ip].arg[0])))); + break; + default: + result = FALSE; + break; + } + } + ip = (result? filter[ip].success: filter[ip].failure); + if (ip == DIVERT_FILTER_RESULT_ACCEPT) + { + return TRUE; + } + if (ip == DIVERT_FILTER_RESULT_REJECT) + { + return FALSE; + } + } + DEBUG("FILTER: REJECT (filter TTL exceeded)"); + return FALSE; +} + +/* + * Compile a divert filter from an IOCTL. + */ +static BOOL divert_filter_compile(divert_ioctl_filter_t ioctl_filter, + size_t ioctl_filter_len, filter_t filter) +{ + struct filter_s filter0[DIVERT_FILTER_MAXLEN]; + UINT8 i; + UINT length; + UINT64 *src, *dst; + + if (ioctl_filter_len % sizeof(struct divert_ioctl_filter_s) != 0) + { + return FALSE; + } + length = ioctl_filter_len / sizeof(struct divert_ioctl_filter_s); + if (length >= DIVERT_FILTER_MAXLEN) + { + return FALSE; + } + + for (i = 0; i < length; i++) + { + if (ioctl_filter[i].field > DIVERT_FILTER_FIELD_MAX || + ioctl_filter[i].test > DIVERT_FILTER_TEST_MAX) + { + return FALSE; + } + switch (ioctl_filter[i].success) + { + case DIVERT_FILTER_RESULT_ACCEPT: case DIVERT_FILTER_RESULT_REJECT: + break; + default: + if (ioctl_filter[i].success <= i || + ioctl_filter[i].success >= length) + { + return FALSE; + } + break; + } + switch (ioctl_filter[i].failure) + { + case DIVERT_FILTER_RESULT_ACCEPT: case DIVERT_FILTER_RESULT_REJECT: + break; + default: + if (ioctl_filter[i].failure <= i || + ioctl_filter[i].failure >= length) + { + return FALSE; + } + break; + } + + // Enforce size limits: + if (ioctl_filter[i].field != DIVERT_FILTER_FIELD_IPV6_SRCADDR && + ioctl_filter[i].field != DIVERT_FILTER_FIELD_IPV6_DSTADDR) + { + if (ioctl_filter[i].arg[1] != 0 || + ioctl_filter[i].arg[2] != 0 || + ioctl_filter[i].arg[3] != 0) + { + return FALSE; + } + } + switch (ioctl_filter[i].field) + { + case DIVERT_FILTER_FIELD_ZERO: + if (ioctl_filter[i].arg[0] != 0) + { + return FALSE; + } + break; + case DIVERT_FILTER_FIELD_INBOUND: + case DIVERT_FILTER_FIELD_OUTBOUND: + case DIVERT_FILTER_FIELD_IP: + case DIVERT_FILTER_FIELD_IPV6: + case DIVERT_FILTER_FIELD_ICMP: + case DIVERT_FILTER_FIELD_ICMPV6: + case DIVERT_FILTER_FIELD_TCP: + case DIVERT_FILTER_FIELD_UDP: + case DIVERT_FILTER_FIELD_IP_DF: + case DIVERT_FILTER_FIELD_IP_MF: + case DIVERT_FILTER_FIELD_TCP_URG: + case DIVERT_FILTER_FIELD_TCP_ACK: + case DIVERT_FILTER_FIELD_TCP_PSH: + case DIVERT_FILTER_FIELD_TCP_RST: + case DIVERT_FILTER_FIELD_TCP_SYN: + case DIVERT_FILTER_FIELD_TCP_FIN: + if (ioctl_filter[i].arg[0] > 1) + { + return FALSE; + } + break; + case DIVERT_FILTER_FIELD_IP_HDRLENGTH: + case DIVERT_FILTER_FIELD_TCP_HDRLENGTH: + if (ioctl_filter[i].arg[0] > 0x0F) + { + return FALSE; + } + break; + case DIVERT_FILTER_FIELD_IP_TTL: + case DIVERT_FILTER_FIELD_IP_PROTOCOL: + case DIVERT_FILTER_FIELD_IPV6_TRAFFICCLASS: + case DIVERT_FILTER_FIELD_IPV6_NEXTHDR: + case DIVERT_FILTER_FIELD_IPV6_HOPLIMIT: + case DIVERT_FILTER_FIELD_ICMP_TYPE: + case DIVERT_FILTER_FIELD_ICMP_CODE: + case DIVERT_FILTER_FIELD_ICMPV6_TYPE: + case DIVERT_FILTER_FIELD_ICMPV6_CODE: + if (ioctl_filter[i].arg[0] > UINT8_MAX) + { + return FALSE; + } + break; + case DIVERT_FILTER_FIELD_IP_FRAGOFF: + if (ioctl_filter[i].arg[0] > 0x1FFF) + { + return FALSE; + } + break; + case DIVERT_FILTER_FIELD_IP_TOS: + case DIVERT_FILTER_FIELD_IP_LENGTH: + case DIVERT_FILTER_FIELD_IP_ID: + case DIVERT_FILTER_FIELD_IP_CHECKSUM: + case DIVERT_FILTER_FIELD_IPV6_LENGTH: + case DIVERT_FILTER_FIELD_ICMP_CHECKSUM: + case DIVERT_FILTER_FIELD_ICMPV6_CHECKSUM: + case DIVERT_FILTER_FIELD_TCP_SRCPORT: + case DIVERT_FILTER_FIELD_TCP_DSTPORT: + case DIVERT_FILTER_FIELD_TCP_WINDOW: + case DIVERT_FILTER_FIELD_TCP_CHECKSUM: + case DIVERT_FILTER_FIELD_TCP_URGPTR: + case DIVERT_FILTER_FIELD_TCP_PAYLOADLENGTH: + case DIVERT_FILTER_FIELD_UDP_SRCPORT: + case DIVERT_FILTER_FIELD_UDP_DSTPORT: + case DIVERT_FILTER_FIELD_UDP_LENGTH: + case DIVERT_FILTER_FIELD_UDP_CHECKSUM: + case DIVERT_FILTER_FIELD_UDP_PAYLOADLENGTH: + if (ioctl_filter[i].arg[0] > UINT16_MAX) + { + return FALSE; + } + break; + case DIVERT_FILTER_FIELD_IPV6_FLOWLABEL: + if (ioctl_filter[i].arg[0] > 0x000FFFFF) + { + return FALSE; + } + break; + default: + break; + } + filter0[i].field = ioctl_filter[i].field; + filter0[i].test = ioctl_filter[i].test; + filter0[i].success = ioctl_filter[i].success; + filter0[i].failure = ioctl_filter[i].failure; + filter0[i].arg[0] = ioctl_filter[i].arg[0]; + filter0[i].arg[1] = ioctl_filter[i].arg[1]; + filter0[i].arg[2] = ioctl_filter[i].arg[2]; + filter0[i].arg[3] = ioctl_filter[i].arg[3]; + + // Protocol selection: + switch (ioctl_filter[i].field) + { + case DIVERT_FILTER_FIELD_ZERO: + case DIVERT_FILTER_FIELD_INBOUND: + case DIVERT_FILTER_FIELD_OUTBOUND: + case DIVERT_FILTER_FIELD_IFIDX: + case DIVERT_FILTER_FIELD_SUBIFIDX: + case DIVERT_FILTER_FIELD_IP: + case DIVERT_FILTER_FIELD_IPV6: + case DIVERT_FILTER_FIELD_ICMP: + case DIVERT_FILTER_FIELD_ICMPV6: + case DIVERT_FILTER_FIELD_TCP: + case DIVERT_FILTER_FIELD_UDP: + filter0[i].protocol = DIVERT_FILTER_PROTOCOL_NONE; + break; + case DIVERT_FILTER_FIELD_IP_HDRLENGTH: + case DIVERT_FILTER_FIELD_IP_TOS: + case DIVERT_FILTER_FIELD_IP_LENGTH: + case DIVERT_FILTER_FIELD_IP_ID: + case DIVERT_FILTER_FIELD_IP_DF: + case DIVERT_FILTER_FIELD_IP_MF: + case DIVERT_FILTER_FIELD_IP_FRAGOFF: + case DIVERT_FILTER_FIELD_IP_TTL: + case DIVERT_FILTER_FIELD_IP_PROTOCOL: + case DIVERT_FILTER_FIELD_IP_CHECKSUM: + case DIVERT_FILTER_FIELD_IP_SRCADDR: + case DIVERT_FILTER_FIELD_IP_DSTADDR: + filter0[i].protocol = DIVERT_FILTER_PROTOCOL_IP; + break; + case DIVERT_FILTER_FIELD_IPV6_TRAFFICCLASS: + case DIVERT_FILTER_FIELD_IPV6_FLOWLABEL: + case DIVERT_FILTER_FIELD_IPV6_LENGTH: + case DIVERT_FILTER_FIELD_IPV6_NEXTHDR: + case DIVERT_FILTER_FIELD_IPV6_HOPLIMIT: + case DIVERT_FILTER_FIELD_IPV6_SRCADDR: + case DIVERT_FILTER_FIELD_IPV6_DSTADDR: + filter0[i].protocol = DIVERT_FILTER_PROTOCOL_IPV6; + break; + case DIVERT_FILTER_FIELD_ICMP_TYPE: + case DIVERT_FILTER_FIELD_ICMP_CODE: + case DIVERT_FILTER_FIELD_ICMP_CHECKSUM: + case DIVERT_FILTER_FIELD_ICMP_BODY: + filter0[i].protocol = DIVERT_FILTER_PROTOCOL_ICMP; + break; + case DIVERT_FILTER_FIELD_ICMPV6_TYPE: + case DIVERT_FILTER_FIELD_ICMPV6_CODE: + case DIVERT_FILTER_FIELD_ICMPV6_CHECKSUM: + case DIVERT_FILTER_FIELD_ICMPV6_BODY: + filter0[i].protocol = DIVERT_FILTER_PROTOCOL_ICMPV6; + break; + case DIVERT_FILTER_FIELD_TCP_SRCPORT: + case DIVERT_FILTER_FIELD_TCP_DSTPORT: + case DIVERT_FILTER_FIELD_TCP_SEQNUM: + case DIVERT_FILTER_FIELD_TCP_ACKNUM: + case DIVERT_FILTER_FIELD_TCP_HDRLENGTH: + case DIVERT_FILTER_FIELD_TCP_URG: + case DIVERT_FILTER_FIELD_TCP_ACK: + case DIVERT_FILTER_FIELD_TCP_PSH: + case DIVERT_FILTER_FIELD_TCP_RST: + case DIVERT_FILTER_FIELD_TCP_SYN: + case DIVERT_FILTER_FIELD_TCP_FIN: + case DIVERT_FILTER_FIELD_TCP_WINDOW: + case DIVERT_FILTER_FIELD_TCP_CHECKSUM: + case DIVERT_FILTER_FIELD_TCP_URGPTR: + case DIVERT_FILTER_FIELD_TCP_PAYLOADLENGTH: + filter0[i].protocol = DIVERT_FILTER_PROTOCOL_TCP; + break; + case DIVERT_FILTER_FIELD_UDP_SRCPORT: + case DIVERT_FILTER_FIELD_UDP_DSTPORT: + case DIVERT_FILTER_FIELD_UDP_LENGTH: + case DIVERT_FILTER_FIELD_UDP_CHECKSUM: + case DIVERT_FILTER_FIELD_UDP_PAYLOADLENGTH: + filter0[i].protocol = DIVERT_FILTER_PROTOCOL_UDP; + break; + default: + return FALSE; + } + } + RtlMoveMemory(filter, filter0, i*sizeof(struct filter_s)); + + return TRUE; +} + diff --git a/sys/divert.inf b/sys/divert.inf new file mode 100644 index 0000000..22206c5 --- /dev/null +++ b/sys/divert.inf @@ -0,0 +1,25 @@ +; divert.inf +; (C) 2011, all rights reserved, +; +; 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 . + +[Version] +Signature="$WINDOWS NT$" + +[divert.NT.Wdf] +KmdfService = divert, divert_WdfSection + +[divert_WdfSection] +KmdfLibraryVersion = 1.9 + diff --git a/sys/divert.rc b/sys/divert.rc new file mode 100644 index 0000000..f5bea15 --- /dev/null +++ b/sys/divert.rc @@ -0,0 +1,30 @@ +/* + * divert.rc + * (C) 2011, all rights reserved, + * + * 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 . + */ + +#include +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_NETWORK +#define VER_FILEDESCRIPTION_STR \ + "Divert Driver for network packet capture and injection" +#define VER_INTERNALNAME_STR "divert.sys" +#define VER_ORIGINALFILENAME_STR "divert.sys" + +#include "common.ver" + diff --git a/sys/sources b/sys/sources new file mode 100644 index 0000000..8ddfe49 --- /dev/null +++ b/sys/sources @@ -0,0 +1,33 @@ +# sources +# (C) 2011, all rights reserved, +# +# 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 . + +TARGETNAME=divert +TARGETTYPE=DRIVER +TARGETPATH=..\install +TARGETLIBS=\ + $(DDK_LIB_PATH)\wdmsec.lib \ + $(DDK_LIB_PATH)\ndis.lib \ + $(DDK_LIB_PATH)\fwpkclnt.lib \ + $(SDK_LIB_PATH)\uuid.lib +NTTARGETFILES=\ + $(TARGETPATH)\$(_BUILDARCH)\divert.inf \ + $(TARGETPATH)\$(_BUILDARCH)\WdfCoInstaller01009.dll +KMDF_VERSION_MAJOR=1 +C_DEFINES=$(C_DEFINES) -DBINARY_COMPATIBLE=0 -DNT -DUNICODE -D_UNICODE \ + -DNDIS60 -DNDIS_SUPPORT_NDIS60 +INCLUDES=$(DDK_INC_PATH);..\include +SOURCES=divert.rc divert.c +