Initial commit after restructure
This commit is contained in:
11
firewall/interception/interception_linux.go
Normal file
11
firewall/interception/interception_linux.go
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright Safing ICS Technologies GmbH. Use of this source code is governed by the AGPL license that can be found in the LICENSE file.
|
||||
|
||||
package interception
|
||||
|
||||
import "github.com/Safing/safing-core/network/packet"
|
||||
|
||||
var Packets chan packet.Packet
|
||||
|
||||
func init() {
|
||||
Packets = make(chan packet.Packet, 1000)
|
||||
}
|
||||
31
firewall/interception/interception_windows.go
Normal file
31
firewall/interception/interception_windows.go
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright Safing ICS Technologies GmbH. Use of this source code is governed by the AGPL license that can be found in the LICENSE file.
|
||||
|
||||
package interception
|
||||
|
||||
import (
|
||||
"github.com/Safing/safing-core/firewall/interception/windivert"
|
||||
"github.com/Safing/safing-core/log"
|
||||
"github.com/Safing/safing-core/modules"
|
||||
"github.com/Safing/safing-core/network/packet"
|
||||
)
|
||||
|
||||
var Packets chan packet.Packet
|
||||
|
||||
func init() {
|
||||
Packets = make(chan packet.Packet, 1000)
|
||||
}
|
||||
|
||||
func Start() {
|
||||
|
||||
windivertModule := modules.Register("Firewall:Interception:WinDivert", 192)
|
||||
|
||||
wd, err := windivert.New("/WinDivert.dll", "")
|
||||
if err != nil {
|
||||
log.Criticalf("firewall/interception: could not init windivert: %s", err)
|
||||
} else {
|
||||
wd.Packets(Packets)
|
||||
}
|
||||
|
||||
<-windivertModule.Stop
|
||||
windivertModule.StopComplete()
|
||||
}
|
||||
314
firewall/interception/nfqueue.go
Normal file
314
firewall/interception/nfqueue.go
Normal file
@@ -0,0 +1,314 @@
|
||||
// Copyright Safing ICS Technologies GmbH. Use of this source code is governed by the AGPL license that can be found in the LICENSE file.
|
||||
|
||||
// +build linux
|
||||
|
||||
package interception
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/go-iptables/iptables"
|
||||
|
||||
"github.com/Safing/safing-core/firewall/interception/nfqueue"
|
||||
"github.com/Safing/safing-core/log"
|
||||
"github.com/Safing/safing-core/modules"
|
||||
)
|
||||
|
||||
// iptables -A OUTPUT -p icmp -j", "NFQUEUE", "--queue-num", "1", "--queue-bypass
|
||||
|
||||
var nfqueueModule *modules.Module
|
||||
|
||||
var v4chains []string
|
||||
var v4rules []string
|
||||
var v4once []string
|
||||
|
||||
var v6chains []string
|
||||
var v6rules []string
|
||||
var v6once []string
|
||||
|
||||
func init() {
|
||||
|
||||
v4chains = []string{
|
||||
"mangle C170",
|
||||
"mangle C171",
|
||||
"filter C17",
|
||||
}
|
||||
|
||||
v4rules = []string{
|
||||
"mangle C170 -j CONNMARK --restore-mark",
|
||||
"mangle C170 -m mark --mark 0 -j NFQUEUE --queue-num 17040 --queue-bypass",
|
||||
|
||||
"mangle C171 -j CONNMARK --restore-mark",
|
||||
"mangle C171 -m mark --mark 0 -j NFQUEUE --queue-num 17140 --queue-bypass",
|
||||
|
||||
"filter C17 -m mark --mark 0 -j DROP",
|
||||
"filter C17 -m mark --mark 1700 -j ACCEPT",
|
||||
"filter C17 -m mark --mark 1701 -j REJECT --reject-with icmp-host-prohibited",
|
||||
"filter C17 -m mark --mark 1702 -j DROP",
|
||||
"filter C17 -j CONNMARK --save-mark",
|
||||
"filter C17 -m mark --mark 1710 -j ACCEPT",
|
||||
"filter C17 -m mark --mark 1711 -j REJECT --reject-with icmp-host-prohibited",
|
||||
"filter C17 -m mark --mark 1712 -j DROP",
|
||||
"filter C17 -m mark --mark 1717 -j ACCEPT",
|
||||
}
|
||||
|
||||
v4once = []string{
|
||||
"mangle OUTPUT -j C170",
|
||||
"mangle INPUT -j C171",
|
||||
"filter OUTPUT -j C17",
|
||||
"filter INPUT -j C17",
|
||||
"nat OUTPUT -m mark --mark 1799 -p udp -j DNAT --to 127.0.0.1:53",
|
||||
"nat OUTPUT -m mark --mark 1717 -p tcp -j DNAT --to 127.0.0.17:1117",
|
||||
"nat OUTPUT -m mark --mark 1717 -p udp -j DNAT --to 127.0.0.17:1117",
|
||||
// "nat OUTPUT -m mark --mark 1717 ! -p tcp ! -p udp -j DNAT --to 127.0.0.17",
|
||||
}
|
||||
|
||||
v6chains = []string{
|
||||
"mangle C170",
|
||||
"mangle C171",
|
||||
"filter C17",
|
||||
}
|
||||
|
||||
v6rules = []string{
|
||||
"mangle C170 -j CONNMARK --restore-mark",
|
||||
"mangle C170 -m mark --mark 0 -j NFQUEUE --queue-num 17060 --queue-bypass",
|
||||
|
||||
"mangle C171 -j CONNMARK --restore-mark",
|
||||
"mangle C171 -m mark --mark 0 -j NFQUEUE --queue-num 17160 --queue-bypass",
|
||||
|
||||
"filter C17 -m mark --mark 0 -j DROP",
|
||||
"filter C17 -m mark --mark 1700 -j ACCEPT",
|
||||
"filter C17 -m mark --mark 1701 -j REJECT --reject-with icmp6-adm-prohibited",
|
||||
"filter C17 -m mark --mark 1702 -j DROP",
|
||||
"filter C17 -j CONNMARK --save-mark",
|
||||
"filter C17 -m mark --mark 1710 -j ACCEPT",
|
||||
"filter C17 -m mark --mark 1711 -j REJECT --reject-with icmp6-adm-prohibited",
|
||||
"filter C17 -m mark --mark 1712 -j DROP",
|
||||
"filter C17 -m mark --mark 1717 -j ACCEPT",
|
||||
}
|
||||
|
||||
v6once = []string{
|
||||
"mangle OUTPUT -j C170",
|
||||
"mangle INPUT -j C171",
|
||||
"filter OUTPUT -j C17",
|
||||
"filter INPUT -j C17",
|
||||
"nat OUTPUT -m mark --mark 1799 -p udp -j DNAT --to [::1]:53",
|
||||
"nat OUTPUT -m mark --mark 1717 -p tcp -j DNAT --to [fd17::17]:1117",
|
||||
"nat OUTPUT -m mark --mark 1717 -p udp -j DNAT --to [fd17::17]:1117",
|
||||
// "nat OUTPUT -m mark --mark 1717 ! -p tcp ! -p udp -j DNAT --to [fd17::17]",
|
||||
}
|
||||
|
||||
// Reverse because we'd like to insert in a loop
|
||||
sort.Reverse(sort.StringSlice(v4once))
|
||||
sort.Reverse(sort.StringSlice(v6once))
|
||||
|
||||
}
|
||||
|
||||
func activateNfqueueFirewall() error {
|
||||
|
||||
// IPv4
|
||||
ip4tables, err := iptables.NewWithProtocol(iptables.ProtocolIPv4)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, chain := range v4chains {
|
||||
splittedRule := strings.Split(chain, " ")
|
||||
if err = ip4tables.ClearChain(splittedRule[0], splittedRule[1]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, rule := range v4rules {
|
||||
splittedRule := strings.Split(rule, " ")
|
||||
if err = ip4tables.Append(splittedRule[0], splittedRule[1], splittedRule[2:]...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, rule := range v4once {
|
||||
splittedRule := strings.Split(rule, " ")
|
||||
ok, err := ip4tables.Exists(splittedRule[0], splittedRule[1], splittedRule[2:]...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !ok {
|
||||
if err = ip4tables.Insert(splittedRule[0], splittedRule[1], 1, splittedRule[2:]...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IPv6
|
||||
ip6tables, err := iptables.NewWithProtocol(iptables.ProtocolIPv6)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, chain := range v6chains {
|
||||
splittedRule := strings.Split(chain, " ")
|
||||
if err = ip6tables.ClearChain(splittedRule[0], splittedRule[1]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, rule := range v6rules {
|
||||
splittedRule := strings.Split(rule, " ")
|
||||
if err = ip6tables.Append(splittedRule[0], splittedRule[1], splittedRule[2:]...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, rule := range v6once {
|
||||
splittedRule := strings.Split(rule, " ")
|
||||
ok, err := ip6tables.Exists(splittedRule[0], splittedRule[1], splittedRule[2:]...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !ok {
|
||||
if err = ip6tables.Insert(splittedRule[0], splittedRule[1], 1, splittedRule[2:]...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func deactivateNfqueueFirewall() error {
|
||||
// IPv4
|
||||
ip4tables, err := iptables.NewWithProtocol(iptables.ProtocolIPv4)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, rule := range v4once {
|
||||
splittedRule := strings.Split(rule, " ")
|
||||
ok, err := ip4tables.Exists(splittedRule[0], splittedRule[1], splittedRule[2:]...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ok {
|
||||
if err = ip4tables.Delete(splittedRule[0], splittedRule[1], splittedRule[2:]...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, chain := range v4chains {
|
||||
splittedRule := strings.Split(chain, " ")
|
||||
if err := ip4tables.ClearChain(splittedRule[0], splittedRule[1]); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ip4tables.DeleteChain(splittedRule[0], splittedRule[1]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// IPv6
|
||||
ip6tables, err := iptables.NewWithProtocol(iptables.ProtocolIPv6)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, rule := range v6once {
|
||||
splittedRule := strings.Split(rule, " ")
|
||||
ok, err := ip6tables.Exists(splittedRule[0], splittedRule[1], splittedRule[2:]...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ok {
|
||||
if err = ip6tables.Delete(splittedRule[0], splittedRule[1], splittedRule[2:]...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, chain := range v6chains {
|
||||
splittedRule := strings.Split(chain, " ")
|
||||
if err := ip6tables.ClearChain(splittedRule[0], splittedRule[1]); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ip6tables.DeleteChain(splittedRule[0], splittedRule[1]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Start() {
|
||||
|
||||
nfqueueModule = modules.Register("Firewall:Interception:Nfqueue", 192)
|
||||
|
||||
if err := activateNfqueueFirewall(); err != nil {
|
||||
log.Criticalf("could not activate firewall for nfqueue: %q", err)
|
||||
}
|
||||
|
||||
out4Queue := nfqueue.NewNFQueue(17040)
|
||||
in4Queue := nfqueue.NewNFQueue(17140)
|
||||
out6Queue := nfqueue.NewNFQueue(17060)
|
||||
in6Queue := nfqueue.NewNFQueue(17160)
|
||||
|
||||
out4Channel := out4Queue.Process()
|
||||
// if err != nil {
|
||||
// log.Criticalf("could not open nfqueue out4")
|
||||
// } else {
|
||||
defer out4Queue.Destroy()
|
||||
// }
|
||||
in4Channel := in4Queue.Process()
|
||||
// if err != nil {
|
||||
// log.Criticalf("could not open nfqueue in4")
|
||||
// } else {
|
||||
defer in4Queue.Destroy()
|
||||
// }
|
||||
out6Channel := out6Queue.Process()
|
||||
// if err != nil {
|
||||
// log.Criticalf("could not open nfqueue out6")
|
||||
// } else {
|
||||
defer out6Queue.Destroy()
|
||||
// }
|
||||
in6Channel := in6Queue.Process()
|
||||
// if err != nil {
|
||||
// log.Criticalf("could not open nfqueue in6")
|
||||
// } else {
|
||||
defer in6Queue.Destroy()
|
||||
// }
|
||||
|
||||
packetInterceptionLoop:
|
||||
for {
|
||||
select {
|
||||
case <-nfqueueModule.Stop:
|
||||
break packetInterceptionLoop
|
||||
case pkt := <-out4Channel:
|
||||
pkt.SetOutbound()
|
||||
Packets <- pkt
|
||||
case pkt := <-in4Channel:
|
||||
pkt.SetInbound()
|
||||
Packets <- pkt
|
||||
case pkt := <-out6Channel:
|
||||
pkt.SetOutbound()
|
||||
Packets <- pkt
|
||||
case pkt := <-in6Channel:
|
||||
pkt.SetInbound()
|
||||
Packets <- pkt
|
||||
}
|
||||
}
|
||||
|
||||
if err := deactivateNfqueueFirewall(); err != nil {
|
||||
log.Criticalf("could not deactivate firewall for nfqueue: %q", err)
|
||||
}
|
||||
|
||||
nfqueueModule.StopComplete()
|
||||
|
||||
}
|
||||
|
||||
func stringInSlice(slice []string, value string) bool {
|
||||
for _, entry := range slice {
|
||||
if value == entry {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
201
firewall/interception/nfqueue/LICENSE
Normal file
201
firewall/interception/nfqueue/LICENSE
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
42
firewall/interception/nfqueue/README.md
Normal file
42
firewall/interception/nfqueue/README.md
Normal file
@@ -0,0 +1,42 @@
|
||||
Go-NFQueue
|
||||
==========
|
||||
Go Wrapper For Creating IPTables' NFQueue clients in Go
|
||||
|
||||
Usage
|
||||
------
|
||||
Check the `examples/main.go` file
|
||||
|
||||
```bash
|
||||
cd $GOPATH/github.com/OneOfOne/go-nfqueue/examples
|
||||
go build -race && sudo ./examples
|
||||
```
|
||||
* Open another terminal :
|
||||
```bash
|
||||
sudo iptables -I INPUT 1 -m conntrack --ctstate NEW -j NFQUEUE --queue-num 0
|
||||
#or
|
||||
sudo iptables -I INPUT -i eth0 -m conntrack --ctstate NEW -j NFQUEUE --queue-num 0
|
||||
curl --head localhost
|
||||
ping localhost
|
||||
sudo iptables -D INPUT -m conntrack --ctstate NEW -j NFQUEUE --queue-num 0
|
||||
```
|
||||
Then you can `ctrl+c` the program to exit.
|
||||
|
||||
* If you have recent enough iptables/nfqueue you could also use a balanced (multithreaded queue).
|
||||
* check the example in `examples/mq/multiqueue.go`
|
||||
|
||||
```bash
|
||||
iptables -I INPUT 1 -m conntrack --ctstate NEW -j NFQUEUE --queue-balance 0:5 --queue-cpu-fanout
|
||||
```
|
||||
Notes
|
||||
-----
|
||||
|
||||
You must run the executable as root.
|
||||
This is *WIP*, but all patches are welcome.
|
||||
|
||||
License
|
||||
-------
|
||||
go-nfqueue is under the Apache v2 license, check the included license file.
|
||||
Copyright © [Ahmed W.](http://www.limitlessfx.com/)
|
||||
See the included `LICENSE` file.
|
||||
|
||||
> Copyright (c) 2014 Ahmed W.
|
||||
46
firewall/interception/nfqueue/multiqueue.go
Normal file
46
firewall/interception/nfqueue/multiqueue.go
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright Safing ICS Technologies GmbH. Use of this source code is governed by the AGPL license that can be found in the LICENSE file.
|
||||
|
||||
package nfqueue
|
||||
|
||||
import (
|
||||
"github.com/Safing/safing-core/network/packet"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type multiQueue struct {
|
||||
qs []*nfQueue
|
||||
}
|
||||
|
||||
func NewMultiQueue(min, max uint16) (mq *multiQueue) {
|
||||
mq = &multiQueue{make([]*nfQueue, 0, max-min)}
|
||||
for i := min; i < max; i++ {
|
||||
mq.qs = append(mq.qs, NewNFQueue(i))
|
||||
}
|
||||
return mq
|
||||
}
|
||||
|
||||
func (mq *multiQueue) Process() <-chan packet.Packet {
|
||||
var (
|
||||
wg sync.WaitGroup
|
||||
out = make(chan packet.Packet, len(mq.qs))
|
||||
)
|
||||
for _, q := range mq.qs {
|
||||
wg.Add(1)
|
||||
go func(ch <-chan packet.Packet) {
|
||||
for pkt := range ch {
|
||||
out <- pkt
|
||||
}
|
||||
wg.Done()
|
||||
}(q.Process())
|
||||
}
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(out)
|
||||
}()
|
||||
return out
|
||||
}
|
||||
func (mq *multiQueue) Destroy() {
|
||||
for _, q := range mq.qs {
|
||||
q.Destroy()
|
||||
}
|
||||
}
|
||||
88
firewall/interception/nfqueue/nfqueue.c
Normal file
88
firewall/interception/nfqueue/nfqueue.c
Normal file
@@ -0,0 +1,88 @@
|
||||
#include "nfqueue.h"
|
||||
#include "_cgo_export.h"
|
||||
|
||||
|
||||
int nfqueue_cb_new(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfa, void *data) {
|
||||
|
||||
struct nfqnl_msg_packet_hdr *ph = nfq_get_msg_packet_hdr(nfa);
|
||||
|
||||
if(ph == NULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int id = ntohl(ph->packet_id);
|
||||
|
||||
unsigned char * payload;
|
||||
unsigned char * saddr, * daddr;
|
||||
uint16_t sport = 0, dport = 0, checksum = 0;
|
||||
uint32_t mark = nfq_get_nfmark(nfa);
|
||||
|
||||
int len = nfq_get_payload(nfa, &payload);
|
||||
|
||||
unsigned char * origpayload = payload;
|
||||
int origlen = len;
|
||||
|
||||
if(len < sizeof(struct iphdr)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct iphdr * ip = (struct iphdr *) payload;
|
||||
|
||||
if(ip->version == 4) {
|
||||
uint32_t ipsz = (ip->ihl << 2);
|
||||
if(len < ipsz) {
|
||||
return 0;
|
||||
}
|
||||
len -= ipsz;
|
||||
payload += ipsz;
|
||||
|
||||
saddr = (unsigned char *)&ip->saddr;
|
||||
daddr = (unsigned char *)&ip->daddr;
|
||||
|
||||
if(ip->protocol == IPPROTO_TCP) {
|
||||
if(len < sizeof(struct tcphdr)) {
|
||||
return 0;
|
||||
}
|
||||
struct tcphdr *tcp = (struct tcphdr *) payload;
|
||||
uint32_t tcpsz = (tcp->doff << 2);
|
||||
if(len < tcpsz) {
|
||||
return 0;
|
||||
}
|
||||
len -= tcpsz;
|
||||
payload += tcpsz;
|
||||
|
||||
sport = ntohs(tcp->source);
|
||||
dport = ntohs(tcp->dest);
|
||||
checksum = ntohs(tcp->check);
|
||||
} else if(ip->protocol == IPPROTO_UDP) {
|
||||
if(len < sizeof(struct udphdr)) {
|
||||
return 0;
|
||||
}
|
||||
struct udphdr *u = (struct udphdr *) payload;
|
||||
len -= sizeof(struct udphdr);
|
||||
payload += sizeof(struct udphdr);
|
||||
|
||||
sport = ntohs(u->source);
|
||||
dport = ntohs(u->dest);
|
||||
checksum = ntohs(u->check);
|
||||
}
|
||||
} else {
|
||||
struct ipv6hdr *ip6 = (struct ipv6hdr*) payload;
|
||||
saddr = (unsigned char *)&ip6->saddr;
|
||||
daddr = (unsigned char *)&ip6->daddr;
|
||||
//ipv6
|
||||
}
|
||||
//pass everything we can and let Go handle it, I'm not a big fan of C
|
||||
uint32_t verdict = go_nfq_callback(id, ntohs(ph->hw_protocol), ph->hook, &mark, ip->version, ip->protocol,
|
||||
ip->tos, ip->ttl, saddr, daddr, sport, dport, checksum, origlen, origpayload, data);
|
||||
return nfq_set_verdict2(qh, id, verdict, mark, 0, NULL);
|
||||
}
|
||||
|
||||
void loop_for_packets(struct nfq_handle *h) {
|
||||
int fd = nfq_fd(h);
|
||||
char buf[65535] __attribute__ ((aligned));
|
||||
int rv;
|
||||
while ((rv = recv(fd, buf, sizeof(buf), 0)) && rv >= 0) {
|
||||
nfq_handle_packet(h, buf, rv);
|
||||
}
|
||||
}
|
||||
203
firewall/interception/nfqueue/nfqueue.go
Normal file
203
firewall/interception/nfqueue/nfqueue.go
Normal file
@@ -0,0 +1,203 @@
|
||||
// Copyright Safing ICS Technologies GmbH. Use of this source code is governed by the AGPL license that can be found in the LICENSE file.
|
||||
|
||||
package nfqueue
|
||||
|
||||
/*
|
||||
#cgo LDFLAGS: -lnetfilter_queue
|
||||
#cgo CFLAGS: -Wall
|
||||
#include "nfqueue.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"runtime"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/Safing/safing-core/network/packet"
|
||||
)
|
||||
|
||||
var queues map[uint16]*nfQueue
|
||||
|
||||
func init() {
|
||||
queues = make(map[uint16]*nfQueue)
|
||||
}
|
||||
|
||||
type nfQueue struct {
|
||||
DefaultVerdict uint32
|
||||
Timeout time.Duration
|
||||
qid uint16
|
||||
qidptr *uint16
|
||||
h *C.struct_nfq_handle
|
||||
//qh *C.struct_q_handle
|
||||
qh *C.struct_nfq_q_handle
|
||||
fd int
|
||||
lk sync.Mutex
|
||||
|
||||
pktch chan packet.Packet
|
||||
}
|
||||
|
||||
func NewNFQueue(qid uint16) (nfq *nfQueue) {
|
||||
if os.Geteuid() != 0 {
|
||||
panic("Must be ran by root.")
|
||||
}
|
||||
nfq = &nfQueue{DefaultVerdict: NFQ_ACCEPT, Timeout: 100 * time.Millisecond, qid: qid, qidptr: &qid}
|
||||
queues[nfq.qid] = nfq
|
||||
return nfq
|
||||
}
|
||||
|
||||
/*
|
||||
This returns a channel that will recieve packets,
|
||||
the user then must call pkt.Accept() or pkt.Drop()
|
||||
*/
|
||||
func (this *nfQueue) Process() <-chan packet.Packet {
|
||||
if this.h != nil {
|
||||
return this.pktch
|
||||
}
|
||||
this.init()
|
||||
|
||||
go func() {
|
||||
runtime.LockOSThread()
|
||||
C.loop_for_packets(this.h)
|
||||
}()
|
||||
|
||||
return this.pktch
|
||||
}
|
||||
|
||||
func (this *nfQueue) init() {
|
||||
var err error
|
||||
if this.h, err = C.nfq_open(); err != nil || this.h == nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
//if this.qh, err = C.nfq_create_queue(this.h, qid, C.get_cb(), unsafe.Pointer(nfq)); err != nil || this.qh == nil {
|
||||
|
||||
this.pktch = make(chan packet.Packet, 1)
|
||||
|
||||
if C.nfq_unbind_pf(this.h, C.AF_INET) < 0 {
|
||||
this.Destroy()
|
||||
panic("nfq_unbind_pf(AF_INET) failed, are you running root?.")
|
||||
}
|
||||
if C.nfq_unbind_pf(this.h, C.AF_INET6) < 0 {
|
||||
this.Destroy()
|
||||
panic("nfq_unbind_pf(AF_INET6) failed.")
|
||||
}
|
||||
|
||||
if C.nfq_bind_pf(this.h, C.AF_INET) < 0 {
|
||||
this.Destroy()
|
||||
panic("nfq_bind_pf(AF_INET) failed.")
|
||||
}
|
||||
|
||||
if C.nfq_bind_pf(this.h, C.AF_INET6) < 0 {
|
||||
this.Destroy()
|
||||
panic("nfq_bind_pf(AF_INET6) failed.")
|
||||
}
|
||||
|
||||
if this.qh, err = C.create_queue(this.h, C.uint16_t(this.qid)); err != nil || this.qh == nil {
|
||||
C.nfq_close(this.h)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
this.fd = int(C.nfq_fd(this.h))
|
||||
|
||||
if C.nfq_set_mode(this.qh, C.NFQNL_COPY_PACKET, 0xffff) < 0 {
|
||||
this.Destroy()
|
||||
panic("nfq_set_mode(NFQNL_COPY_PACKET) failed.")
|
||||
}
|
||||
if C.nfq_set_queue_maxlen(this.qh, 1024*8) < 0 {
|
||||
this.Destroy()
|
||||
panic("nfq_set_queue_maxlen(1024 * 8) failed.")
|
||||
}
|
||||
}
|
||||
|
||||
func (this *nfQueue) Destroy() {
|
||||
this.lk.Lock()
|
||||
defer this.lk.Unlock()
|
||||
|
||||
if this.fd != 0 && this.Valid() {
|
||||
syscall.Close(this.fd)
|
||||
}
|
||||
if this.qh != nil {
|
||||
C.nfq_destroy_queue(this.qh)
|
||||
this.qh = nil
|
||||
}
|
||||
if this.h != nil {
|
||||
C.nfq_close(this.h)
|
||||
this.h = nil
|
||||
}
|
||||
|
||||
// TODO: don't close, we're exiting anyway
|
||||
// if this.pktch != nil {
|
||||
// close(this.pktch)
|
||||
// }
|
||||
}
|
||||
|
||||
func (this *nfQueue) Valid() bool {
|
||||
return this.h != nil && this.qh != nil
|
||||
}
|
||||
|
||||
//export go_nfq_callback
|
||||
func go_nfq_callback(id uint32, hwproto uint16, hook uint8, mark *uint32,
|
||||
version, protocol, tos, ttl uint8, saddr, daddr unsafe.Pointer,
|
||||
sport, dport, checksum uint16, payload_len uint32, payload, data unsafe.Pointer) (v uint32) {
|
||||
|
||||
qidptr := (*uint16)(data)
|
||||
qid := uint16(*qidptr)
|
||||
|
||||
// nfq := (*nfQueue)(nfqptr)
|
||||
new_version := version
|
||||
ipver := packet.IPVersion(new_version)
|
||||
ipsz := C.int(ipver.ByteSize())
|
||||
bs := C.GoBytes(payload, (C.int)(payload_len))
|
||||
|
||||
verdict := make(chan uint32, 1)
|
||||
pkt := Packet{
|
||||
QueueId: qid,
|
||||
Id: id,
|
||||
HWProtocol: hwproto,
|
||||
Hook: hook,
|
||||
Mark: *mark,
|
||||
verdict: verdict,
|
||||
// StartedHandling: time.Now(),
|
||||
}
|
||||
|
||||
// Payload
|
||||
pkt.Payload = bs
|
||||
|
||||
// IPHeader
|
||||
pkt.IPHeader = &packet.IPHeader{
|
||||
Version: 4,
|
||||
Protocol: packet.IPProtocol(protocol),
|
||||
Tos: tos,
|
||||
TTL: ttl,
|
||||
Src: net.IP(C.GoBytes(saddr, ipsz)),
|
||||
Dst: net.IP(C.GoBytes(daddr, ipsz)),
|
||||
}
|
||||
|
||||
// TCPUDPHeader
|
||||
pkt.TCPUDPHeader = &packet.TCPUDPHeader{
|
||||
SrcPort: sport,
|
||||
DstPort: dport,
|
||||
Checksum: checksum,
|
||||
}
|
||||
|
||||
// fmt.Printf("%s queuing packet\n", time.Now().Format("060102 15:04:05.000"))
|
||||
// BUG: "panic: send on closed channel" when shutting down
|
||||
queues[qid].pktch <- &pkt
|
||||
|
||||
select {
|
||||
case v = <-pkt.verdict:
|
||||
*mark = pkt.Mark
|
||||
// *mark = 1710
|
||||
case <-time.After(queues[qid].Timeout):
|
||||
v = queues[qid].DefaultVerdict
|
||||
}
|
||||
|
||||
// log.Tracef("nfqueue: took %s to handle packet", time.Now().Sub(pkt.StartedHandling).String())
|
||||
|
||||
return v
|
||||
}
|
||||
30
firewall/interception/nfqueue/nfqueue.h
Normal file
30
firewall/interception/nfqueue/nfqueue.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
// #define _BSD_SOURCE
|
||||
// #define __BSD_SOURCE
|
||||
|
||||
// #define __FAVOR_BSD // Just Using _BSD_SOURCE didn't work on my system for some reason
|
||||
// #define __USE_BSD
|
||||
#include <stdlib.h>
|
||||
// #include <sys/socket.h>
|
||||
// #include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/udp.h>
|
||||
#include <linux/ipv6.h>
|
||||
// #include <linux/netfilter.h>
|
||||
#include <libnetfilter_queue/libnetfilter_queue.h>
|
||||
|
||||
// extern int nfq_callback(uint8_t version, uint8_t protocol, unsigned char *saddr, unsigned char *daddr,
|
||||
// uint16_t sport, uint16_t dport, unsigned char * extra, void* data);
|
||||
|
||||
int nfqueue_cb_new(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfa, void *data);
|
||||
void loop_for_packets(struct nfq_handle *h);
|
||||
|
||||
static inline struct nfq_q_handle * create_queue(struct nfq_handle *h, uint16_t qid) {
|
||||
//we use this because it's more convient to pass the callback in C
|
||||
// FIXME: check malloc success
|
||||
uint16_t *data = malloc(sizeof(uint16_t));
|
||||
*data = qid;
|
||||
return nfq_create_queue(h, qid, &nfqueue_cb_new, (void*)data);
|
||||
}
|
||||
116
firewall/interception/nfqueue/packet.go
Normal file
116
firewall/interception/nfqueue/packet.go
Normal file
@@ -0,0 +1,116 @@
|
||||
// Copyright Safing ICS Technologies GmbH. Use of this source code is governed by the AGPL license that can be found in the LICENSE file.
|
||||
|
||||
package nfqueue
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Safing/safing-core/network/packet"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrVerdictSentOrTimedOut error = fmt.Errorf("The verdict was already sent or timed out.")
|
||||
)
|
||||
|
||||
const (
|
||||
NFQ_DROP uint32 = 0 // discarded the packet
|
||||
NFQ_ACCEPT uint32 = 1 // the packet passes, continue iterations
|
||||
NFQ_STOLEN uint32 = 2 // gone away
|
||||
NFQ_QUEUE uint32 = 3 // inject the packet into a different queue (the target queue number is in the high 16 bits of the verdict)
|
||||
NFQ_REPEAT uint32 = 4 // iterate the same cycle once more
|
||||
NFQ_STOP uint32 = 5 // accept, but don't continue iterations
|
||||
)
|
||||
|
||||
type Packet struct {
|
||||
packet.PacketBase
|
||||
|
||||
QueueId uint16
|
||||
Id uint32
|
||||
HWProtocol uint16
|
||||
Hook uint8
|
||||
Mark uint32
|
||||
|
||||
// StartedHandling time.Time
|
||||
|
||||
verdict chan uint32
|
||||
}
|
||||
|
||||
// func (pkt *Packet) String() string {
|
||||
// return fmt.Sprintf("<Packet QId: %d, Id: %d, Type: %s, Src: %s:%d, Dst: %s:%d, Mark: 0x%X, Checksum: 0x%X, TOS: 0x%X, TTL: %d>",
|
||||
// pkt.QueueId, pkt.Id, pkt.Protocol, pkt.Src, pkt.SrcPort, pkt.Dst, pkt.DstPort, pkt.Mark, pkt.Checksum, pkt.Tos, pkt.TTL)
|
||||
// }
|
||||
|
||||
func (pkt *Packet) setVerdict(v uint32) (err error) {
|
||||
defer func() {
|
||||
if x := recover(); x != nil {
|
||||
err = ErrVerdictSentOrTimedOut
|
||||
}
|
||||
}()
|
||||
pkt.verdict <- v
|
||||
close(pkt.verdict)
|
||||
// log.Tracef("firewall: packet %s verdict %d", pkt, v)
|
||||
return err
|
||||
}
|
||||
|
||||
// Marks:
|
||||
// 17: Identifier
|
||||
// 0/1: Just this packet/this Link
|
||||
// 0/1/2: Accept, Block, Drop
|
||||
|
||||
// func (pkt *Packet) Accept() error {
|
||||
// return pkt.setVerdict(NFQ_STOP)
|
||||
// }
|
||||
//
|
||||
// func (pkt *Packet) Block() error {
|
||||
// pkt.Mark = 1701
|
||||
// return pkt.setVerdict(NFQ_ACCEPT)
|
||||
// }
|
||||
//
|
||||
// func (pkt *Packet) Drop() error {
|
||||
// return pkt.setVerdict(NFQ_DROP)
|
||||
// }
|
||||
|
||||
func (pkt *Packet) Accept() error {
|
||||
pkt.Mark = 1700
|
||||
return pkt.setVerdict(NFQ_ACCEPT)
|
||||
}
|
||||
|
||||
func (pkt *Packet) Block() error {
|
||||
pkt.Mark = 1701
|
||||
return pkt.setVerdict(NFQ_ACCEPT)
|
||||
}
|
||||
|
||||
func (pkt *Packet) Drop() error {
|
||||
pkt.Mark = 1702
|
||||
return pkt.setVerdict(NFQ_ACCEPT)
|
||||
}
|
||||
|
||||
func (pkt *Packet) PermanentAccept() error {
|
||||
pkt.Mark = 1710
|
||||
return pkt.setVerdict(NFQ_ACCEPT)
|
||||
}
|
||||
|
||||
func (pkt *Packet) PermanentBlock() error {
|
||||
pkt.Mark = 1711
|
||||
return pkt.setVerdict(NFQ_ACCEPT)
|
||||
}
|
||||
|
||||
func (pkt *Packet) PermanentDrop() error {
|
||||
pkt.Mark = 1712
|
||||
return pkt.setVerdict(NFQ_ACCEPT)
|
||||
}
|
||||
|
||||
func (pkt *Packet) RerouteToNameserver() error {
|
||||
pkt.Mark = 1799
|
||||
return pkt.setVerdict(NFQ_ACCEPT)
|
||||
}
|
||||
|
||||
func (pkt *Packet) RerouteToTunnel() error {
|
||||
pkt.Mark = 1717
|
||||
return pkt.setVerdict(NFQ_ACCEPT)
|
||||
}
|
||||
|
||||
//HUGE warning, if the iptables rules aren't set correctly this can cause some problems.
|
||||
// func (pkt *Packet) Repeat() error {
|
||||
// return this.SetVerdict(REPEAT)
|
||||
// }
|
||||
154
firewall/interception/windivert/handler.go
Normal file
154
firewall/interception/windivert/handler.go
Normal file
@@ -0,0 +1,154 @@
|
||||
package windivert
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
|
||||
"github.com/Safing/safing-core/log"
|
||||
"github.com/Safing/safing-core/network/packet"
|
||||
|
||||
"github.com/tevino/abool"
|
||||
)
|
||||
|
||||
func (wd *WinDivert) Packets(packets chan packet.Packet) error {
|
||||
go wd.packetHandler(packets)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (wd *WinDivert) packetHandler(packets chan packet.Packet) {
|
||||
defer close(packets)
|
||||
|
||||
for {
|
||||
if !wd.valid.IsSet() {
|
||||
return
|
||||
}
|
||||
|
||||
packetData, packetAddress, err := wd.Recv()
|
||||
if err != nil {
|
||||
log.Warningf("failed to get packet from windivert: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
ipHeader, tpcUdpHeader, payload, err := parseIpPacket(packetData)
|
||||
if err != nil {
|
||||
log.Warningf("failed to parse packet from windivert: %s", err)
|
||||
log.Warningf("failed packet payload (%d): %s", len(packetData), string(packetData))
|
||||
continue
|
||||
}
|
||||
|
||||
new := &Packet{
|
||||
windivert: wd,
|
||||
packetData: packetData,
|
||||
packetAddress: packetAddress,
|
||||
verdictSet: abool.NewBool(false),
|
||||
}
|
||||
new.IPHeader = ipHeader
|
||||
new.TCPUDPHeader = tpcUdpHeader
|
||||
new.Payload = payload
|
||||
if packetAddress.Direction == directionInbound {
|
||||
new.Direction = packet.InBound
|
||||
} else {
|
||||
new.Direction = packet.OutBound
|
||||
}
|
||||
|
||||
packets <- new
|
||||
}
|
||||
}
|
||||
|
||||
func parseIpPacket(packetData []byte) (ipHeader *packet.IPHeader, tpcUdpHeader *packet.TCPUDPHeader, payload []byte, err error) {
|
||||
|
||||
var parsedPacket gopacket.Packet
|
||||
|
||||
if len(packetData) == 0 {
|
||||
return nil, nil, nil, errors.New("empty packet")
|
||||
}
|
||||
|
||||
switch packetData[0] >> 4 {
|
||||
case 4:
|
||||
parsedPacket = gopacket.NewPacket(packetData, layers.LayerTypeIPv4, gopacket.DecodeOptions{Lazy: true, NoCopy: true})
|
||||
if ipv4Layer := parsedPacket.Layer(layers.LayerTypeIPv4); ipv4Layer != nil {
|
||||
ipv4, _ := ipv4Layer.(*layers.IPv4)
|
||||
ipHeader = &packet.IPHeader{
|
||||
Version: 4,
|
||||
Protocol: packet.IPProtocol(ipv4.Protocol),
|
||||
Tos: ipv4.TOS,
|
||||
TTL: ipv4.TTL,
|
||||
Src: ipv4.SrcIP,
|
||||
Dst: ipv4.DstIP,
|
||||
}
|
||||
} else {
|
||||
var err error
|
||||
if errLayer := parsedPacket.ErrorLayer(); errLayer != nil {
|
||||
err = errLayer.Error()
|
||||
}
|
||||
return nil, nil, nil, fmt.Errorf("failed to parse IPv4 packet: %s", err)
|
||||
}
|
||||
case 6:
|
||||
parsedPacket = gopacket.NewPacket(packetData, layers.LayerTypeIPv6, gopacket.DecodeOptions{Lazy: true, NoCopy: true})
|
||||
if ipv6Layer := parsedPacket.Layer(layers.LayerTypeIPv6); ipv6Layer != nil {
|
||||
ipv6, _ := ipv6Layer.(*layers.IPv6)
|
||||
ipHeader = &packet.IPHeader{
|
||||
Version: 6,
|
||||
Protocol: packet.IPProtocol(ipv6.NextHeader),
|
||||
Tos: ipv6.TrafficClass,
|
||||
TTL: ipv6.HopLimit,
|
||||
Src: ipv6.SrcIP,
|
||||
Dst: ipv6.DstIP,
|
||||
}
|
||||
} else {
|
||||
var err error
|
||||
if errLayer := parsedPacket.ErrorLayer(); errLayer != nil {
|
||||
err = errLayer.Error()
|
||||
}
|
||||
return nil, nil, nil, fmt.Errorf("failed to parse IPv6 packet: %s", err)
|
||||
}
|
||||
default:
|
||||
return nil, nil, nil, errors.New("unknown IP version")
|
||||
}
|
||||
|
||||
switch ipHeader.Protocol {
|
||||
case packet.TCP:
|
||||
if tcpLayer := parsedPacket.Layer(layers.LayerTypeTCP); tcpLayer != nil {
|
||||
tcp, _ := tcpLayer.(*layers.TCP)
|
||||
tpcUdpHeader = &packet.TCPUDPHeader{
|
||||
SrcPort: uint16(tcp.SrcPort),
|
||||
DstPort: uint16(tcp.DstPort),
|
||||
Checksum: tcp.Checksum,
|
||||
}
|
||||
} else {
|
||||
var err error
|
||||
if errLayer := parsedPacket.ErrorLayer(); errLayer != nil {
|
||||
err = errLayer.Error()
|
||||
}
|
||||
return nil, nil, nil, fmt.Errorf("could not parse TCP layer: %s", err)
|
||||
}
|
||||
case packet.UDP:
|
||||
if udpLayer := parsedPacket.Layer(layers.LayerTypeUDP); udpLayer != nil {
|
||||
udp, _ := udpLayer.(*layers.UDP)
|
||||
tpcUdpHeader = &packet.TCPUDPHeader{
|
||||
SrcPort: uint16(udp.SrcPort),
|
||||
DstPort: uint16(udp.DstPort),
|
||||
Checksum: udp.Checksum,
|
||||
}
|
||||
} else {
|
||||
var err error
|
||||
if errLayer := parsedPacket.ErrorLayer(); errLayer != nil {
|
||||
err = errLayer.Error()
|
||||
}
|
||||
return nil, nil, nil, fmt.Errorf("could not parse UDP layer: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
if appLayer := parsedPacket.ApplicationLayer(); appLayer != nil {
|
||||
payload = appLayer.Payload()
|
||||
}
|
||||
|
||||
if errLayer := parsedPacket.ErrorLayer(); errLayer != nil {
|
||||
return nil, nil, nil, errLayer.Error()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
32
firewall/interception/windivert/notes.md
Normal file
32
firewall/interception/windivert/notes.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# Notes
|
||||
|
||||
## Interception
|
||||
|
||||
- use windivert DLL
|
||||
- cgo or loadDLL?
|
||||
|
||||
- netfilter exmaple: https://reqrypt.org/samples/netfilter.html
|
||||
- v1.4 docs: https://reqrypt.org/windivert-doc.html#divert_recv_ex
|
||||
- source: https://github.com/basil00/Divert
|
||||
|
||||
- other GO package wrapping this: https://github.com/clmul/go-windivert/blob/master/divert_windows.go
|
||||
|
||||
## Packet/Process Attribution
|
||||
|
||||
- use Iphlpapi.dll
|
||||
- GetExtendedTcpTable
|
||||
- GetOwnerModuleFromTcpEntry
|
||||
- GetExtendedUdpTable
|
||||
- GetOwnerModuleFromUdpEntry
|
||||
- for generic IP?
|
||||
|
||||
## Helpful resources
|
||||
|
||||
Calling Windows APIs
|
||||
https://stackoverflow.com/questions/33709033/golang-how-can-i-call-win32-api-without-cgo#33709631
|
||||
|
||||
GetExtendedTcpTable (from Iphlpapi.dll)
|
||||
https://msdn.microsoft.com/en-us/library/windows/desktop/aa365928(v=vs.85).aspx
|
||||
|
||||
GetUdpTable Example
|
||||
https://stackoverflow.com/questions/49167311/how-to-convert-uintptr-to-go-struct
|
||||
55
firewall/interception/windivert/packet.go
Normal file
55
firewall/interception/windivert/packet.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package windivert
|
||||
|
||||
import (
|
||||
"github.com/Safing/safing-core/network/packet"
|
||||
"github.com/tevino/abool"
|
||||
)
|
||||
|
||||
type Packet struct {
|
||||
packet.PacketBase
|
||||
|
||||
windivert *WinDivert
|
||||
packetData []byte
|
||||
packetAddress *WinDivertAddress
|
||||
|
||||
verdictSet *abool.AtomicBool
|
||||
}
|
||||
|
||||
func (pkt *Packet) Accept() error {
|
||||
if pkt.verdictSet.SetToIf(false, true) {
|
||||
return pkt.windivert.Send(pkt.packetData, pkt.packetAddress)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pkt *Packet) Block() error {
|
||||
if pkt.verdictSet.SetToIf(false, true) {
|
||||
// TODO: implement blocking mechanism
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pkt *Packet) Drop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pkt *Packet) PermanentAccept() error {
|
||||
return pkt.Accept()
|
||||
}
|
||||
|
||||
func (pkt *Packet) PermanentBlock() error {
|
||||
return pkt.Block()
|
||||
}
|
||||
|
||||
func (pkt *Packet) PermanentDrop() error {
|
||||
return pkt.Drop()
|
||||
}
|
||||
|
||||
func (pkt *Packet) RerouteToNameserver() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pkt *Packet) RerouteToTunnel() error {
|
||||
return nil
|
||||
}
|
||||
54
firewall/interception/windivert/test/build
Normal file
54
firewall/interception/windivert/test/build
Normal file
@@ -0,0 +1,54 @@
|
||||
#!/bin/bash
|
||||
|
||||
# get build data
|
||||
if [[ "$BUILD_COMMIT" == "" ]]; then
|
||||
BUILD_COMMIT=$(git describe --all --long --abbrev=99 --dirty 2>/dev/null)
|
||||
fi
|
||||
if [[ "$BUILD_USER" == "" ]]; then
|
||||
BUILD_USER=$(id -un)
|
||||
fi
|
||||
if [[ "$BUILD_HOST" == "" ]]; then
|
||||
BUILD_HOST=$(hostname -f)
|
||||
fi
|
||||
if [[ "$BUILD_DATE" == "" ]]; then
|
||||
BUILD_DATE=$(date +%d.%m.%Y)
|
||||
fi
|
||||
if [[ "$BUILD_SOURCE" == "" ]]; then
|
||||
BUILD_SOURCE=$(git remote -v | grep origin | cut -f2 | cut -d" " -f1 | head -n 1)
|
||||
fi
|
||||
if [[ "$BUILD_SOURCE" == "" ]]; then
|
||||
BUILD_SOURCE=$(git remote -v | cut -f2 | cut -d" " -f1 | head -n 1)
|
||||
fi
|
||||
BUILD_BUILDOPTIONS=$(echo $* | sed "s/ /§/g")
|
||||
|
||||
# check
|
||||
if [[ "$BUILD_COMMIT" == "" ]]; then
|
||||
echo "could not automatically determine BUILD_COMMIT, please supply manually as environment variable."
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$BUILD_USER" == "" ]]; then
|
||||
echo "could not automatically determine BUILD_USER, please supply manually as environment variable."
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$BUILD_HOST" == "" ]]; then
|
||||
echo "could not automatically determine BUILD_HOST, please supply manually as environment variable."
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$BUILD_DATE" == "" ]]; then
|
||||
echo "could not automatically determine BUILD_DATE, please supply manually as environment variable."
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$BUILD_SOURCE" == "" ]]; then
|
||||
echo "could not automatically determine BUILD_SOURCE, please supply manually as environment variable."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Please notice, that this build script includes metadata into the build."
|
||||
echo "This information is useful for debugging and license compliance."
|
||||
echo "Run the compiled binary with the -v flag to see the information included."
|
||||
|
||||
# build
|
||||
if [[ "$BUILD_PATH" == "" ]]; then
|
||||
BUILD_PATH=$(go list)
|
||||
fi
|
||||
go build -ldflags "-X ${BUILD_PATH}/meta.commit=${BUILD_COMMIT} -X ${BUILD_PATH}/meta.buildOptions=${BUILD_BUILDOPTIONS} -X ${BUILD_PATH}/meta.buildUser=${BUILD_USER} -X ${BUILD_PATH}/meta.buildHost=${BUILD_HOST} -X ${BUILD_PATH}/meta.buildDate=${BUILD_DATE} -X ${BUILD_PATH}/meta.buildSource=${BUILD_SOURCE}" $*
|
||||
BIN
firewall/interception/windivert/test/main.exe
Normal file
BIN
firewall/interception/windivert/test/main.exe
Normal file
Binary file not shown.
66
firewall/interception/windivert/test/main.go
Normal file
66
firewall/interception/windivert/test/main.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"runtime/pprof"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/Safing/safing-core/firewall/interception/windivert"
|
||||
"github.com/Safing/safing-core/log"
|
||||
"github.com/Safing/safing-core/modules"
|
||||
"github.com/Safing/safing-core/network/packet"
|
||||
)
|
||||
|
||||
func main() {
|
||||
modules.RegisterLogger(log.Logger)
|
||||
|
||||
wd, err := windivert.New("C:/WinDivert.dll", "")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer wd.Close()
|
||||
|
||||
packets := make(chan packet.Packet, 1000)
|
||||
wd.Packets(packets)
|
||||
go func() {
|
||||
for pkt := range packets {
|
||||
log.Infof("pkt: %s", pkt)
|
||||
if pkt.GetIPHeader().Protocol == 0 || pkt.GetIPHeader().Protocol == 128 {
|
||||
pl := pkt.GetPayload()
|
||||
log.Infof("payload (%d): %s", len(pl), string(pl))
|
||||
}
|
||||
pkt.Accept()
|
||||
}
|
||||
}()
|
||||
|
||||
// SHUTDOWN
|
||||
// catch interrupt for clean shutdown
|
||||
signalCh := make(chan os.Signal)
|
||||
signal.Notify(
|
||||
signalCh,
|
||||
os.Interrupt,
|
||||
os.Kill,
|
||||
syscall.SIGHUP,
|
||||
syscall.SIGINT,
|
||||
syscall.SIGTERM,
|
||||
syscall.SIGQUIT,
|
||||
syscall.SIGKILL,
|
||||
syscall.SIGSEGV,
|
||||
)
|
||||
select {
|
||||
case <-signalCh:
|
||||
log.Warning("program was interrupted, shutting down.")
|
||||
modules.InitiateFullShutdown()
|
||||
case <-modules.GlobalShutdown:
|
||||
}
|
||||
|
||||
// wait for shutdown to complete, panic after timeout
|
||||
time.Sleep(5 * time.Second)
|
||||
fmt.Println("===== TAKING TOO LONG FOR SHUTDOWN - PRINTING STACK TRACES =====")
|
||||
pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
|
||||
os.Exit(1)
|
||||
|
||||
}
|
||||
248
firewall/interception/windivert/windivert.go
Normal file
248
firewall/interception/windivert/windivert.go
Normal file
@@ -0,0 +1,248 @@
|
||||
package windivert
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
|
||||
"github.com/tevino/abool"
|
||||
)
|
||||
|
||||
type WinDivert struct {
|
||||
dll *windows.DLL
|
||||
handle uintptr
|
||||
|
||||
open *windows.Proc
|
||||
recv *windows.Proc
|
||||
send *windows.Proc
|
||||
close *windows.Proc
|
||||
setParam *windows.Proc
|
||||
getParam *windows.Proc
|
||||
helperCalcChecksums *windows.Proc
|
||||
helperCheckFilter *windows.Proc
|
||||
|
||||
valid *abool.AtomicBool
|
||||
}
|
||||
|
||||
// copied from windivert.h
|
||||
type WinDivertAddress struct {
|
||||
Timestamp int64 /* Packet's timestamp. */
|
||||
IfIdx uint32 /* Packet's interface index. */
|
||||
SubIfIdx uint32 /* Packet's sub-interface index. */
|
||||
Direction uint8 /* Packet's direction. */
|
||||
Loopback uint8 /* Packet is loopback? */
|
||||
Impostor uint8 /* Packet is impostor? */
|
||||
PseudoIPChecksum uint8 /* Packet has pseudo IPv4 checksum? */
|
||||
PseudoTCPChecksum uint8 /* Packet has pseudo TCP checksum? */
|
||||
PseudoUDPChecksum uint8 /* Packet has pseudo UDP checksum? */
|
||||
Reserved uint8
|
||||
}
|
||||
|
||||
// copied from windivert.h
|
||||
const (
|
||||
directionInbound uint8 = 1
|
||||
directionOutbound uint8 = 0
|
||||
|
||||
// Divert layers
|
||||
layerNetwork uintptr = 0 /* Network layer. */
|
||||
layerNetworkForward uintptr = 1 /* Network layer (forwarded packets) */
|
||||
|
||||
// Divert parameters
|
||||
flagSniff uintptr = 1
|
||||
flagDrop uintptr = 2
|
||||
flagDebug uintptr = 4
|
||||
|
||||
paramQueueLen uintptr = 0 /* Packet queue length. */
|
||||
paramQueueTime uintptr = 1 /* Packet queue time. */
|
||||
paramQueueSize uintptr = 2 /* Packet queue size. */
|
||||
|
||||
rvInvalidHandle int = -1
|
||||
rvFalse uintptr = 0
|
||||
rvTrue uintptr = 1
|
||||
)
|
||||
|
||||
func New(dllLocation, filter string) (*WinDivert, error) {
|
||||
|
||||
new := &WinDivert{}
|
||||
var err error
|
||||
|
||||
// load dll
|
||||
new.dll, err = windows.LoadDLL(dllLocation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// load functions
|
||||
new.open, err = new.dll.FindProc("WinDivertOpen")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not find proc WinDivertOpen: %s", err)
|
||||
}
|
||||
new.recv, err = new.dll.FindProc("WinDivertRecv")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not find proc WinDivertRecv: %s", err)
|
||||
}
|
||||
new.send, err = new.dll.FindProc("WinDivertSend")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not find proc WinDivertSend: %s", err)
|
||||
}
|
||||
new.close, err = new.dll.FindProc("WinDivertClose")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not find proc WinDivertClose: %s", err)
|
||||
}
|
||||
new.setParam, err = new.dll.FindProc("WinDivertSetParam")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not find proc WinDivertSetParam: %s", err)
|
||||
}
|
||||
new.getParam, err = new.dll.FindProc("WinDivertGetParam")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not find proc WinDivertGetParam: %s", err)
|
||||
}
|
||||
new.helperCalcChecksums, err = new.dll.FindProc("WinDivertHelperCalcChecksums")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not find proc WinDivertHelperCalcChecksums: %s", err)
|
||||
}
|
||||
new.helperCheckFilter, err = new.dll.FindProc("WinDivertHelperCheckFilter")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not find proc WinDivertHelperCheckFilter: %s", err)
|
||||
}
|
||||
|
||||
// default filter
|
||||
if filter == "" {
|
||||
filter = "true"
|
||||
}
|
||||
|
||||
// open
|
||||
err = new.Open(filter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not open new windivert handle: %s", err)
|
||||
}
|
||||
|
||||
return new, nil
|
||||
|
||||
}
|
||||
|
||||
func (wd *WinDivert) Open(filter string) error {
|
||||
|
||||
r1, _, lastErr := wd.open.Call(
|
||||
stringToPtr(filter), // __in const char *filter
|
||||
layerNetwork, // __in WINDIVERT_LAYER layer
|
||||
0, // __in INT16 priority
|
||||
0, // __in UINT64 flags
|
||||
)
|
||||
if int(r1) == rvInvalidHandle {
|
||||
return lastErr
|
||||
}
|
||||
|
||||
wd.handle = r1
|
||||
wd.valid = abool.NewBool(true)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (wd *WinDivert) Recv() ([]byte, *WinDivertAddress, error) {
|
||||
buf := make([]byte, 4096) // TODO: we can do this better
|
||||
address := &WinDivertAddress{}
|
||||
readLen := 0
|
||||
|
||||
r1, _, lastErr := wd.recv.Call(
|
||||
wd.handle, // __in HANDLE handle
|
||||
byteSliceToPtr(buf), // __out PVOID pPacket
|
||||
uintptr(len(buf)), // __in UINT packetLen
|
||||
uintptr(unsafe.Pointer(address)), // __out_opt PWINDIVERT_ADDRESS pAddr
|
||||
uintptr(unsafe.Pointer(&readLen)), // __out_opt UINT *readLen
|
||||
)
|
||||
if r1 == rvFalse {
|
||||
return nil, nil, lastErr
|
||||
}
|
||||
if readLen == 0 {
|
||||
return nil, nil, errors.New("empty read")
|
||||
}
|
||||
|
||||
return buf[:readLen], address, nil
|
||||
}
|
||||
|
||||
func (wd *WinDivert) Send(packetData []byte, address *WinDivertAddress) error {
|
||||
writeLen := 0
|
||||
|
||||
r1, _, lastErr := wd.send.Call(
|
||||
wd.handle, // __in HANDLE handle
|
||||
byteSliceToPtr(packetData), // __in PVOID pPacket
|
||||
uintptr(len(packetData)), // __in UINT packetLen
|
||||
uintptr(unsafe.Pointer(address)), // __in PWINDIVERT_ADDRESS pAddr
|
||||
uintptr(unsafe.Pointer(&writeLen)), // __out_opt UINT *writeLen
|
||||
)
|
||||
if r1 == rvFalse {
|
||||
return lastErr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (wd *WinDivert) Close() error {
|
||||
r1, _, lastErr := wd.close.Call(
|
||||
wd.handle, // __in HANDLE handle
|
||||
)
|
||||
if r1 == rvFalse {
|
||||
return lastErr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (wd *WinDivert) SetParam(param, value uintptr) error {
|
||||
r1, _, lastErr := wd.setParam.Call(
|
||||
wd.handle, // __in HANDLE handle
|
||||
param, // __in WINDIVERT_PARAM param
|
||||
value, // __in UINT64 value
|
||||
)
|
||||
if r1 == rvFalse {
|
||||
return lastErr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (wd *WinDivert) GetParam(param uintptr) (uint64, error) {
|
||||
var value uint64
|
||||
|
||||
r1, _, lastErr := wd.getParam.Call(
|
||||
wd.handle, // __in HANDLE handle
|
||||
param, // __in WINDIVERT_PARAM param
|
||||
uintptr(unsafe.Pointer(&value)), // __out UINT64 *pValue
|
||||
)
|
||||
if r1 == rvFalse {
|
||||
return 0, lastErr
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (wd *WinDivert) HelperCalcChecksums(packetData []byte, address *WinDivertAddress, flags uintptr) error {
|
||||
r1, _, lastErr := wd.setParam.Call(
|
||||
byteSliceToPtr(packetData), // __inout PVOID pPacket
|
||||
uintptr(len(packetData)), // __in UINT packetLen
|
||||
uintptr(unsafe.Pointer(address)), // __in_opt PWINDIVERT_ADDRESS pAddr
|
||||
flags, // __in UINT64 flags
|
||||
)
|
||||
if r1 == rvFalse {
|
||||
return lastErr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// func (wd *WinDivert) HelperCheckFilter() {
|
||||
// // __in const char *filter
|
||||
// // __in WINDIVERT_LAYER layer
|
||||
// // __out_opt const char **errorStr
|
||||
// // __out_opt UINT *errorPos
|
||||
// }
|
||||
|
||||
func stringToPtr(s string) uintptr {
|
||||
if !strings.HasSuffix(s, "\x00") {
|
||||
s = s + "\x00"
|
||||
}
|
||||
a := []byte(s)
|
||||
return uintptr(unsafe.Pointer(&a[0]))
|
||||
}
|
||||
|
||||
func byteSliceToPtr(a []byte) uintptr {
|
||||
return uintptr(unsafe.Pointer(&a[0]))
|
||||
}
|
||||
410
firewall/interception/windivert/windivert.h
Normal file
410
firewall/interception/windivert/windivert.h
Normal file
@@ -0,0 +1,410 @@
|
||||
/*
|
||||
* windivert.h
|
||||
* (C) 2018, all rights reserved,
|
||||
*
|
||||
* This file is part of WinDivert.
|
||||
*
|
||||
* WinDivert is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser 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 Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* WinDivert is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __WINDIVERT_H
|
||||
#define __WINDIVERT_H
|
||||
|
||||
#ifndef WINDIVERT_KERNEL
|
||||
#include <windows.h>
|
||||
#endif /* WINDIVERT_KERNEL */
|
||||
|
||||
#ifndef WINDIVERTEXPORT
|
||||
#define WINDIVERTEXPORT __declspec(dllimport)
|
||||
#endif /* WINDIVERTEXPORT */
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#define __in
|
||||
#define __in_opt
|
||||
#define __out
|
||||
#define __out_opt
|
||||
#define __inout
|
||||
#define __inout_opt
|
||||
#include <stdint.h>
|
||||
#define INT8 int8_t
|
||||
#define UINT8 uint8_t
|
||||
#define INT16 int16_t
|
||||
#define UINT16 uint16_t
|
||||
#define INT32 int32_t
|
||||
#define UINT32 uint32_t
|
||||
#define INT64 int64_t
|
||||
#define UINT64 uint64_t
|
||||
#endif /* __MINGW32__ */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/****************************************************************************/
|
||||
/* WINDIVERT API */
|
||||
/****************************************************************************/
|
||||
|
||||
/*
|
||||
* Divert address.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
INT64 Timestamp; /* Packet's timestamp. */
|
||||
UINT32 IfIdx; /* Packet's interface index. */
|
||||
UINT32 SubIfIdx; /* Packet's sub-interface index. */
|
||||
UINT8 Direction:1; /* Packet's direction. */
|
||||
UINT8 Loopback:1; /* Packet is loopback? */
|
||||
UINT8 Impostor:1; /* Packet is impostor? */
|
||||
UINT8 PseudoIPChecksum:1; /* Packet has pseudo IPv4 checksum? */
|
||||
UINT8 PseudoTCPChecksum:1; /* Packet has pseudo TCP checksum? */
|
||||
UINT8 PseudoUDPChecksum:1; /* Packet has pseudo UDP checksum? */
|
||||
UINT8 Reserved:2;
|
||||
} WINDIVERT_ADDRESS, *PWINDIVERT_ADDRESS;
|
||||
|
||||
#define WINDIVERT_DIRECTION_OUTBOUND 0
|
||||
#define WINDIVERT_DIRECTION_INBOUND 1
|
||||
|
||||
/*
|
||||
* Divert layers.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
WINDIVERT_LAYER_NETWORK = 0, /* Network layer. */
|
||||
WINDIVERT_LAYER_NETWORK_FORWARD = 1 /* Network layer (forwarded packets) */
|
||||
} WINDIVERT_LAYER, *PWINDIVERT_LAYER;
|
||||
|
||||
/*
|
||||
* Divert flags.
|
||||
*/
|
||||
#define WINDIVERT_FLAG_SNIFF 1
|
||||
#define WINDIVERT_FLAG_DROP 2
|
||||
#define WINDIVERT_FLAG_DEBUG 4
|
||||
|
||||
/*
|
||||
* Divert parameters.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
WINDIVERT_PARAM_QUEUE_LEN = 0, /* Packet queue length. */
|
||||
WINDIVERT_PARAM_QUEUE_TIME = 1, /* Packet queue time. */
|
||||
WINDIVERT_PARAM_QUEUE_SIZE = 2 /* Packet queue size. */
|
||||
} WINDIVERT_PARAM, *PWINDIVERT_PARAM;
|
||||
#define WINDIVERT_PARAM_MAX WINDIVERT_PARAM_QUEUE_SIZE
|
||||
|
||||
#ifndef WINDIVERT_KERNEL
|
||||
|
||||
/*
|
||||
* Open a WinDivert handle.
|
||||
*/
|
||||
extern WINDIVERTEXPORT HANDLE WinDivertOpen(
|
||||
__in const char *filter,
|
||||
__in WINDIVERT_LAYER layer,
|
||||
__in INT16 priority,
|
||||
__in UINT64 flags);
|
||||
|
||||
/*
|
||||
* Receive (read) a packet from a WinDivert handle.
|
||||
*/
|
||||
extern WINDIVERTEXPORT BOOL WinDivertRecv(
|
||||
__in HANDLE handle,
|
||||
__out PVOID pPacket,
|
||||
__in UINT packetLen,
|
||||
__out_opt PWINDIVERT_ADDRESS pAddr,
|
||||
__out_opt UINT *readLen);
|
||||
|
||||
/*
|
||||
* Receive (read) a packet from a WinDivert handle.
|
||||
*/
|
||||
extern WINDIVERTEXPORT BOOL WinDivertRecvEx(
|
||||
__in HANDLE handle,
|
||||
__out PVOID pPacket,
|
||||
__in UINT packetLen,
|
||||
__in UINT64 flags,
|
||||
__out_opt PWINDIVERT_ADDRESS pAddr,
|
||||
__out_opt UINT *readLen,
|
||||
__inout_opt LPOVERLAPPED lpOverlapped);
|
||||
|
||||
/*
|
||||
* Send (write/inject) a packet to a WinDivert handle.
|
||||
*/
|
||||
extern WINDIVERTEXPORT BOOL WinDivertSend(
|
||||
__in HANDLE handle,
|
||||
__in PVOID pPacket,
|
||||
__in UINT packetLen,
|
||||
__in PWINDIVERT_ADDRESS pAddr,
|
||||
__out_opt UINT *writeLen);
|
||||
|
||||
/*
|
||||
* Send (write/inject) a packet to a WinDivert handle.
|
||||
*/
|
||||
extern WINDIVERTEXPORT BOOL WinDivertSendEx(
|
||||
__in HANDLE handle,
|
||||
__in PVOID pPacket,
|
||||
__in UINT packetLen,
|
||||
__in UINT64 flags,
|
||||
__in PWINDIVERT_ADDRESS pAddr,
|
||||
__out_opt UINT *writeLen,
|
||||
__inout_opt LPOVERLAPPED lpOverlapped);
|
||||
|
||||
/*
|
||||
* Close a WinDivert handle.
|
||||
*/
|
||||
extern WINDIVERTEXPORT BOOL WinDivertClose(
|
||||
__in HANDLE handle);
|
||||
|
||||
/*
|
||||
* Set a WinDivert handle parameter.
|
||||
*/
|
||||
extern WINDIVERTEXPORT BOOL WinDivertSetParam(
|
||||
__in HANDLE handle,
|
||||
__in WINDIVERT_PARAM param,
|
||||
__in UINT64 value);
|
||||
|
||||
/*
|
||||
* Get a WinDivert handle parameter.
|
||||
*/
|
||||
extern WINDIVERTEXPORT BOOL WinDivertGetParam(
|
||||
__in HANDLE handle,
|
||||
__in WINDIVERT_PARAM param,
|
||||
__out UINT64 *pValue);
|
||||
|
||||
#endif /* WINDIVERT_KERNEL */
|
||||
|
||||
/****************************************************************************/
|
||||
/* WINDIVERT 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;
|
||||
} WINDIVERT_IPHDR, *PWINDIVERT_IPHDR;
|
||||
|
||||
#define WINDIVERT_IPHDR_GET_FRAGOFF(hdr) \
|
||||
(((hdr)->FragOff0) & 0xFF1F)
|
||||
#define WINDIVERT_IPHDR_GET_MF(hdr) \
|
||||
((((hdr)->FragOff0) & 0x0020) != 0)
|
||||
#define WINDIVERT_IPHDR_GET_DF(hdr) \
|
||||
((((hdr)->FragOff0) & 0x0040) != 0)
|
||||
#define WINDIVERT_IPHDR_GET_RESERVED(hdr) \
|
||||
((((hdr)->FragOff0) & 0x0080) != 0)
|
||||
|
||||
#define WINDIVERT_IPHDR_SET_FRAGOFF(hdr, val) \
|
||||
do \
|
||||
{ \
|
||||
(hdr)->FragOff0 = (((hdr)->FragOff0) & 0x00E0) | \
|
||||
((val) & 0xFF1F); \
|
||||
} \
|
||||
while (FALSE)
|
||||
#define WINDIVERT_IPHDR_SET_MF(hdr, val) \
|
||||
do \
|
||||
{ \
|
||||
(hdr)->FragOff0 = (((hdr)->FragOff0) & 0xFFDF) | \
|
||||
(((val) & 0x0001) << 5); \
|
||||
} \
|
||||
while (FALSE)
|
||||
#define WINDIVERT_IPHDR_SET_DF(hdr, val) \
|
||||
do \
|
||||
{ \
|
||||
(hdr)->FragOff0 = (((hdr)->FragOff0) & 0xFFBF) | \
|
||||
(((val) & 0x0001) << 6); \
|
||||
} \
|
||||
while (FALSE)
|
||||
#define WINDIVERT_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];
|
||||
} WINDIVERT_IPV6HDR, *PWINDIVERT_IPV6HDR;
|
||||
|
||||
#define WINDIVERT_IPV6HDR_GET_TRAFFICCLASS(hdr) \
|
||||
((((hdr)->TrafficClass0) << 4) | ((hdr)->TrafficClass1))
|
||||
#define WINDIVERT_IPV6HDR_GET_FLOWLABEL(hdr) \
|
||||
((((UINT32)(hdr)->FlowLabel0) << 16) | ((UINT32)(hdr)->FlowLabel1))
|
||||
|
||||
#define WINDIVERT_IPV6HDR_SET_TRAFFICCLASS(hdr, val) \
|
||||
do \
|
||||
{ \
|
||||
(hdr)->TrafficClass0 = ((UINT8)(val) >> 4); \
|
||||
(hdr)->TrafficClass1 = (UINT8)(val); \
|
||||
} \
|
||||
while (FALSE)
|
||||
#define WINDIVERT_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;
|
||||
} WINDIVERT_ICMPHDR, *PWINDIVERT_ICMPHDR;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
UINT8 Type;
|
||||
UINT8 Code;
|
||||
UINT16 Checksum;
|
||||
UINT32 Body;
|
||||
} WINDIVERT_ICMPV6HDR, *PWINDIVERT_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;
|
||||
} WINDIVERT_TCPHDR, *PWINDIVERT_TCPHDR;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
UINT16 SrcPort;
|
||||
UINT16 DstPort;
|
||||
UINT16 Length;
|
||||
UINT16 Checksum;
|
||||
} WINDIVERT_UDPHDR, *PWINDIVERT_UDPHDR;
|
||||
|
||||
#ifndef WINDIVERT_KERNEL
|
||||
|
||||
/*
|
||||
* Flags for WinDivertHelperCalcChecksums()
|
||||
*/
|
||||
#define WINDIVERT_HELPER_NO_IP_CHECKSUM 1
|
||||
#define WINDIVERT_HELPER_NO_ICMP_CHECKSUM 2
|
||||
#define WINDIVERT_HELPER_NO_ICMPV6_CHECKSUM 4
|
||||
#define WINDIVERT_HELPER_NO_TCP_CHECKSUM 8
|
||||
#define WINDIVERT_HELPER_NO_UDP_CHECKSUM 16
|
||||
|
||||
/*
|
||||
* Parse IPv4/IPv6/ICMP/ICMPv6/TCP/UDP headers from a raw packet.
|
||||
*/
|
||||
extern WINDIVERTEXPORT BOOL WinDivertHelperParsePacket(
|
||||
__in PVOID pPacket,
|
||||
__in UINT packetLen,
|
||||
__out_opt PWINDIVERT_IPHDR *ppIpHdr,
|
||||
__out_opt PWINDIVERT_IPV6HDR *ppIpv6Hdr,
|
||||
__out_opt PWINDIVERT_ICMPHDR *ppIcmpHdr,
|
||||
__out_opt PWINDIVERT_ICMPV6HDR *ppIcmpv6Hdr,
|
||||
__out_opt PWINDIVERT_TCPHDR *ppTcpHdr,
|
||||
__out_opt PWINDIVERT_UDPHDR *ppUdpHdr,
|
||||
__out_opt PVOID *ppData,
|
||||
__out_opt UINT *pDataLen);
|
||||
|
||||
/*
|
||||
* Parse an IPv4 address.
|
||||
*/
|
||||
extern WINDIVERTEXPORT BOOL WinDivertHelperParseIPv4Address(
|
||||
__in const char *addrStr,
|
||||
__out_opt UINT32 *pAddr);
|
||||
|
||||
/*
|
||||
* Parse an IPv6 address.
|
||||
*/
|
||||
extern WINDIVERTEXPORT BOOL WinDivertHelperParseIPv6Address(
|
||||
__in const char *addrStr,
|
||||
__out_opt UINT32 *pAddr);
|
||||
|
||||
/*
|
||||
* Calculate IPv4/IPv6/ICMP/ICMPv6/TCP/UDP checksums.
|
||||
*/
|
||||
extern WINDIVERTEXPORT UINT WinDivertHelperCalcChecksums(
|
||||
__inout PVOID pPacket,
|
||||
__in UINT packetLen,
|
||||
__in_opt PWINDIVERT_ADDRESS pAddr,
|
||||
__in UINT64 flags);
|
||||
|
||||
/*
|
||||
* Check the given filter string.
|
||||
*/
|
||||
extern WINDIVERTEXPORT BOOL WinDivertHelperCheckFilter(
|
||||
__in const char *filter,
|
||||
__in WINDIVERT_LAYER layer,
|
||||
__out_opt const char **errorStr,
|
||||
__out_opt UINT *errorPos);
|
||||
|
||||
/*
|
||||
* Evaluate the given filter string.
|
||||
*/
|
||||
extern WINDIVERTEXPORT BOOL WinDivertHelperEvalFilter(
|
||||
__in const char *filter,
|
||||
__in WINDIVERT_LAYER layer,
|
||||
__in PVOID pPacket,
|
||||
__in UINT packetLen,
|
||||
__in PWINDIVERT_ADDRESS pAddr);
|
||||
|
||||
#endif /* WINDIVERT_KERNEL */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __WINDIVERT_H */
|
||||
Reference in New Issue
Block a user