commit 019f9d509be94dfd38606552b40ef24cdceddc06
Author: basil00
Date: Fri Aug 19 20:11:17 2011 +0800
First commit of the Windows Divert project
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:
+
+- Download and install the latest Windows Driver Kit.
+
- Open a Free Build Environment console (or
+ Checked Build Environment for debugging).
+
- 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:
+
+- Include the divert.h header file
+
+#include "divert.h"
+
+ - 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
+
+- Is a pointer into the original pPacket packet; and
+- There is enough space in pPacket to fit the header.
+
+
+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
+
+- outbound;
+- contains data; and
+- 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:
+
+
+
+| outbound | Is outbound? |
+| inbound | Is inbound? |
+| ifIdx | Interface index |
+| subIfIdx | Sub-interface index |
+| ip | Is IPv4? |
+| ipv6 | Is IPv6? |
+| icmp | Is ICMP? |
+| icmpv6 | Is ICMPv6? |
+| tcp | Is TCP? |
+| udp | Is 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.PayloadLength | The TCP payload length |
+| udp.* | UDP fields (see DIVERT_UDPHDR) |
+| udp.PayloadLength | The 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
+
+
+
+-
+Divert all outbound web traffic:
+
+HANDLE handle = DivertOpen(
+ "outbound and "
+ "(tcp.DstPort == 80 or udp.DstPort == 53)"
+ );
+
+
+-
+Divert all inbound TCP SYNs:
+
+HANDLE handle = DivertOpen(
+ "inbound and "
+ "tcp.Syn"
+ );
+
+
+-
+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)"
+ );
+
+
+-
+Divert all traffic:
+
+HANDLE handle = DivertOpen("true");
+
+
+-
+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\tBLOCKED!
\n"
+ "\t\t
\n"
+ "\t\tThis 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
+